From a0ae35d7e71f2ba0d268c0ca912690805f15fc7a Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Mon, 6 Jan 2025 09:20:07 -0800 Subject: [PATCH 1/2] Update Go SDK and examples --- .trunk/configs/cspell.json | 2 + sdk/go/examples/textgeneration/main.go | 158 +--- sdk/go/examples/textgeneration/media.go | 207 +++++ sdk/go/examples/textgeneration/modus.json | 21 +- sdk/go/examples/textgeneration/product.go | 31 - sdk/go/examples/textgeneration/products.go | 141 +++ sdk/go/examples/textgeneration/simple.go | 55 ++ sdk/go/examples/textgeneration/toolcalling.go | 169 ++++ sdk/go/go.mod | 3 +- sdk/go/go.sum | 12 +- sdk/go/pkg/localtime/localtime.go | 6 + sdk/go/pkg/models/models.go | 4 +- sdk/go/pkg/models/openai/chat.go | 820 ++++++++++++++++-- sdk/go/pkg/utils/utils.go | 1 - 14 files changed, 1337 insertions(+), 293 deletions(-) create mode 100644 sdk/go/examples/textgeneration/media.go delete mode 100644 sdk/go/examples/textgeneration/product.go create mode 100644 sdk/go/examples/textgeneration/products.go create mode 100644 sdk/go/examples/textgeneration/simple.go create mode 100644 sdk/go/examples/textgeneration/toolcalling.go diff --git a/.trunk/configs/cspell.json b/.trunk/configs/cspell.json index 34e767ef4..6a2224284 100644 --- a/.trunk/configs/cspell.json +++ b/.trunk/configs/cspell.json @@ -134,6 +134,7 @@ "omitif", "omitnull", "openai", + "openspeech", "operationreport", "PEMS", "pgconn", @@ -175,6 +176,7 @@ "textgeneration", "tidwall", "tinygo", + "toolcalling", "tseslint", "tsrv", "typedarray", diff --git a/sdk/go/examples/textgeneration/main.go b/sdk/go/examples/textgeneration/main.go index a86947762..24359e7ae 100644 --- a/sdk/go/examples/textgeneration/main.go +++ b/sdk/go/examples/textgeneration/main.go @@ -6,154 +6,10 @@ package main -import ( - "encoding/json" - "fmt" - "strings" - - "github.com/hypermodeinc/modus/sdk/go/pkg/models" - "github.com/hypermodeinc/modus/sdk/go/pkg/models/openai" -) - -// In this example, we will generate text using the OpenAI Chat model. -// See https://platform.openai.com/docs/api-reference/chat/create for more details -// about the options available on the model, which you can set on the input object. - -// This model name should match the one defined in the modus.json manifest file. -const modelName = "text-generator" - -// This function generates some text based on the instruction and prompt provided. -func GenerateText(instruction, prompt string) (string, error) { - - // The imported ChatModel type follows the OpenAI Chat completion model input format. - model, err := models.GetModel[openai.ChatModel](modelName) - if err != nil { - return "", err - } - - // We'll start by creating an input object using the instruction and prompt provided. - input, err := model.CreateInput( - openai.NewSystemMessage(instruction), - openai.NewUserMessage(prompt), - // ... if we wanted to add more messages, we could do so here. - ) - if err != nil { - return "", err - } - - // This is one of many optional parameters available for the OpenAI Chat model. - input.Temperature = 0.7 - - // Here we invoke the model with the input we created. - output, err := model.Invoke(input) - if err != nil { - return "", err - } - - // The output is also specific to the ChatModel interface. - // Here we return the trimmed content of the first choice. - return strings.TrimSpace(output.Choices[0].Message.Content), nil -} - -// This function generates a single product. -func GenerateProduct(category string) (*Product, error) { - - // We can get creative with the instruction and prompt to guide the model - // in generating the desired output. Here we provide a sample JSON of the - // object we want the model to generate. - instruction := "Generate a product for the category provided.\n" + - "Only respond with valid JSON object in this format:\n" + sampleProductJson - prompt := fmt.Sprintf(`The category is "%s".`, category) - - // Set up the input for the model, creating messages for the instruction and prompt. - model, err := models.GetModel[openai.ChatModel](modelName) - if err != nil { - return nil, err - } - input, err := model.CreateInput( - openai.NewSystemMessage(instruction), - openai.NewUserMessage(prompt), - ) - if err != nil { - return nil, err - } - - // Let's increase the temperature to get more creative responses. - // Be careful though, if the temperature is too high, the model may generate invalid JSON. - input.Temperature = 1.2 - - // This model also has a response format parameter that can be set to JSON, - // Which, along with the instruction, can help guide the model in generating valid JSON output. - input.ResponseFormat = openai.ResponseFormatJson - - // Here we invoke the model with the input we created. - output, err := model.Invoke(input) - if err != nil { - return nil, err - } - - // The output should contain the JSON string we asked for. - content := strings.TrimSpace(output.Choices[0].Message.Content) - - // We can now parse the JSON string as a Product object. - var product Product - if err := json.Unmarshal([]byte(content), &product); err != nil { - return nil, fmt.Errorf("failed to parse JSON: %w", err) - } - - return &product, nil -} - -// This function generates multiple product. -func GenerateProducts(category string, quantity int) ([]Product, error) { - - // Similar to the previous example above, we can tailor the instruction and prompt - // to guide the model in generating the desired output. Note that understanding the behavior - // of the model is important to get the desired results. In this case, we need the model - // to return an _object_ containing an array, not an array of objects directly. - // That's because the model will not reliably generate an array of objects directly. - instruction := fmt.Sprintf("Generate %d products for the category provided.\n"+ - "Only respond with a valid JSON object containing a valid JSON array named 'list', in this format:\n"+ - `{"list":[%s]}`, quantity, sampleProductJson) - prompt := fmt.Sprintf(`The category is "%s".`, category) - - // Set up the input for the model, creating messages for the instruction and prompt. - model, err := models.GetModel[openai.ChatModel](modelName) - if err != nil { - return nil, err - } - input, err := model.CreateInput( - openai.NewSystemMessage(instruction), - openai.NewUserMessage(prompt), - ) - if err != nil { - return nil, err - } - - // Adjust the model inputs, just like in the previous example. - // Be careful, if the temperature is too high, the model may generate invalid JSON. - input.Temperature = 1.2 - input.ResponseFormat = openai.ResponseFormatJson - - // Here we invoke the model with the input we created. - output, err := model.Invoke(input) - if err != nil { - return nil, err - } - - // The output should contain the JSON string we asked for. - content := strings.TrimSpace(output.Choices[0].Message.Content) - - // We can parse that JSON to a compatible object, to get the data we're looking for. - var data map[string][]Product - if err := json.Unmarshal([]byte(content), &data); err != nil { - return nil, fmt.Errorf("failed to parse JSON: %w", err) - } - - // Now we can extract the list of products from the data. - products, found := data["list"] - if !found { - return nil, fmt.Errorf("expected 'list' key in JSON object") - } - return products, nil -} +// The examples have been split into separate files for clarity. +// See each of the following files for more details about the specific example. +// +// - simple.go +// - products.go +// - media.go +// - toolcalling.go diff --git a/sdk/go/examples/textgeneration/media.go b/sdk/go/examples/textgeneration/media.go new file mode 100644 index 000000000..b0ce403a7 --- /dev/null +++ b/sdk/go/examples/textgeneration/media.go @@ -0,0 +1,207 @@ +/* + * This example is part of the Modus project, licensed under the Apache License 2.0. + * You may modify and use this example in accordance with the license. + * See the LICENSE file that accompanied this code for further details. + */ + +package main + +import ( + "fmt" + "math/rand/v2" + "strings" + + "github.com/hypermodeinc/modus/sdk/go/pkg/http" + "github.com/hypermodeinc/modus/sdk/go/pkg/models" + "github.com/hypermodeinc/modus/sdk/go/pkg/models/openai" +) + +// These examples demonstrate how to use audio or image data with OpenAI chat models. +// Currently, audio can be used for input or output, but images can be used only for input. + +// This type is used in these examples to represent images or audio. +type Media struct { + + // The content type of the media. + ContentType string + + // The binary data of the media. + // This value will be base64 encoded when used in an API response. + Data []byte + + // A text description or transcription of the media. + Text string +} + +// This function generates an audio response based on the instruction and prompt provided. +func GenerateAudio(instruction, prompt string) (*Media, error) { + + // Note, this is similar to GenerateText above, but with audio output requested. + + // We'll generate the audio using an audio-enabled OpenAI chat model. + model, err := models.GetModel[openai.ChatModel]("audio-model") + if err != nil { + return nil, err + } + + input, err := model.CreateInput( + openai.NewSystemMessage(instruction), + openai.NewUserMessage(prompt), + ) + if err != nil { + return nil, err + } + + input.Temperature = 0.7 + + // Request audio output from the model. + // Note, this is a convenience method that requests audio modality and sets the voice and format. + // You can also set these values manually on the input object, if you prefer. + input.RequestAudioOutput("ash", "wav") + + output, err := model.Invoke(input) + if err != nil { + return nil, err + } + + // Return the audio and its transcription. + // Note that the message Content field will be empty for audio responses. + // Instead, the text will be in the Message.Audio.Transcript field. + audio := output.Choices[0].Message.Audio + + media := &Media{ + ContentType: "audio/wav", + Data: audio.Data, + Text: strings.TrimSpace(audio.Transcript), + } + + return media, nil +} + +// This function generates text that describes the image at the provided url. +// In this example the image url is passed to the model, and the model retrieves the image. +func DescribeImage(url string) (string, error) { + + // Note that because the model retrieves the image, any URL can be used. + // However, this means that there is a risk of sending data to an unauthorized host, if the URL is not hardcoded or sanitized. + // See the DescribeRandomImage function below for a safer approach. + + model, err := models.GetModel[openai.ChatModel]("text-generator") + if err != nil { + return "", err + } + + input, err := model.CreateInput( + openai.NewUserMessageFromParts( + openai.NewTextContentPart("Describe this image."), + openai.NewImageContentPartFromUrl(url), + ), + ) + if err != nil { + return "", err + } + + output, err := model.Invoke(input) + if err != nil { + return "", err + } + + return strings.TrimSpace(output.Choices[0].Message.Content), nil +} + +// This function fetches a random image, and then generates text that describes it. +// In this example the image is retrieved by the function before passing it as data to the model. +func DescribeRandomImage() (*Media, error) { + + // Because this approach fetches the image directly, it is safer than the DescribeImage function above. + // The host URL is allow-listed in the modus.json file, so we can trust the image source. + + // Fetch a random image from the Picsum API. We'll just hardcode the size to make the demo simple to call. + response, err := http.Fetch("https://picsum.photos/640/480") + if err != nil { + return nil, err + } + data := response.Body + contentType := *response.Headers.Get("Content-Type") + + // Describe the image using the OpenAI chat model. + model, err := models.GetModel[openai.ChatModel]("text-generator") + if err != nil { + return nil, err + } + + input, err := model.CreateInput( + openai.NewUserMessageFromParts( + openai.NewTextContentPart("Describe this image."), + openai.NewImageContentPartFromData(data, contentType), + ), + ) + if err != nil { + return nil, err + } + + output, err := model.Invoke(input) + if err != nil { + return nil, err + } + + // Return the image and its generated description. + text := strings.TrimSpace(output.Choices[0].Message.Content) + media := &Media{ + ContentType: contentType, + Data: data, + Text: text, + } + + return media, nil +} + +// This function fetches a random "Harvard Sentences" speech file from OpenSpeech, and then generates a transcript from it. +// The sentences are from https://www.cs.columbia.edu/~hgs/audio/harvard.html +func TranscribeRandomSpeech() (*Media, error) { + + // Pick a random file number from the list of available here: + // https://www.voiptroubleshooter.com/open_speech/american.html + numbers := []int{10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 30, 31, 32, 34, 35, 36, 37, 38, 39, 40, 57, 58, 59, 60, 61} + num := numbers[rand.IntN(len(numbers))] + + // Fetch the speech file corresponding to the number. + url := fmt.Sprintf("https://www.voiptroubleshooter.com/open_speech/american/OSR_us_000_%04d_8k.wav", num) + response, err := http.Fetch(url) + if err != nil { + return nil, err + } + data := response.Body + + // Transcribe the audio using an audio-enabled OpenAI chat model. + model, err := models.GetModel[openai.ChatModel]("audio-model") + if err != nil { + return nil, err + } + + input, err := model.CreateInput( + openai.NewDeveloperMessage("Do not include any newlines or surrounding quotation marks in the response. Omit any explanation beyond the request."), + openai.NewUserMessageFromParts( + openai.NewTextContentPart("Provide an exact transcription of the contents of this audio file."), + openai.NewAudioContentPartFromData(data, "wav"), + ), + ) + if err != nil { + return nil, err + } + + output, err := model.Invoke(input) + if err != nil { + return nil, err + } + + // Return the audio file and its transcript. + text := strings.TrimSpace(output.Choices[0].Message.Content) + media := &Media{ + ContentType: "audio/wav", + Data: data, + Text: text, + } + + return media, nil +} diff --git a/sdk/go/examples/textgeneration/modus.json b/sdk/go/examples/textgeneration/modus.json index e8f4b6717..2b457dd25 100644 --- a/sdk/go/examples/textgeneration/modus.json +++ b/sdk/go/examples/textgeneration/modus.json @@ -8,9 +8,15 @@ } }, "models": { - // This defines the model that will be used for text generation. + // This defines the models that will be used for text generation. "text-generator": { - "sourceModel": "gpt-3.5-turbo", + "sourceModel": "gpt-4o-mini", + "connection": "openai", + "path": "v1/chat/completions" + }, + // This defines the models that will be used for audio input and output. + "audio-model": { + "sourceModel": "gpt-4o-audio-preview", "connection": "openai", "path": "v1/chat/completions" } @@ -24,6 +30,17 @@ "headers": { "Authorization": "Bearer {{API_KEY}}" } + }, + + // These are some additional hosts that we use in the examples + // for fetching images and audio files. + "picsum": { + "type": "http", + "baseUrl": "https://picsum.photos/" + }, + "openspeech": { + "type": "http", + "baseUrl": "https://www.voiptroubleshooter.com/open_speech/" } } } diff --git a/sdk/go/examples/textgeneration/product.go b/sdk/go/examples/textgeneration/product.go deleted file mode 100644 index 7c56aa3bf..000000000 --- a/sdk/go/examples/textgeneration/product.go +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This example is part of the Modus project, licensed under the Apache License 2.0. - * You may modify and use this example in accordance with the license. - * See the LICENSE file that accompanied this code for further details. - */ - -package main - -import "github.com/hypermodeinc/modus/sdk/go/pkg/utils" - -// The Product struct and the sample product will be used in the some of the examples. - -type Product struct { - Id string `json:"id,omitempty"` - Name string `json:"name"` - Price float64 `json:"price"` - Description string `json:"description"` -} - -var sampleProduct = Product{ - Id: "123", - Name: "Shoes", - Price: 50.0, - Description: "Great shoes for walking.", -} - -// since we'll be using this sample data often, we'll serialize it once and store it as a string. -var sampleProductJson string = func() string { - bytes, _ := utils.JsonSerialize(sampleProduct) - return string(bytes) -}() diff --git a/sdk/go/examples/textgeneration/products.go b/sdk/go/examples/textgeneration/products.go new file mode 100644 index 000000000..994b27c56 --- /dev/null +++ b/sdk/go/examples/textgeneration/products.go @@ -0,0 +1,141 @@ +/* + * This example is part of the Modus project, licensed under the Apache License 2.0. + * You may modify and use this example in accordance with the license. + * See the LICENSE file that accompanied this code for further details. + */ + +package main + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/hypermodeinc/modus/sdk/go/pkg/models" + "github.com/hypermodeinc/modus/sdk/go/pkg/models/openai" + "github.com/hypermodeinc/modus/sdk/go/pkg/utils" +) + +// In this example, we'll demonstrate how to generated structured data using OpenAI chat models. +// We'll be asking the model to populate Product objects with data that fits a particular category. +// The data the model generates will need to conform to the schema of the Product object. + +type Product struct { + Id string `json:"id,omitempty"` + Name string `json:"name"` + Price float64 `json:"price"` + Description string `json:"description"` +} + +var sampleProductJson string = func() string { + bytes, _ := utils.JsonSerialize(Product{ + Id: "123", + Name: "Shoes", + Price: 50.0, + Description: "Great shoes for walking.", + }) + return string(bytes) +}() + +// This function generates a single product. +func GenerateProduct(category string) (*Product, error) { + + // We can get creative with the instruction and prompt to guide the model + // in generating the desired output. Here we provide a sample JSON of the + // object we want the model to generate. + instruction := "Generate a product for the category provided.\n" + + "Only respond with valid JSON object in this format:\n" + sampleProductJson + prompt := fmt.Sprintf(`The category is "%s".`, category) + + // Set up the input for the model, creating messages for the instruction and prompt. + model, err := models.GetModel[openai.ChatModel]("text-generator") + if err != nil { + return nil, err + } + input, err := model.CreateInput( + openai.NewSystemMessage(instruction), + openai.NewUserMessage(prompt), + ) + if err != nil { + return nil, err + } + + // Let's increase the temperature to get more creative responses. + // Be careful though, if the temperature is too high, the model may generate invalid JSON. + input.Temperature = 1.2 + + // This model also has a response format parameter that can be set to JSON, + // Which, along with the instruction, can help guide the model in generating valid JSON output. + input.ResponseFormat = openai.ResponseFormatJson + + // Here we invoke the model with the input we created. + output, err := model.Invoke(input) + if err != nil { + return nil, err + } + + // The output should contain the JSON string we asked for. + content := strings.TrimSpace(output.Choices[0].Message.Content) + + // We can now parse the JSON string as a Product object. + var product Product + if err := json.Unmarshal([]byte(content), &product); err != nil { + return nil, fmt.Errorf("failed to parse JSON: %w", err) + } + + return &product, nil +} + +// This function generates multiple product. +func GenerateProducts(category string, quantity int) ([]Product, error) { + + // Similar to the generateText example, we can tailor the instruction and prompt + // to guide the model in generating the desired output. Note that understanding the behavior + // of the model is important to get the desired results. In this case, we need the model + // to return an _object_ containing an array, not an array of objects directly. + // That's because the model will not reliably generate an array of objects directly. + instruction := fmt.Sprintf("Generate %d products for the category provided.\n"+ + "Only respond with a valid JSON object containing a valid JSON array named 'list', in this format:\n"+ + `{"list":[%s]}`, quantity, sampleProductJson) + prompt := fmt.Sprintf(`The category is "%s".`, category) + + // Set up the input for the model, creating messages for the instruction and prompt. + model, err := models.GetModel[openai.ChatModel]("text-generator") + if err != nil { + return nil, err + } + input, err := model.CreateInput( + openai.NewSystemMessage(instruction), + openai.NewUserMessage(prompt), + ) + if err != nil { + return nil, err + } + + // Adjust the model inputs, just like in the previous example. + // Be careful, if the temperature is too high, the model may generate invalid JSON. + input.Temperature = 1.2 + input.ResponseFormat = openai.ResponseFormatJson + + // Here we invoke the model with the input we created. + output, err := model.Invoke(input) + if err != nil { + return nil, err + } + + // The output should contain the JSON string we asked for. + content := strings.TrimSpace(output.Choices[0].Message.Content) + + // We can parse that JSON to a compatible object, to get the data we're looking for. + var data map[string][]Product + if err := json.Unmarshal([]byte(content), &data); err != nil { + return nil, fmt.Errorf("failed to parse JSON: %w", err) + } + + // Now we can extract the list of products from the data. + products, found := data["list"] + if !found { + return nil, fmt.Errorf("expected 'list' key in JSON object") + } + return products, nil +} diff --git a/sdk/go/examples/textgeneration/simple.go b/sdk/go/examples/textgeneration/simple.go new file mode 100644 index 000000000..b61eb903e --- /dev/null +++ b/sdk/go/examples/textgeneration/simple.go @@ -0,0 +1,55 @@ +/* + * This example is part of the Modus project, licensed under the Apache License 2.0. + * You may modify and use this example in accordance with the license. + * See the LICENSE file that accompanied this code for further details. + */ + +package main + +import ( + "strings" + + "github.com/hypermodeinc/modus/sdk/go/pkg/models" + "github.com/hypermodeinc/modus/sdk/go/pkg/models/openai" +) + +// In this example, we will generate text and other content using OpenAI chat models. +// See https://platform.openai.com/docs/api-reference/chat/create for more details +// about the options available on the model, which you can set on the input object. + +// For more advanced examples see: +// - toolcalling.go for using tools with the model +// - media.go for using audio or image content with the model + +// This function generates some text based on the instruction and prompt provided. +func GenerateText(instruction, prompt string) (string, error) { + + // The imported ChatModel type follows the OpenAI Chat completion model input format. + model, err := models.GetModel[openai.ChatModel]("text-generator") + if err != nil { + return "", err + } + + // We'll start by creating an input object using the instruction and prompt provided. + input, err := model.CreateInput( + openai.NewSystemMessage(instruction), + openai.NewUserMessage(prompt), + // ... if we wanted to add more messages, we could do so here. + ) + if err != nil { + return "", err + } + + // This is one of many optional parameters available for the OpenAI chat model. + input.Temperature = 0.7 + + // Here we invoke the model with the input we created. + output, err := model.Invoke(input) + if err != nil { + return "", err + } + + // The output is also specific to the ChatModel interface. + // Here we return the trimmed content of the first choice. + return strings.TrimSpace(output.Choices[0].Message.Content), nil +} diff --git a/sdk/go/examples/textgeneration/toolcalling.go b/sdk/go/examples/textgeneration/toolcalling.go new file mode 100644 index 000000000..62816f385 --- /dev/null +++ b/sdk/go/examples/textgeneration/toolcalling.go @@ -0,0 +1,169 @@ +/* + * This example is part of the Modus project, licensed under the Apache License 2.0. + * You may modify and use this example in accordance with the license. + * See the LICENSE file that accompanied this code for further details. + */ + +package main + +import ( + "errors" + "fmt" + "os" + "strings" + "time" + + "github.com/hypermodeinc/modus/sdk/go/pkg/localtime" + "github.com/hypermodeinc/modus/sdk/go/pkg/models" + "github.com/hypermodeinc/modus/sdk/go/pkg/models/openai" + "github.com/tidwall/gjson" +) + +// This function generates some text from the model, which has already been given some tools to call, +// and instructions on how to use them. The tools allow the model to answer time-related questions, +// such as "What time is it?", or "What time is it in New York?". +func GenerateTextWithTools(prompt string) (string, error) { + + model, err := models.GetModel[openai.ChatModel]("text-generator") + if err != nil { + return "", err + } + + instruction := ` + You are a helpful assistant that understands time in various parts of the world. + Answer the user's question as directly as possible. If you need more information, ask for it. + When stating a time zone to the user, either use a descriptive name such as "Pacific Time" or use the location's name. + If asked for a time in a location, and the given location has multiple time zones, return the time in each of them. + Make your answers as brief as possible. + ` + + input, err := model.CreateInput( + openai.NewSystemMessage(instruction), + openai.NewUserMessage(prompt), + ) + if err != nil { + return "", err + } + + // Let's turn the temperature down a bit, to make the model's responses more predictable. + input.Temperature = 0.2 + + // This is how you make tools available to the model. + // Each tool is specified by the name of the function to call, and a description of what it does. + // + // If the function requires parameters, you can specify simple parameters individually with the WithParameter method, + // or you can pass a JSON Schema string directly to the WithParametersSchema method. + // + // NOTE: A future release of Modus may further simplify this process. + input.Tools = []openai.Tool{ + openai.NewToolForFunction("getUserTimeZone", "Returns the user's current IANA time zone."), + openai.NewToolForFunction("getCurrentTimeInUserTimeZone", "Gets the current date and time in the user's time zone. Returns the user's IANA time zone, and the time as an ISO 8601 string."), + openai.NewToolForFunction("getCurrentTime", "Returns the current date and time in the specified IANA time zone as an ISO 8601 string."). + WithParameter("tz", "string", `An IANA time zone identifier such as "America/New_York".`), + } + + // To use the tools, you'll need to have a "conversation loop" like this one. + // The model may be called multiple times, depending on the complexity of the conversation and the tools used. + for { + output, err := model.Invoke(input) + if err != nil { + return "", err + } + + msg := output.Choices[0].Message + + // If the model requested tools to be called, we'll need to do that before continuing. + if len(msg.ToolCalls) > 0 { + + // First, add the model's response to the conversation as an "Assistant Message". + input.Messages = append(input.Messages, msg.ToAssistantMessage()) + + // Then call the tools the model requested. Add each result to the conversation as a "Tool Message". + for _, tc := range msg.ToolCalls { + + // The model will tell us which tool to call, and what arguments to pass to it. + // We'll need to parse the function name and arguments in order to call the appropriate function. + // + // If the function errors (for example, due to invalid input), we can either continue + // the conversation by providing the model with the error message (as shown here), + // or we can end the conversation by returning the error directly. + // + // NOTE: A future release of Modus may simplify this process. + var toolMsg *openai.ToolMessage[string] + switch tc.Function.Name { + + case "getCurrentTime": + tz := gjson.Get(tc.Function.Arguments, "tz").Str + if result, err := getCurrentTime(tz); err == nil { + toolMsg = openai.NewToolMessage(result, tc.Id) + } else { + toolMsg = openai.NewToolMessage(err, tc.Id) + } + + case "getUserTimeZone": + timeZone := getUserTimeZone() + toolMsg = openai.NewToolMessage(timeZone, tc.Id) + + case "getCurrentTimeInUserTimeZone": + if result, err := getCurrentTimeInUserTimeZone(); err == nil { + toolMsg = openai.NewToolMessage(result, tc.Id) + } else { + toolMsg = openai.NewToolMessage(err, tc.Id) + } + + default: + return "", fmt.Errorf("Unknown tool call: %s", tc.Function.Name) + } + + // Add the tool's response to the conversation. + input.Messages = append(input.Messages, toolMsg) + } + + } else if msg.Content != "" { + // return the model's final response to the user. + return strings.TrimSpace(msg.Content), nil + } else { + // If the model didn't ask for tools, and didn't return any content, something went wrong. + return "", errors.New("Invalid response from model.") + } + } +} + +// The following functions are made available as "tools" +// for the model to call in the example above. + +// This function will return the current time in a given time zone. +func getCurrentTime(tz string) (string, error) { + if !localtime.IsValidTimeZone(tz) { + return "", errors.New("Invalid time zone.") + } + + now, err := localtime.NowInZone(tz) + if err != nil { + return "", err + } + return now.Format(time.RFC3339), nil +} + +// This function will return the default time zone for the user. +func getUserTimeZone() string { + tz := os.Getenv("TZ") + if tz == "" { + return "(unknown)" + } + return tz +} + +// This function combines the functionality of getCurrentTime and getUserTimeZone, +// to reduce the number of tool calls required by the model. +func getCurrentTimeInUserTimeZone() (*struct{ Time, Zone string }, error) { + tz := os.Getenv("TZ") + if tz == "" { + return nil, errors.New("Cannot determine user's time zone.") + } + time, err := getCurrentTime(tz) + if err != nil { + return nil, err + } + return &struct{ Time, Zone string }{Time: time, Zone: tz}, nil +} diff --git a/sdk/go/go.mod b/sdk/go/go.mod index e1173e74b..0ffbf0651 100644 --- a/sdk/go/go.mod +++ b/sdk/go/go.mod @@ -13,6 +13,7 @@ require ( github.com/fatih/color v1.18.0 github.com/hashicorp/go-version v1.7.0 github.com/rs/xid v1.6.0 + github.com/tidwall/gjson v1.18.0 github.com/tidwall/sjson v1.2.5 golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 golang.org/x/mod v0.22.0 @@ -22,9 +23,7 @@ require ( require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect - github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/jsonc v0.3.2 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect diff --git a/sdk/go/go.sum b/sdk/go/go.sum index f95105a1e..9ffaaf654 100644 --- a/sdk/go/go.sum +++ b/sdk/go/go.sum @@ -1,11 +1,11 @@ +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hypermodeinc/modus/lib/manifest v0.16.0 h1:O40na0MUM7UulFOXcuWw1cOH5C77+xSSUPHW4gHj9k4= -github.com/hypermodeinc/modus/lib/manifest v0.16.0/go.mod h1:ymRlTZWerFnIUVpvEonTMTo38EDYzHcGSNYTOv2MITk= github.com/hypermodeinc/modus/lib/manifest v0.16.1 h1:/37OgLlRhn9UNxNChzCUSDNNUqWHyRliPxQ1EoeQrRM= github.com/hypermodeinc/modus/lib/manifest v0.16.1/go.mod h1:NaG6aE+ekaufwqblbd70t/s1urmAQjNPL1nB4YhM27E= github.com/hypermodeinc/modus/lib/wasmextractor v0.13.0 h1:9o8qqAllL9qIPYqc5adF+Aw3XWLmLqPiBPMu0AIyiMI= @@ -17,8 +17,6 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= -github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw= github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -33,8 +31,6 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= -golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= @@ -43,13 +39,9 @@ golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= -golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= diff --git a/sdk/go/pkg/localtime/localtime.go b/sdk/go/pkg/localtime/localtime.go index 331bc382d..3b067e679 100644 --- a/sdk/go/pkg/localtime/localtime.go +++ b/sdk/go/pkg/localtime/localtime.go @@ -61,3 +61,9 @@ func GetLocation(tz string) (*time.Location, error) { } return hostGetTimeLocation(tz) } + +// IsValidTimeZone returns true if the given time zone is valid. +func IsValidTimeZone(tz string) bool { + _, err := hostGetTimeInZone(tz) + return err == nil +} diff --git a/sdk/go/pkg/models/models.go b/sdk/go/pkg/models/models.go index 5fe988c44..bedbd0884 100644 --- a/sdk/go/pkg/models/models.go +++ b/sdk/go/pkg/models/models.go @@ -85,7 +85,7 @@ func (m ModelBase[TIn, TOut]) Invoke(input *TIn) (*TOut, error) { } if m.Debug { - console.Logf("Invoking model %s with input: %s", modelName, inputJson) + console.Debugf("Invoking model %s with input: %s", modelName, inputJson) } sInputJson := string(inputJson) @@ -95,7 +95,7 @@ func (m ModelBase[TIn, TOut]) Invoke(input *TIn) (*TOut, error) { } if m.Debug { - console.Logf("Received output for model %s: %s", modelName, *sOutputJson) + console.Debugf("Received output for model %s: %s", modelName, *sOutputJson) } var result TOut diff --git a/sdk/go/pkg/models/openai/chat.go b/sdk/go/pkg/models/openai/chat.go index 8a65e6c9f..d8994d3f8 100644 --- a/sdk/go/pkg/models/openai/chat.go +++ b/sdk/go/pkg/models/openai/chat.go @@ -10,10 +10,15 @@ package openai import ( + "bytes" + "encoding/base64" "encoding/json" + "strings" + "time" "github.com/hypermodeinc/modus/sdk/go/pkg/models" "github.com/hypermodeinc/modus/sdk/go/pkg/utils" + "github.com/tidwall/gjson" "github.com/tidwall/sjson" ) @@ -37,7 +42,15 @@ type ChatModelInput struct { Model string `json:"model"` // The list of messages to send to the chat model. - Messages []Message `json:"messages"` + Messages []RequestMessage `json:"messages"` + + // Output types that you want the model to generate. + // Text modality is implied if no modalities are specified. + Modalities []Modality `json:"modalities,omitempty"` + + // Parameters for audio output. + // Required when audio output is requested in the modalities field. + Audio *AudioParameters `json:"audio,omitempty"` // Number between -2.0 and 2.0. // @@ -65,8 +78,15 @@ type ChatModelInput struct { // The maximum number of tokens to generate in the chat completion. // // The default (0) is equivalent to 4096. + // + // Deprecated: Use the MaxCompletionTokens parameter instead, unless the model specifically requires passing "max_tokens". MaxTokens int `json:"max_tokens,omitempty"` + // The maximum number of tokens to generate in the chat completion. + // + // The default (0) is equivalent to 4096. + MaxCompletionTokens int `json:"max_completion_tokens,omitempty"` + // The number of completions to generate for each prompt. // // The default (0) is equivalent to 1. @@ -97,11 +117,14 @@ type ChatModelInput struct { Seed int `json:"seed,omitempty"` // Specifies the latency tier to use for processing the request. - // - ServiceTierAuto utilizes scale tier credits until they are exhausted. - // - ServiceTierDefault processes the request using the default OpenAI service - // tier with a lower uptime SLA and no latency guarantee. + // This is relevant for customers subscribed to the scale tier service of the model hosting platform. // - // The default is ServiceTierAuto. + // - If set to 'ServiceTierAuto', and the Project is Scale tier enabled, the system will utilize scale tier credits until they are exhausted. + // - If set to 'ServiceTierAuto', and the Project is not Scale tier enabled, the request will be processed using the default service tier with a lower uptime SLA and no latency guarantee. + // - If set to 'ServiceTierDefault', the request will be processed using the default service tier with a lower uptime SLA and no latency guarantee. + // - When not set, the default behavior is 'ServiceTierAuto'. + // + // When this parameter is set, the response `serviceTier` property will indicate the service tier utilized. ServiceTier ServiceTier `json:"service_tier,omitempty"` // Up to 4 sequences where the API will stop generating further tokens. @@ -153,6 +176,53 @@ type ChatModelInput struct { User string `json:"user,omitempty"` } +// A type that represents the modality of the chat. +type Modality string + +const ( + // Text modality requests the model to respond with text. + // This is the default if no other modality is requested. + ModalityText Modality = "text" + + // Audio modality requests the model to respond with spoken audio. + // The model and host must support audio output for this to work. + // Most models that support audio require both text and audio modalities to be specified, + // but the text will come as a transcript in the audio response. + ModalityAudio Modality = "audio" +) + +// Parameters for audio output. +// Required when audio output is requested in the modalities field. +type AudioParameters struct { + + // The voice the model should use for audio output, such as "ash" or "ballad". + // See the model's documentation for a list of all supported voices. + Voice string `json:"voice"` + + // The format of the audio data, such as "wav" or "mp3". + // See the model's documentation for a list of all supported formats. + Format string `json:"format"` +} + +// Creates a new audio output parameters object. +// - The voice parameter is the voice the model should use for audio output, such as "ash" or "ballad". +// - The format parameter is the format of the audio data, such as "wav" or "mp3". +// +// See the model's documentation for a list of all supported voices and formats. +func NewAudioParameters(voice, format string) *AudioParameters { + return &AudioParameters{Voice: voice, Format: format} +} + +// Requests audio modality and sets the audio parameters for the input object. +// - The voice parameter is the voice the model should use for audio output, such as "ash" or "ballad". +// - The format parameter is the format of the audio data, such as "wav" or "mp3". +// +// See the model's documentation for a list of all supported voices and formats. +func (ci *ChatModelInput) RequestAudioOutput(voice, format string) { + ci.Modalities = []Modality{ModalityText, ModalityAudio} + ci.Audio = NewAudioParameters(voice, format) +} + // An object specifying which tool the model should call. type ToolChoice struct { @@ -184,6 +254,7 @@ var ( } ) +// Implements the json.Marshaler interface to serialize the ToolChoice object. func (tc ToolChoice) MarshalJSON() ([]byte, error) { if tc.Type != "function" { return json.Marshal(tc.Type) @@ -206,8 +277,8 @@ type ChatModelOutput struct { // A list of chat completion choices. Can be more than one if n is greater than 1 in the input options. Choices []Choice `json:"choices"` - // The Unix timestamp (in seconds) of when the chat completion was created. - Created int `json:"created"` + // The timestamp of when the chat completion was created. + Created time.Time `json:"created"` // The name of the model used to generate the chat. // In most cases, this will match the requested model field in the input. @@ -228,112 +299,643 @@ type ChatModelOutput struct { Usage Usage `json:"usage"` } -// An interface to any message object. -type Message interface { - isMessage() +// Implements the json.Unmarshaler interface to deserialize the ChatModelOutput object. +func (o *ChatModelOutput) UnmarshalJSON(data []byte) error { + type alias ChatModelOutput + aux := &struct { + *alias + Created int64 `json:"created"` + }{ + alias: (*alias)(o), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + o.Created = time.Unix(aux.Created, 0).UTC() + return nil } -// The base implementation for all MessageBase objects. -type MessageBase struct { +// An interface to any request message. +type RequestMessage interface { + json.Marshaler // The role of the author of this message. - Role string `json:"role"` + Role() string +} + +// An interface for a message content part. +type ContentPart interface { + isContentPart() + + // The type of the content part. + Type() string +} + +// An interface for a system message content part. +type SystemContentPart interface { + ContentPart + isSystemMessageContentPart() +} + +// An interface for a developer message content part. +type DeveloperContentPart = SystemContentPart + +// An interface for a user message content part. +type UserMessageContentPart interface { + ContentPart + isUserMessageContentPart() +} + +// An interface for an assistant message content part. +type AssistantMessageContentPart interface { + ContentPart + isAssistantMessageContentPart() +} + +// An interface for a tool content part. +type ToolMessageContentPart interface { + ContentPart + isToolMessageContentPart() +} + +func (*TextContentPart) isContentPart() {} +func (*TextContentPart) isSystemMessageContentPart() {} +func (*TextContentPart) isUserMessageContentPart() {} +func (*TextContentPart) isAssistantMessageContentPart() {} +func (*TextContentPart) isToolMessageContentPart() {} + +func (*ImageContentPart) isContentPart() {} +func (*ImageContentPart) isUserMessageContentPart() {} + +func (*AudioContentPart) isContentPart() {} +func (*AudioContentPart) isUserMessageContentPart() {} + +func (*RefusalContentPart) isContentPart() {} +func (*RefusalContentPart) isAssistantMessageContentPart() {} + +// The type of this content part, in this case "text". +func (*TextContentPart) Type() string { return "text" } + +// The type of this content part, in this case "image_url". +func (*ImageContentPart) Type() string { return "image_url" } + +// The type of this content part, in this case "input_audio". +func (*AudioContentPart) Type() string { return "input_audio" } + +// The type of this content part, in this case "refusal". +func (*RefusalContentPart) Type() string { return "refusal" } + +// A text content part. +type TextContentPart struct { + // The text string. + Text string +} + +// Implements the json.Marshaler interface to serialize the TextContentPart object. +func (m TextContentPart) MarshalJSON() ([]byte, error) { + buf := bytes.NewBufferString(`{"type":"text","text":`) + buf.Write(gjson.AppendJSONString(nil, m.Text)) + buf.WriteByte('}') + return buf.Bytes(), nil +} + +// An image content part. +type ImageContentPart struct { + // The image information. + Image Image +} + +// Implements the json.Marshaler interface to serialize the ImageContentPart object. +func (m ImageContentPart) MarshalJSON() ([]byte, error) { + buf := bytes.NewBufferString(`{"type":"image_url","image_url":`) + + if b, err := utils.JsonSerialize(m.Image); err != nil { + return nil, err + } else { + buf.Write(b) + } + + buf.WriteByte('}') + return buf.Bytes(), nil +} + +// An audio content part. +type AudioContentPart struct { + // The audio information. + Audio Audio +} + +// Implements the json.Marshaler interface to serialize the AudioContentPart object. +func (m AudioContentPart) MarshalJSON() ([]byte, error) { + buf := bytes.NewBufferString(`{"type":"input_audio","input_audio":`) + + if b, err := utils.JsonSerialize(m.Audio); err != nil { + return nil, err + } else { + buf.Write(b) + } + + buf.WriteByte('}') + return buf.Bytes(), nil +} + +// A refusal content part. +type RefusalContentPart struct { + // The refusal message generated by the model. + Refusal string +} + +// Implements the json.Marshaler interface to serialize the RefusalContentPart object. +func (m RefusalContentPart) MarshalJSON() ([]byte, error) { + buf := bytes.NewBufferString(`{"type":"refusal","refusal":`) + buf.Write(gjson.AppendJSONString(nil, m.Refusal)) + buf.WriteByte('}') + return buf.Bytes(), nil +} + +// An image object, used to represent an image in a content part. +type Image struct { + + // The URL of the image. + Url string `json:"url"` + + // An optional detail string for the image. + // Can be set to "low", "high", or "auto". + // The default is "auto". + Detail string `json:"detail,omitempty"` +} + +// An audio object, used to represent audio in a content part. +type Audio struct { + + // The raw audio data. + Data []byte `json:"data"` + + // The format of the audio data, such as "wav" or "mp3". + // The format must be a valid audio format supported by the model. + Format string `json:"format"` +} + +// Creates a new text content part. +func NewTextContentPart(text string) *TextContentPart { + return &TextContentPart{Text: text} +} + +// Creates a new image content part from a URL. +// The model must support image input for this to work. +// The URL will be sent directly to the model. +// The detail parameter is optional and can be set to "low", "high", or "auto". +func NewImageContentPartFromUrl(url string, detail ...string) *ImageContentPart { + img := Image{Url: url} + if len(detail) > 0 { + img.Detail = detail[0] + } + return &ImageContentPart{Image: img} +} + +// Creates a new image content part from raw image data. +// The model must support image input for this to work. +// The contentType parameter must be a valid image MIME type supported by the model, such as "image/jpeg" or "image/png". +// The detail parameter is optional and can be set to "low", "high", or "auto". +func NewImageContentPartFromData(data []byte, contentType string, detail ...string) *ImageContentPart { + // Unlike audio, the url needs to contain a full mime type, not just the format. + // Thus, add the "image/" prefix if it's missing. + if !strings.HasPrefix(contentType, "image/") { + contentType = "image/" + contentType + } + + b64 := base64.StdEncoding.EncodeToString(data) + url := "data:" + contentType + ";base64," + b64 + + img := Image{Url: url} + if len(detail) > 0 { + img.Detail = detail[0] + } + return &ImageContentPart{Image: img} +} + +// Creates a new audio content part from raw audio data. +// The model must support audio input for this to work. +// The format parameter must be a valid audio format supported by the model, such as "wav" or "mp3". +func NewAudioContentPartFromData(data []byte, format string) *AudioContentPart { + + // Unlike images, the model expects just the format, not a mime type. + // Thus, strip the "audio/" prefix if present. + format, _ = strings.CutPrefix(format, "audio/") + + audio := Audio{Data: data, Format: format} + return &AudioContentPart{Audio: audio} +} + +// Creates a new refusal content part. +func NewRefusalContentPart(refusal string) *RefusalContentPart { + return &RefusalContentPart{Refusal: refusal} +} + +// A system message. +// System messages are used to provide setup instructions to the model. +// +// Note that system and developer messages are identical in functionality, +// but the "system" role was renamed to "developer" in the OpenAI Chat API. +// Certain models may require one or the other, so use the type that matches the model's requirements. +type SystemMessage[T string | []SystemContentPart] struct { // The content of the message. - Content string `json:"content"` + Content T + + // An optional name for the participant. + // Provides the model information to differentiate between participants of the same role. + Name string } -func (m *MessageBase) isMessage() {} +// Creates a new system message object. +// +// Note that system and developer messages are identical in functionality, +// but the "system" role was renamed to "developer" in the OpenAI Chat API. +// Certain models may require one or the other, so use the type that matches the model's requirements. +func NewSystemMessage[T string | []SystemContentPart](content T) *SystemMessage[T] { + return &SystemMessage[T]{ + Content: content, + } +} -// Adds a Name for the participant to the base message. -type participantMessage struct { - MessageBase +// Creates a new system message object from multiple content parts. +// +// Note that system and developer messages are identical in functionality, +// but the "system" role was renamed to "developer" in the OpenAI Chat API. +// Certain models may require one or the other, so use the type that matches the model's requirements. +func NewSystemMessageFromParts(parts ...SystemContentPart) *SystemMessage[[]SystemContentPart] { + return NewSystemMessage(parts) +} + +// The role of the author of this message, in this case "system". +func (m *SystemMessage[T]) Role() string { return "system" } + +// Implements the json.Marshaler interface to serialize the SystemMessage object. +func (m SystemMessage[T]) MarshalJSON() ([]byte, error) { + buf := bytes.NewBufferString(`{"role":"system","content":`) + + if b, err := utils.JsonSerialize(m.Content); err != nil { + return nil, err + } else { + buf.Write(b) + } + + if m.Name != "" { + buf.WriteString(`,"name":`) + buf.Write(gjson.AppendJSONString(nil, m.Name)) + } + + buf.WriteByte('}') + + return buf.Bytes(), nil +} + +// A developer message. +// Developer messages are used to provide setup instructions to the model. +// +// Note that system and developer messages are identical in functionality, +// but the "system" role was renamed to "developer" in the OpenAI Chat API. +// Certain models may require one or the other, so use the type that matches the model's requirements. +type DeveloperMessage[T string | []DeveloperContentPart] struct { + + // The content of the message. + Content T // An optional name for the participant. // Provides the model information to differentiate between participants of the same role. - Name string `json:"name,omitempty"` + Name string } -// A system message object. -type SystemMessage struct { - participantMessage +// Creates a new developer message object. +// +// Note that system and developer messages are identical in functionality, +// but the "system" role was renamed to "developer" in the OpenAI Chat API. +// Certain models may require one or the other, so use the type that matches the model's requirements. +func NewDeveloperMessage[T string | []DeveloperContentPart](content T) *DeveloperMessage[T] { + return &DeveloperMessage[T]{ + Content: content, + } } -// Creates a new system message object. -func NewSystemMessage(content string) *SystemMessage { - return &SystemMessage{ - participantMessage{ - MessageBase: MessageBase{ - Role: "system", - Content: content, - }, - }, +// Creates a new developer message object from multiple content parts. +// +// Note that system and developer messages are identical in functionality, +// but the "system" role was renamed to "developer" in the OpenAI Chat API. +// Certain models may require one or the other, so use the type that matches the model's requirements. +func NewDeveloperMessageFromParts(parts ...DeveloperContentPart) *DeveloperMessage[[]DeveloperContentPart] { + return NewDeveloperMessage(parts) +} + +// The role of the author of this message, in this case "developer". +func (m *DeveloperMessage[T]) Role() string { return "developer" } + +// Implements the json.Marshaler interface to serialize the DeveloperMessage object. +func (m DeveloperMessage[T]) MarshalJSON() ([]byte, error) { + buf := bytes.NewBufferString(`{"role":"developer","content":`) + + if b, err := utils.JsonSerialize(m.Content); err != nil { + return nil, err + } else { + buf.Write(b) + } + + if m.Name != "" { + buf.WriteString(`,"name":`) + buf.Write(gjson.AppendJSONString(nil, m.Name)) } + + buf.WriteByte('}') + + return buf.Bytes(), nil } // A user message object. -type UserMessage struct { - participantMessage +type UserMessage[T string | []UserMessageContentPart] struct { + + // The content of the message. + Content T + + // An optional name for the participant. + // Provides the model information to differentiate between participants of the same role. + Name string } // Creates a new user message object. -func NewUserMessage(content string) *UserMessage { - return &UserMessage{ - participantMessage{ - MessageBase: MessageBase{ - Role: "user", - Content: content, - }, - }, +func NewUserMessage[T string | []UserMessageContentPart](content T) *UserMessage[T] { + return &UserMessage[T]{ + Content: content, + } +} + +// Creates a new user message object from multiple content parts. +func NewUserMessageFromParts(parts ...UserMessageContentPart) *UserMessage[[]UserMessageContentPart] { + return NewUserMessage(parts) +} + +// The role of the author of this message, in this case "user". +func (m *UserMessage[T]) Role() string { return "user" } + +// Implements the json.Marshaler interface to serialize the UserMessage object. +func (m UserMessage[T]) MarshalJSON() ([]byte, error) { + buf := bytes.NewBufferString(`{"role":"user","content":`) + + if b, err := utils.JsonSerialize(m.Content); err != nil { + return nil, err + } else { + buf.Write(b) + } + + if m.Name != "" { + buf.WriteString(`,"name":`) + buf.Write(gjson.AppendJSONString(nil, m.Name)) } + + buf.WriteByte('}') + + return buf.Bytes(), nil } -// An assistant message object. -type AssistantMessage struct { - participantMessage +// An assistant message object, representing a message previously generated by the model. +type AssistantMessage[T string | []AssistantMessageContentPart] struct { + + // The content of the message. + Content T + + // An optional name for the participant. + // Provides the model information to differentiate between participants of the same role. + Name string + + // The refusal message generated by the model, if any. + Refusal string // The tool calls generated by the model, such as function calls. - ToolCalls []ToolCall `json:"tool_calls,omitempty"` + ToolCalls []ToolCall + + // Data about a previous audio response from the model. + Audio *AudioRef +} + +// Represents a reference to a previous audio response from the model. +type AudioRef struct { + + // Unique identifier for a previous audio response from the model. + Id string `json:"id"` } // Creates a new assistant message object. -func NewAssistantMessage(content string, toolCalls ...ToolCall) *AssistantMessage { - return &AssistantMessage{ - participantMessage{ - MessageBase: MessageBase{ - Role: "assistant", - Content: content, - }, - }, - toolCalls, +func NewAssistantMessage[T string | []AssistantMessageContentPart](content T) *AssistantMessage[T] { + return &AssistantMessage[T]{ + Content: content, } } +// Creates a new assistant message object from multiple content parts. +func NewAssistantMessageFromParts(parts ...AssistantMessageContentPart) *AssistantMessage[[]AssistantMessageContentPart] { + return NewAssistantMessage(parts) +} + +// Creates a new assistant message object from a completion message, so it can be used in a conversation. +func NewAssistantMessageFromCompletionMessage(cm *CompletionMessage) *AssistantMessage[string] { + return cm.ToAssistantMessage() +} + +// Converts the completion message to an assistant message, so it can be used in a conversation. +func (m *CompletionMessage) ToAssistantMessage() *AssistantMessage[string] { + am := &AssistantMessage[string]{ + Content: m.Content, + Refusal: m.Refusal, + ToolCalls: m.ToolCalls, + } + + if m.Audio != nil { + am.Audio = &AudioRef{Id: m.Audio.Id} + } + + return am +} + +// The role of the author of this message, in this case "assistant". +func (m *CompletionMessage) Role() string { return "assistant" } + +// The role of the author of this message, in this case "assistant". +func (m *AssistantMessage[T]) Role() string { return "assistant" } + +// Implements the json.Marshaler interface to serialize the AssistantMessage object. +func (m AssistantMessage[T]) MarshalJSON() ([]byte, error) { + buf := bytes.NewBufferString(`{"role":"assistant","content":`) + + if b, err := utils.JsonSerialize(m.Content); err != nil { + return nil, err + } else { + buf.Write(b) + } + + if m.Name != "" { + buf.WriteString(`,"name":`) + buf.Write(gjson.AppendJSONString(nil, m.Name)) + } + + if m.Refusal != "" { + buf.WriteString(`,"refusal":`) + buf.Write(gjson.AppendJSONString(nil, m.Refusal)) + } + + if len(m.ToolCalls) > 0 { + buf.WriteString(`,"tool_calls":[`) + for i, tc := range m.ToolCalls { + if i > 0 { + buf.WriteByte(',') + } + b, err := json.Marshal(tc) + if err != nil { + return nil, err + } + buf.Write(b) + } + buf.WriteByte(']') + } + + if m.Audio != nil { + buf.WriteString(`,"audio":`) + b, err := json.Marshal(m.Audio) + if err != nil { + return nil, err + } + buf.Write(b) + } + + buf.WriteByte('}') + + return buf.Bytes(), nil +} + // A tool message object. -type ToolMessage struct { - MessageBase +type ToolMessage[T string | []ToolMessageContentPart] struct { + + // The content of the message. + Content T // The tool call that this message is responding to. - ToolCallId string `json:"tool_call_id"` + ToolCallId string } // Creates a new tool message object. -func NewToolMessage(content, toolCallId string) *ToolMessage { - return &ToolMessage{ - MessageBase{ - Role: "tool", - Content: content, - }, - toolCallId, +// If the content is a string, it will be passed through unaltered. +// If the content is an error object, its error message will be used as the content. +// Otherwise, the object will be JSON serialized and sent as a JSON string. +// This function will panic if the object cannot be serialized to JSON. +func NewToolMessage(content any, toolCallId string) *ToolMessage[string] { + if content == nil { + return &ToolMessage[string]{ + ToolCallId: toolCallId, + } } + + if str, ok := content.(string); ok { + return &ToolMessage[string]{ + Content: str, + ToolCallId: toolCallId, + } + } + + if err, ok := content.(error); ok { + return &ToolMessage[string]{ + Content: err.Error(), + ToolCallId: toolCallId, + } + } + + if b, err := utils.JsonSerialize(content); err != nil { + panic(err) + } else { + return &ToolMessage[string]{ + Content: string(b), + ToolCallId: toolCallId, + } + } +} + +// Creates a new tool message object from multiple content parts. +func NewToolMessageFromParts(toolCallId string, parts ...ToolMessageContentPart) *ToolMessage[[]ToolMessageContentPart] { + return &ToolMessage[[]ToolMessageContentPart]{ + Content: parts, + ToolCallId: toolCallId, + } +} + +// The role of the author of this message, in this case "tool". +func (m *ToolMessage[T]) Role() string { return "tool" } + +// Implements the json.Marshaler interface to serialize the ToolMessage object. +func (m ToolMessage[T]) MarshalJSON() ([]byte, error) { + buf := bytes.NewBufferString(`{"role":"tool","content":`) + + if b, err := utils.JsonSerialize(m.Content); err != nil { + return nil, err + } else { + buf.Write(b) + } + + buf.WriteString(`,"tool_call_id":"` + m.ToolCallId + `"}`) + return buf.Bytes(), nil } // A chat completion message generated by the model. +// +// Note that a completion message is not a valid request message. +// To use a completion message in a chat, convert it to an assistant message with the ToAssistantMessage method. type CompletionMessage struct { - MessageBase - // The refusal message generated by the model. + // The content of the message. + Content string `json:"content"` + + // The refusal message generated by the model, if any. Refusal string `json:"refusal,omitempty"` + + // The tool calls generated by the model, such as function calls. + ToolCalls []ToolCall `json:"tool_calls,omitempty"` + + // The audio output generated by the model, if any. + // Used only when audio output is requested in the modalities field, and when the model and host support audio output. + Audio *AudioOutput `json:"audio,omitempty"` +} + +// An audio output object generated by the model when using audio modality. +type AudioOutput struct { + + // Unique identifier for this audio response. + Id string `json:"id"` + + // The time at which this audio content will no longer be accessible on the server for use in multi-turn conversations. + ExpiresAt time.Time `json:"expires_at"` + + // The raw audio data, in the format specified in the request. + Data []byte `json:"data"` + + // Transcript of the audio generated by the model. + Transcript string `json:"transcript"` +} + +// Implements the json.Unmarshaler interface to deserialize the AudioOutput object. +func (a *AudioOutput) UnmarshalJSON(data []byte) error { + var raw struct { + Id string `json:"id"` + ExpiresAt int64 `json:"expires_at"` + Data []byte `json:"data"` + Transcript string `json:"transcript"` + } + + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + a.Id = raw.Id + a.ExpiresAt = time.Unix(raw.ExpiresAt, 0).UTC() + a.Data = raw.Data + a.Transcript = raw.Transcript + + return nil } // A tool call object that the model may generate. @@ -349,15 +951,6 @@ type ToolCall struct { Function FunctionCall `json:"function"` } -// Creates a new tool call object. -func NewToolCall(id, name, arguments string) ToolCall { - return ToolCall{ - Id: id, - Type: "function", - Function: FunctionCall{name, arguments}, - } -} - // A function call object that the model may generate. type FunctionCall struct { @@ -365,10 +958,6 @@ type FunctionCall struct { Name string `json:"name"` // The arguments to call the function with, as generated by the model in JSON format. - // - // NOTE: - // The model does not always generate valid JSON, and may hallucinate parameters not - // defined by your function schema. Validate the arguments in your code before calling your function. Arguments string `json:"arguments"` } @@ -428,7 +1017,7 @@ type Choice struct { // The index of the choice in the list of choices. Index int `json:"index"` - // A chat completion message generated by the model. + // A message generated by the model. Message CompletionMessage `json:"message"` // Log probability information for the choice. @@ -467,8 +1056,15 @@ type LogprobsContentObject struct { Bytes []byte `json:"bytes"` } +type funcParam struct { + Name string + Type string + Description string +} + // A tool object that the model may call. type Tool struct { + funcParams []funcParam // The type of the tool. Currently, only `"function"` is supported. Type string `json:"type"` @@ -477,14 +1073,58 @@ type Tool struct { Function FunctionDefinition `json:"function"` } -// Creates a new tool object. -func NewTool() Tool { - return Tool{Type: "function"} +// Creates a new tool object for a function. +func NewToolForFunction(name, description string) Tool { + return Tool{ + Type: "function", + Function: FunctionDefinition{ + Name: name, + Description: description, + }, + } +} + +// Adds a parameter to the function used by the tool. +// Note that the type must be a valid JSON Schema type, not a Go type. +// For example, use "integer", not "int32". +func (t Tool) WithParameter(name, jsonSchemaType, description string) Tool { + t.funcParams = append(t.funcParams, funcParam{name, jsonSchemaType, description}) + + buf := bytes.NewBufferString(`{"type":"object","properties":{`) + for i, p := range t.funcParams { + if i > 0 { + buf.WriteByte(',') + } + buf.Write(gjson.AppendJSONString(nil, p.Name)) + buf.WriteString(`:{"type":`) + buf.Write(gjson.AppendJSONString(nil, p.Type)) + buf.WriteString(`,"description":`) + buf.Write(gjson.AppendJSONString(nil, p.Description)) + buf.WriteByte('}') + } + buf.WriteString(`},"required":[`) + for i, p := range t.funcParams { + if i > 0 { + buf.WriteByte(',') + } + buf.Write(gjson.AppendJSONString(nil, p.Name)) + } + buf.WriteString(`],"additionalProperties":false}`) + + t.Function.Parameters = utils.RawJsonString(buf.String()) + return t +} + +// Sets the JSON Schema for the parameters of the function used by the tool. +// Use this for defining complex parameters. Prefer WithParameter for adding simple parameters, +// which will generate the schema for you automatically. +func (t Tool) WithParametersSchema(jsonSchema string) Tool { + t.Function.Parameters = utils.RawJsonString(jsonSchema) + return t } // The definition of a function that can be called by the model. type FunctionDefinition struct { - // The name of the function to be called. // // Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64. @@ -514,18 +1154,18 @@ type FunctionDefinition struct { } // Creates an input object for the OpenAI Chat API. -func (m *ChatModel) CreateInput(messages ...Message) (*ChatModelInput, error) { +func (m *ChatModel) CreateInput(messages ...RequestMessage) (*ChatModelInput, error) { return &ChatModelInput{ Model: m.Info().FullName, Messages: messages, ResponseFormat: ResponseFormatText, - ServiceTier: ServiceTierAuto, Temperature: 1.0, TopP: 1.0, ParallelToolCalls: true, }, nil } +// Implements the json.Marshaler interface to serialize the ChatModelInput object. func (mi *ChatModelInput) MarshalJSON() ([]byte, error) { type alias ChatModelInput @@ -542,14 +1182,6 @@ func (mi *ChatModelInput) MarshalJSON() ([]byte, error) { } } - // omit default service_tier - if mi.ServiceTier == ServiceTierAuto { - b, err = sjson.DeleteBytes(b, "service_tier") - if err != nil { - return nil, err - } - } - // omit default temperature if mi.Temperature == 1.0 { b, err = sjson.DeleteBytes(b, "temperature") diff --git a/sdk/go/pkg/utils/utils.go b/sdk/go/pkg/utils/utils.go index d6be3f60e..4375457b0 100644 --- a/sdk/go/pkg/utils/utils.go +++ b/sdk/go/pkg/utils/utils.go @@ -19,7 +19,6 @@ import ( // JsonSerialize serializes the given value to JSON. // Unlike json.Marshal, it does not escape HTML characters. -// It also returns results without an extra newline at the end. func JsonSerialize(v any) ([]byte, error) { buf := new(bytes.Buffer) enc := json.NewEncoder(buf) From 394737bba1d68825df80695146859e0db2412664 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Mon, 6 Jan 2025 09:28:47 -0800 Subject: [PATCH 2/2] Update AssemblyScript SDK and examples --- CHANGELOG.md | 2 + .../examples/textgeneration/assembly/index.ts | 111 +-- .../examples/textgeneration/assembly/media.ts | 181 ++++ .../textgeneration/assembly/product.ts | 25 - .../textgeneration/assembly/products.ts | 113 +++ .../textgeneration/assembly/simple.ts | 42 + .../textgeneration/assembly/toolcalling.ts | 161 ++++ .../examples/textgeneration/modus.json | 23 +- .../examples/textgeneration/package-lock.json | 658 +++++-------- sdk/assemblyscript/src/assembly/localtime.ts | 12 + sdk/assemblyscript/src/models/openai/chat.ts | 894 ++++++++++++++++-- sdk/assemblyscript/src/package-lock.json | 570 ++++++----- sdk/assemblyscript/src/package.json | 1 + 13 files changed, 1957 insertions(+), 836 deletions(-) create mode 100644 sdk/assemblyscript/examples/textgeneration/assembly/media.ts delete mode 100644 sdk/assemblyscript/examples/textgeneration/assembly/product.ts create mode 100644 sdk/assemblyscript/examples/textgeneration/assembly/products.ts create mode 100644 sdk/assemblyscript/examples/textgeneration/assembly/simple.ts create mode 100644 sdk/assemblyscript/examples/textgeneration/assembly/toolcalling.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e6cd8e4a..c119726df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ - chore: remove localHypermodeModels list and handle 404s properly instead in local dev [#703](https://github.com/hypermodeinc/modus/pull/703) - fix: remove fallback to default time zone [#706](https://github.com/hypermodeinc/modus/pull/706) +- feat: improve OpenAI model APIs and examples to better support audio, images, and tool calling + [#707](https://github.com/hypermodeinc/modus/pull/707) ## 2025-01-09 - CLI 0.16.6 diff --git a/sdk/assemblyscript/examples/textgeneration/assembly/index.ts b/sdk/assemblyscript/examples/textgeneration/assembly/index.ts index 4fefba8a1..118869e48 100644 --- a/sdk/assemblyscript/examples/textgeneration/assembly/index.ts +++ b/sdk/assemblyscript/examples/textgeneration/assembly/index.ts @@ -4,109 +4,10 @@ * See the LICENSE file that accompanied this code for further details. */ -import { JSON } from "json-as"; -import { models } from "@hypermode/modus-sdk-as"; -import { Product, sampleProductJson } from "./product"; +// The examples have been split into separate files for clarity. +// See each file for more details about the specific example. -import { - OpenAIChatModel, - ResponseFormat, - SystemMessage, - UserMessage, -} from "@hypermode/modus-sdk-as/models/openai/chat"; - -// In this example, we will generate text using the OpenAI Chat model. -// See https://platform.openai.com/docs/api-reference/chat/create for more details -// about the options available on the model, which you can set on the input object. - -// This model name should match the one defined in the modus.json manifest file. -const modelName: string = "text-generator"; - -// This function generates some text based on the instruction and prompt provided. -export function generateText(instruction: string, prompt: string): string { - // The imported ChatModel interface follows the OpenAI Chat completion model input format. - const model = models.getModel(modelName); - const input = model.createInput([ - new SystemMessage(instruction), - new UserMessage(prompt), - ]); - - // This is one of many optional parameters available for the OpenAI Chat model. - input.temperature = 0.7; - - // Here we invoke the model with the input we created. - const output = model.invoke(input); - - // The output is also specific to the ChatModel interface. - // Here we return the trimmed content of the first choice. - return output.choices[0].message.content.trim(); -} - -// This function generates a single product. -export function generateProduct(category: string): Product { - // We can get creative with the instruction and prompt to guide the model - // in generating the desired output. Here we provide a sample JSON of the - // object we want the model to generate. - const instruction = `Generate a product for the category provided. -Only respond with valid JSON object in this format: -${sampleProductJson}`; - const prompt = `The category is "${category}".`; - - // Set up the input for the model, creating messages for the instruction and prompt. - const model = models.getModel(modelName); - const input = model.createInput([ - new SystemMessage(instruction), - new UserMessage(prompt), - ]); - - // Let's increase the temperature to get more creative responses. - // Be careful though, if the temperature is too high, the model may generate invalid JSON. - input.temperature = 1.2; - - // This model also has a response format parameter that can be set to JSON, - // Which, along with the instruction, can help guide the model in generating valid JSON output. - input.responseFormat = ResponseFormat.Json; - - // Here we invoke the model with the input we created. - const output = model.invoke(input); - - // The output should contain the JSON string we asked for. - const json = output.choices[0].message.content.trim(); - const product = JSON.parse(json); - return product; -} - -// This function generates multiple products. -export function generateProducts(category: string, quantity: i32): Product[] { - // Similar to the previous example above, we can tailor the instruction and prompt - // to guide the model in generating the desired output. Note that understanding the behavior - // of the model is important to get the desired results. In this case, we need the model - // to return an _object_ containing an array, not an array of objects directly. That's because - // the model will not reliably generate an array of objects directly. - const instruction = `Generate ${quantity} products for the category provided. -Only respond with valid JSON object containing a valid JSON array named 'list', in this format: -{"list":[${sampleProductJson}]}`; - const prompt = `The category is "${category}".`; - - // Set up the input for the model, creating messages for the instruction and prompt. - const model = models.getModel(modelName); - const input = model.createInput([ - new SystemMessage(instruction), - new UserMessage(prompt), - ]); - - // Adjust the model inputs, just like in the previous example. - // Be careful, if the temperature is too high, the model may generate invalid JSON. - input.temperature = 1.2; - input.responseFormat = ResponseFormat.Json; - - // Here we invoke the model with the input we created. - const output = model.invoke(input); - - // The output should contain the JSON string we asked for. - const json = output.choices[0].message.content.trim(); - - // We can parse that JSON to a compatible object, to get the data we're looking for. - const results = JSON.parse>(json); - return results.get("list"); -} +export * from "./simple"; +export * from "./products"; +export * from "./media"; +export * from "./toolcalling"; diff --git a/sdk/assemblyscript/examples/textgeneration/assembly/media.ts b/sdk/assemblyscript/examples/textgeneration/assembly/media.ts new file mode 100644 index 000000000..93c237afa --- /dev/null +++ b/sdk/assemblyscript/examples/textgeneration/assembly/media.ts @@ -0,0 +1,181 @@ +/* + * This example is part of the Modus project, licensed under the Apache License 2.0. + * You may modify and use this example in accordance with the license. + * See the LICENSE file that accompanied this code for further details. + */ + +import { models, http } from "@hypermode/modus-sdk-as"; + +import { + OpenAIChatModel, + DeveloperMessage, + SystemMessage, + UserMessage, + TextContentPart, + AudioContentPart, + ImageContentPart, + Image, + Audio, +} from "@hypermode/modus-sdk-as/models/openai/chat"; + +// These examples demonstrate how to use audio or image data with OpenAI chat models. +// Currently, audio can be used for input or output, but images can be used only for input. + +/** + * This type is used in these examples to represent images or audio. + */ +class Media { + // The content type of the media. + contentType!: string; + + // The binary data of the media. + // This value will be base64 encoded when used in an API response. + data!: Uint8Array; + + // A text description or transcription of the media. + text!: string; +} + +/** + * This function generates an audio response based on the instruction and prompt provided. + */ +export function generateAudio(instruction: string, prompt: string): Media { + // Note, this is similar to the generateText example, but with audio output requested. + + // We'll generate the audio using an audio-enabled OpenAI chat model. + const model = models.getModel("audio-model"); + + const input = model.createInput([ + new SystemMessage(instruction), + new UserMessage(prompt), + ]); + + input.temperature = 0.7; + + // Request audio output from the model. + // Note, this is a convenience method that requests audio modality and sets the voice and format. + // You can also set these values manually on the input object, if you prefer. + input.requestAudioOutput("ash", "wav"); + + const output = model.invoke(input); + + // Return the audio and its transcription. + // Note that the message Content field will be empty for audio responses. + // Instead, the text will be in the Message.Audio.Transcript field. + const audio = output.choices[0].message.audio!; + + const media = { + contentType: "audio/wav", + data: audio.data, + text: audio.transcript.trim(), + }; + + return media; +} + +/** + * This function generates text that describes the image at the provided url. + * In this example the image url is passed to the model, and the model retrieves the image. + */ +export function describeImage(url: string): string { + // Note that because the model retrieves the image, any URL can be used. + // However, this means that there is a risk of sending data to an unauthorized host, if the URL is not hardcoded or sanitized. + // See the describeRandomImage function below for a safer approach. + + const model = models.getModel("text-generator"); + + const input = model.createInput([ + UserMessage.fromParts([ + new TextContentPart("Describe this image."), + new ImageContentPart(Image.fromURL(url)), + ]), + ]); + + const output = model.invoke(input); + + return output.choices[0].message.content.trim(); +} + +/** + * This function fetches a random image, and then generates text that describes it. + * In this example the image is retrieved by the function before passing it as data to the model. + */ +export function describeRandomImage(): Media { + // Because this approach fetches the image directly, it is safer than the describeImage function above. + // The host URL is allow-listed in the modus.json file, so we can trust the image source. + + // Fetch a random image from the Picsum API. We'll just hardcode the size to make the demo simple to call. + const response = http.fetch("https://picsum.photos/640/480"); + const data = Uint8Array.wrap(response.body); + const contentType = response.headers.get("Content-Type")!; + + // Describe the image using the OpenAI chat model. + const model = models.getModel("text-generator"); + + const input = model.createInput([ + UserMessage.fromParts([ + new TextContentPart("Describe this image."), + new ImageContentPart(Image.fromData(data, contentType)), + ]), + ]); + + input.temperature = 0.7; + + const output = model.invoke(input); + + // Return the image and its generated description. + const text = output.choices[0].message.content.trim(); + const media = { + contentType, + data, + text, + }; + + return media; +} + +/** + * This function fetches a random "Harvard Sentences" speech file from OpenSpeech, and then generates a transcript from it. + * The sentences are from https://www.cs.columbia.edu/~hgs/audio/harvard.html + */ +export function transcribeRandomSpeech(): Media { + // Pick a random file number from the list of available here: + // https://www.voiptroubleshooter.com/open_speech/american.html + const numbers: i32[] = [ + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 30, 31, 32, 34, 35, 36, 37, 38, 39, + 40, 57, 58, 59, 60, 61, + ]; + const num = numbers[(Math.random() * numbers.length)]; + + // Fetch the speech file corresponding to the number. + const url = `https://www.voiptroubleshooter.com/open_speech/american/OSR_us_000_00${num}_8k.wav`; + const response = http.fetch(url); + const data = Uint8Array.wrap(response.body); + + // Transcribe the audio using an audio-enabled OpenAI chat model. + const model = models.getModel("audio-model"); + + const input = model.createInput([ + new DeveloperMessage( + "Do not include any newlines or surrounding quotation marks in the response. Omit any explanation beyond the request.", + ), + UserMessage.fromParts([ + new TextContentPart( + "Provide an exact transcription of the contents of this audio file.", + ), + new AudioContentPart(Audio.fromData(data, "wav")), + ]), + ]); + + const output = model.invoke(input); + + // Return the audio file and its transcript. + const text = output.choices[0].message.content.trim(); + const media = { + contentType: "audio/wav", + data, + text, + }; + + return media; +} diff --git a/sdk/assemblyscript/examples/textgeneration/assembly/product.ts b/sdk/assemblyscript/examples/textgeneration/assembly/product.ts deleted file mode 100644 index 545166474..000000000 --- a/sdk/assemblyscript/examples/textgeneration/assembly/product.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * This example is part of the Modus project, licensed under the Apache License 2.0. - * You may modify and use this example in accordance with the license. - * See the LICENSE file that accompanied this code for further details. - */ - -import { JSON } from "json-as"; - -// The Product class and the sample product will be used in the some of the examples. -// Note that the class must be decorated with @json so that it can be serialized -// and deserialized properly when interacting with OpenAI. -@json -export class Product { - id: string | null = null; - name: string = ""; - price: f64 = 0.0; - description: string = ""; -} - -export const sampleProductJson = JSON.stringify({ - id: "123", - name: "Shoes", - price: 50.0, - description: "Great shoes for walking.", -}); diff --git a/sdk/assemblyscript/examples/textgeneration/assembly/products.ts b/sdk/assemblyscript/examples/textgeneration/assembly/products.ts new file mode 100644 index 000000000..ea795784d --- /dev/null +++ b/sdk/assemblyscript/examples/textgeneration/assembly/products.ts @@ -0,0 +1,113 @@ +/* + * This example is part of the Modus project, licensed under the Apache License 2.0. + * You may modify and use this example in accordance with the license. + * See the LICENSE file that accompanied this code for further details. + */ + +import { JSON } from "json-as"; +import { models } from "@hypermode/modus-sdk-as"; + +import { + OpenAIChatModel, + ResponseFormat, + SystemMessage, + UserMessage, +} from "@hypermode/modus-sdk-as/models/openai/chat"; + +// In this example, we'll demonstrate how to generated structured data using OpenAI chat models. +// We'll be asking the model to populate Product objects with data that fits a particular category. +// The data the model generates will need to conform to the schema of the Product object. + +@json +class Product { + id: string | null = null; + name: string = ""; + price: f64 = 0.0; + description: string = ""; +} + +const sampleProductJson = JSON.stringify({ + id: "123", + name: "Shoes", + price: 50.0, + description: "Great shoes for walking.", +}); + +/** + * This function generates a single product. + */ +export function generateProduct(category: string): Product { + // We can get creative with the instruction and prompt to guide the model + // in generating the desired output. Here we provide a sample JSON of the + // object we want the model to generate. + const instruction = `Generate a product for the category provided. +Only respond with valid JSON object in this format: +${sampleProductJson}`; + const prompt = `The category is "${category}".`; + + // Set up the input for the model, creating messages for the instruction and prompt. + const model = models.getModel("text-generator"); + const input = model.createInput([ + new SystemMessage(instruction), + new UserMessage(prompt), + ]); + + // Let's increase the temperature to get more creative responses. + // Be careful though, if the temperature is too high, the model may generate invalid JSON. + input.temperature = 1.2; + + // This model also has a response format parameter that can be set to JSON, + // Which, along with the instruction, can help guide the model in generating valid JSON output. + input.responseFormat = ResponseFormat.Json; + + // Here we invoke the model with the input we created. + const output = model.invoke(input); + + // The output should contain the JSON string we asked for. + const json = output.choices[0].message.content.trim(); + + // We can now parse the JSON string as a Product object. + const product = JSON.parse(json); + + return product; +} + +/** + * This function generates multiple products. + */ +export function generateProducts(category: string, quantity: i32): Product[] { + // We can tailor the instruction and prompt to guide the model in generating the desired output. + // Note that understanding the behavior of the model is important to get the desired results. + // In this case, we need the model to return an _object_ containing an array, not an array of objects directly. + // That's because the model will not reliably generate an array of objects directly. + const instruction = `Generate ${quantity} products for the category provided. +Only respond with valid JSON object containing a valid JSON array named 'list', in this format: +{"list":[${sampleProductJson}]}`; + const prompt = `The category is "${category}".`; + + // Set up the input for the model, creating messages for the instruction and prompt. + const model = models.getModel("text-generator"); + const input = model.createInput([ + new SystemMessage(instruction), + new UserMessage(prompt), + ]); + + // Adjust the model inputs, just like in the previous example. + // Be careful, if the temperature is too high, the model may generate invalid JSON. + input.temperature = 1.2; + input.responseFormat = ResponseFormat.Json; + + // Here we invoke the model with the input we created. + const output = model.invoke(input); + + // The output should contain the JSON string we asked for. + const json = output.choices[0].message.content.trim(); + + // We can parse that JSON to a compatible object, to get the data we're looking for. + const results = JSON.parse>(json); + + // Now we can extract the list of products from the data. + const products = results.get("list"); + + return products; +} diff --git a/sdk/assemblyscript/examples/textgeneration/assembly/simple.ts b/sdk/assemblyscript/examples/textgeneration/assembly/simple.ts new file mode 100644 index 000000000..cda60569c --- /dev/null +++ b/sdk/assemblyscript/examples/textgeneration/assembly/simple.ts @@ -0,0 +1,42 @@ +/* + * This example is part of the Modus project, licensed under the Apache License 2.0. + * You may modify and use this example in accordance with the license. + * See the LICENSE file that accompanied this code for further details. + */ + +import { models } from "@hypermode/modus-sdk-as"; + +import { + OpenAIChatModel, + SystemMessage, + UserMessage, +} from "@hypermode/modus-sdk-as/models/openai/chat"; + +// In this example, we will generate text and other content using OpenAI chat models. +// See https://platform.openai.com/docs/api-reference/chat/create for more details +// about the options available on the model, which you can set on the input object. + +/** + * This function generates some text based on the instruction and prompt provided. + */ +export function generateText(instruction: string, prompt: string): string { + // The imported OpenAIChatModel interface follows the OpenAI chat completion model input format. + const model = models.getModel("text-generator"); + + // We'll start by creating an input object using the instruction and prompt provided. + const input = model.createInput([ + new SystemMessage(instruction), + new UserMessage(prompt), + // ... if we wanted to add more messages, we could do so here. + ]); + + // This is one of many optional parameters available for the OpenAI chat model. + input.temperature = 0.7; + + // Here we invoke the model with the input we created. + const output = model.invoke(input); + + // The output is also specific to the OpenAIChatModel interface. + // Here we return the trimmed content of the first choice. + return output.choices[0].message.content.trim(); +} diff --git a/sdk/assemblyscript/examples/textgeneration/assembly/toolcalling.ts b/sdk/assemblyscript/examples/textgeneration/assembly/toolcalling.ts new file mode 100644 index 000000000..b9153b3f4 --- /dev/null +++ b/sdk/assemblyscript/examples/textgeneration/assembly/toolcalling.ts @@ -0,0 +1,161 @@ +/* + * This example is part of the Modus project, licensed under the Apache License 2.0. + * You may modify and use this example in accordance with the license. + * See the LICENSE file that accompanied this code for further details. + */ + +import { models, localtime } from "@hypermode/modus-sdk-as"; +import { JSON } from "json-as"; + +import { + OpenAIChatModel, + SystemMessage, + Tool, + ToolMessage, + UserMessage, +} from "@hypermode/modus-sdk-as/models/openai/chat"; + +/** + * This function generates some text from the model, which has already been given some tools to call, + * and instructions on how to use them. The tools allow the model to answer time-related questions, + * such as "What time is it?", or "What time is it in New York?". + * @param prompt The prompt to provide to the model. + */ +export function generateTextWithTools(prompt: string): string { + const model = models.getModel("text-generator"); + + const instruction = ` + You are a helpful assistant that understands time in various parts of the world. + Answer the user's question as directly as possible. If you need more information, ask for it. + When stating a time zone to the user, either use a descriptive name such as "Pacific Time" or use the location's name. + If asked for a time in a location, and the given location has multiple time zones, return the time in each of them. + Make your answers as brief as possible. + `; + + const input = model.createInput([ + new SystemMessage(instruction), + new UserMessage(prompt), + ]); + + // Let's turn the temperature down a bit, to make the model's responses more predictable. + input.temperature = 0.2; + + // This is how you make tools available to the model. + // Each tool is specified by the name of the function to call, and a description of what it does. + // + // If the function requires parameters, you can specify simple parameters individually with the withParameter method, + // or you can pass a JSON Schema string directly to the withParametersSchema method. + // + // NOTE: A future release of Modus may further simplify this process. + input.tools = [ + Tool.forFunction( + "getUserTimeZone", + "Returns the user's current IANA time zone.", + ), + Tool.forFunction( + "getCurrentTimeInUserTimeZone", + "Gets the current date and time in the user's time zone. Returns the user's IANA time zone, and the time as an ISO 8601 string.", + ), + Tool.forFunction( + "getCurrentTime", + "Returns the current date and time in the specified IANA time zone as an ISO 8601 string.", + ).withParameter( + "tz", + "string", + `An IANA time zone identifier such as "America/New_York".`, + ), + ]; + + // To use the tools, you'll need to have a "conversation loop" like this one. + // The model may be called multiple times, depending on the complexity of the conversation and the tools used. + while (true) { + const output = model.invoke(input); + const msg = output.choices[0].message; + + // If the model requested tools to be called, we'll need to do that before continuing. + if (msg.toolCalls.length > 0) { + // First, add the model's response to the conversation as an "Assistant Message". + input.messages.push(msg.toAssistantMessage()); + + // Then call the tools the model requested. Add each result to the conversation as a "Tool Message". + for (let i = 0; i < msg.toolCalls.length; i++) { + const tc = msg.toolCalls[i]; + + // The model will tell us which tool to call, and what arguments to pass to it. + // We'll need to parse the function name and arguments in order to call the appropriate function. + // + // If the function errors (for example, due to invalid input), we can either continue + // the conversation by providing the model with the error message (as shown here), + // or we can end the conversation by returning error directly or throwing an exception. + // + // NOTE: A future release of Modus may simplify this process. + let toolMsg: ToolMessage; + const fnName = tc.function.name; + if (fnName === "getCurrentTime") { + const args = JSON.parse>(tc.function.arguments); + const result = getCurrentTime(args.get("tz")); + toolMsg = new ToolMessage(result, tc.id); + } else if (fnName === "getUserTimeZone") { + const timeZone = getUserTimeZone(); + toolMsg = new ToolMessage(timeZone, tc.id); + } else if (fnName === "getCurrentTimeInUserTimeZone") { + const result = getCurrentTimeInUserTimeZone(); + toolMsg = new ToolMessage(result, tc.id); + } else { + throw new Error(`Unknown tool call: ${tc.function.name}`); + } + + // Add the tool's response to the conversation. + input.messages.push(toolMsg); + } + } else if (msg.content != "") { + // return the model's final response to the user. + return msg.content.trim(); + } else { + // If the model didn't ask for tools, and didn't return any content, something went wrong. + throw new Error("Invalid response from model."); + } + } +} + +// The following functions are made available as "tools" +// for the model to call in the example above. + +/** + * This function will return the current time in a given time zone. + */ +function getCurrentTime(tz: string): string { + if (!localtime.IsValidTimeZone(tz)) { + return "error: invalid time zone"; + } + return localtime.NowInZone(tz); +} + +/** + * This function will return the default time zone for the user. + */ +function getUserTimeZone(): string { + return localtime.GetTimeZone(); +} + +/** + * This function combines the functionality of getCurrentTime and getUserTimeZone, + * to reduce the number of tool calls required by the model. + */ +function getCurrentTimeInUserTimeZone(): string { + const tz = localtime.GetTimeZone(); + if (tz === "") { + return "error: cannot determine user's time zone"; + } + + const time = localtime.NowInZone(tz); + const result = { time, zone: tz }; + return JSON.stringify(result); +} + + +@json +class TimeAndZone { + time!: string; + zone!: string; +} diff --git a/sdk/assemblyscript/examples/textgeneration/modus.json b/sdk/assemblyscript/examples/textgeneration/modus.json index 8bbb2221c..2b457dd25 100644 --- a/sdk/assemblyscript/examples/textgeneration/modus.json +++ b/sdk/assemblyscript/examples/textgeneration/modus.json @@ -8,22 +8,39 @@ } }, "models": { - // This defines the model that will be used for text generation. + // This defines the models that will be used for text generation. "text-generator": { - "sourceModel": "gpt-3.5-turbo", + "sourceModel": "gpt-4o-mini", + "connection": "openai", + "path": "v1/chat/completions" + }, + // This defines the models that will be used for audio input and output. + "audio-model": { + "sourceModel": "gpt-4o-audio-preview", "connection": "openai", "path": "v1/chat/completions" } }, "connections": { // This defines the OpenAI host, which is used by the model above. - // The {{API_KEY}} will be replaced by the secret provided in the Hypermode Console. + // The {{API_KEY}} will be replaced by the secret's value at run time. "openai": { "type": "http", "baseUrl": "https://api.openai.com/", "headers": { "Authorization": "Bearer {{API_KEY}}" } + }, + + // These are some additional hosts that we use in the examples + // for fetching images and audio files. + "picsum": { + "type": "http", + "baseUrl": "https://picsum.photos/" + }, + "openspeech": { + "type": "http", + "baseUrl": "https://www.voiptroubleshooter.com/open_speech/" } } } diff --git a/sdk/assemblyscript/examples/textgeneration/package-lock.json b/sdk/assemblyscript/examples/textgeneration/package-lock.json index 27d0df9ca..c3eee5f6c 100644 --- a/sdk/assemblyscript/examples/textgeneration/package-lock.json +++ b/sdk/assemblyscript/examples/textgeneration/package-lock.json @@ -28,6 +28,7 @@ "license": "Apache-2.0", "dependencies": { "@assemblyscript/wasi-shim": "^0.1.0", + "as-base64": "^0.2.0", "chalk": "^5.4.1", "json-as": "^0.9.28", "semver": "^7.6.3", @@ -62,19 +63,33 @@ } }, "../../src/node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", + "version": "4.4.1", "dev": true, "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "../../src/node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "../../src/node_modules/@eslint-community/regexpp": { "version": "4.12.1", "dev": true, @@ -84,11 +99,11 @@ } }, "../../src/node_modules/@eslint/config-array": { - "version": "0.19.0", + "version": "0.19.1", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.4", + "@eslint/object-schema": "^2.1.5", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -97,9 +112,12 @@ } }, "../../src/node_modules/@eslint/core": { - "version": "0.9.0", + "version": "0.10.0", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -127,7 +145,7 @@ } }, "../../src/node_modules/@eslint/js": { - "version": "9.15.0", + "version": "9.18.0", "dev": true, "license": "MIT", "engines": { @@ -135,7 +153,7 @@ } }, "../../src/node_modules/@eslint/object-schema": { - "version": "2.1.4", + "version": "2.1.5", "dev": true, "license": "Apache-2.0", "engines": { @@ -143,10 +161,11 @@ } }, "../../src/node_modules/@eslint/plugin-kit": { - "version": "0.2.3", + "version": "0.2.5", "dev": true, "license": "Apache-2.0", "dependencies": { + "@eslint/core": "^0.10.0", "levn": "^0.4.1" }, "engines": { @@ -225,31 +244,6 @@ "node": ">=12" } }, - "../../src/node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "../../src/node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "../../src/node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "dev": true, @@ -282,17 +276,8 @@ "node": ">= 8" } }, - "../../src/node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, "../../src/node_modules/@types/eslint": { - "version": "9.6.0", + "version": "9.6.1", "dev": true, "license": "MIT", "dependencies": { @@ -319,27 +304,27 @@ "license": "MIT" }, "../../src/node_modules/@types/node": { - "version": "22.9.0", + "version": "22.10.7", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.19.8" + "undici-types": "~6.20.0" } }, "../../src/node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.15.0", + "version": "8.21.0", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.15.0", - "@typescript-eslint/type-utils": "8.15.0", - "@typescript-eslint/utils": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0", + "@typescript-eslint/scope-manager": "8.21.0", + "@typescript-eslint/type-utils": "8.21.0", + "@typescript-eslint/utils": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -350,23 +335,19 @@ }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "../../src/node_modules/@typescript-eslint/parser": { - "version": "8.15.0", + "version": "8.21.0", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.15.0", - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/typescript-estree": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0", + "@typescript-eslint/scope-manager": "8.21.0", + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/typescript-estree": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0", "debug": "^4.3.4" }, "engines": { @@ -377,21 +358,17 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "../../src/node_modules/@typescript-eslint/scope-manager": { - "version": "8.15.0", + "version": "8.21.0", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0" + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -402,14 +379,14 @@ } }, "../../src/node_modules/@typescript-eslint/type-utils": { - "version": "8.15.0", + "version": "8.21.0", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.15.0", - "@typescript-eslint/utils": "8.15.0", + "@typescript-eslint/typescript-estree": "8.21.0", + "@typescript-eslint/utils": "8.21.0", "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -419,16 +396,12 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "../../src/node_modules/@typescript-eslint/types": { - "version": "8.15.0", + "version": "8.21.0", "dev": true, "license": "MIT", "engines": { @@ -440,18 +413,18 @@ } }, "../../src/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.15.0", + "version": "8.21.0", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0", + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -460,10 +433,16 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" + } + }, + "../../src/node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" } }, "../../src/node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { @@ -481,14 +460,14 @@ } }, "../../src/node_modules/@typescript-eslint/utils": { - "version": "8.15.0", + "version": "8.21.0", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.15.0", - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/typescript-estree": "8.15.0" + "@typescript-eslint/scope-manager": "8.21.0", + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/typescript-estree": "8.21.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -498,20 +477,16 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "../../src/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.15.0", + "version": "8.21.0", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.15.0", + "@typescript-eslint/types": "8.21.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -522,17 +497,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "../../src/node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "../../src/node_modules/acorn": { "version": "8.14.0", "dev": true, @@ -568,22 +532,22 @@ } }, "../../src/node_modules/ansi-regex": { - "version": "5.0.1", + "version": "6.1.0", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "../../src/node_modules/ansi-styles": { - "version": "4.3.0", + "version": "6.2.1", "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -594,6 +558,10 @@ "dev": true, "license": "Python-2.0" }, + "../../src/node_modules/as-base64": { + "version": "0.2.0", + "license": "MIT" + }, "../../src/node_modules/as-console": { "version": "7.0.0", "dev": true, @@ -626,42 +594,6 @@ "ast": "bin/index.js" } }, - "../../src/node_modules/as-test/node_modules/glob": { - "version": "11.0.0", - "dev": true, - "license": "ISC", - "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" - } - }, - "../../src/node_modules/as-test/node_modules/minimatch": { - "version": "10.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "../../src/node_modules/as-variant": { "version": "0.4.1", "dev": true @@ -671,7 +603,7 @@ "license": "MIT" }, "../../src/node_modules/assemblyscript": { - "version": "0.27.31", + "version": "0.27.32", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -717,11 +649,12 @@ } }, "../../src/node_modules/brace-expansion": { - "version": "2.0.1", + "version": "1.1.11", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "../../src/node_modules/braces": { @@ -744,7 +677,7 @@ } }, "../../src/node_modules/chalk": { - "version": "5.3.0", + "version": "5.4.1", "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -788,11 +721,11 @@ } }, "../../src/node_modules/debug": { - "version": "4.3.5", + "version": "4.4.0", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -830,17 +763,17 @@ } }, "../../src/node_modules/eslint": { - "version": "9.15.0", + "version": "9.18.0", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.9.0", + "@eslint/core": "^0.10.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.15.0", - "@eslint/plugin-kit": "^0.2.3", + "@eslint/js": "9.18.0", + "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.1", @@ -848,7 +781,7 @@ "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.5", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.2.0", @@ -903,40 +836,43 @@ } }, "../../src/node_modules/eslint-visitor-keys": { - "version": "3.4.3", + "version": "4.2.0", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "../../src/node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", + "../../src/node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "../../src/node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.0", + "../../src/node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "../../src/node_modules/espree": { @@ -955,19 +891,8 @@ "url": "https://opencollective.com/eslint" } }, - "../../src/node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "../../src/node_modules/esquery": { - "version": "1.5.0", + "version": "1.6.0", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -1010,7 +935,7 @@ "license": "MIT" }, "../../src/node_modules/fast-glob": { - "version": "3.3.2", + "version": "3.3.3", "dev": true, "license": "MIT", "dependencies": { @@ -1018,7 +943,7 @@ "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -1046,7 +971,7 @@ "license": "MIT" }, "../../src/node_modules/fastq": { - "version": "1.17.1", + "version": "1.18.0", "dev": true, "license": "ISC", "dependencies": { @@ -1103,12 +1028,12 @@ } }, "../../src/node_modules/flatted": { - "version": "3.3.1", + "version": "3.3.2", "dev": true, "license": "ISC" }, "../../src/node_modules/foreground-child": { - "version": "3.2.1", + "version": "3.3.0", "dev": true, "license": "ISC", "dependencies": { @@ -1122,6 +1047,28 @@ "url": "https://github.com/sponsors/isaacs" } }, + "../../src/node_modules/glob": { + "version": "11.0.1", + "dev": true, + "license": "ISC", + "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" + } + }, "../../src/node_modules/glob-parent": { "version": "6.0.2", "dev": true, @@ -1133,6 +1080,28 @@ "node": ">=10.13.0" } }, + "../../src/node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "../../src/node_modules/glob/node_modules/minimatch": { + "version": "10.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "../../src/node_modules/globals": { "version": "14.0.0", "dev": true, @@ -1158,7 +1127,7 @@ } }, "../../src/node_modules/ignore": { - "version": "5.3.1", + "version": "5.3.2", "dev": true, "license": "MIT", "engines": { @@ -1229,7 +1198,7 @@ "license": "ISC" }, "../../src/node_modules/jackspeak": { - "version": "4.0.1", + "version": "4.0.2", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -1240,9 +1209,6 @@ }, "funding": { "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" } }, "../../src/node_modules/js-yaml": { @@ -1257,7 +1223,7 @@ } }, "../../src/node_modules/json-as": { - "version": "0.9.26", + "version": "0.9.28", "license": "MIT", "dependencies": { "as-virtual": "^0.2.0" @@ -1323,12 +1289,12 @@ "license": "MIT" }, "../../src/node_modules/long": { - "version": "5.2.3", + "version": "5.2.4", "dev": true, "license": "Apache-2.0" }, "../../src/node_modules/lru-cache": { - "version": "11.0.0", + "version": "11.0.2", "dev": true, "license": "ISC", "engines": { @@ -1366,15 +1332,6 @@ "node": "*" } }, - "../../src/node_modules/minimatch/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "../../src/node_modules/minipass": { "version": "7.1.2", "dev": true, @@ -1384,7 +1341,7 @@ } }, "../../src/node_modules/ms": { - "version": "2.1.2", + "version": "2.1.3", "dev": true, "license": "MIT" }, @@ -1438,7 +1395,7 @@ } }, "../../src/node_modules/package-json-from-dist": { - "version": "1.0.0", + "version": "1.0.1", "dev": true, "license": "BlueOak-1.0.0" }, @@ -1504,7 +1461,7 @@ } }, "../../src/node_modules/prettier": { - "version": "3.3.3", + "version": "3.4.2", "dev": true, "license": "MIT", "bin": { @@ -1653,23 +1610,31 @@ "node": ">=8" } }, + "../../src/node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "../../src/node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", "dev": true, "license": "MIT" }, - "../../src/node_modules/string-width/node_modules/ansi-regex": { + "../../src/node_modules/string-width-cjs/node_modules/strip-ansi": { "version": "6.0.1", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "ansi-regex": "^5.0.1" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "engines": { + "node": ">=8" } }, - "../../src/node_modules/string-width/node_modules/strip-ansi": { + "../../src/node_modules/strip-ansi": { "version": "7.1.0", "dev": true, "license": "MIT", @@ -1683,7 +1648,8 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "../../src/node_modules/strip-ansi": { + "../../src/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", "version": "6.0.1", "dev": true, "license": "MIT", @@ -1694,14 +1660,10 @@ "node": ">=8" } }, - "../../src/node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", + "../../src/node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, "engines": { "node": ">=8" } @@ -1745,14 +1707,14 @@ } }, "../../src/node_modules/ts-api-utils": { - "version": "1.4.0", + "version": "2.0.0", "dev": true, "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "../../src/node_modules/ts-mixer": { @@ -1780,7 +1742,7 @@ } }, "../../src/node_modules/typescript": { - "version": "5.6.3", + "version": "5.7.3", "dev": true, "license": "Apache-2.0", "bin": { @@ -1792,13 +1754,13 @@ } }, "../../src/node_modules/typescript-eslint": { - "version": "8.15.0", + "version": "8.21.0", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.15.0", - "@typescript-eslint/parser": "8.15.0", - "@typescript-eslint/utils": "8.15.0" + "@typescript-eslint/eslint-plugin": "8.21.0", + "@typescript-eslint/parser": "8.21.0", + "@typescript-eslint/utils": "8.21.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1808,16 +1770,12 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "../../src/node_modules/undici-types": { - "version": "6.19.8", + "version": "6.20.0", "dev": true, "license": "MIT" }, @@ -1896,6 +1854,28 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "../../src/node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "../../src/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "../../src/node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", "dev": true, @@ -1914,40 +1894,15 @@ "node": ">=8" } }, - "../../src/node_modules/wrap-ansi/node_modules/ansi-regex": { + "../../src/node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { "version": "6.0.1", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "../../src/node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "../../src/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "dev": true, - "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=8" } }, "../../src/node_modules/xid-ts": { @@ -1969,19 +1924,33 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", + "version": "4.4.1", "dev": true, "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { "version": "4.12.1", "dev": true, @@ -1991,11 +1960,11 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.0", + "version": "0.19.1", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.4", + "@eslint/object-schema": "^2.1.5", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -2025,8 +1994,6 @@ }, "node_modules/@eslint/core": { "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", - "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2080,8 +2047,6 @@ }, "node_modules/@eslint/js": { "version": "9.18.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz", - "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", "dev": true, "license": "MIT", "engines": { @@ -2089,7 +2054,7 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.4", + "version": "2.1.5", "dev": true, "license": "Apache-2.0", "engines": { @@ -2098,8 +2063,6 @@ }, "node_modules/@eslint/plugin-kit": { "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", - "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2172,8 +2135,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -2186,8 +2147,6 @@ }, "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, "license": "MIT", "engines": { @@ -2196,8 +2155,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -2209,7 +2166,7 @@ } }, "node_modules/@types/eslint": { - "version": "9.6.0", + "version": "9.6.1", "dev": true, "license": "MIT", "dependencies": { @@ -2237,8 +2194,6 @@ }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.21.0.tgz", - "integrity": "sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA==", "dev": true, "license": "MIT", "dependencies": { @@ -2267,8 +2222,6 @@ }, "node_modules/@typescript-eslint/parser": { "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.21.0.tgz", - "integrity": "sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA==", "dev": true, "license": "MIT", "dependencies": { @@ -2292,8 +2245,6 @@ }, "node_modules/@typescript-eslint/scope-manager": { "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.21.0.tgz", - "integrity": "sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -2310,8 +2261,6 @@ }, "node_modules/@typescript-eslint/type-utils": { "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.21.0.tgz", - "integrity": "sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2334,8 +2283,6 @@ }, "node_modules/@typescript-eslint/types": { "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.21.0.tgz", - "integrity": "sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A==", "dev": true, "license": "MIT", "engines": { @@ -2348,8 +2295,6 @@ }, "node_modules/@typescript-eslint/typescript-estree": { "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.21.0.tgz", - "integrity": "sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg==", "dev": true, "license": "MIT", "dependencies": { @@ -2375,8 +2320,6 @@ }, "node_modules/@typescript-eslint/utils": { "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.21.0.tgz", - "integrity": "sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw==", "dev": true, "license": "MIT", "dependencies": { @@ -2399,8 +2342,6 @@ }, "node_modules/@typescript-eslint/visitor-keys": { "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.21.0.tgz", - "integrity": "sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==", "dev": true, "license": "MIT", "dependencies": { @@ -2415,19 +2356,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/acorn": { "version": "8.14.0", "dev": true, @@ -2487,8 +2415,6 @@ }, "node_modules/assemblyscript": { "version": "0.27.32", - "resolved": "https://registry.npmjs.org/assemblyscript/-/assemblyscript-0.27.32.tgz", - "integrity": "sha512-A8ULHwC6hp4F3moAeQWdciKoccZGZLM9Fsk4pQGywY/fd/S+tslmqBcINroFSI9tW18nO9mC8zh76bik1BkyUA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2535,8 +2461,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -2545,8 +2469,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -2614,11 +2536,11 @@ } }, "node_modules/debug": { - "version": "4.3.5", + "version": "4.4.0", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -2647,8 +2569,6 @@ }, "node_modules/eslint": { "version": "9.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz", - "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==", "dev": true, "license": "MIT", "dependencies": { @@ -2721,11 +2641,11 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.3", + "version": "4.2.0", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -2740,17 +2660,6 @@ "concat-map": "0.0.1" } }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.2", "dev": true, @@ -2778,17 +2687,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/esquery": { "version": "1.6.0", "dev": true, @@ -2834,8 +2732,6 @@ }, "node_modules/fast-glob": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", "dependencies": { @@ -2851,8 +2747,6 @@ }, "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, "license": "ISC", "dependencies": { @@ -2874,8 +2768,6 @@ }, "node_modules/fastq": { "version": "1.18.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", - "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", "dev": true, "license": "ISC", "dependencies": { @@ -2895,8 +2787,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -2934,7 +2824,7 @@ } }, "node_modules/flatted": { - "version": "3.3.1", + "version": "3.3.2", "dev": true, "license": "ISC" }, @@ -2962,8 +2852,6 @@ }, "node_modules/graphemer": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true, "license": "MIT" }, @@ -2976,7 +2864,7 @@ } }, "node_modules/ignore": { - "version": "5.3.1", + "version": "5.3.2", "dev": true, "license": "MIT", "engines": { @@ -3027,8 +2915,6 @@ }, "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, "license": "MIT", "engines": { @@ -3053,8 +2939,6 @@ }, "node_modules/json-as": { "version": "0.9.28", - "resolved": "https://registry.npmjs.org/json-as/-/json-as-0.9.28.tgz", - "integrity": "sha512-IsjpsoUix0nXwW9A5iU15EFLuB62JEiZTIytsnPrfoe3qkJVUTiHWctjoYNAbysMgLRiBWDdTepu/ENnx5LbOg==", "license": "MIT", "dependencies": { "as-virtual": "^0.2.0" @@ -3120,14 +3004,12 @@ "license": "MIT" }, "node_modules/long": { - "version": "5.2.3", + "version": "5.2.4", "dev": true, "license": "Apache-2.0" }, "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, "license": "MIT", "engines": { @@ -3136,8 +3018,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, "license": "MIT", "dependencies": { @@ -3150,8 +3030,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, "license": "ISC", "dependencies": { @@ -3165,7 +3043,7 @@ } }, "node_modules/ms": { - "version": "2.1.2", + "version": "2.1.3", "dev": true, "license": "MIT" }, @@ -3247,8 +3125,6 @@ }, "node_modules/picomatch": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", "engines": { @@ -3268,8 +3144,6 @@ }, "node_modules/prettier": { "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", "dev": true, "license": "MIT", "bin": { @@ -3292,8 +3166,6 @@ }, "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": [ { @@ -3321,8 +3193,6 @@ }, "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, "license": "MIT", "engines": { @@ -3332,8 +3202,6 @@ }, "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": [ { @@ -3356,8 +3224,6 @@ }, "node_modules/semver": { "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "license": "ISC", "bin": { @@ -3410,8 +3276,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -3423,8 +3287,6 @@ }, "node_modules/ts-api-utils": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", - "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", "dev": true, "license": "MIT", "engines": { @@ -3452,8 +3314,6 @@ }, "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, "license": "Apache-2.0", "bin": { @@ -3466,8 +3326,6 @@ }, "node_modules/typescript-eslint": { "version": "8.21.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.21.0.tgz", - "integrity": "sha512-txEKYY4XMKwPXxNkN8+AxAdX6iIJAPiJbHE/FpQccs/sxw8Lf26kqwC3cn0xkHlW8kEbLhkhCsjWuMveaY9Rxw==", "dev": true, "license": "MIT", "dependencies": { diff --git a/sdk/assemblyscript/src/assembly/localtime.ts b/sdk/assemblyscript/src/assembly/localtime.ts index 20796a961..5022eb89b 100644 --- a/sdk/assemblyscript/src/assembly/localtime.ts +++ b/sdk/assemblyscript/src/assembly/localtime.ts @@ -54,3 +54,15 @@ export function NowInZone(tz: string): string { export function GetTimeZone(): string { return process.env.get("TZ"); } + +/** + * Determines whether the specified time zone is valid. + * @param tz A time zone identifier, in IANA format. + * @returns `true` if the time zone is valid; otherwise, `false`. + */ +export function IsValidTimeZone(tz: string): bool { + if (tz === "") { + return false; + } + return hostGetTimeInZone(tz) !== null; +} diff --git a/sdk/assemblyscript/src/models/openai/chat.ts b/sdk/assemblyscript/src/models/openai/chat.ts index ca0019110..804b1fe22 100644 --- a/sdk/assemblyscript/src/models/openai/chat.ts +++ b/sdk/assemblyscript/src/models/openai/chat.ts @@ -9,6 +9,7 @@ import { Model } from "../../assembly/models"; import { JSON } from "json-as"; +import * as base64 from "as-base64/assembly"; /** * Provides input and output types that conform to the OpenAI Chat API. @@ -22,7 +23,7 @@ export class OpenAIChatModel extends Model { * @param messages: An array of messages to send to the chat model. * @returns An input object that can be passed to the `invoke` method. */ - createInput(messages: Message[]): OpenAIChatInput { + createInput(messages: RequestMessage[]): OpenAIChatInput { const model = this.info.fullName; return { model, messages }; } @@ -37,17 +38,38 @@ export class OpenAIChatInput { * The name of the model to use for the chat. * Must be the exact string expected by the model provider. * For example, "gpt-3.5-turbo". - * - * @remarks - * This field is automatically set by the `createInput` method when creating this object. - * It does not need to be set manually. */ model!: string; /** - * An array of messages to send to the chat model. + * The list of messages to send to the chat model. + */ + messages!: RequestMessage[]; + + /** + * Output types that you want the model to generate. + * Text modality is implied if no modalities are specified. + */ + @omitif("this.modalities.length == 0") + modalities: Modality[] = []; + + /** + * Parameters for audio output. + * Required when audio output is requested in the `modalities` field. + */ + @omitnull() + audio: AudioParameters | null = null; + + /** + * Requests audio modality and sets the audio parameters for the input object. + * @param voice The voice the model should use for audio output, such as "ash" or "ballad". + * @param format The format of the audio data, such as "wav" or "mp3". + * @remarks See the model's documentation for a list of all supported voices and formats. */ - messages!: Message[]; + requestAudioOutput(voice: string, format: string): void { + this.modalities = [Modality.Text, Modality.Audio]; + this.audio = new AudioParameters(voice, format); + } /** * Number between `-2.0` and `2.0`. @@ -95,10 +117,20 @@ export class OpenAIChatInput { * The maximum number of tokens to generate in the chat completion. * * @default 4096 + * @deprecated Use the `maxCompletionTokens` parameter instead, unless the model specifically requires passing "max_tokens". */ @alias("max_tokens") @omitif("this.maxTokens == 4096") - maxTokens: i32 = 4096; // TODO: make this an `i32 | null` when supported + maxTokens: i32 = 4096; + + /** + * The maximum number of tokens to generate in the chat completion. + * + * @default 4096 + */ + @alias("max_completion_tokens") + @omitif("this.maxCompletionTokens == 4096") + maxCompletionTokens: i32 = 4096; /** * The number of completions to generate for each prompt. @@ -140,10 +172,18 @@ export class OpenAIChatInput { * parameter to monitor changes in the backend. */ @omitif("this.seed == -1") - seed: i32 = -1; // TODO: make this an `i32 | null` when supported + seed: i32 = -1; /** * Specifies the latency tier to use for processing the request. + * This is relevant for customers subscribed to the scale tier service of the model hosting platform. + * + * - If set to 'ServiceTier.Auto', and the Project is Scale tier enabled, the system will utilize scale tier credits until they are exhausted. + * - If set to 'ServiceTier.Auto', and the Project is not Scale tier enabled, the request will be processed using the default service tier with a lower uptime SLA and no latency guarantee. + * - If set to 'ServiceTier.Default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarantee. + * - When not set, the default behavior is 'ServiceTier.Auto'. + * + * When this is set, the response `serviceTier` property will indicate the service tier utilized. */ @alias("service_tier") @omitnull() @@ -155,12 +195,6 @@ export class OpenAIChatInput { @omitnull() stop: string[] | null = null; - // stream: bool = false; - - // @omitif("this.stream == false") - // @alias("stream_options") - // streamOptions: StreamOptions | null = null; - /** * A number between `0.0` and `2.0` that controls the sampling temperature. * @@ -196,16 +230,16 @@ export class OpenAIChatInput { /** * Controls which (if any) tool is called by the model. - * - `"none"` means the model will not call any tool and instead generates a message. - * - `"auto"` means the model can pick between generating a message or calling one or more tools. - * - `"required"` means the model must call one or more tools. - * - Specifying a particular tool via `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. + * - `ToolChoice.None` means the model will not call any tool and instead generates a message. + * - `ToolChoice.Auto` means the model can pick between generating a message or calling one or more tools. + * - `ToolChoice.Required` means the model must call one or more tools. + * - Specifying a particular tool via `ToolChoice.Function("my_function")` forces the model to call that tool. * - * `none` is the default when no tools are present. `auto` is the default if tools are present. + * `None` is the default when no tools are present. `Auto` is the default if tools are present. */ @alias("tool_choice") @omitnull() - toolChoice: string | null = null; // TODO: Make this work with a custom tool object + toolChoice: ToolChoice | null = null; /** * Whether to enable parallel function calling during tool use. @@ -225,6 +259,97 @@ export class OpenAIChatInput { user: string | null = null; } +/** + * An object specifying which tool the model should call. + */ +export class ToolChoice { + /** + * Constructs a new tool choice object. + * + * @remarks + * Constructor is private to prevent direct instantiation. + * Use the static `None`, `Auto` or `Required` properties, or the static `Function` method instead. + */ + private constructor(type: string, fnName: string | null = null) { + this.type = type; + if (fnName) { + this.function = { name: fnName }; + } + } + + /** + * The type of tool to call. + */ + type: string; + + /** + * The function to call. + */ + @omitnull() + function: ToolChoiceFunction | null = null; + + /** + * Directs the model to not call any tool and instead generates a message. + */ + static get None(): ToolChoice { + return new ToolChoice("none"); + } + + /** + * Directs the model to pick between generating a message or calling one or more tools. + */ + static get Auto(): ToolChoice { + return new ToolChoice("auto"); + } + + /** + * Directs that the model must call one or more tools. + */ + static get Required(): ToolChoice { + return new ToolChoice("required"); + } + + /** + * Forces the model to call a specific tool. + * @param name + * @returns + */ + static Function(name: string): ToolChoice { + return new ToolChoice("function", name); + } +} + +/** + * An object for specifying a function to call. + */ +class ToolChoiceFunction { + /** + * The name of the function to call. + */ + name!: string; +} + +/** + * A type that represents the modality of the chat. + */ +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Modality { + /** + * Text modality requests the model to respond with text. + * This is the default if no other modality is requested. + */ + export const Text = "text"; + + /** + * Audio modality requests the model to respond with spoken audio. + * The model and host must support audio output for this to work. + * Most models that support audio require both text and audio modalities to be specified, + * but the text will come as a transcript in the audio response. + */ + export const Audio = "audio"; +} +export type Modality = string; + /** * The OpenAI service tier used to process the request. */ @@ -243,6 +368,35 @@ export namespace ServiceTier { } export type ServiceTier = string; +/** + * Parameters for audio output. + * Required when audio output is requested in the `modalities` field. + */ +@json +export class AudioParameters { + /** + * Creates a new audio output parameters object. + * @param voice The voice the model should use for audio output, such as "ash" or "ballad". + * @param format The format of the audio data, such as "wav" or "mp3". + */ + constructor(voice: string, format: string) { + this.voice = voice; + this.format = format; + } + + /** + * The voice the model should use for audio output, such as "ash" or "ballad". + * See the model's documentation for a list of all supported voices. + */ + voice: string; + + /** + * The format of the audio data, such as "wav" or "mp3". + * See the model's documentation for a list of all supported formats. + */ + format: string; +} + /** * The output object for the OpenAI Chat API. */ @@ -265,9 +419,15 @@ export class OpenAIChatOutput { choices!: Choice[]; /** - * The Unix timestamp (in seconds) of when the chat completion was created. + * The timestamp of when the chat completion was created. */ - created!: i32; + get created(): Date { + return new Date(this._created * 1000); + } + + + @alias("created") + private _created!: i64; /** * The model used for the chat completion. @@ -281,7 +441,7 @@ export class OpenAIChatOutput { * This field is only included if the `serviceTier` parameter is specified in the request. */ @alias("service_tier") - serviceTier: string | null = null; + serviceTier: ServiceTier | null = null; /** * This fingerprint represents the OpenAI backend configuration that the model runs with. @@ -351,19 +511,21 @@ export class ResponseFormat { static Text: ResponseFormat = { type: "text", jsonSchema: null }; } -// @json -// export class StreamOptions { - -// @omitif("this.includeUsage == false") -// @alias("include_usage") -// includeUsage: bool = false; -// } +class funcParam { + name!: string; + description!: string; + type!: string; +} /** * A tool object that the model may call. */ @json export class Tool { + + @omitif("true") + private _funcParams: funcParam[] = []; + /** * The type of the tool. Currently, only `"function"` is supported. * @@ -375,6 +537,69 @@ export class Tool { * The definition of the function. */ function!: FunctionDefinition; + + /** + * Creates a new tool object for a function. + * @param name The name of the function to call. + * @param description The description of the function. + */ + static forFunction(name: string, description: string): Tool { + const tool = new Tool(); + tool.function = { name, description }; + return tool; + } + + /** + * Adds a parameter to the function used by the tool. + * Note that the type must be a valid JSON Schema type, not an AssemblyScript type. + * For example, use "integer", not "i32". + * @param name The name of the parameter. + * @param jsonSchemaType The JSON Schema type of the parameter. + * @param description The description of the parameter. + */ + withParameter( + name: string, + jsonSchemaType: string, + description: string, + ): Tool { + const param = { + name: name, + type: jsonSchemaType, + description: description, + }; + this._funcParams.push(param); + + let schema = `{"type":"object","properties":{`; + for (let i = 0; i < this._funcParams.length; i++) { + if (i > 0) { + schema += ","; + } + const p = this._funcParams[i]; + schema += `${JSON.stringify(p.name)}:{"type":${JSON.stringify(p.type)},"description":${JSON.stringify(p.description)}}`; + } + schema += `},"required":[`; + for (let i = 0; i < this._funcParams.length; i++) { + if (i > 0) { + schema += ","; + } + schema += JSON.stringify(this._funcParams[i].name); + } + schema += `],"additionalProperties":false}`; + + this.function.parameters = schema; + return this; + } + + /** + * Sets the JSON Schema for the parameters of the function used by the tool. + * Use this for defining complex parameters. Prefer `withParameter` for adding simple parameters, + * which will generate the schema for you automatically. + * @jsonSchema A JSON Schema object as a string, describing the parameters the function accepts. + */ + withParametersSchema(jsonSchema: string): Tool { + this.function.parameters = jsonSchema; + return this; + } } /** @@ -511,7 +736,7 @@ export class Choice { index!: i32; /** - * A chat completion message generated by the model. + * A message generated by the model. */ message!: CompletionMessage; @@ -555,14 +780,14 @@ export class LogprobsContent { * representations must be combined to generate the correct text representation. * Can be null if there is no bytes representation for the token. */ - bytes!: u8[] | null; // TODO: verify this works + bytes!: u8[] | null; /** * List of the most likely tokens and their log probability, at this token position. * In rare cases, there may be fewer than the number of requested `topLogprobs` returned. */ @alias("top_logprobs") - topLogprobs!: TopLogprobsContent[]; // TODO: verify this works + topLogprobs!: TopLogprobsContent[]; } /** @@ -587,23 +812,21 @@ export class TopLogprobsContent { * representations must be combined to generate the correct text representation. * Can be null if there is no bytes representation for the token. */ - bytes!: u8[] | null; // TODO: verify this works + bytes!: u8[] | null; } /** - * A message object that can be sent to the chat model. + * A request message object that can be sent to the chat model. */ @json -export abstract class Message { +export abstract class RequestMessage { /** - * Creates a new message object. + * Creates a new request message object. * * @param role The role of the author of this message. - * @param content The contents of the message. */ - constructor(role: string, content: string) { + constructor(role: string) { this._role = role; - this.content = content; } @@ -616,48 +839,402 @@ export abstract class Message { get role(): string { return this._role; } +} +/** + * A content part object. + */ +@json +export abstract class ContentPart { /** - * The contents of the message. + * Creates a new content part. */ - content: string; + constructor(type: string) { + this._type = type; + } + + /** + * The type of the content part. + */ + get type(): string { + return this._type; + } + + + @alias("type") + private _type: string; +} + +/** + * A text content part. + */ +@json +export class TextContentPart extends ContentPart { + /** + * Creates a new text content part. + */ + constructor(text: string) { + super("text"); + this.text = text; + } + + /** + * The text string. + */ + text: string; +} + +/** + * An image content part. + */ +@json +export class ImageContentPart extends ContentPart { + /** + * Creates a new image content part. + * The model must support image input for this to work. + * @param image The image information, created using the `Image.fromData` or `Image.fromURL` methods. + */ + constructor(image: Image) { + super("image_url"); + this.image = image; + } + + /** + * The image information. + */ + @alias("image_url") + image: Image; +} + +/** + * An audio content part. + */ +@json +export class AudioContentPart extends ContentPart { + /** + * Creates a new audio content part. + * The model must support audio input for this to work. + * @param audio The audio information, created using the `Audio.fromData` method. + */ + constructor(audio: Audio) { + super("input_audio"); + this.audio = audio; + } + + /** + * The audio information. + */ + @alias("input_audio") + audio: Audio; +} + +/** + * A refusal content part. + */ +@json +export class RefusalContentPart extends ContentPart { + /** + * Creates a new refusal content part. + * @param refusal The reason for the refusal. + */ + constructor(refusal: string) { + super("refusal"); + this.refusal = refusal; + } + + /** + * The refusal message generated by the model. + */ + refusal: string; +} + +/** + * An image object, used to represent an image in a content part. + */ +@json +export class Image { + /** + * Creates a new image object. + * @param url The URL of the image. + * @param detail Specifies the detail level of the image. + * + * @remarks + * Constructor is private to prevent direct instantiation. + * Use the static `fromData` or `fromURL` methods instead. + */ + private constructor(url: string, detail: string | null) { + this.url = url; + this.detail = detail; + } + + /** + * The URL of the image. + */ + url: string; + + /** + * Specifies the detail level of the image. + * Can be set to "low", "high", or "auto". + * The default is "auto". + */ + @omitnull() + detail: string | null = null; + + /** + * Creates a new image object from raw image data. + * @param data The raw image data. + * @param contentType A valid image MIME type supported by the model, such as "image/jpeg" or "image/png". + * @param detail Specifies the detail level of the image. Can be set to "low", "high", or "auto". The default is "auto". + */ + static fromData( + data: Uint8Array, + contentType: string, + detail: string | null = null, + ): Image { + // Unlike audio, the url needs to contain a full mime type, not just the format. + // Thus, add the "image/" prefix if it's missing. + if (!contentType.startsWith("image/")) { + contentType = "image/" + contentType; + } + + const url = `data:${contentType};base64,${base64.encode(data)}`; + return new Image(url, detail); + } + + /** + * Creates a new image object from a URL. + * The URL will be sent directly to the model. + * @param url The URL of the image. + * @param detail Specifies the detail level of the image. Can be set to "low", "high", or "auto". The default is "auto". + */ + static fromURL(url: string, detail: string | null = null): Image { + return new Image(url, detail); + } +} + +/** + * An audio object, used to represent audio in a content part. + */ +@json +export class Audio { + /** + * Creates a new audio object. + * @param data The raw audio data. + * @param format A valid audio format supported by the model, such as "wav" or "mp3". + * + * @remarks + * Constructor is private to prevent direct instantiation. + * Use the static `fromData` method instead. + */ + private constructor(data: Uint8Array, format: string) { + this._data = base64.encode(data); + this.format = format; + } + + /** + * The raw audio data. + */ + get data(): Uint8Array { + return base64.decode(this._data); + } + set data(value: Uint8Array) { + this._data = base64.encode(value); + } + + + @alias("data") + private _data: string; + + /** + * The format of the audio data, such as "wav" or "mp3". + * The format must be a valid audio format supported by the model. + */ + format: string; + + /** + * Creates a new audio object from raw audio data. + * @param data The raw audio data. + * @param format A valid audio format supported by the model, such as "wav" or "mp3". + */ + static fromData(data: Uint8Array, format: string): Audio { + // Unlike images, the model expects just the format, not a mime type. + // Thus, strip the "audio/" prefix if present. + if (format.startsWith("audio/")) { + format = format.substring(6); + } + + return new Audio(data, format); + } } /** * A system message. + * System messages are used to provide setup instructions to the model. + * + * @remarks + * Note that system and developer messages are identical in functionality, + * but the "system" role was renamed to "developer" in the OpenAI Chat API. + * Certain models may require one or the other, so use the type that matches the model's requirements. */ @json -export class SystemMessage extends Message { +export class SystemMessage extends RequestMessage { /** * Creates a new system message object. * - * @param content The contents of the message. - */ - constructor(content: string) { - super("system", content); + * @param content The contents of the message, as either a string or as an array of text content parts. + * + * @remarks + * Note that system and developer messages are identical in functionality, + * but the "system" role was renamed to "developer" in the OpenAI Chat API. + * Certain models may require one or the other, so use the type that matches the model's requirements. + */ + constructor(content: T) { + super("system"); + + if (isString(content)) { + this.content = content; + } else if (isArray(content)) { + const arr = content as ContentPart[]; + for (let i = 0; i < arr.length; i++) { + const t = arr[i].type; + if (t != "text") { + throw new Error( + "Invalid content part type. Content parts must be of text type.", + ); + } + } + this.content = content; + } else { + throw new Error( + "Invalid content type. Content must be a string or an array of content parts.", + ); + } } + /** + * The contents of the message. + */ + content: T; + /** * An optional name for the participant. * Provides the model information to differentiate between participants of the same role. */ @omitnull() name: string | null = null; + + /** + * Creates a new system message object from content parts. + * The parts should be `TextContentPart` objects. + * @param parts The content parts of the message. + */ + static fromParts(parts: ContentPart[]): SystemMessage { + return new SystemMessage(parts); + } } /** - * A user message. + * A developer message. + * Developer messages are used to provide setup instructions to the model. + * + * @remarks + * Note that system and developer messages are identical in functionality, + * but the "system" role was renamed to "developer" in the OpenAI Chat API. + * Certain models may require one or the other, so use the type that matches the model's requirements. */ @json -export class UserMessage extends Message { +export class DeveloperMessage extends RequestMessage { /** - * Creates a new user message object. + * Creates a new developer message object. * * @param content The contents of the message. + * + * @remarks + * Note that system and developer messages are identical in functionality, + * but the "system" role was renamed to "developer" in the OpenAI Chat API. + * Certain models may require one or the other, so use the type that matches the model's requirements. + */ + constructor(content: T) { + super("developer"); + + if (isString(content)) { + this.content = content; + } else if (isArray(content)) { + const arr = content as ContentPart[]; + for (let i = 0; i < arr.length; i++) { + const t = arr[i].type; + if (t != "text") { + throw new Error( + "Invalid content part type. Content parts must be of text type.", + ); + } + } + this.content = content; + } else { + throw new Error( + "Invalid content type. Content must be a string or an array of content parts.", + ); + } + } + + /** + * The contents of the message. + */ + content: T; + + /** + * An optional name for the participant. + * Provides the model information to differentiate between participants of the same role. */ - constructor(content: string) { - super("user", content); + @omitnull() + name: string | null = null; + + /** + * Creates a new developer message object from content parts. + * The parts should be `TextContentPart` objects. + * @param parts The content parts of the message. + */ + static fromParts(parts: ContentPart[]): DeveloperMessage { + return new DeveloperMessage(parts); } +} + +/** + * A user message. + */ +@json +export class UserMessage extends RequestMessage { + /** + * Creates a new user message object. + * @param content The contents of the message, as either a string or as an array of text, image, or audio content parts. + */ + constructor(content: T) { + super("user"); + + if (isString(content)) { + this.content = content; + } else if (isArray(content)) { + const arr = content as ContentPart[]; + for (let i = 0; i < arr.length; i++) { + const t = arr[i].type; + if (t != "text" && t != "image_url" && t != "input_audio") { + throw new Error( + "Invalid content part type. Content parts must be of text, image, or audio type.", + ); + } + } + this.content = content; + } else { + throw new Error( + "Invalid content type. Content must be a string or an array of content parts.", + ); + } + } + + /** + * The contents of the message. + */ + content: T; /** * An optional name for the participant. @@ -665,22 +1242,55 @@ export class UserMessage extends Message { */ @omitnull() name: string | null = null; + + /** + * Creates a new user message object from content parts. + * The parts should be `TextContentPart`, `ImageContentPart`, or `AudioContentPart` objects. + * @param parts The content parts of the message. + */ + static fromParts(parts: ContentPart[]): UserMessage { + return new UserMessage(parts); + } } /** - * An assistant message. + * An assistant message object, representing a message generated by the model. */ @json -export class AssistantMessage extends Message { +export class AssistantMessage extends RequestMessage { /** * Creates a new assistant message object. * * @param content The contents of the message. */ - constructor(content: string) { - super("assistant", content); + constructor(content: T) { + super("assistant"); + + if (isString(content)) { + this.content = content; + } else if (isArray(content)) { + const arr = content as ContentPart[]; + for (let i = 0; i < arr.length; i++) { + const t = arr[i].type; + if (t != "text" && t != "refusal") { + throw new Error( + "Invalid content part type. Content parts must be of text or refusal type.", + ); + } + } + this.content = content; + } else { + throw new Error( + "Invalid content type. Content must be a string or an array of content parts.", + ); + } } + /** + * The contents of the message. + */ + content: T; + /** * An optional name for the participant. * Provides the model information to differentiate between participants of the same role. @@ -688,59 +1298,205 @@ export class AssistantMessage extends Message { @omitnull() name: string | null = null; + /** + * The refusal message generated by the model, if any. + */ + @omitnull() + refusal: string | null = null; + /** * The tool calls generated by the model, such as function calls. */ @alias("tool_calls") @omitif("this.toolCalls.length == 0") toolCalls: ToolCall[] = []; + + /** + * Data about a previous audio response from the model. + */ + audio: AudioRef | null = null; + + /** + * Creates a new assistant message object from content parts. + * The parts should be `TextContentPart` or `RefusalContentPart` objects. + * @param parts The content parts of the message. + */ + static fromParts(parts: ContentPart[]): AssistantMessage { + return new AssistantMessage(parts); + } + + /** + * Creates a new assistant message object from a completion message, so it can be used in a conversation. + */ + static fromCompletionMessage( + cm: CompletionMessage, + ): AssistantMessage { + return cm.toAssistantMessage(); + } +} + +/** + * Represents a reference to a previous audio response from the model. + */ +export class AudioRef { + /** + * Creates a new audio reference object. + * @param id Unique identifier for a previous audio response from the model. + */ + constructor(id: string) { + this.id = id; + } + + /** + * Unique identifier for a previous audio response from the model. + */ + id: string; } /** * A tool message. */ @json -export class ToolMessage extends Message { +export class ToolMessage extends RequestMessage { /** * Creates a new tool message object. * * @param content The contents of the message. + * @param toolCallId The tool call that this message is responding to. */ - constructor(content: string, toolCallId: string) { - super("tool", content); + constructor(content: T, toolCallId: string) { + super("tool"); this.toolCallId = toolCallId; + + if (isString(content)) { + this.content = content; + } else if (isArray(content)) { + const arr = content as ContentPart[]; + for (let i = 0; i < arr.length; i++) { + const t = arr[i].type; + if (t != "text") { + throw new Error( + "Invalid content part type. Content parts must be of text type.", + ); + } + } + this.content = content; + } else { + throw new Error( + "Invalid content type. Content must be a string or an array of content parts.", + ); + } } + /** + * The contents of the message. + */ + content: T; + /** * Tool call that this message is responding to. */ @alias("tool_call_id") - toolCallId!: string; + toolCallId: string; + + /** + * Creates a new tool message object from content parts. + * The parts should be `TextContentPart` objects. + * @param toolCallId The tool call that this message is responding to. + * @param parts The content parts of the message. + */ + static fromParts( + toolCallId: string, + parts: ContentPart[], + ): ToolMessage { + return new ToolMessage(parts, toolCallId); + } } /** * A chat completion message generated by the model. + * + * @remarks + * Note that a completion message is not a valid request message. + * To use a completion message in a chat, convert it to an assistant message with the `toAssistantMessage` method. */ @json -export class CompletionMessage extends Message { +export class CompletionMessage { /** - * Creates a new completion message object. - * - * @param role The role of the author of this message. - * @param content The contents of the message. + * The contents of the message. */ - constructor(role: string, content: string) { - super(role, content); - } + content!: string; /** * The refusal message generated by the model. */ + @omitnull() refusal: string | null = null; /** * The tool calls generated by the model, such as function calls. */ @alias("tool_calls") + @omitif("this.toolCalls.length == 0") toolCalls: ToolCall[] = []; + + /** + * The audio output generated by the model, if any. + * Used only when audio output is requested in the `modalities` field, and when the model and host support audio output. + */ + @omitnull() + audio: AudioOutput | null = null; + + /** + * Converts the completion message to an assistant message, so it can be used in a conversation. + */ + toAssistantMessage(): AssistantMessage { + const am = new AssistantMessage(this.content); + am.refusal = this.refusal; + am.toolCalls = this.toolCalls; + + if (this.audio) { + am.audio = new AudioRef(this.audio!.id); + } + + return am; + } +} + +/** + * An audio output object generated by the model when using audio modality. + */ +@json +export class AudioOutput { + /** + * Unique identifier for this audio response. + */ + id!: string; + + /** + * The time at which this audio content will no longer be accessible on the server for use in multi-turn conversations. + */ + get expiresAt(): Date { + return new Date(this._expiresAt * 1000); + } + + + @alias("expires_at") + private _expiresAt!: i64; + + /** + * The raw audio data, in the format specified in the request. + */ + get data(): Uint8Array { + return base64.decode(this._data); + } + + + @alias("data") + private _data!: string; + + /** + * Transcript of the audio generated by the model. + */ + transcript!: string; } diff --git a/sdk/assemblyscript/src/package-lock.json b/sdk/assemblyscript/src/package-lock.json index fbb89a65e..ff21e38dd 100644 --- a/sdk/assemblyscript/src/package-lock.json +++ b/sdk/assemblyscript/src/package-lock.json @@ -8,6 +8,7 @@ "license": "Apache-2.0", "dependencies": { "@assemblyscript/wasi-shim": "^0.1.0", + "as-base64": "^0.2.0", "chalk": "^5.4.1", "json-as": "^0.9.28", "semver": "^7.6.3", @@ -35,6 +36,8 @@ }, "node_modules/@assemblyscript/wasi-shim": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@assemblyscript/wasi-shim/-/wasi-shim-0.1.0.tgz", + "integrity": "sha512-fSLH7MdJHf2uDW5llA5VCF/CG62Jp2WkYGui9/3vIWs3jDhViGeQF7nMYLUjpigluM5fnq61I6obtCETy39FZw==", "license": "Apache-2.0", "funding": { "type": "opencollective", @@ -42,35 +45,55 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { "version": "4.12.1", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/config-array": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.0.tgz", - "integrity": "sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", + "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.4", + "@eslint/object-schema": "^2.1.5", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -96,6 +119,7 @@ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -125,10 +149,11 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", - "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", + "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -152,6 +177,7 @@ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18.0" } @@ -161,6 +187,7 @@ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" @@ -174,6 +201,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -184,6 +212,8 @@ }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -199,6 +229,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -225,35 +256,6 @@ "node": ">=12" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "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, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -292,21 +294,10 @@ "node": ">= 8" } }, - "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, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, "node_modules/@types/eslint": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.0.tgz", - "integrity": "sha512-gi6WQJ7cHRgZxtkQEoyHMppPjq9Kxo5Tjn2prSKDSmZrCz8TZ3jSRCeTJm+WoM+oB0WG37bRqLzaaU3q7JypGg==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "license": "MIT", "dependencies": { @@ -328,7 +319,8 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -485,6 +477,16 @@ "typescript": ">=4.8.4 <5.8.0" } }, + "node_modules/@typescript-eslint/typescript-estree/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, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -543,24 +545,12 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -573,6 +563,7 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -582,6 +573,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -594,22 +586,26 @@ } }, "node_modules/ansi-regex": { - "version": "5.0.1", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/ansi-styles": { - "version": "4.3.0", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -619,7 +615,14 @@ "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 + "dev": true, + "license": "Python-2.0" + }, + "node_modules/as-base64": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/as-base64/-/as-base64-0.2.0.tgz", + "integrity": "sha512-j6JxprAVN4SXUcZiSChuMBf0JJOCdh5OfrHAepluxmFWuDZZm+YjmfNSk8djUHRPEB+Ui5HGvrz46GLvTJf3ig==", + "license": "MIT" }, "node_modules/as-console": { "version": "7.0.0", @@ -659,46 +662,6 @@ "ast": "bin/index.js" } }, - "node_modules/as-test/node_modules/glob": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", - "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", - "dev": true, - "license": "ISC", - "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/as-test/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, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/as-variant": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/as-variant/-/as-variant-0.4.1.tgz", @@ -708,6 +671,8 @@ }, "node_modules/as-virtual": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/as-virtual/-/as-virtual-0.2.0.tgz", + "integrity": "sha512-zI+8VwD6BPUtXrxs4R7v2N+59OAIBRwiiv9nGIMwcQ/w/lspyaualMqM7yok3jMRglKqLkfOx+dCJimeflEJcA==", "license": "MIT" }, "node_modules/assemblyscript": { @@ -735,6 +700,8 @@ }, "node_modules/assemblyscript-prettier": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/assemblyscript-prettier/-/assemblyscript-prettier-3.0.1.tgz", + "integrity": "sha512-q5D9rJOr1xG8p2MdcAF3wvIBPrmHaKgenJbmYrwM1T96sHKFeTOfCtgHHmrDnrqoly7wSGfWtbE84omC1IUJEA==", "dev": true, "license": "MIT", "dependencies": { @@ -746,11 +713,15 @@ }, "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, "license": "MIT" }, "node_modules/binaryen": { "version": "116.0.0-nightly.20240114", + "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-116.0.0-nightly.20240114.tgz", + "integrity": "sha512-0GZrojJnuhoe+hiwji7QFaL3tBlJoA+KFUN7ouYSDGZLSo9CKM8swQX8n/UcbR0d1VuZKU+nhogNzv423JEu5A==", "dev": true, "license": "Apache-2.0", "bin": { @@ -759,13 +730,14 @@ } }, "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==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/braces": { @@ -786,6 +758,7 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -804,6 +777,8 @@ }, "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, "license": "MIT", "dependencies": { @@ -815,6 +790,8 @@ }, "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, "license": "MIT" }, @@ -822,13 +799,15 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "dev": true, + "license": "MIT" }, "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, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -839,11 +818,13 @@ } }, "node_modules/debug": { - "version": "4.3.5", + "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, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -856,6 +837,8 @@ }, "node_modules/deep-is": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, "license": "MIT" }, @@ -875,6 +858,8 @@ }, "node_modules/escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", "engines": { @@ -949,6 +934,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -961,16 +947,34 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.3", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/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, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -988,24 +992,12 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/espree": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", @@ -1018,20 +1010,10 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/esquery": { - "version": "1.5.0", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -1046,6 +1028,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -1055,6 +1038,8 @@ }, "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, "license": "BSD-2-Clause", "engines": { @@ -1075,7 +1060,8 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/fast-glob": { "version": "3.3.3", @@ -1111,10 +1097,13 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, "license": "MIT" }, @@ -1156,6 +1145,8 @@ }, "node_modules/find-up": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { @@ -1184,16 +1175,16 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "dev": true, "license": "ISC" }, "node_modules/foreground-child": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", - "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "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, "license": "ISC", "dependencies": { @@ -1207,8 +1198,34 @@ "url": "https://github.com/sponsors/isaacs" } }, + "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, + "license": "ISC", + "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/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, "license": "ISC", "dependencies": { @@ -1218,11 +1235,38 @@ "node": ">=10.13.0" } }, + "node_modules/glob/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, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/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, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -1248,7 +1292,9 @@ } }, "node_modules/ignore": { - "version": "5.3.1", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { @@ -1260,6 +1306,7 @@ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -1273,6 +1320,8 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", "engines": { @@ -1281,6 +1330,8 @@ }, "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, "license": "MIT", "engines": { @@ -1299,6 +1350,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, "license": "MIT", "dependencies": { @@ -1320,13 +1373,15 @@ }, "node_modules/isexe": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, "license": "ISC" }, "node_modules/jackspeak": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.1.tgz", - "integrity": "sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -1337,9 +1392,6 @@ }, "funding": { "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/js-yaml": { @@ -1347,6 +1399,7 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -1374,10 +1427,13 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, "license": "MIT" }, @@ -1393,6 +1449,8 @@ }, "node_modules/levn": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1405,6 +1463,8 @@ }, "node_modules/locate-path": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { @@ -1419,23 +1479,29 @@ }, "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==", "dev": true, "license": "MIT" }, "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, "license": "MIT" }, "node_modules/long": { - "version": "5.2.3", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz", + "integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==", "dev": true, "license": "Apache-2.0" }, "node_modules/lru-cache": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.0.tgz", - "integrity": "sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==", + "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, "license": "ISC", "engines": { @@ -1471,6 +1537,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1478,16 +1545,6 @@ "node": "*" } }, - "node_modules/minimatch/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/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -1499,17 +1556,23 @@ } }, "node_modules/ms": { - "version": "2.1.2", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, "node_modules/natural-compare": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, "license": "MIT" }, "node_modules/optionator": { "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", "dependencies": { @@ -1526,6 +1589,8 @@ }, "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==", "dev": true, "license": "MIT", "dependencies": { @@ -1540,6 +1605,8 @@ }, "node_modules/p-locate": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { @@ -1553,9 +1620,9 @@ } }, "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "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, "license": "BlueOak-1.0.0" }, @@ -1564,6 +1631,7 @@ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -1573,6 +1641,8 @@ }, "node_modules/path-exists": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", "engines": { @@ -1581,6 +1651,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, "license": "MIT", "engines": { @@ -1619,6 +1691,8 @@ }, "node_modules/prelude-ls": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", "engines": { @@ -1646,6 +1720,7 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -1676,6 +1751,7 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -1719,6 +1795,7 @@ "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -1728,6 +1805,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, "license": "MIT", "dependencies": { @@ -1739,6 +1818,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, "license": "MIT", "engines": { @@ -1792,6 +1873,16 @@ "node": ">=8" } }, + "node_modules/string-width-cjs/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, + "license": "MIT", + "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", @@ -1799,20 +1890,20 @@ "dev": true, "license": "MIT" }, - "node_modules/string-width/node_modules/ansi-regex": { + "node_modules/string-width-cjs/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "ansi-regex": "^5.0.1" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "engines": { + "node": ">=8" } }, - "node_modules/string-width/node_modules/strip-ansi": { + "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==", @@ -1828,8 +1919,11 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/strip-ansi": { + "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, "license": "MIT", "dependencies": { @@ -1839,16 +1933,12 @@ "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==", + "node_modules/strip-ansi-cjs/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, "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, "engines": { "node": ">=8" } @@ -1858,6 +1948,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -1913,11 +2004,15 @@ }, "node_modules/ts-mixer": { "version": "6.0.4", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", + "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==", "dev": true, "license": "MIT" }, "node_modules/type-check": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", "dependencies": { @@ -1986,12 +2081,15 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } }, "node_modules/visitor-as": { "version": "0.11.4", + "resolved": "https://registry.npmjs.org/visitor-as/-/visitor-as-0.11.4.tgz", + "integrity": "sha512-uih7AooY2V3LhzobjLqyEQzhYYBGeq0y/rZk295foM1Ko498f24NNXBDyM1SgzcLMFCAT/fpmSRco1BpTIdKNQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2004,6 +2102,8 @@ }, "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, "license": "ISC", "dependencies": { @@ -2018,6 +2118,8 @@ }, "node_modules/word-wrap": { "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", "engines": { @@ -2061,6 +2163,32 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs/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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/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, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?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", @@ -2083,58 +2211,32 @@ "node": ">=8" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/wrap-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, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/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==", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=8" } }, "node_modules/xid-ts": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/xid-ts/-/xid-ts-1.1.4.tgz", "integrity": "sha512-bdBn9nW1e8O/dcrNYhAHJ+Iabe384mLoPkRypAylyazI+AfJnV0JMIoH/1DUnsdcCHLn/ZEH7Pt5kRUH4xnJdA==", + "license": "MIT", "engines": { "node": ">=18.0.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==", "dev": true, "license": "MIT", "engines": { diff --git a/sdk/assemblyscript/src/package.json b/sdk/assemblyscript/src/package.json index 7e1f385fb..d10f7e07b 100644 --- a/sdk/assemblyscript/src/package.json +++ b/sdk/assemblyscript/src/package.json @@ -21,6 +21,7 @@ }, "dependencies": { "@assemblyscript/wasi-shim": "^0.1.0", + "as-base64": "^0.2.0", "chalk": "^5.4.1", "json-as": "^0.9.28", "semver": "^7.6.3",