From 915735432dda4708d87ad2083e7ef911689a8601 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Fri, 28 Mar 2025 13:14:29 +0100 Subject: [PATCH 01/67] initial week 14 commit --- .../BedrockService/Converse/ConversionExtensions.swift | 4 ++++ backend/Sources/BedrockTypes/Content.swift | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions.swift index d52a11c1..b5e38617 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions.swift @@ -48,6 +48,8 @@ extension Content { switch sdkContentBlock { case .text(let text): self = .text(text) + // case .image(let image): // TODO + // self = .image(image) case .sdkUnknown(let unknownContentBlock): throw BedrockServiceError.notImplemented( "ContentBlock \(unknownContentBlock) is not implemented by BedrockRuntimeClientTypes" @@ -63,6 +65,8 @@ extension Content { switch self { case .text(let text): return BedrockRuntimeClientTypes.ContentBlock.text(text) + // case .image(let format, let source): + // return BedrockRuntimeClientTypes.ContentBlock.image(BedrockRuntimeClientTypes.ImageBlock(format: format, source: source)) } } } diff --git a/backend/Sources/BedrockTypes/Content.swift b/backend/Sources/BedrockTypes/Content.swift index 3fedbf9d..dcdc7435 100644 --- a/backend/Sources/BedrockTypes/Content.swift +++ b/backend/Sources/BedrockTypes/Content.swift @@ -17,4 +17,12 @@ import Foundation public enum Content: Codable { case text(String) + // case image(ImageFormat, String) //string are the bytes + + public enum ImageFormat: Codable { + case gif + case jpeg + case png + case webp + } } \ No newline at end of file From 9e4ddd87b0e9bd43b921f269e5bef995f3d71756 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 10:34:19 +0200 Subject: [PATCH 02/67] backend: converse image --- .../Converse/ConverseRequest.swift | 8 +-- .../Converse/ConversionExtensions.swift | 71 ++++++++++++++++--- .../BedrockService/ParameterValidation.swift | 4 +- .../Sources/BedrockService/SwiftBedrock.swift | 11 ++- .../Sources/BedrockTypes/BedrockModel.swift | 29 ++++++++ backend/Sources/BedrockTypes/Content.swift | 2 +- .../Modalities/ConverseModality.swift | 23 ++++++ .../Models/Amazon/Nova/Nova.swift | 6 +- .../Parameters/ConverseParameters.swift | 46 ++++++++++++ 9 files changed, 181 insertions(+), 19 deletions(-) create mode 100644 backend/Sources/BedrockTypes/Modalities/ConverseModality.swift create mode 100644 backend/Sources/BedrockTypes/Parameters/ConverseParameters.swift diff --git a/backend/Sources/BedrockService/Converse/ConverseRequest.swift b/backend/Sources/BedrockService/Converse/ConverseRequest.swift index c11f8488..913a6c56 100644 --- a/backend/Sources/BedrockService/Converse/ConverseRequest.swift +++ b/backend/Sources/BedrockService/Converse/ConverseRequest.swift @@ -40,7 +40,7 @@ public struct ConverseRequest { ) } - func getConverseInput() -> ConverseInput { + func getConverseInput() throws -> ConverseInput { let sdkInferenceConfig: BedrockRuntimeClientTypes.InferenceConfiguration? if inferenceConfig != nil { sdkInferenceConfig = inferenceConfig!.getSDKInferenceConfig() @@ -49,13 +49,13 @@ public struct ConverseRequest { } return ConverseInput( inferenceConfig: sdkInferenceConfig, - messages: getSDKMessages(), + messages: try getSDKMessages(), modelId: model.id ) } - private func getSDKMessages() -> [BedrockRuntimeClientTypes.Message] { - messages.map { $0.getSDKMessage() } + private func getSDKMessages() throws -> [BedrockRuntimeClientTypes.Message] { + try messages.map { try $0.getSDKMessage() } } struct InferenceConfig { diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions.swift index b5e38617..c76c7814 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions.swift @@ -14,8 +14,8 @@ //===----------------------------------------------------------------------===// @preconcurrency import AWSBedrockRuntime -import Foundation import BedrockTypes +import Foundation extension Message { @@ -30,10 +30,10 @@ extension Message { self = Message(from: try Role(from: sdkRole), content: content) } - func getSDKMessage() -> BedrockRuntimeClientTypes.Message { - let contentBlocks: [BedrockRuntimeClientTypes.ContentBlock] = content.map { + func getSDKMessage() throws -> BedrockRuntimeClientTypes.Message { + let contentBlocks: [BedrockRuntimeClientTypes.ContentBlock] = try content.map { content -> BedrockRuntimeClientTypes.ContentBlock in - return content.getSDKContentBlock() + return try content.getSDKContentBlock() } return BedrockRuntimeClientTypes.Message( content: contentBlocks, @@ -48,8 +48,25 @@ extension Content { switch sdkContentBlock { case .text(let text): self = .text(text) - // case .image(let image): // TODO - // self = .image(image) + case .image(let sdkImage): + guard let sdkFormat = sdkImage.format else { + throw BedrockServiceError.decodingError( + "Could not extract format from BedrockRuntimeClientTypes.ImageBlock" + ) + } + guard let sdkImageSource = sdkImage.source else { + throw BedrockServiceError.decodingError( + "Could not extract source from BedrockRuntimeClientTypes.ImageBlock" + ) + } + switch sdkImageSource { + case .bytes(let data): + self = .image(format: try Content.ImageFormat(from: sdkFormat), source: data.base64EncodedString()) + case .sdkUnknown(let unknownImageSource): + throw BedrockServiceError.notImplemented( + "ImageSource \(unknownImageSource) is not implemented by BedrockRuntimeClientTypes" + ) + } case .sdkUnknown(let unknownContentBlock): throw BedrockServiceError.notImplemented( "ContentBlock \(unknownContentBlock) is not implemented by BedrockRuntimeClientTypes" @@ -61,14 +78,50 @@ extension Content { } } - func getSDKContentBlock() -> BedrockRuntimeClientTypes.ContentBlock { + func getSDKContentBlock() throws -> BedrockRuntimeClientTypes.ContentBlock { switch self { case .text(let text): return BedrockRuntimeClientTypes.ContentBlock.text(text) - // case .image(let format, let source): - // return BedrockRuntimeClientTypes.ContentBlock.image(BedrockRuntimeClientTypes.ImageBlock(format: format, source: source)) + case .image(let format, let source): + guard let data = Data(base64Encoded: source) else { + throw BedrockServiceError.decodingError( + "Could not decode image source from base64 string. String: \(source)" + ) + } + return BedrockRuntimeClientTypes.ContentBlock.image( + BedrockRuntimeClientTypes.ImageBlock( + format: format.getSDKImageFormat(), + source: BedrockRuntimeClientTypes.ImageSource.bytes(data) + ) + ) + } + } +} + +extension Content.ImageFormat { + + init(from sdkImageFormat: BedrockRuntimeClientTypes.ImageFormat) throws { + switch sdkImageFormat { + case .gif: self = .gif + case .jpeg: self = .jpeg + case .png: self = .png + case .webp: self = .webp + case .sdkUnknown(let unknownImageFormat): + throw BedrockServiceError.notImplemented( + "ImageFormat \(unknownImageFormat) is not implemented by BedrockRuntimeClientTypes" + ) + } + } + + func getSDKImageFormat() -> BedrockRuntimeClientTypes.ImageFormat { + switch self { + case .gif: return .gif + case .jpeg: return .jpeg + case .png: return .png + case .webp: return .webp } } + } extension Role { diff --git a/backend/Sources/BedrockService/ParameterValidation.swift b/backend/Sources/BedrockService/ParameterValidation.swift index a097c44a..429a6849 100644 --- a/backend/Sources/BedrockService/ParameterValidation.swift +++ b/backend/Sources/BedrockService/ParameterValidation.swift @@ -108,7 +108,7 @@ extension BedrockService { /// Validate parameters for a converse request public func validateConverseParams( - modality: any TextModality, + modality: any ConverseModality, prompt: String, history: [Message], maxTokens: Int?, @@ -116,7 +116,7 @@ extension BedrockService { topP: Double?, stopSequences: [String]? ) throws { - let parameters = modality.getParameters() + let parameters = modality.getConverseParameters() try validatePrompt(prompt, maxPromptTokens: parameters.prompt.maxSize) if maxTokens != nil { try validateParameterValue(maxTokens!, parameter: parameters.maxTokens) diff --git a/backend/Sources/BedrockService/SwiftBedrock.swift b/backend/Sources/BedrockService/SwiftBedrock.swift index 9e6df845..29f2b4b1 100644 --- a/backend/Sources/BedrockService/SwiftBedrock.swift +++ b/backend/Sources/BedrockService/SwiftBedrock.swift @@ -460,6 +460,8 @@ public struct BedrockService: Sendable { public func converse( with model: BedrockModel, prompt: String, + imageFormat: Content.ImageFormat? = nil, + imageBytes: String? = nil, history: [Message] = [], maxTokens: Int? = nil, temperature: Double? = nil, @@ -475,7 +477,7 @@ public struct BedrockService: Sendable { ] ) do { - let modality = try model.getTextModality() // FIXME later: ConverseModality? + let modality: ConverseModality = try model.getConverseModality() try validateConverseParams( modality: modality, prompt: prompt, @@ -488,6 +490,11 @@ public struct BedrockService: Sendable { var messages = history messages.append(Message(from: .user, content: [.text(prompt)])) + if let imageFormat: Content.ImageFormat = imageFormat, + let imageBytes: String = imageBytes + { + messages.append(Message(from: .user, content: [.image(format: imageFormat, source: imageBytes)])) + } let converseRequest = ConverseRequest( model: model, @@ -497,7 +504,7 @@ public struct BedrockService: Sendable { topP: topP, stopSequences: stopSequences ) - let input = converseRequest.getConverseInput() + let input = try converseRequest.getConverseInput() logger.trace( "Created ConverseInput", metadata: ["messages.count": "\(messages.count)", "model": "\(model.id)"] diff --git a/backend/Sources/BedrockTypes/BedrockModel.swift b/backend/Sources/BedrockTypes/BedrockModel.swift index fafea5b3..78f24eb4 100644 --- a/backend/Sources/BedrockTypes/BedrockModel.swift +++ b/backend/Sources/BedrockTypes/BedrockModel.swift @@ -113,6 +113,35 @@ public struct BedrockModel: Hashable, Sendable, Equatable, RawRepresentable { return textModality } + /// Checks if the model supports text generation and returns ConverseModality + /// - Returns: ConverseModality if the model supports text modality + public func getConverseModality() throws -> any ConverseModality { + guard let modality = modality as? any ConverseModality else { + throw BedrockServiceError.invalidModality( + self, + modality, + "Model \(id) does not support text generation" + ) + } + return modality + } + + // FIXME: this would be cleaner + // Error: Only concrete types such as structs, enums and classes can conform to protocols + public func getModality() throws -> M { + guard let modality = modality as? M else { + throw BedrockServiceError.invalidModality( + self, + modality, + "Model \(id) does not support \(M.self)" + ) + } + return modality + } + public func hasModality(_ type: M.Type) -> Bool { + modality as? M != nil + } + /// Checks if the model supports image generation /// - Returns: True if the model supports image generation public func hasImageModality() -> Bool { diff --git a/backend/Sources/BedrockTypes/Content.swift b/backend/Sources/BedrockTypes/Content.swift index dcdc7435..2e2200ff 100644 --- a/backend/Sources/BedrockTypes/Content.swift +++ b/backend/Sources/BedrockTypes/Content.swift @@ -17,7 +17,7 @@ import Foundation public enum Content: Codable { case text(String) - // case image(ImageFormat, String) //string are the bytes + case image(format: ImageFormat, source: String) // String are the 64 encoded bytes public enum ImageFormat: Codable { case gif diff --git a/backend/Sources/BedrockTypes/Modalities/ConverseModality.swift b/backend/Sources/BedrockTypes/Modalities/ConverseModality.swift new file mode 100644 index 00000000..3745c608 --- /dev/null +++ b/backend/Sources/BedrockTypes/Modalities/ConverseModality.swift @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Foundation + +public protocol ConverseModality: Modality { + // init(textGenerationParameters: TextGenerationParameters?, imageGenerationparameters: ImageGenerationParameters?) + // init(parameters: ConverseParameters) + + func getConverseParameters() -> ConverseParameters +} diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift b/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift index 8525b357..10c82f43 100644 --- a/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift +++ b/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift @@ -15,7 +15,7 @@ import Foundation -struct NovaText: TextModality { +struct NovaText: TextModality, ConverseModality { func getName() -> String { "Nova Text Generation" } let parameters: TextGenerationParameters @@ -28,6 +28,10 @@ struct NovaText: TextModality { parameters } + func getConverseParameters() -> ConverseParameters { + ConverseParameters(textGenerationParameters: parameters) + } + func getTextRequestBody( prompt: String, maxTokens: Int?, diff --git a/backend/Sources/BedrockTypes/Parameters/ConverseParameters.swift b/backend/Sources/BedrockTypes/Parameters/ConverseParameters.swift new file mode 100644 index 00000000..94859d12 --- /dev/null +++ b/backend/Sources/BedrockTypes/Parameters/ConverseParameters.swift @@ -0,0 +1,46 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Foundation + +public struct ConverseParameters: Parameters { + public let temperature: Parameter + public let maxTokens: Parameter + public let topP: Parameter + public let prompt: PromptParams + public let stopSequences: StopSequenceParams + + public init( + temperature: Parameter, + maxTokens: Parameter, + topP: Parameter, + stopSequences: StopSequenceParams, + maxPromptSize: Int? + ) { + self.temperature = temperature + self.maxTokens = maxTokens + self.topP = topP + self.prompt = PromptParams(maxSize: maxPromptSize) + self.stopSequences = stopSequences + } + + public init(textGenerationParameters: TextGenerationParameters) { + self.temperature = textGenerationParameters.temperature + self.maxTokens = textGenerationParameters.maxTokens + self.topP = textGenerationParameters.topP + self.prompt = textGenerationParameters.prompt + self.stopSequences = textGenerationParameters.stopSequences + } +} From beee69bb7ba07ad06e25c8686dcce40f131af638 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 11:42:15 +0200 Subject: [PATCH 03/67] frontend: converse vision + backend: modality changes --- backend/Sources/App/Application+build.swift | 6 +- backend/Sources/App/Types/Chat.swift | 2 + .../Sources/BedrockService/SwiftBedrock.swift | 8 ++ .../Sources/BedrockTypes/BedrockModel.swift | 28 ++++++- .../Modalities/ConverseModality.swift | 13 ++- .../Models/Amazon/Nova/Nova.swift | 14 ++++ .../Amazon/Nova/NovaBedrockModels.swift | 19 ++++- .../chatPlayground/ChatComponent.jsx | 80 ++++++++++++++++++- frontend/helpers/modelData.js | 14 ++++ 9 files changed, 173 insertions(+), 11 deletions(-) diff --git a/backend/Sources/App/Application+build.swift b/backend/Sources/App/Application+build.swift index 4e807eae..d896b650 100644 --- a/backend/Sources/App/Application+build.swift +++ b/backend/Sources/App/Application+build.swift @@ -168,13 +168,15 @@ func buildRouter(useSSO: Bool, logger: Logger) async throws -> Router Bool { modality as? any ConditionedTextToImageModality != nil } + + /// Checks if the model supports converse + /// - Returns: True if the model supports converse + public func hasConverseModality() -> Bool { + modality as? any ConverseModality != nil + } + + /// Checks if the model supports converse vision + /// - Returns: True if the model supports converse vision + public func hasConverseVisionModality() -> Bool { + modality as? any ConverseVisionModality != nil + } + + public func hasConverseDocumentModality() -> Bool { + modality as? any ConverseDocumentModality != nil + } + + public func hasConverseToolModality() -> Bool { + modality as? any ConverseToolModality != nil + } } extension BedrockModel: Encodable { diff --git a/backend/Sources/BedrockTypes/Modalities/ConverseModality.swift b/backend/Sources/BedrockTypes/Modalities/ConverseModality.swift index 3745c608..cde2c5cd 100644 --- a/backend/Sources/BedrockTypes/Modalities/ConverseModality.swift +++ b/backend/Sources/BedrockTypes/Modalities/ConverseModality.swift @@ -15,9 +15,16 @@ import Foundation +// Text public protocol ConverseModality: Modality { - // init(textGenerationParameters: TextGenerationParameters?, imageGenerationparameters: ImageGenerationParameters?) - // init(parameters: ConverseParameters) - func getConverseParameters() -> ConverseParameters } + +// Vision +public protocol ConverseVisionModality: ConverseModality {} + +// Document +public protocol ConverseDocumentModality: ConverseModality {} + +// Tool use +public protocol ConverseToolModality: ConverseModality {} diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift b/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift index 10c82f43..8be92506 100644 --- a/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift +++ b/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift @@ -15,6 +15,20 @@ import Foundation +struct NovaConverse: ConverseVisionModality, ConverseDocumentModality, ConverseToolModality { + func getName() -> String { "Nova Lite Converse Modality" } + + let converseParameters: ConverseParameters + + init(parameters: ConverseParameters) { + self.converseParameters = parameters + } + + func getConverseParameters() -> ConverseParameters { + converseParameters + } +} + struct NovaText: TextModality, ConverseModality { func getName() -> String { "Nova Text Generation" } diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift index 2a624534..b0a7317d 100644 --- a/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift +++ b/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift @@ -22,7 +22,8 @@ typealias NovaMicro = NovaText extension BedrockModel { public static let nova_micro: BedrockModel = BedrockModel( - id: "amazon.nova-micro-v1:0", name: "Nova Micro", + id: "amazon.nova-micro-v1:0", + name: "Nova Micro", modality: NovaText( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0.00001, maxValue: 1, defaultValue: 0.7), @@ -34,6 +35,19 @@ extension BedrockModel { ) ) ) + public static let nova_lite: BedrockModel = BedrockModel( + id: "amazon.nova-lite-v1:0", + name: "Nova Lite", + modality: NovaConverse( + parameters: ConverseParameters( + temperature: Parameter(.temperature, minValue: 0.00001, maxValue: 1, defaultValue: 0.7), + maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 5_000, defaultValue: 5_000), + topP: Parameter(.topP, minValue: 0, maxValue: 1.0, defaultValue: 0.9), + stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), + maxPromptSize: nil + ) + ) + ) } // MARK: image generation @@ -42,7 +56,8 @@ typealias NovaCanvas = AmazonImage extension BedrockModel { public static let nova_canvas: BedrockModel = BedrockModel( - id: "amazon.nova-canvas-v1:0", name: "Nova Canvas", + id: "amazon.nova-canvas-v1:0", + name: "Nova Canvas", modality: NovaCanvas( parameters: ImageGenerationParameters( nrOfImages: Parameter(.nrOfImages, minValue: 1, maxValue: 5, defaultValue: 1), diff --git a/frontend/components/chatPlayground/ChatComponent.jsx b/frontend/components/chatPlayground/ChatComponent.jsx index 4383880f..9fe020de 100644 --- a/frontend/components/chatPlayground/ChatComponent.jsx +++ b/frontend/components/chatPlayground/ChatComponent.jsx @@ -8,6 +8,7 @@ import GlobalConfig from "@/app/app.config"; import ChatModelSelector from "./ChatModelSelector"; import { defaultModel } from "@/helpers/modelData"; import NumericInput from "../NumericInput"; +import Image from 'next/image'; export default function ChatContainer() { @@ -21,7 +22,8 @@ export default function ChatContainer() { const [topP, setTopP] = useState(0.9); const [stopSequences, setStopSequences] = useState([]); const [stopSequenceInput, setStopSequenceInput] = useState(''); - + const [referenceImage, setReferenceImage] = useState(null); + const [previewImage, setPreviewImage] = useState('/placeholder.png'); const endpoint = `/foundation-models/chat/${selectedModel.modelId}`; const api = `${GlobalConfig.apiHost}:${GlobalConfig.apiPort}${endpoint}`; @@ -66,6 +68,63 @@ export default function ChatContainer() { setHistory([]); }; + const handleFileUpload = async (event) => { + const file = event.target.files[0]; + if (file) { + // Create preview for the UI + const reader = new FileReader(); + reader.onload = (e) => { + setPreviewImage(e.target.result); + }; + reader.readAsDataURL(file); + + // Compress and store the image + const compressedBase64 = await compressImage(file); + console.log('Compressed image size:', compressedBase64.length); + setReferenceImage(compressedBase64); + } + }; + + const compressImage = (file) => { + return new Promise((resolve) => { + const reader = new FileReader(); + reader.onload = (event) => { + const img = document.createElement('img'); // Use regular img element instead of Next.js Image + img.onload = () => { + const canvas = document.createElement('canvas'); + // Reduce max dimensions to 512x512 + const maxSize = 512; + let width = img.width; + let height = img.height; + + if (width > height) { + if (width > maxSize) { + height *= maxSize / width; + width = maxSize; + } + } else { + if (height > maxSize) { + width *= maxSize / height; + height = maxSize; + } + } + + canvas.width = width; + canvas.height = height; + + const ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0, width, height); + + // Increase compression by reducing quality to 0.5 (50%) + const compressedBase64 = canvas.toDataURL('image/jpeg', 0.5); + resolve(compressedBase64.split(',')[1]); + }; + img.src = event.target.result; + }; + reader.readAsDataURL(file); + }); + }; + const sendMessage = async () => { const newMessage = { sender: "Human", message: inputValue }; setConversation(prevConversation => [...prevConversation, newMessage]); @@ -80,6 +139,7 @@ export default function ChatContainer() { body: JSON.stringify({ prompt: inputValue, history: history, + imageBytes: referenceImage, maxTokens: parseInt(maxTokens, 10), temperature: parseFloat(temperature), topP: parseFloat(topP), @@ -236,6 +296,7 @@ export default function ChatContainer() { {/* Input window */}
+ {/* Text input */}
+ {/* Reference image with preview */} +
+ + Reference image +
+ + {/* Send button */}
+
diff --git a/frontend/helpers/modelData.js b/frontend/helpers/modelData.js index 600b5a07..d8ae6881 100644 --- a/frontend/helpers/modelData.js +++ b/frontend/helpers/modelData.js @@ -87,6 +87,20 @@ export const models = [ default: 200 } }, + { + modelName: "Amazon Nova Lite", + modelId: "amazon.nova-lite-v1:0", + temperatureRange: { + min: 0, + max: 1, + default: 0.5 + }, + maxTokenRange: { + min: 0, + max: 8191, + default: 200 + } + }, { modelName: "Amazon Titan Text Express", modelId: "amazon.titan-text-express-v1", From bebf86a642a1c6ae3741d07ba80be82f2f6848c9 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 12:43:21 +0200 Subject: [PATCH 04/67] backend: converse modalitity added to all supported models --- .../Sources/BedrockService/SwiftBedrock.swift | 2 +- .../Sources/BedrockTypes/BedrockModel.swift | 26 +++++---- .../Modalities/ConverseModality.swift | 51 +++++++++++++++-- .../Modalities/TextModality.swift | 1 - .../Models/Amazon/Nova/Nova.swift | 25 +++------ .../Amazon/Nova/NovaBedrockModels.swift | 17 +++++- .../Models/Amazon/Titan/Titan.swift | 12 +++- .../Amazon/Titan/TitanBedrockModels.swift | 3 +- .../Models/Anthropic/Anthropic.swift | 12 +++- .../Anthropic/AnthropicBedrockModels.swift | 30 ++++++---- .../Models/DeepSeek/DeepSeek.swift | 11 +++- .../BedrockTypes/Models/Llama/Llama.swift | 11 +++- .../Models/Llama/LlamaBedrockModels.swift | 55 ++++++++++--------- 13 files changed, 175 insertions(+), 81 deletions(-) diff --git a/backend/Sources/BedrockService/SwiftBedrock.swift b/backend/Sources/BedrockService/SwiftBedrock.swift index 99097294..bd2d5869 100644 --- a/backend/Sources/BedrockService/SwiftBedrock.swift +++ b/backend/Sources/BedrockService/SwiftBedrock.swift @@ -494,7 +494,7 @@ public struct BedrockService: Sendable { if let imageFormat: Content.ImageFormat = imageFormat, let imageBytes: String = imageBytes { - guard model.hasConverseVisionModality() else { + guard model.hasConverseModality(.vision) else { throw BedrockServiceError.invalidModality( model, modality, "This model does not support converse vision." diff --git a/backend/Sources/BedrockTypes/BedrockModel.swift b/backend/Sources/BedrockTypes/BedrockModel.swift index cd060627..417e694f 100644 --- a/backend/Sources/BedrockTypes/BedrockModel.swift +++ b/backend/Sources/BedrockTypes/BedrockModel.swift @@ -78,6 +78,8 @@ public struct BedrockModel: Hashable, Sendable, Equatable, RawRepresentable { self = BedrockModel.nova_micro case BedrockModel.nova_lite.id: self = BedrockModel.nova_lite + case BedrockModel.nova_pro.id: + self = BedrockModel.nova_pro case BedrockModel.nova_canvas.id: self = BedrockModel.nova_canvas // deepseek @@ -213,19 +215,23 @@ public struct BedrockModel: Hashable, Sendable, Equatable, RawRepresentable { modality as? any ConverseModality != nil } - /// Checks if the model supports converse vision - /// - Returns: True if the model supports converse vision - public func hasConverseVisionModality() -> Bool { - modality as? any ConverseVisionModality != nil + // /// Checks if the model supports converse vision + // /// - Returns: True if the model supports converse vision + public func hasConverseModality(_ feature: ConverseFeature = .textGeneration) -> Bool { + if let converseModality = modality as? any ConverseModality { + let features = converseModality.getConverseFeatures() + return features.contains(feature) + } + return false } - public func hasConverseDocumentModality() -> Bool { - modality as? any ConverseDocumentModality != nil - } + // public func hasConverseDocumentModality() -> Bool { + // modality as? any ConverseDocumentModality != nil + // } - public func hasConverseToolModality() -> Bool { - modality as? any ConverseToolModality != nil - } + // public func hasConverseToolModality() -> Bool { + // modality as? any ConverseToolModality != nil + // } } extension BedrockModel: Encodable { diff --git a/backend/Sources/BedrockTypes/Modalities/ConverseModality.swift b/backend/Sources/BedrockTypes/Modalities/ConverseModality.swift index cde2c5cd..5793bd15 100644 --- a/backend/Sources/BedrockTypes/Modalities/ConverseModality.swift +++ b/backend/Sources/BedrockTypes/Modalities/ConverseModality.swift @@ -17,14 +17,53 @@ import Foundation // Text public protocol ConverseModality: Modality { + var converseParameters: ConverseParameters { get } + var converseFeatures: [ConverseFeature] { get } + func getConverseParameters() -> ConverseParameters + func getConverseFeatures() -> [ConverseFeature] + + init(parameters: ConverseParameters, features: [ConverseFeature]) } -// Vision -public protocol ConverseVisionModality: ConverseModality {} +// https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html +public enum ConverseFeature: String, Codable, Sendable { + case textGeneration = "text-generation" + case vision = "vision" + case document = "document" + case toolUse = "tool-use" + case systemPrompts = "system-prompts" +} + +// defualt implementation +extension ConverseModality { + init(parameters: ConverseParameters, features: [ConverseFeature]) { + self = .init(parameters: parameters, features: features) + } + + func getConverseParameters() -> ConverseParameters { + converseParameters + } + + func getConverseFeatures() -> [ConverseFeature] { + converseFeatures + } +} +// extension ConverseModality { +// // func getConverseParameters() -> ConverseParameters { +// // ConverseParameters(textGenerationParameters: parameters) +// // } + +// func getConverseFeatures() -> [ConverseFeature] { +// [.textGeneration] +// } +// } + +// // Vision +// public protocol ConverseVisionModality: ConverseModality {} -// Document -public protocol ConverseDocumentModality: ConverseModality {} +// // Document +// public protocol ConverseDocumentModality: ConverseModality {} -// Tool use -public protocol ConverseToolModality: ConverseModality {} +// // Tool use +// public protocol ConverseToolModality: ConverseModality {} diff --git a/backend/Sources/BedrockTypes/Modalities/TextModality.swift b/backend/Sources/BedrockTypes/Modalities/TextModality.swift index 2995b795..82c12196 100644 --- a/backend/Sources/BedrockTypes/Modalities/TextModality.swift +++ b/backend/Sources/BedrockTypes/Modalities/TextModality.swift @@ -17,7 +17,6 @@ import Foundation public protocol TextModality: Modality { - init(parameters: TextGenerationParameters) func getParameters() -> TextGenerationParameters func getTextRequestBody( diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift b/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift index 8be92506..bc0367d7 100644 --- a/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift +++ b/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift @@ -15,37 +15,30 @@ import Foundation -struct NovaConverse: ConverseVisionModality, ConverseDocumentModality, ConverseToolModality { - func getName() -> String { "Nova Lite Converse Modality" } +struct NovaConverse: ConverseModality { + func getName() -> String { "Nova Lite and Nova Pro Converse Modality" } let converseParameters: ConverseParameters - - init(parameters: ConverseParameters) { - self.converseParameters = parameters - } - - func getConverseParameters() -> ConverseParameters { - converseParameters - } + let converseFeatures: [ConverseFeature] } struct NovaText: TextModality, ConverseModality { func getName() -> String { "Nova Text Generation" } - + let parameters: TextGenerationParameters + let converseFeatures: [ConverseFeature] + let converseParameters: ConverseParameters - init(parameters: TextGenerationParameters) { + init(parameters: TextGenerationParameters, features: [ConverseFeature] = [.textGeneration]) { self.parameters = parameters + self.converseFeatures = features + self.converseParameters = ConverseParameters(textGenerationParameters: parameters) } func getParameters() -> TextGenerationParameters { parameters } - func getConverseParameters() -> ConverseParameters { - ConverseParameters(textGenerationParameters: parameters) - } - func getTextRequestBody( prompt: String, maxTokens: Int?, diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift index b0a7317d..4e82dbdc 100644 --- a/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift +++ b/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift @@ -45,7 +45,22 @@ extension BedrockModel { topP: Parameter(.topP, minValue: 0, maxValue: 1.0, defaultValue: 0.9), stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), maxPromptSize: nil - ) + ), + features: [.textGeneration, .vision, .systemPrompts, .document, .toolUse] + ) + ) + public static let nova_pro: BedrockModel = BedrockModel( + id: "amazon.nova-pro-v1:0", + name: "Nova Pro", + modality: NovaConverse( + parameters: ConverseParameters( + temperature: Parameter(.temperature, minValue: 0.00001, maxValue: 1, defaultValue: 0.7), + maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 5_000, defaultValue: 5_000), + topP: Parameter(.topP, minValue: 0, maxValue: 1.0, defaultValue: 0.9), + stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), + maxPromptSize: nil + ), + features: [.textGeneration, .systemPrompts, .document, .vision, .toolUse] ) ) } diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Titan/Titan.swift b/backend/Sources/BedrockTypes/Models/Amazon/Titan/Titan.swift index 7ac6d4a5..109ab7b0 100644 --- a/backend/Sources/BedrockTypes/Models/Amazon/Titan/Titan.swift +++ b/backend/Sources/BedrockTypes/Models/Amazon/Titan/Titan.swift @@ -15,19 +15,27 @@ import Foundation -struct TitanText: TextModality { +struct TitanText: TextModality, ConverseModality { func getName() -> String { "Titan Text Generation" } let parameters: TextGenerationParameters + let converseParameters: ConverseParameters + let converseFeatures: [ConverseFeature] - init(parameters: TextGenerationParameters) { + init(parameters: TextGenerationParameters, features: [ConverseFeature] = [.textGeneration, .document]) { self.parameters = parameters + self.converseFeatures = features + self.converseParameters = ConverseParameters(textGenerationParameters: parameters) } func getParameters() -> TextGenerationParameters { parameters } + func getConverseParameters() -> ConverseParameters { + ConverseParameters(textGenerationParameters: parameters) + } + func getTextRequestBody( prompt: String, maxTokens: Int?, diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanBedrockModels.swift index 48560d83..a3c276ba 100644 --- a/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanBedrockModels.swift +++ b/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanBedrockModels.swift @@ -33,7 +33,8 @@ extension BedrockModel { topK: Parameter.notSupported(.topK), stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), maxPromptSize: nil - ) + ), + features: [.textGeneration] ) ) public static let titan_text_g1_express: BedrockModel = BedrockModel( diff --git a/backend/Sources/BedrockTypes/Models/Anthropic/Anthropic.swift b/backend/Sources/BedrockTypes/Models/Anthropic/Anthropic.swift index 092c1c2c..93eb94b2 100644 --- a/backend/Sources/BedrockTypes/Models/Anthropic/Anthropic.swift +++ b/backend/Sources/BedrockTypes/Models/Anthropic/Anthropic.swift @@ -15,19 +15,27 @@ import Foundation -struct AnthropicText: TextModality { +struct AnthropicText: TextModality, ConverseModality { let parameters: TextGenerationParameters + let converseParameters: ConverseParameters + let converseFeatures: [ConverseFeature] func getName() -> String { "Anthropic Text Generation" } - init(parameters: TextGenerationParameters) { + init(parameters: TextGenerationParameters, features: [ConverseFeature] = [.textGeneration, .systemPrompts, .document]) { self.parameters = parameters + self.converseFeatures = features + self.converseParameters = ConverseParameters(textGenerationParameters: parameters) } func getParameters() -> TextGenerationParameters { parameters } + func getConverseParameters() -> ConverseParameters { + ConverseParameters(textGenerationParameters: parameters) + } + func getTextRequestBody( prompt: String, maxTokens: Int?, diff --git a/backend/Sources/BedrockTypes/Models/Anthropic/AnthropicBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Anthropic/AnthropicBedrockModels.swift index f3d20831..dd9d2e90 100644 --- a/backend/Sources/BedrockTypes/Models/Anthropic/AnthropicBedrockModels.swift +++ b/backend/Sources/BedrockTypes/Models/Anthropic/AnthropicBedrockModels.swift @@ -39,7 +39,8 @@ extension BedrockModel { topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), maxPromptSize: 200_000 - ) + ), + features: [] ) ) public static let claudev1: BedrockModel = BedrockModel( @@ -52,7 +53,8 @@ extension BedrockModel { topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), maxPromptSize: 200_000 - ) + ), + features: [] ) ) public static let claudev2: BedrockModel = BedrockModel( @@ -65,7 +67,8 @@ extension BedrockModel { topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), maxPromptSize: 200_000 - ) + ), + features: [.textGeneration, .systemPrompts, .document] ) ) public static let claudev2_1: BedrockModel = BedrockModel( @@ -78,7 +81,8 @@ extension BedrockModel { topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), maxPromptSize: 200_000 - ) + ), + features: [.textGeneration, .systemPrompts, .document] ) ) public static let claudev3_opus: BedrockModel = BedrockModel( @@ -91,7 +95,8 @@ extension BedrockModel { topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), maxPromptSize: 200_000 - ) + ), + features: [.textGeneration, .systemPrompts, .document, .vision, .toolUse] ) ) public static let claudev3_haiku: BedrockModel = BedrockModel( @@ -104,7 +109,8 @@ extension BedrockModel { topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), maxPromptSize: 200_000 - ) + ), + features: [.textGeneration, .systemPrompts, .document, .vision, .toolUse] ) ) public static let claudev3_5_haiku: BedrockModel = BedrockModel( @@ -117,7 +123,8 @@ extension BedrockModel { topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), maxPromptSize: 200_000 - ) + ), + features: [.textGeneration, .systemPrompts, .document, .toolUse] ) ) public static let claudev3_5_sonnet: BedrockModel = BedrockModel( @@ -130,7 +137,8 @@ extension BedrockModel { topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), maxPromptSize: 200_000 - ) + ), + features: [.textGeneration, .systemPrompts, .document, .vision, .toolUse] ) ) public static let claudev3_5_sonnet_v2: BedrockModel = BedrockModel( @@ -143,7 +151,8 @@ extension BedrockModel { topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), maxPromptSize: 200_000 - ) + ), + features: [.textGeneration, .systemPrompts, .document, .vision, .toolUse] ) ) public static let claudev3_7_sonnet: BedrockModel = BedrockModel( @@ -156,7 +165,8 @@ extension BedrockModel { topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), maxPromptSize: 200_000 - ) + ), + features: [.textGeneration, .systemPrompts, .document, .vision, .toolUse] ) ) } diff --git a/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeek.swift b/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeek.swift index 4e65c4e5..44da21e9 100644 --- a/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeek.swift +++ b/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeek.swift @@ -15,13 +15,20 @@ import Foundation -struct DeepSeekText: TextModality { +struct DeepSeekText: TextModality, ConverseModality { let parameters: TextGenerationParameters + let converseFeatures: [ConverseFeature] + let converseParameters: ConverseParameters func getName() -> String { "DeepSeek Text Generation" } - init(parameters: TextGenerationParameters) { + init( + parameters: TextGenerationParameters, + features: [ConverseFeature] = [.textGeneration, .systemPrompts, .document] + ) { self.parameters = parameters + self.converseFeatures = features + self.converseParameters = ConverseParameters(textGenerationParameters: parameters) } func getParameters() -> TextGenerationParameters { diff --git a/backend/Sources/BedrockTypes/Models/Llama/Llama.swift b/backend/Sources/BedrockTypes/Models/Llama/Llama.swift index cfcf2d8c..a8fa1c16 100644 --- a/backend/Sources/BedrockTypes/Models/Llama/Llama.swift +++ b/backend/Sources/BedrockTypes/Models/Llama/Llama.swift @@ -15,13 +15,20 @@ import Foundation -struct LlamaText: TextModality { +struct LlamaText: TextModality, ConverseModality { func getName() -> String { "Llama Text Generation" } let parameters: TextGenerationParameters + let converseParameters: ConverseParameters + let converseFeatures: [ConverseFeature] - init(parameters: TextGenerationParameters) { + init( + parameters: TextGenerationParameters, + features: [ConverseFeature] = [.textGeneration, .systemPrompts, .document] + ) { self.parameters = parameters + self.converseFeatures = features + self.converseParameters = ConverseParameters(textGenerationParameters: parameters) } func getParameters() -> TextGenerationParameters { diff --git a/backend/Sources/BedrockTypes/Models/Llama/LlamaBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Llama/LlamaBedrockModels.swift index 3e0a7de4..e0963a23 100644 --- a/backend/Sources/BedrockTypes/Models/Llama/LlamaBedrockModels.swift +++ b/backend/Sources/BedrockTypes/Models/Llama/LlamaBedrockModels.swift @@ -19,7 +19,8 @@ import Foundation extension BedrockModel { public static let llama_3_8b_instruct: BedrockModel = BedrockModel( - id: "meta.llama3-8b-instruct-v1:0", name: "Llama 3 8B Instruct", + id: "meta.llama3-8b-instruct-v1:0", + name: "Llama 3 8B Instruct", modality: LlamaText( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), @@ -28,11 +29,13 @@ extension BedrockModel { topK: Parameter.notSupported(.topK), stopSequences: StopSequenceParams.notSupported(), maxPromptSize: nil - ) + ), + features: [.textGeneration, .systemPrompts, .document] ) ) public static let llama3_70b_instruct: BedrockModel = BedrockModel( - id: "meta.llama3-70b-instruct-v1:0", name: "Llama 3 70B Instruct", + id: "meta.llama3-70b-instruct-v1:0", + name: "Llama 3 70B Instruct", modality: LlamaText( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), @@ -41,11 +44,13 @@ extension BedrockModel { topK: Parameter.notSupported(.topK), stopSequences: StopSequenceParams.notSupported(), maxPromptSize: nil - ) + ), + features: [.textGeneration, .systemPrompts, .document] ) ) public static let llama3_1_8b_instruct: BedrockModel = BedrockModel( - id: "us.meta.llama3-1-8b-instruct-v1:0", name: "Llama 3.1 8B Instruct", + id: "us.meta.llama3-1-8b-instruct-v1:0", + name: "Llama 3.1 8B Instruct", modality: LlamaText( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), @@ -54,11 +59,13 @@ extension BedrockModel { topK: Parameter.notSupported(.topK), stopSequences: StopSequenceParams.notSupported(), maxPromptSize: nil - ) + ), + features: [.textGeneration, .systemPrompts, .document, .toolUse] ) ) public static let llama3_1_70b_instruct: BedrockModel = BedrockModel( - id: "us.meta.llama3-1-70b-instruct-v1:0", name: "Llama 3.1 70B Instruct", + id: "us.meta.llama3-1-70b-instruct-v1:0", + name: "Llama 3.1 70B Instruct", modality: LlamaText( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), @@ -67,11 +74,13 @@ extension BedrockModel { topK: Parameter.notSupported(.topK), stopSequences: StopSequenceParams.notSupported(), maxPromptSize: nil - ) + ), + features: [.textGeneration, .systemPrompts, .document, .toolUse] ) ) public static let llama3_2_1b_instruct: BedrockModel = BedrockModel( - id: "us.meta.llama3-2-1b-instruct-v1:0", name: "Llama 3.2 1B Instruct", + id: "us.meta.llama3-2-1b-instruct-v1:0", + name: "Llama 3.2 1B Instruct", modality: LlamaText( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), @@ -80,11 +89,13 @@ extension BedrockModel { topK: Parameter.notSupported(.topK), stopSequences: StopSequenceParams.notSupported(), maxPromptSize: nil - ) + ), + features: [.textGeneration, .systemPrompts, .document] ) ) public static let llama3_2_3b_instruct: BedrockModel = BedrockModel( - id: "us.meta.llama3-2-3b-instruct-v1:0", name: "Llama 3.2 3B Instruct", + id: "us.meta.llama3-2-3b-instruct-v1:0", + name: "Llama 3.2 3B Instruct", modality: LlamaText( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), @@ -93,11 +104,13 @@ extension BedrockModel { topK: Parameter.notSupported(.topK), stopSequences: StopSequenceParams.notSupported(), maxPromptSize: nil - ) + ), + features: [.textGeneration, .systemPrompts, .document] ) ) public static let llama3_3_70b_instruct: BedrockModel = BedrockModel( - id: "us.meta.llama3-3-70b-instruct-v1:0", name: "Llama 3.3 70B Instruct", + id: "us.meta.llama3-3-70b-instruct-v1:0", + name: "Llama 3.3 70B Instruct", modality: LlamaText( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), @@ -106,20 +119,8 @@ extension BedrockModel { topK: Parameter.notSupported(.topK), stopSequences: StopSequenceParams.notSupported(), maxPromptSize: nil - ) - ) - ) - public static let llama3_8b_instruct: BedrockModel = BedrockModel( - id: "meta.llama3-8b-instruct-v1:0", name: "Llama 3 8B Instruct", - modality: LlamaText( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), - maxTokens: Parameter(.maxTokens, minValue: 0, maxValue: 2_048, defaultValue: 512), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.9), - topK: Parameter.notSupported(.topK), - stopSequences: StopSequenceParams.notSupported(), - maxPromptSize: nil - ) + ), + features: [] ) ) } From 3e1d72418c04b1ac9c56779fe61b402cfd62b2d1 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 15:10:14 +0200 Subject: [PATCH 05/67] Mistral support only for converse added --- backend/Sources/App/Application+build.swift | 4 + .../Sources/BedrockTypes/BedrockModel.swift | 21 +++-- .../Amazon/Nova/NovaBedrockModels.swift | 3 +- .../BedrockTypes/Models/Mistral/Mistral.swift | 28 +++++++ .../Models/Mistral/MistralBedrockModels.swift | 79 +++++++++++++++++++ frontend/helpers/modelData.js | 28 +++++++ 6 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 backend/Sources/BedrockTypes/Models/Mistral/Mistral.swift create mode 100644 backend/Sources/BedrockTypes/Models/Mistral/MistralBedrockModels.swift diff --git a/backend/Sources/App/Application+build.swift b/backend/Sources/App/Application+build.swift index d896b650..d284bb4b 100644 --- a/backend/Sources/App/Application+build.swift +++ b/backend/Sources/App/Application+build.swift @@ -165,9 +165,13 @@ func buildRouter(useSSO: Bool, logger: Logger) async throws -> Router String { "Mistral Converse Modality" } + + let converseParameters: ConverseParameters + let converseFeatures: [ConverseFeature] + + init(parameters: ConverseParameters, features: [ConverseFeature]) { + self.converseParameters = parameters + self.converseFeatures = features + } +} diff --git a/backend/Sources/BedrockTypes/Models/Mistral/MistralBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Mistral/MistralBedrockModels.swift new file mode 100644 index 00000000..b6b29273 --- /dev/null +++ b/backend/Sources/BedrockTypes/Models/Mistral/MistralBedrockModels.swift @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Foundation + +// MARK: converse only +// https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-mistral-text-completion.html +// https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html + +extension BedrockModel { + public static let mistral_large_2402 = BedrockModel( + id: "mistral.mistral-large-2402-v1:0", + name: "Mistral Large (24.02)", + modality: MistralConverse( + parameters: ConverseParameters( + temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.7), + maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 8_192, defaultValue: 8_192), + topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 1), + stopSequences: StopSequenceParams(maxSequences: 10, defaultValue: []), + maxPromptSize: nil + ), + features: [.textGeneration, .systemPrompts, .document, .toolUse] + ) + ) + public static let mistral_small_2402 = BedrockModel( + id: "mistral.mistral-small-2402-v1:0", + name: "Mistral Small (24.02)", + modality: MistralConverse( + parameters: ConverseParameters( + temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.7), + maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 8_192, defaultValue: 8_192), + topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 1), + stopSequences: StopSequenceParams(maxSequences: 10, defaultValue: []), + maxPromptSize: nil + ), + features: [.textGeneration, .systemPrompts, .toolUse] + ) + ) + public static let mistral_7B_instruct = BedrockModel( + id: "mistral.mistral-7b-instruct-v0:2", + name: "Mistral 7B Instruct", + modality: MistralConverse( + parameters: ConverseParameters( + temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), + maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 8_192, defaultValue: 512), + topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.9), + stopSequences: StopSequenceParams(maxSequences: 10, defaultValue: []), + maxPromptSize: nil + ), + features: [.textGeneration, .document] + ) + ) + public static let mistral_8x7B_instruct = BedrockModel( + id: "mistral.mixtral-8x7b-instruct-v0:1", + name: "Mixtral 8x7B Instruct", + modality: MistralConverse( + parameters: ConverseParameters( + temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), + maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 4_096, defaultValue: 512), + topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.9), + stopSequences: StopSequenceParams(maxSequences: 10, defaultValue: []), + maxPromptSize: nil + ), + features: [.textGeneration, .document] + ) + ) +} diff --git a/frontend/helpers/modelData.js b/frontend/helpers/modelData.js index d8ae6881..48594f97 100644 --- a/frontend/helpers/modelData.js +++ b/frontend/helpers/modelData.js @@ -101,6 +101,34 @@ export const models = [ default: 200 } }, + { + modelName: "Mistral Large (24.02)", + modelId: "mistral.mistral-large-2402-v1:0", + temperatureRange: { + min: 0, + max: 1, + default: 0.7 + }, + maxTokenRange: { + min: 0, + max: 8191, + default: 8191 + } + }, + { + modelName: "Mistral Small (24.02)", + modelId: "mistral.mistral-small-2402-v1:0", + temperatureRange: { + min: 0, + max: 1, + default: 0.7 + }, + maxTokenRange: { + min: 0, + max: 8191, + default: 8191 + } + }, { modelName: "Amazon Titan Text Express", modelId: "amazon.titan-text-express-v1", From 71456294abba964cd1c2906495bd14db83e1d53a Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 15:14:55 +0200 Subject: [PATCH 06/67] converse image input --- backend/Sources/App/Application+build.swift | 6 +---- .../Sources/BedrockTypes/BedrockModel.swift | 18 +++++-------- .../Modalities/ConverseModality.swift | 25 +------------------ .../Models/Amazon/Nova/Nova.swift | 5 ++++ 4 files changed, 13 insertions(+), 41 deletions(-) diff --git a/backend/Sources/App/Application+build.swift b/backend/Sources/App/Application+build.swift index d284bb4b..bdda426f 100644 --- a/backend/Sources/App/Application+build.swift +++ b/backend/Sources/App/Application+build.swift @@ -165,13 +165,9 @@ func buildRouter(useSSO: Bool, logger: Logger) async throws -> Router Router ConverseParameters func getConverseFeatures() -> [ConverseFeature] - - init(parameters: ConverseParameters, features: [ConverseFeature]) } // https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html @@ -35,11 +33,8 @@ public enum ConverseFeature: String, Codable, Sendable { case systemPrompts = "system-prompts" } -// defualt implementation +// default implementation extension ConverseModality { - init(parameters: ConverseParameters, features: [ConverseFeature]) { - self = .init(parameters: parameters, features: features) - } func getConverseParameters() -> ConverseParameters { converseParameters @@ -49,21 +44,3 @@ extension ConverseModality { converseFeatures } } -// extension ConverseModality { -// // func getConverseParameters() -> ConverseParameters { -// // ConverseParameters(textGenerationParameters: parameters) -// // } - -// func getConverseFeatures() -> [ConverseFeature] { -// [.textGeneration] -// } -// } - -// // Vision -// public protocol ConverseVisionModality: ConverseModality {} - -// // Document -// public protocol ConverseDocumentModality: ConverseModality {} - -// // Tool use -// public protocol ConverseToolModality: ConverseModality {} diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift b/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift index bc0367d7..5a30bf3a 100644 --- a/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift +++ b/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift @@ -20,6 +20,11 @@ struct NovaConverse: ConverseModality { let converseParameters: ConverseParameters let converseFeatures: [ConverseFeature] + + init(parameters: ConverseParameters, features: [ConverseFeature]) { + self.converseParameters = parameters + self.converseFeatures = features + } } struct NovaText: TextModality, ConverseModality { From f5e8bae6b4fde526d8114036344de611d40c07b5 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 16:37:38 +0200 Subject: [PATCH 07/67] backend: converse refactor using Blocks --- backend/Sources/App/Types/Chat.swift | 2 +- .../Converse/ConverseRequest.swift | 1 + .../Converse/ConversionExtensions.swift | 145 ------------------ .../ContentConversionExtension.swift | 50 ++++++ .../ImageConversionExtension.swift | 79 ++++++++++ .../MessageConversionExtension.swift | 43 ++++++ .../RoleConversionExtension.swift | 38 +++++ .../ToolConversionExtension.swift | 46 ++++++ .../Sources/BedrockService/SwiftBedrock.swift | 7 +- .../{ => ConverseContent}/Content.swift | 14 +- .../ConverseContent/DocumentBlock.swift | 34 ++++ .../ConverseContent/ImageBlock.swift | 33 ++++ .../ConverseContent/S3Location.swift | 21 +++ .../ConverseContent/ToolBlocks.swift | 46 ++++++ .../ConverseContent/VideoBlock.swift | 38 +++++ 15 files changed, 438 insertions(+), 159 deletions(-) delete mode 100644 backend/Sources/BedrockService/Converse/ConversionExtensions.swift create mode 100644 backend/Sources/BedrockService/Converse/ConversionExtensions/ContentConversionExtension.swift create mode 100644 backend/Sources/BedrockService/Converse/ConversionExtensions/ImageConversionExtension.swift create mode 100644 backend/Sources/BedrockService/Converse/ConversionExtensions/MessageConversionExtension.swift create mode 100644 backend/Sources/BedrockService/Converse/ConversionExtensions/RoleConversionExtension.swift create mode 100644 backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift rename backend/Sources/BedrockTypes/{ => ConverseContent}/Content.swift (77%) create mode 100644 backend/Sources/BedrockTypes/ConverseContent/DocumentBlock.swift create mode 100644 backend/Sources/BedrockTypes/ConverseContent/ImageBlock.swift create mode 100644 backend/Sources/BedrockTypes/ConverseContent/S3Location.swift create mode 100644 backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift create mode 100644 backend/Sources/BedrockTypes/ConverseContent/VideoBlock.swift diff --git a/backend/Sources/App/Types/Chat.swift b/backend/Sources/App/Types/Chat.swift index 7dcf14a5..d397b637 100644 --- a/backend/Sources/App/Types/Chat.swift +++ b/backend/Sources/App/Types/Chat.swift @@ -22,7 +22,7 @@ extension Message: ResponseCodable {} struct ChatInput: Codable { let prompt: String let history: [Message] - let imageFormat: Content.ImageFormat? + let imageFormat: ImageFormat? let imageBytes: String? let maxTokens: Int? let temperature: Double? diff --git a/backend/Sources/BedrockService/Converse/ConverseRequest.swift b/backend/Sources/BedrockService/Converse/ConverseRequest.swift index 913a6c56..c584ac8b 100644 --- a/backend/Sources/BedrockService/Converse/ConverseRequest.swift +++ b/backend/Sources/BedrockService/Converse/ConverseRequest.swift @@ -21,6 +21,7 @@ public struct ConverseRequest { let model: BedrockModel let messages: [Message] let inferenceConfig: InferenceConfig? + // let toolConfig: ToolConfig? init( model: BedrockModel, diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions.swift deleted file mode 100644 index c76c7814..00000000 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions.swift +++ /dev/null @@ -1,145 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import BedrockTypes -import Foundation - -extension Message { - - init(from sdkMessage: BedrockRuntimeClientTypes.Message) throws { - guard let sdkRole = sdkMessage.role else { - throw BedrockServiceError.decodingError("Could not extract role from BedrockRuntimeClientTypes.Message") - } - guard let sdkContent = sdkMessage.content else { - throw BedrockServiceError.decodingError("Could not extract content from BedrockRuntimeClientTypes.Message") - } - let content: [Content] = try sdkContent.map { try Content(from: $0) } - self = Message(from: try Role(from: sdkRole), content: content) - } - - func getSDKMessage() throws -> BedrockRuntimeClientTypes.Message { - let contentBlocks: [BedrockRuntimeClientTypes.ContentBlock] = try content.map { - content -> BedrockRuntimeClientTypes.ContentBlock in - return try content.getSDKContentBlock() - } - return BedrockRuntimeClientTypes.Message( - content: contentBlocks, - role: role.getSDKConversationRole() - ) - } -} - -extension Content { - - init(from sdkContentBlock: BedrockRuntimeClientTypes.ContentBlock) throws { - switch sdkContentBlock { - case .text(let text): - self = .text(text) - case .image(let sdkImage): - guard let sdkFormat = sdkImage.format else { - throw BedrockServiceError.decodingError( - "Could not extract format from BedrockRuntimeClientTypes.ImageBlock" - ) - } - guard let sdkImageSource = sdkImage.source else { - throw BedrockServiceError.decodingError( - "Could not extract source from BedrockRuntimeClientTypes.ImageBlock" - ) - } - switch sdkImageSource { - case .bytes(let data): - self = .image(format: try Content.ImageFormat(from: sdkFormat), source: data.base64EncodedString()) - case .sdkUnknown(let unknownImageSource): - throw BedrockServiceError.notImplemented( - "ImageSource \(unknownImageSource) is not implemented by BedrockRuntimeClientTypes" - ) - } - case .sdkUnknown(let unknownContentBlock): - throw BedrockServiceError.notImplemented( - "ContentBlock \(unknownContentBlock) is not implemented by BedrockRuntimeClientTypes" - ) - default: - throw BedrockServiceError.notImplemented( - "\(sdkContentBlock.self) is not implemented by this library" - ) - } - } - - func getSDKContentBlock() throws -> BedrockRuntimeClientTypes.ContentBlock { - switch self { - case .text(let text): - return BedrockRuntimeClientTypes.ContentBlock.text(text) - case .image(let format, let source): - guard let data = Data(base64Encoded: source) else { - throw BedrockServiceError.decodingError( - "Could not decode image source from base64 string. String: \(source)" - ) - } - return BedrockRuntimeClientTypes.ContentBlock.image( - BedrockRuntimeClientTypes.ImageBlock( - format: format.getSDKImageFormat(), - source: BedrockRuntimeClientTypes.ImageSource.bytes(data) - ) - ) - } - } -} - -extension Content.ImageFormat { - - init(from sdkImageFormat: BedrockRuntimeClientTypes.ImageFormat) throws { - switch sdkImageFormat { - case .gif: self = .gif - case .jpeg: self = .jpeg - case .png: self = .png - case .webp: self = .webp - case .sdkUnknown(let unknownImageFormat): - throw BedrockServiceError.notImplemented( - "ImageFormat \(unknownImageFormat) is not implemented by BedrockRuntimeClientTypes" - ) - } - } - - func getSDKImageFormat() -> BedrockRuntimeClientTypes.ImageFormat { - switch self { - case .gif: return .gif - case .jpeg: return .jpeg - case .png: return .png - case .webp: return .webp - } - } - -} - -extension Role { - init(from sdkConversationRole: BedrockRuntimeClientTypes.ConversationRole) throws { - switch sdkConversationRole { - case .user: self = .user - case .assistant: self = .assistant - case .sdkUnknown(let unknownRole): - throw BedrockServiceError.notImplemented( - "Role \(unknownRole) is not implemented by BedrockRuntimeClientTypes" - ) - } - } - - func getSDKConversationRole() -> BedrockRuntimeClientTypes.ConversationRole { - switch self { - case .user: return .user - case .assistant: return .assistant - } - } -} diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/ContentConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/ContentConversionExtension.swift new file mode 100644 index 00000000..2ba3cf90 --- /dev/null +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/ContentConversionExtension.swift @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +@preconcurrency import AWSBedrockRuntime +import BedrockTypes +import Foundation + +extension Content { + + init(from sdkContentBlock: BedrockRuntimeClientTypes.ContentBlock) throws { + switch sdkContentBlock { + case .text(let text): + self = .text(text) + case .image(let sdkImage): + self = .image(try ImageBlock(from: sdkImage)) + case .sdkUnknown(let unknownContentBlock): + throw BedrockServiceError.notImplemented( + "ContentBlock \(unknownContentBlock) is not implemented by BedrockRuntimeClientTypes" + ) + default: + throw BedrockServiceError.notImplemented( + "\(sdkContentBlock.self) is not implemented by this library" + ) + } + } + + func getSDKContentBlock() throws -> BedrockRuntimeClientTypes.ContentBlock { + switch self { + case .text(let text): + return BedrockRuntimeClientTypes.ContentBlock.text(text) + case .image(let imageBlock): + return BedrockRuntimeClientTypes.ContentBlock.image(try imageBlock.getSDKImageBlock()) + default: + print("TODO") + return BedrockRuntimeClientTypes.ContentBlock.text("TODO") + } + } +} diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/ImageConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/ImageConversionExtension.swift new file mode 100644 index 00000000..59be2206 --- /dev/null +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/ImageConversionExtension.swift @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +@preconcurrency import AWSBedrockRuntime +import BedrockTypes +import Foundation + +extension ImageBlock { + init(from sdkImageBlock: BedrockRuntimeClientTypes.ImageBlock) throws { + guard let sdkFormat = sdkImageBlock.format else { + throw BedrockServiceError.decodingError( + "Could not extract format from BedrockRuntimeClientTypes.ImageBlock" + ) + } + guard let sdkImageSource = sdkImageBlock.source else { + throw BedrockServiceError.decodingError( + "Could not extract source from BedrockRuntimeClientTypes.ImageBlock" + ) + } + let format = try ImageFormat(from: sdkFormat) + switch sdkImageSource { + case .bytes(let data): + self = ImageBlock(format: format, source: data.base64EncodedString()) + case .sdkUnknown(let unknownImageSource): + throw BedrockServiceError.notImplemented( + "ImageSource \(unknownImageSource) is not implemented by BedrockRuntimeClientTypes" + ) + } + } + + func getSDKImageBlock() throws -> BedrockRuntimeClientTypes.ImageBlock { + guard let data = Data(base64Encoded: source) else { + throw BedrockServiceError.decodingError( + "Could not decode image source from base64 string. String: \(source)" + ) + } + return BedrockRuntimeClientTypes.ImageBlock( + format: format.getSDKImageFormat(), + source: BedrockRuntimeClientTypes.ImageSource.bytes(data) + ) + } +} + +extension ImageFormat { + + init(from sdkImageFormat: BedrockRuntimeClientTypes.ImageFormat) throws { + switch sdkImageFormat { + case .gif: self = .gif + case .jpeg: self = .jpeg + case .png: self = .png + case .webp: self = .webp + case .sdkUnknown(let unknownImageFormat): + throw BedrockServiceError.notImplemented( + "ImageFormat \(unknownImageFormat) is not implemented by BedrockRuntimeClientTypes" + ) + } + } + + func getSDKImageFormat() -> BedrockRuntimeClientTypes.ImageFormat { + switch self { + case .gif: return .gif + case .jpeg: return .jpeg + case .png: return .png + case .webp: return .webp + } + } +} diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/MessageConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/MessageConversionExtension.swift new file mode 100644 index 00000000..b3d575f7 --- /dev/null +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/MessageConversionExtension.swift @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +@preconcurrency import AWSBedrockRuntime +import BedrockTypes +import Foundation + +extension Message { + + init(from sdkMessage: BedrockRuntimeClientTypes.Message) throws { + guard let sdkRole = sdkMessage.role else { + throw BedrockServiceError.decodingError("Could not extract role from BedrockRuntimeClientTypes.Message") + } + guard let sdkContent = sdkMessage.content else { + throw BedrockServiceError.decodingError("Could not extract content from BedrockRuntimeClientTypes.Message") + } + let content: [Content] = try sdkContent.map { try Content(from: $0) } + self = Message(from: try Role(from: sdkRole), content: content) + } + + func getSDKMessage() throws -> BedrockRuntimeClientTypes.Message { + let contentBlocks: [BedrockRuntimeClientTypes.ContentBlock] = try content.map { + content -> BedrockRuntimeClientTypes.ContentBlock in + return try content.getSDKContentBlock() + } + return BedrockRuntimeClientTypes.Message( + content: contentBlocks, + role: role.getSDKConversationRole() + ) + } +} diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/RoleConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/RoleConversionExtension.swift new file mode 100644 index 00000000..7c9680ad --- /dev/null +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/RoleConversionExtension.swift @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +@preconcurrency import AWSBedrockRuntime +import BedrockTypes +import Foundation + +extension Role { + init(from sdkConversationRole: BedrockRuntimeClientTypes.ConversationRole) throws { + switch sdkConversationRole { + case .user: self = .user + case .assistant: self = .assistant + case .sdkUnknown(let unknownRole): + throw BedrockServiceError.notImplemented( + "Role \(unknownRole) is not implemented by BedrockRuntimeClientTypes" + ) + } + } + + func getSDKConversationRole() -> BedrockRuntimeClientTypes.ConversationRole { + switch self { + case .user: return .user + case .assistant: return .assistant + } + } +} diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift new file mode 100644 index 00000000..ce44cad1 --- /dev/null +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift @@ -0,0 +1,46 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +@preconcurrency import AWSBedrockRuntime +import BedrockTypes +import Foundation + +extension ToolUseBlock { + init(from sdkToolUseBlock: BedrockRuntimeClientTypes.ToolUseBlock) throws { + guard let sdkId = sdkToolUseBlock.toolUseId else { + throw BedrockServiceError.decodingError( + "Could not extract toolUseId from BedrockRuntimeClientTypes.ToolUseBlock" + ) + } + guard let sdkName = sdkToolUseBlock.name else { + throw BedrockServiceError.decodingError( + "Could not extract name from BedrockRuntimeClientTypes.ToolUseBlock" + ) + } + self = ToolUseBlock( + id: sdkId, + name: sdkName + // input: sdkToolUseBlock.input + ) + } + + func getSDKToolUseBlock() throws -> BedrockRuntimeClientTypes.ToolUseBlock { + .init( + name: name, + toolUseId: id + // input: input + ) + } +} \ No newline at end of file diff --git a/backend/Sources/BedrockService/SwiftBedrock.swift b/backend/Sources/BedrockService/SwiftBedrock.swift index bd2d5869..a8a5a95f 100644 --- a/backend/Sources/BedrockService/SwiftBedrock.swift +++ b/backend/Sources/BedrockService/SwiftBedrock.swift @@ -460,7 +460,7 @@ public struct BedrockService: Sendable { public func converse( with model: BedrockModel, prompt: String, - imageFormat: Content.ImageFormat? = nil, + imageFormat: ImageFormat? = nil, imageBytes: String? = nil, history: [Message] = [], maxTokens: Int? = nil, @@ -491,7 +491,7 @@ public struct BedrockService: Sendable { var messages = history messages.append(Message(from: .user, content: [.text(prompt)])) - if let imageFormat: Content.ImageFormat = imageFormat, + if let imageFormat: ImageFormat = imageFormat, let imageBytes: String = imageBytes { guard model.hasConverseModality(.vision) else { @@ -500,8 +500,7 @@ public struct BedrockService: Sendable { "This model does not support converse vision." ) } - messages.append(Message(from: .user, content: [.image(format: imageFormat, source: imageBytes)])) - print("HIER!") + messages.append(Message(from: .user, content: [.image(ImageBlock(format: imageFormat, source: imageBytes))])) } let converseRequest = ConverseRequest( diff --git a/backend/Sources/BedrockTypes/Content.swift b/backend/Sources/BedrockTypes/ConverseContent/Content.swift similarity index 77% rename from backend/Sources/BedrockTypes/Content.swift rename to backend/Sources/BedrockTypes/ConverseContent/Content.swift index 2e2200ff..59c9ce48 100644 --- a/backend/Sources/BedrockTypes/Content.swift +++ b/backend/Sources/BedrockTypes/ConverseContent/Content.swift @@ -17,12 +17,8 @@ import Foundation public enum Content: Codable { case text(String) - case image(format: ImageFormat, source: String) // String are the 64 encoded bytes - - public enum ImageFormat: Codable { - case gif - case jpeg - case png - case webp - } -} \ No newline at end of file + case image(ImageBlock) // String are the 64 encoded bytes + case toolUse(ToolUseBlock) + case toolresult(ToolResultBlock) + // case reasoningcontent(String) +} diff --git a/backend/Sources/BedrockTypes/ConverseContent/DocumentBlock.swift b/backend/Sources/BedrockTypes/ConverseContent/DocumentBlock.swift new file mode 100644 index 00000000..79b48328 --- /dev/null +++ b/backend/Sources/BedrockTypes/ConverseContent/DocumentBlock.swift @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Foundation + +struct DocumentBlock: Codable { + let name: String + let format: DocumentFormat + let source: String // 64 encoded +} + +enum DocumentFormat: Codable { + case csv + case doc + case docx + case html + case md + case pdf + case txt + case xls + case xlsx +} diff --git a/backend/Sources/BedrockTypes/ConverseContent/ImageBlock.swift b/backend/Sources/BedrockTypes/ConverseContent/ImageBlock.swift new file mode 100644 index 00000000..aac21ce1 --- /dev/null +++ b/backend/Sources/BedrockTypes/ConverseContent/ImageBlock.swift @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Foundation + +public struct ImageBlock: Codable { + public let format: ImageFormat + public let source: String // 64 encoded + + public init(format: ImageFormat, source: String) { + self.format = format + self.source = source + } +} + +public enum ImageFormat: Codable { + case gif + case jpeg + case png + case webp +} diff --git a/backend/Sources/BedrockTypes/ConverseContent/S3Location.swift b/backend/Sources/BedrockTypes/ConverseContent/S3Location.swift new file mode 100644 index 00000000..d4021446 --- /dev/null +++ b/backend/Sources/BedrockTypes/ConverseContent/S3Location.swift @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Foundation + +public struct S3Location: Codable { + let bucketOwner: String + let uri: String +} diff --git a/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift b/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift new file mode 100644 index 00000000..9d2ca1e1 --- /dev/null +++ b/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift @@ -0,0 +1,46 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Foundation + +public struct ToolUseBlock: Codable { + public let id: String + public let name: String + // let input: [String: Any] + + public init(id: String, name: String) { + self.id = id + self.name = name + } +} + +public struct ToolResultBlock: Codable { + let content: String + let status: Status // currently only supported by Anthropic Claude 3 models + let toolUseId: String + + enum Status: Codable { + case success + case error + } + + enum ToolResultContent: Codable { + // case json([String: Any]) + case text(String) + case image(ImageBlock) // currently only supported by Anthropic Claude 3 models + case document(DocumentBlock) + case video(VideoBlock) + } +} diff --git a/backend/Sources/BedrockTypes/ConverseContent/VideoBlock.swift b/backend/Sources/BedrockTypes/ConverseContent/VideoBlock.swift new file mode 100644 index 00000000..4770441b --- /dev/null +++ b/backend/Sources/BedrockTypes/ConverseContent/VideoBlock.swift @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Foundation + +public struct VideoBlock: Codable { + let format: Format + let source: Source + + enum Source: Codable { + case bytes(String) // base64 + case s3(S3Location) + } + + enum Format: Codable { + case flv + case mkv + case mov + case mp4 + case mpeg + case mpg + case threeGp + case webm + case wmv + } +} From 94bf47e86087fb065116c1acdb1c5be4a8a6d866 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 16:47:30 +0200 Subject: [PATCH 08/67] DocumentFormat --- .../DocumentConversionExtension.swift | 48 +++++++++++++++++++ .../ConverseContent/DocumentBlock.swift | 2 +- 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift new file mode 100644 index 00000000..c1a9356a --- /dev/null +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +@preconcurrency import AWSBedrockRuntime +import BedrockTypes +import Foundation + +// extension DocumentBlock { +// init(from sdkDocumentBlock: BedrockRuntimeClientTypes.DocumentBlock) throws { +// return BedrockRuntimeClientTypes.DocumentBlock( +// format: format, +// name: , +// source: , +// ) +// } +// } + +extension DocumentFormat { + init(from sdkDocumentFormat: BedrockRuntimeClientTypes.DocumentFormat) throws { + switch sdkDocumentFormat { + case .csv: self = .csv + case .doc: self = .doc + case .docx: self = .docx + case .html: self = .html + case .md: self = .md + case .pdf: self = .pdf + case .txt: self = .txt + case .xls: self = .xls + case .xlsx: self = .xlsx + case .sdkUnknown(let unknownDocumentFormat): + throw BedrockServiceError.notImplemented( + "DocumentFormat \(unknownDocumentFormat) is not implemented by BedrockRuntimeClientTypes" + ) + } + } +} diff --git a/backend/Sources/BedrockTypes/ConverseContent/DocumentBlock.swift b/backend/Sources/BedrockTypes/ConverseContent/DocumentBlock.swift index 79b48328..2bb0d82d 100644 --- a/backend/Sources/BedrockTypes/ConverseContent/DocumentBlock.swift +++ b/backend/Sources/BedrockTypes/ConverseContent/DocumentBlock.swift @@ -21,7 +21,7 @@ struct DocumentBlock: Codable { let source: String // 64 encoded } -enum DocumentFormat: Codable { +public enum DocumentFormat: Codable { case csv case doc case docx From d5b0575188a06f29b5c9406413a77ac0c8116bc0 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 16:48:29 +0200 Subject: [PATCH 09/67] DocumentFormat --- .../DocumentConversionExtension.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift index c1a9356a..875b650d 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift @@ -45,4 +45,18 @@ extension DocumentFormat { ) } } + + func getSDKDocumentFormat() -> BedrockRuntimeClientTypes.DocumentFormat { + switch self { + case .csv: return .csv + case .doc: return .doc + case .docx: return .docx + case .html: return .html + case .md: return .md + case .pdf: return .pdf + case .txt: return .txt + case .xls: return .xls + case .xlsx: return .xlsx + } + } } From 193896d26b09b37a3e9b841708bb23682ae72c40 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 16:58:31 +0200 Subject: [PATCH 10/67] DocumentBlock --- .../DocumentConversionExtension.swift | 50 +++++++++++++++---- .../ConverseContent/DocumentBlock.swift | 14 ++++-- 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift index 875b650d..59f58fcb 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift @@ -17,15 +17,47 @@ import BedrockTypes import Foundation -// extension DocumentBlock { -// init(from sdkDocumentBlock: BedrockRuntimeClientTypes.DocumentBlock) throws { -// return BedrockRuntimeClientTypes.DocumentBlock( -// format: format, -// name: , -// source: , -// ) -// } -// } +extension DocumentBlock { + init(from sdkDocumentBlock: BedrockRuntimeClientTypes.DocumentBlock) throws { + guard let sdkDocumentSource = sdkDocumentBlock.source else { + throw BedrockServiceError.decodingError( + "Could not extract source from BedrockRuntimeClientTypes.DocumentBlock" + ) + } + guard let name = sdkDocumentBlock.name else { + throw BedrockServiceError.decodingError( + "Could not extract name from BedrockRuntimeClientTypes.DocumentBlock" + ) + } + guard let sdkFormat = sdkDocumentBlock.format else { + throw BedrockServiceError.decodingError( + "Could not extract format from BedrockRuntimeClientTypes.DocumentBlock" + ) + } + let format = try DocumentFormat(from: sdkFormat) + switch sdkDocumentSource { + case .bytes(let data): + self = DocumentBlock(name: name, format: format, source: data.base64EncodedString()) + case .sdkUnknown(let unknownImageSource): + throw BedrockServiceError.notImplemented( + "ImageSource \(unknownImageSource) is not implemented by BedrockRuntimeClientTypes" + ) + } + } + + func getSDKDocumentBlock() throws -> BedrockRuntimeClientTypes.DocumentBlock { + guard let data = Data(base64Encoded: source) else { + throw BedrockServiceError.decodingError( + "Could not decode document source from base64 string. String: \(source)" + ) + } + return BedrockRuntimeClientTypes.DocumentBlock( + format: format.getSDKDocumentFormat(), + name: name, + source: BedrockRuntimeClientTypes.DocumentSource.bytes(data) + ) + } +} extension DocumentFormat { init(from sdkDocumentFormat: BedrockRuntimeClientTypes.DocumentFormat) throws { diff --git a/backend/Sources/BedrockTypes/ConverseContent/DocumentBlock.swift b/backend/Sources/BedrockTypes/ConverseContent/DocumentBlock.swift index 2bb0d82d..237a3427 100644 --- a/backend/Sources/BedrockTypes/ConverseContent/DocumentBlock.swift +++ b/backend/Sources/BedrockTypes/ConverseContent/DocumentBlock.swift @@ -15,10 +15,16 @@ import Foundation -struct DocumentBlock: Codable { - let name: String - let format: DocumentFormat - let source: String // 64 encoded +public struct DocumentBlock: Codable { + public let name: String + public let format: DocumentFormat + public let source: String // 64 encoded + + public init(name: String, format: DocumentFormat, source: String) { + self.name = name + self.format = format + self.source = source + } } public enum DocumentFormat: Codable { From ac5cb6c529cfb933f00943894ff50a525b5a507d Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 17:02:12 +0200 Subject: [PATCH 11/67] DocumentBlock --- .../ConversionExtensions/ContentConversionExtension.swift | 4 ++++ backend/Sources/BedrockTypes/ConverseContent/Content.swift | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/ContentConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/ContentConversionExtension.swift index 2ba3cf90..56ae882c 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions/ContentConversionExtension.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/ContentConversionExtension.swift @@ -25,6 +25,8 @@ extension Content { self = .text(text) case .image(let sdkImage): self = .image(try ImageBlock(from: sdkImage)) + case .document(let sdkDocumentBlock): + self = .document(try DocumentBlock(from: sdkDocumentBlock)) case .sdkUnknown(let unknownContentBlock): throw BedrockServiceError.notImplemented( "ContentBlock \(unknownContentBlock) is not implemented by BedrockRuntimeClientTypes" @@ -42,6 +44,8 @@ extension Content { return BedrockRuntimeClientTypes.ContentBlock.text(text) case .image(let imageBlock): return BedrockRuntimeClientTypes.ContentBlock.image(try imageBlock.getSDKImageBlock()) + case .document(let documentBlock): + return BedrockRuntimeClientTypes.ContentBlock.document(try documentBlock.getSDKDocumentBlock()) default: print("TODO") return BedrockRuntimeClientTypes.ContentBlock.text("TODO") diff --git a/backend/Sources/BedrockTypes/ConverseContent/Content.swift b/backend/Sources/BedrockTypes/ConverseContent/Content.swift index 59c9ce48..3699392e 100644 --- a/backend/Sources/BedrockTypes/ConverseContent/Content.swift +++ b/backend/Sources/BedrockTypes/ConverseContent/Content.swift @@ -20,5 +20,6 @@ public enum Content: Codable { case image(ImageBlock) // String are the 64 encoded bytes case toolUse(ToolUseBlock) case toolresult(ToolResultBlock) - // case reasoningcontent(String) + case document(DocumentBlock) + // case reasoningcontent(ReasoningBlock) } From fec96e22775e883784eb7adf8dd8d9808ad4b190 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 17:17:45 +0200 Subject: [PATCH 12/67] S3Location --- .../S3LocationConversionExtension.swift | 33 +++++++++++++++++ .../VideoConversionExtension.swift | 37 +++++++++++++++++++ .../BedrockTypes/BedrockServiceError.swift | 1 + .../ConverseContent/S3Location.swift | 9 ++++- .../ConverseContent/VideoBlock.swift | 34 ++++++++--------- 5 files changed, 95 insertions(+), 19 deletions(-) create mode 100644 backend/Sources/BedrockService/Converse/ConversionExtensions/S3LocationConversionExtension.swift create mode 100644 backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/S3LocationConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/S3LocationConversionExtension.swift new file mode 100644 index 00000000..2d9931e2 --- /dev/null +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/S3LocationConversionExtension.swift @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +@preconcurrency import AWSBedrockRuntime +import BedrockTypes +import Foundation + +extension S3Location { + init(from sdkS3Location: BedrockRuntimeClientTypes.S3Location) throws { + guard let uri = sdkS3Location.uri else { + throw BedrockServiceError.decodingError( + "Could not extract URI from BedrockRuntimeClientTypes.S3Location" + ) + } + guard uri.hasPrefix("") else { + throw BedrockServiceError.invalidURI("URI should start with \"s3://\". Your URI: \(uri)") + } + self = S3Location(bucketOwner: sdkS3Location.bucketOwner, uri: uri) + } +} + diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift new file mode 100644 index 00000000..2091e403 --- /dev/null +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +@preconcurrency import AWSBedrockRuntime +import BedrockTypes +import Foundation + +extension VideoBlock { + // init(from sdkVideoBlock: BedrockRuntimeClientTypes.VideoBlock) throws + // func getSDKVideoBlock() throws -> BedrockRuntimeClientTypes.VideoBlock +} + +extension VideoFormat { + // init(from sdkVideoFormat: BedrockRuntimeClientTypes.VideoFormat) throws + // func getSDKVideoFormat() throws -> BedrockRuntimeClientTypes.VideoFormat +} + +extension VideoSource { + init(from sdkVideoSource: BedrockRuntimeClientTypes.VideoSource) throws { + switch sdkVideoSource { + case .bytes(let data): self = .bytes(data.base64EncodedString()) + case .s3location(let sdkS3Location): .s3() + } + } +} diff --git a/backend/Sources/BedrockTypes/BedrockServiceError.swift b/backend/Sources/BedrockTypes/BedrockServiceError.swift index e5c56fef..24193dac 100644 --- a/backend/Sources/BedrockTypes/BedrockServiceError.swift +++ b/backend/Sources/BedrockTypes/BedrockServiceError.swift @@ -21,6 +21,7 @@ public enum BedrockServiceError: Error { // case invalidModel(BedrockModel, String) case invalidPrompt(String) case invalidStopSequences([String], String) + case invalidURI(String) case invalidSDKResponse(String) case invalidSDKResponseBody(Data?) case completionNotFound(String) diff --git a/backend/Sources/BedrockTypes/ConverseContent/S3Location.swift b/backend/Sources/BedrockTypes/ConverseContent/S3Location.swift index d4021446..81e23703 100644 --- a/backend/Sources/BedrockTypes/ConverseContent/S3Location.swift +++ b/backend/Sources/BedrockTypes/ConverseContent/S3Location.swift @@ -15,7 +15,12 @@ import Foundation -public struct S3Location: Codable { - let bucketOwner: String +public struct S3Location: Codable, Sendable { + let bucketOwner: String? let uri: String + + public init(bucketOwner: String? = nil, uri: String) { + self.bucketOwner = bucketOwner + self.uri = uri + } } diff --git a/backend/Sources/BedrockTypes/ConverseContent/VideoBlock.swift b/backend/Sources/BedrockTypes/ConverseContent/VideoBlock.swift index 4770441b..902acc5b 100644 --- a/backend/Sources/BedrockTypes/ConverseContent/VideoBlock.swift +++ b/backend/Sources/BedrockTypes/ConverseContent/VideoBlock.swift @@ -16,23 +16,23 @@ import Foundation public struct VideoBlock: Codable { - let format: Format - let source: Source + public let format: VideoFormat + public let source: VideoSource +} - enum Source: Codable { - case bytes(String) // base64 - case s3(S3Location) - } +public enum VideoSource: Codable { + case bytes(String) // base64 + case s3(S3Location) +} - enum Format: Codable { - case flv - case mkv - case mov - case mp4 - case mpeg - case mpg - case threeGp - case webm - case wmv - } +public enum VideoFormat: Codable { + case flv + case mkv + case mov + case mp4 + case mpeg + case mpg + case threeGp + case webm + case wmv } From 3fef779d362b90878272a7c2c51f327777158d69 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 17:26:29 +0200 Subject: [PATCH 13/67] VideoSource --- .../DocumentConversionExtension.swift | 2 +- .../S3LocationConversionExtension.swift | 4 ++++ .../VideoConversionExtension.swift | 24 +++++++++++++++++-- .../ConverseContent/S3Location.swift | 4 ++-- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift index 59f58fcb..82b27414 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift @@ -37,7 +37,7 @@ extension DocumentBlock { let format = try DocumentFormat(from: sdkFormat) switch sdkDocumentSource { case .bytes(let data): - self = DocumentBlock(name: name, format: format, source: data.base64EncodedString()) + self = DocumentBlock(name: name,format: format, source: data.base64EncodedString()) case .sdkUnknown(let unknownImageSource): throw BedrockServiceError.notImplemented( "ImageSource \(unknownImageSource) is not implemented by BedrockRuntimeClientTypes" diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/S3LocationConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/S3LocationConversionExtension.swift index 2d9931e2..9f750ffa 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions/S3LocationConversionExtension.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/S3LocationConversionExtension.swift @@ -29,5 +29,9 @@ extension S3Location { } self = S3Location(bucketOwner: sdkS3Location.bucketOwner, uri: uri) } + + func getSDKS3Location() -> BedrockRuntimeClientTypes.S3Location { + BedrockRuntimeClientTypes.S3Location(bucketOwner: self.bucketOwner, uri: self.uri) + } } diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift index 2091e403..d912c753 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift @@ -30,8 +30,28 @@ extension VideoFormat { extension VideoSource { init(from sdkVideoSource: BedrockRuntimeClientTypes.VideoSource) throws { switch sdkVideoSource { - case .bytes(let data): self = .bytes(data.base64EncodedString()) - case .s3location(let sdkS3Location): .s3() + case .bytes(let data): + self = .bytes(data.base64EncodedString()) + case .s3location(let sdkS3Location): + self = .s3(try S3Location(from: sdkS3Location)) + case .sdkUnknown(let unknownVideoSource): + throw BedrockServiceError.notImplemented( + "VideoSource \(unknownVideoSource) is not implemented by BedrockRuntimeClientTypes" + ) + } + } + + func getSDKVideoSource() throws -> BedrockRuntimeClientTypes.VideoSource { + switch self { + case .bytes(let data): + guard let sdkData = Data(base64Encoded: data) else { + throw BedrockServiceError.decodingError( + "Could not decode video source from base64 string. String: \(data)" + ) + } + return .bytes(sdkData) + case .s3(let s3Location): + return .s3location(s3Location.getSDKS3Location()) } } } diff --git a/backend/Sources/BedrockTypes/ConverseContent/S3Location.swift b/backend/Sources/BedrockTypes/ConverseContent/S3Location.swift index 81e23703..ecf11d46 100644 --- a/backend/Sources/BedrockTypes/ConverseContent/S3Location.swift +++ b/backend/Sources/BedrockTypes/ConverseContent/S3Location.swift @@ -16,8 +16,8 @@ import Foundation public struct S3Location: Codable, Sendable { - let bucketOwner: String? - let uri: String + public let bucketOwner: String? + public let uri: String public init(bucketOwner: String? = nil, uri: String) { self.bucketOwner = bucketOwner From da385b2e051e34fe37953fb1c9db7c02deb13c1a Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 17:32:02 +0200 Subject: [PATCH 14/67] VideoFormat --- .../VideoConversionExtension.swift | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift index d912c753..92a6ad57 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift @@ -23,8 +23,41 @@ extension VideoBlock { } extension VideoFormat { - // init(from sdkVideoFormat: BedrockRuntimeClientTypes.VideoFormat) throws - // func getSDKVideoFormat() throws -> BedrockRuntimeClientTypes.VideoFormat + init(from sdkVideoFormat: BedrockRuntimeClientTypes.VideoFormat) throws { + switch sdkVideoFormat { + case .flv: self = .flv + case .mkv: self = .mkv + case .mov: self = .mov + case .mp4: self = .mp4 + case .mpeg: self = .mpeg + case .mpg: self = .mpg + case .threeGp: self = .threeGp + case .webm: self = .webm + case .wmv: self = .wmv + case .sdkUnknown(let unknownVideoFormat): + throw BedrockServiceError.notImplemented( + "VideoFormat \(unknownVideoFormat) is not implemented by BedrockRuntimeClientTypes" + ) + // default: // in case new video formats get added to the sdk + // throw BedrockServiceError.notSupported( + // "VideoFormat \(sdkVideoFormat) is not supported by BedrockTypes" + // ) + } + } + + func getSDKVideoFormat() throws -> BedrockRuntimeClientTypes.VideoFormat { + switch self { + case .flv: return .flv + case .mkv: return .mkv + case .mov: return .mov + case .mp4: return .mp4 + case .mpeg: return .mpeg + case .mpg: return .mpg + case .threeGp: return .threeGp + case .webm: return .webm + case .wmv: return .wmv + } + } } extension VideoSource { From 00faaf46335d8c181212f380635d4dc62450dfd2 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 17:35:27 +0200 Subject: [PATCH 15/67] VideoBlock --- .../VideoConversionExtension.swift | 25 +++++++++++++++++-- .../ConverseContent/VideoBlock.swift | 5 ++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift index 92a6ad57..72ac1095 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift @@ -18,8 +18,29 @@ import BedrockTypes import Foundation extension VideoBlock { - // init(from sdkVideoBlock: BedrockRuntimeClientTypes.VideoBlock) throws - // func getSDKVideoBlock() throws -> BedrockRuntimeClientTypes.VideoBlock + init(from sdkVideoBlock: BedrockRuntimeClientTypes.VideoBlock) throws { + guard let sdkFormat = sdkVideoBlock.format else { + throw BedrockServiceError.decodingError( + "Could not extract format from BedrockRuntimeClientTypes.VideoBlock" + ) + } + guard let sdkSource = sdkVideoBlock.source else { + throw BedrockServiceError.decodingError( + "Could not extract source from BedrockRuntimeClientTypes.VideoBlock" + ) + } + self = VideoBlock( + format: try VideoFormat(from: sdkFormat), + source: try VideoSource(from: sdkSource) + ) + } + + func getSDKVideoBlock() throws -> BedrockRuntimeClientTypes.VideoBlock { + BedrockRuntimeClientTypes.VideoBlock( + format: try format.getSDKVideoFormat(), + source: try source.getSDKVideoSource() + ) + } } extension VideoFormat { diff --git a/backend/Sources/BedrockTypes/ConverseContent/VideoBlock.swift b/backend/Sources/BedrockTypes/ConverseContent/VideoBlock.swift index 902acc5b..5aa24c00 100644 --- a/backend/Sources/BedrockTypes/ConverseContent/VideoBlock.swift +++ b/backend/Sources/BedrockTypes/ConverseContent/VideoBlock.swift @@ -18,6 +18,11 @@ import Foundation public struct VideoBlock: Codable { public let format: VideoFormat public let source: VideoSource + + public init(format: VideoFormat, source: VideoSource) { + self.format = format + self.source = source + } } public enum VideoSource: Codable { From 1e8f0bebe362dca4743274cc4cd44346a8fc0e8c Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 17:43:06 +0200 Subject: [PATCH 16/67] ToolStatus --- .../ToolConversionExtension.swift | 26 +++++++++++++++-- .../ConverseContent/ToolBlocks.swift | 28 +++++++++---------- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift index ce44cad1..2d0bf863 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift @@ -32,7 +32,7 @@ extension ToolUseBlock { self = ToolUseBlock( id: sdkId, name: sdkName - // input: sdkToolUseBlock.input + // input: sdkToolUseBlock.input ) } @@ -40,7 +40,27 @@ extension ToolUseBlock { .init( name: name, toolUseId: id - // input: input + // input: input ) } -} \ No newline at end of file +} + +extension ToolStatus { + init(from sdkToolStatus: BedrockRuntimeClientTypes.ToolResultStatus) throws { + switch sdkToolStatus { + case .success: self = .success + case .error: self = .error + case .sdkUnknown(let unknownToolStatus): + throw BedrockServiceError.notImplemented( + "ToolResultStatus \(unknownToolStatus) is not implemented by BedrockRuntimeClientTypes" + ) + } + } + + func getSDKToolStatus() -> BedrockRuntimeClientTypes.ToolResultStatus { + switch self { + case .success: .success + case .error: .error + } + } +} diff --git a/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift b/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift index 9d2ca1e1..e3d47b2a 100644 --- a/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift +++ b/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift @@ -27,20 +27,20 @@ public struct ToolUseBlock: Codable { } public struct ToolResultBlock: Codable { - let content: String - let status: Status // currently only supported by Anthropic Claude 3 models - let toolUseId: String + public let content: String + public let status: ToolStatus // currently only supported by Anthropic Claude 3 models + public let toolUseId: String +} - enum Status: Codable { - case success - case error - } +public enum ToolStatus: Codable { + case success + case error +} - enum ToolResultContent: Codable { - // case json([String: Any]) - case text(String) - case image(ImageBlock) // currently only supported by Anthropic Claude 3 models - case document(DocumentBlock) - case video(VideoBlock) - } +public enum ToolResultContent: Codable { + // case json([String: Any]) + case text(String) + case image(ImageBlock) // currently only supported by Anthropic Claude 3 models + case document(DocumentBlock) + case video(VideoBlock) } From 8ac47e1c16b1d299cd54fb8ee2e90879fab69358 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 17:50:31 +0200 Subject: [PATCH 17/67] ToolResultContent --- .../ToolConversionExtension.swift | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift index 2d0bf863..d2756aff 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift @@ -45,6 +45,7 @@ extension ToolUseBlock { } } + extension ToolStatus { init(from sdkToolStatus: BedrockRuntimeClientTypes.ToolResultStatus) throws { switch sdkToolStatus { @@ -59,8 +60,33 @@ extension ToolStatus { func getSDKToolStatus() -> BedrockRuntimeClientTypes.ToolResultStatus { switch self { - case .success: .success - case .error: .error + case .success: .success + case .error: .error + } + } +} + +extension ToolResultContent { + init(from sdkToolResultContent: BedrockRuntimeClientTypes.ToolResultContentBlock) throws { + switch sdkToolResultContent { + case .document(let sdkDocumentBlock): + self = .document(try DocumentBlock(from: sdkDocumentBlock)) + case .image(let sdkImageBlock): + self = .image(try ImageBlock(from: sdkImageBlock)) + case .text(let text): + self = .text(text) + case .video(let sdkVideoBlock): + self = .video(try VideoBlock(from: sdkVideoBlock)) + // case .json(let sdkJSON): + // self = .json() + case .sdkUnknown(let unknownToolResultContent): + throw BedrockServiceError.notImplemented( + "ToolResultContentBlock \(unknownToolResultContent) is not implemented by BedrockRuntimeClientTypes" + ) + default: + throw BedrockServiceError.notImplemented( + "ToolResultContentBlock \(sdkToolResultContent) is not implemented by BedrockTypes" + ) } } } From 3b1fd1aa6c101240445b9f3b70a5ccf412dfb052 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 18:05:57 +0200 Subject: [PATCH 18/67] ToolResultBlock --- .../ToolConversionExtension.swift | 21 +++++++++++++++++++ .../ConverseContent/ToolBlocks.swift | 12 ++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift index d2756aff..fce1304b 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift @@ -45,6 +45,27 @@ extension ToolUseBlock { } } +extension ToolResultBlock { + init(from sdkToolResultBlock: BedrockRuntimeClientTypes.ToolResultBlock) throws { + guard let sdkToolResultContent = sdkToolResultBlock.content else { + throw BedrockServiceError.decodingError( + "Could not extract content from BedrockRuntimeClientTypes.ToolResultBlock" + ) + } + guard let id = sdkToolResultBlock.toolUseId else { + throw BedrockServiceError.decodingError( + "Could not extract toolUseId from BedrockRuntimeClientTypes.ToolResultBlock" + ) + } + let sdkToolStatus: BedrockRuntimeClientTypes.ToolResultStatus? = sdkToolResultBlock.status + var status: ToolStatus? = nil + if let sdkToolStatus = sdkToolStatus { + status = try ToolStatus(from: sdkToolStatus) + } + let toolContents = try sdkToolResultContent.map { try ToolResultContent(from: $0) } + self = ToolResultBlock(id: id, content: toolContents, status: status) + } +} extension ToolStatus { init(from sdkToolStatus: BedrockRuntimeClientTypes.ToolResultStatus) throws { diff --git a/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift b/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift index e3d47b2a..c2913f0e 100644 --- a/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift +++ b/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift @@ -27,9 +27,15 @@ public struct ToolUseBlock: Codable { } public struct ToolResultBlock: Codable { - public let content: String - public let status: ToolStatus // currently only supported by Anthropic Claude 3 models - public let toolUseId: String + public let id: String + public let content: [ToolResultContent] + public let status: ToolStatus? // currently only supported by Anthropic Claude 3 models + + public init(id: String, content: [ToolResultContent], status: ToolStatus? = nil) { + self.id = id + self.content = content + self.status = status + } } public enum ToolStatus: Codable { From b8c20a270814b9ab28e6b86025e1350036f9e3ad Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 18:15:00 +0200 Subject: [PATCH 19/67] Content --- .../ConversionExtensions/ContentConversionExtension.swift | 6 ++++++ backend/Sources/BedrockTypes/ConverseContent/Content.swift | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/ContentConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/ContentConversionExtension.swift index 56ae882c..ad98d6d3 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions/ContentConversionExtension.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/ContentConversionExtension.swift @@ -27,6 +27,12 @@ extension Content { self = .image(try ImageBlock(from: sdkImage)) case .document(let sdkDocumentBlock): self = .document(try DocumentBlock(from: sdkDocumentBlock)) + case .tooluse(let sdkToolUseBlock): + self = .toolUse(try ToolUseBlock(from: sdkToolUseBlock)) + case .toolresult(let sdkToolResultBlock): + self = .toolResult(try ToolResultBlock(from: sdkToolResultBlock)) + case .video(let sdkVideoBlock): + self = .video(try VideoBlock(from: sdkVideoBlock)) case .sdkUnknown(let unknownContentBlock): throw BedrockServiceError.notImplemented( "ContentBlock \(unknownContentBlock) is not implemented by BedrockRuntimeClientTypes" diff --git a/backend/Sources/BedrockTypes/ConverseContent/Content.swift b/backend/Sources/BedrockTypes/ConverseContent/Content.swift index 3699392e..70d63b72 100644 --- a/backend/Sources/BedrockTypes/ConverseContent/Content.swift +++ b/backend/Sources/BedrockTypes/ConverseContent/Content.swift @@ -19,7 +19,8 @@ public enum Content: Codable { case text(String) case image(ImageBlock) // String are the 64 encoded bytes case toolUse(ToolUseBlock) - case toolresult(ToolResultBlock) + case toolResult(ToolResultBlock) case document(DocumentBlock) + case video(VideoBlock) // case reasoningcontent(ReasoningBlock) } From 8b9a7b92c5c1595ed64529caf7cd26e0972b4283 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 31 Mar 2025 18:47:15 +0200 Subject: [PATCH 20/67] base for toolconfig --- .../Converse/ConverseRequest.swift | 55 ++++++++++++++++++- .../ContentConversionExtension.swift | 12 +++- .../ToolConversionExtension.swift | 22 ++++++++ 3 files changed, 84 insertions(+), 5 deletions(-) diff --git a/backend/Sources/BedrockService/Converse/ConverseRequest.swift b/backend/Sources/BedrockService/Converse/ConverseRequest.swift index c584ac8b..e68206d2 100644 --- a/backend/Sources/BedrockService/Converse/ConverseRequest.swift +++ b/backend/Sources/BedrockService/Converse/ConverseRequest.swift @@ -14,14 +14,14 @@ //===----------------------------------------------------------------------===// @preconcurrency import AWSBedrockRuntime -import Foundation import BedrockTypes +import Foundation public struct ConverseRequest { let model: BedrockModel let messages: [Message] let inferenceConfig: InferenceConfig? - // let toolConfig: ToolConfig? + let toolConfig: ToolConfig? init( model: BedrockModel, @@ -39,6 +39,7 @@ public struct ConverseRequest { topP: topP, stopSequences: stopSequences ) + self.toolConfig = nil } func getConverseInput() throws -> ConverseInput { @@ -87,3 +88,53 @@ public struct ConverseRequest { } } } + +public struct ToolConfig { + // let toolChoice: ToolChoice? + let tools: [ToolSpecification] +} + +// public enum ToolChoice { +// /// (Default). The Model automatically decides if a tool should be called or whether to generate text instead. +// case auto(_) +// /// The model must request at least one tool (no text is generated). +// case any(_) +// /// The Model must request the specified tool. Only supported by Anthropic Claude 3 models. +// case tool(String) +// } + +public struct ToolSpecification { + public let name: String + public let inputSchema: [String: Any] + public let description: String? + + init(name: String, inputSchema: [String: Any], description: String? = nil) { + self.name = name + self.inputSchema = inputSchema + self.description = description + } + + init(from sdkToolSpecification: BedrockRuntimeClientTypes.ToolSpecification) throws { + guard let name = sdkToolSpecification.name else { + throw BedrockServiceError.decodingError( + "Could not extract name from BedrockRuntimeClientTypes.ToolSpecification" + ) + } + guard let sdkInputSchema = sdkToolSpecification.inputSchema else { + throw BedrockServiceError.decodingError( + "Could not extract inputSchema from BedrockRuntimeClientTypes.ToolSpecification" + ) + } + guard case .json(let smithyDocument) = sdkInputSchema else { + throw BedrockServiceError.decodingError( + "Could not extract JSON from BedrockRuntimeClientTypes.ToolSpecification.inputSchema" + ) + } + let inputSchema = try smithyDocument.asStringMap() + self = ToolSpecification( + name: name, + inputSchema: inputSchema, + description: sdkToolSpecification.description + ) + } +} diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/ContentConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/ContentConversionExtension.swift index ad98d6d3..8b3e995e 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions/ContentConversionExtension.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/ContentConversionExtension.swift @@ -52,9 +52,15 @@ extension Content { return BedrockRuntimeClientTypes.ContentBlock.image(try imageBlock.getSDKImageBlock()) case .document(let documentBlock): return BedrockRuntimeClientTypes.ContentBlock.document(try documentBlock.getSDKDocumentBlock()) - default: - print("TODO") - return BedrockRuntimeClientTypes.ContentBlock.text("TODO") + case .toolResult(let toolResultBlock): + return BedrockRuntimeClientTypes.ContentBlock.toolresult(try toolResultBlock.getSDKToolResultBlock()) + case .toolUse(let toolUseBlock): + return BedrockRuntimeClientTypes.ContentBlock.tooluse(try toolUseBlock.getSDKToolUseBlock()) + case .video(let videoBlock): + return BedrockRuntimeClientTypes.ContentBlock.video(try videoBlock.getSDKVideoBlock()) + // default: + // print("TODO") + // return BedrockRuntimeClientTypes.ContentBlock.text("TODO") } } } diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift index fce1304b..4b757120 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift @@ -65,6 +65,15 @@ extension ToolResultBlock { let toolContents = try sdkToolResultContent.map { try ToolResultContent(from: $0) } self = ToolResultBlock(id: id, content: toolContents, status: status) } + + func getSDKToolResultBlock() throws -> BedrockRuntimeClientTypes.ToolResultBlock { + BedrockRuntimeClientTypes.ToolResultBlock( + content: try content.map { try $0.getSDKToolResultContentBlock() }, + status: status?.getSDKToolStatus(), + toolUseId: id + ) + + } } extension ToolStatus { @@ -110,4 +119,17 @@ extension ToolResultContent { ) } } + + func getSDKToolResultContentBlock() throws -> BedrockRuntimeClientTypes.ToolResultContentBlock { + switch self { + case .document(let documentBlock): + .document(try documentBlock.getSDKDocumentBlock()) + case .image(let imageBlock): + .image(try imageBlock.getSDKImageBlock()) + case .text(let text): + .text(text) + case .video(let videoBlock): + .video(try videoBlock.getSDKVideoBlock()) + } + } } From 226ab12db39ed38c2d7f7c2e5a6cbd588e8e2c4f Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:15:55 +0200 Subject: [PATCH 21/67] nested types for Blocks --- backend/Sources/App/Types/Chat.swift | 2 +- .../Converse/ConverseRequest.swift | 43 ++------------- .../DocumentConversionExtension.swift | 4 +- .../ImageConversionExtension.swift | 4 +- .../VideoConversionExtension.swift | 8 +-- .../BedrockService/Converse/Tool.swift | 54 +++++++++++++++++++ .../Sources/BedrockService/SwiftBedrock.swift | 7 +-- .../Sources/BedrockTypes/BedrockModel.swift | 8 --- .../ConverseContent/Content.swift | 2 +- .../ConverseContent/DocumentBlock.swift | 26 ++++----- .../ConverseContent/ImageBlock.swift | 8 +-- .../ConverseContent/ToolBlocks.swift | 2 +- .../ConverseContent/VideoBlock.swift | 36 ++++++------- 13 files changed, 108 insertions(+), 96 deletions(-) create mode 100644 backend/Sources/BedrockService/Converse/Tool.swift diff --git a/backend/Sources/App/Types/Chat.swift b/backend/Sources/App/Types/Chat.swift index d397b637..c98b9920 100644 --- a/backend/Sources/App/Types/Chat.swift +++ b/backend/Sources/App/Types/Chat.swift @@ -22,7 +22,7 @@ extension Message: ResponseCodable {} struct ChatInput: Codable { let prompt: String let history: [Message] - let imageFormat: ImageFormat? + let imageFormat: ImageBlock.Format? let imageBytes: String? let maxTokens: Int? let temperature: Double? diff --git a/backend/Sources/BedrockService/Converse/ConverseRequest.swift b/backend/Sources/BedrockService/Converse/ConverseRequest.swift index e68206d2..b90daddb 100644 --- a/backend/Sources/BedrockService/Converse/ConverseRequest.swift +++ b/backend/Sources/BedrockService/Converse/ConverseRequest.swift @@ -21,7 +21,7 @@ public struct ConverseRequest { let model: BedrockModel let messages: [Message] let inferenceConfig: InferenceConfig? - let toolConfig: ToolConfig? + // let toolConfig: ToolConfig? init( model: BedrockModel, @@ -30,6 +30,7 @@ public struct ConverseRequest { temperature: Double?, topP: Double?, stopSequences: [String]? + // tools: [Tool] ) { self.messages = messages self.model = model @@ -39,7 +40,7 @@ public struct ConverseRequest { topP: topP, stopSequences: stopSequences ) - self.toolConfig = nil + // self.toolConfig = ToolConfig(tools: tools) } func getConverseInput() throws -> ConverseInput { @@ -91,7 +92,7 @@ public struct ConverseRequest { public struct ToolConfig { // let toolChoice: ToolChoice? - let tools: [ToolSpecification] + let tools: [Tool] } // public enum ToolChoice { @@ -102,39 +103,3 @@ public struct ToolConfig { // /// The Model must request the specified tool. Only supported by Anthropic Claude 3 models. // case tool(String) // } - -public struct ToolSpecification { - public let name: String - public let inputSchema: [String: Any] - public let description: String? - - init(name: String, inputSchema: [String: Any], description: String? = nil) { - self.name = name - self.inputSchema = inputSchema - self.description = description - } - - init(from sdkToolSpecification: BedrockRuntimeClientTypes.ToolSpecification) throws { - guard let name = sdkToolSpecification.name else { - throw BedrockServiceError.decodingError( - "Could not extract name from BedrockRuntimeClientTypes.ToolSpecification" - ) - } - guard let sdkInputSchema = sdkToolSpecification.inputSchema else { - throw BedrockServiceError.decodingError( - "Could not extract inputSchema from BedrockRuntimeClientTypes.ToolSpecification" - ) - } - guard case .json(let smithyDocument) = sdkInputSchema else { - throw BedrockServiceError.decodingError( - "Could not extract JSON from BedrockRuntimeClientTypes.ToolSpecification.inputSchema" - ) - } - let inputSchema = try smithyDocument.asStringMap() - self = ToolSpecification( - name: name, - inputSchema: inputSchema, - description: sdkToolSpecification.description - ) - } -} diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift index 82b27414..440cadcb 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift @@ -34,7 +34,7 @@ extension DocumentBlock { "Could not extract format from BedrockRuntimeClientTypes.DocumentBlock" ) } - let format = try DocumentFormat(from: sdkFormat) + let format = try DocumentBlock.Format(from: sdkFormat) switch sdkDocumentSource { case .bytes(let data): self = DocumentBlock(name: name,format: format, source: data.base64EncodedString()) @@ -59,7 +59,7 @@ extension DocumentBlock { } } -extension DocumentFormat { +extension DocumentBlock.Format { init(from sdkDocumentFormat: BedrockRuntimeClientTypes.DocumentFormat) throws { switch sdkDocumentFormat { case .csv: self = .csv diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/ImageConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/ImageConversionExtension.swift index 59be2206..35b22505 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions/ImageConversionExtension.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/ImageConversionExtension.swift @@ -29,7 +29,7 @@ extension ImageBlock { "Could not extract source from BedrockRuntimeClientTypes.ImageBlock" ) } - let format = try ImageFormat(from: sdkFormat) + let format = try ImageBlock.Format(from: sdkFormat) switch sdkImageSource { case .bytes(let data): self = ImageBlock(format: format, source: data.base64EncodedString()) @@ -53,7 +53,7 @@ extension ImageBlock { } } -extension ImageFormat { +extension ImageBlock.Format { init(from sdkImageFormat: BedrockRuntimeClientTypes.ImageFormat) throws { switch sdkImageFormat { diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift index 72ac1095..0476a947 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift @@ -30,8 +30,8 @@ extension VideoBlock { ) } self = VideoBlock( - format: try VideoFormat(from: sdkFormat), - source: try VideoSource(from: sdkSource) + format: try VideoBlock.Format(from: sdkFormat), + source: try VideoBlock.Source(from: sdkSource) ) } @@ -43,7 +43,7 @@ extension VideoBlock { } } -extension VideoFormat { +extension VideoBlock.Format { init(from sdkVideoFormat: BedrockRuntimeClientTypes.VideoFormat) throws { switch sdkVideoFormat { case .flv: self = .flv @@ -81,7 +81,7 @@ extension VideoFormat { } } -extension VideoSource { +extension VideoBlock.Source { init(from sdkVideoSource: BedrockRuntimeClientTypes.VideoSource) throws { switch sdkVideoSource { case .bytes(let data): diff --git a/backend/Sources/BedrockService/Converse/Tool.swift b/backend/Sources/BedrockService/Converse/Tool.swift new file mode 100644 index 00000000..9facd50d --- /dev/null +++ b/backend/Sources/BedrockService/Converse/Tool.swift @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +@preconcurrency import AWSBedrockRuntime +import BedrockTypes +import Foundation + +public struct Tool { + public let name: String + public let inputSchema: [String: Any] + public let description: String? + + init(name: String, inputSchema: [String: Any], description: String? = nil) { + self.name = name + self.inputSchema = inputSchema + self.description = description + } + + init(from sdkToolSpecification: BedrockRuntimeClientTypes.ToolSpecification) throws { + guard let name = sdkToolSpecification.name else { + throw BedrockServiceError.decodingError( + "Could not extract name from BedrockRuntimeClientTypes.ToolSpecification" + ) + } + guard let sdkInputSchema = sdkToolSpecification.inputSchema else { + throw BedrockServiceError.decodingError( + "Could not extract inputSchema from BedrockRuntimeClientTypes.ToolSpecification" + ) + } + guard case .json(let smithyDocument) = sdkInputSchema else { + throw BedrockServiceError.decodingError( + "Could not extract JSON from BedrockRuntimeClientTypes.ToolSpecification.inputSchema" + ) + } + let inputSchema = try smithyDocument.asStringMap() + self = Tool( + name: name, + inputSchema: inputSchema, + description: sdkToolSpecification.description + ) + } +} \ No newline at end of file diff --git a/backend/Sources/BedrockService/SwiftBedrock.swift b/backend/Sources/BedrockService/SwiftBedrock.swift index a8a5a95f..8c97c48c 100644 --- a/backend/Sources/BedrockService/SwiftBedrock.swift +++ b/backend/Sources/BedrockService/SwiftBedrock.swift @@ -460,13 +460,14 @@ public struct BedrockService: Sendable { public func converse( with model: BedrockModel, prompt: String, - imageFormat: ImageFormat? = nil, + imageFormat: ImageBlock.Format? = nil, imageBytes: String? = nil, history: [Message] = [], maxTokens: Int? = nil, temperature: Double? = nil, topP: Double? = nil, - stopSequences: [String]? = nil + stopSequences: [String]? = nil, + tools: [Tool]? = nil ) async throws -> (String, [Message]) { logger.trace( "Conversing", @@ -491,7 +492,7 @@ public struct BedrockService: Sendable { var messages = history messages.append(Message(from: .user, content: [.text(prompt)])) - if let imageFormat: ImageFormat = imageFormat, + if let imageFormat: ImageBlock.Format = imageFormat, let imageBytes: String = imageBytes { guard model.hasConverseModality(.vision) else { diff --git a/backend/Sources/BedrockTypes/BedrockModel.swift b/backend/Sources/BedrockTypes/BedrockModel.swift index b7561b22..8f8226c6 100644 --- a/backend/Sources/BedrockTypes/BedrockModel.swift +++ b/backend/Sources/BedrockTypes/BedrockModel.swift @@ -229,14 +229,6 @@ public struct BedrockModel: Hashable, Sendable, Equatable, RawRepresentable { } return false } - - // public func hasConverseDocumentModality() -> Bool { - // modality as? any ConverseDocumentModality != nil - // } - - // public func hasConverseToolModality() -> Bool { - // modality as? any ConverseToolModality != nil - // } } extension BedrockModel: Encodable { diff --git a/backend/Sources/BedrockTypes/ConverseContent/Content.swift b/backend/Sources/BedrockTypes/ConverseContent/Content.swift index 70d63b72..4fca0366 100644 --- a/backend/Sources/BedrockTypes/ConverseContent/Content.swift +++ b/backend/Sources/BedrockTypes/ConverseContent/Content.swift @@ -17,7 +17,7 @@ import Foundation public enum Content: Codable { case text(String) - case image(ImageBlock) // String are the 64 encoded bytes + case image(ImageBlock) case toolUse(ToolUseBlock) case toolResult(ToolResultBlock) case document(DocumentBlock) diff --git a/backend/Sources/BedrockTypes/ConverseContent/DocumentBlock.swift b/backend/Sources/BedrockTypes/ConverseContent/DocumentBlock.swift index 237a3427..f34c97c2 100644 --- a/backend/Sources/BedrockTypes/ConverseContent/DocumentBlock.swift +++ b/backend/Sources/BedrockTypes/ConverseContent/DocumentBlock.swift @@ -17,24 +17,24 @@ import Foundation public struct DocumentBlock: Codable { public let name: String - public let format: DocumentFormat + public let format: Format public let source: String // 64 encoded - public init(name: String, format: DocumentFormat, source: String) { + public init(name: String, format: Format, source: String) { self.name = name self.format = format self.source = source } -} -public enum DocumentFormat: Codable { - case csv - case doc - case docx - case html - case md - case pdf - case txt - case xls - case xlsx + public enum Format: Codable { + case csv + case doc + case docx + case html + case md + case pdf + case txt + case xls + case xlsx + } } diff --git a/backend/Sources/BedrockTypes/ConverseContent/ImageBlock.swift b/backend/Sources/BedrockTypes/ConverseContent/ImageBlock.swift index aac21ce1..ba66561f 100644 --- a/backend/Sources/BedrockTypes/ConverseContent/ImageBlock.swift +++ b/backend/Sources/BedrockTypes/ConverseContent/ImageBlock.swift @@ -16,18 +16,18 @@ import Foundation public struct ImageBlock: Codable { - public let format: ImageFormat + public let format: Format public let source: String // 64 encoded - public init(format: ImageFormat, source: String) { + public init(format: Format, source: String) { self.format = format self.source = source } -} -public enum ImageFormat: Codable { + public enum Format: Codable { case gif case jpeg case png case webp } +} diff --git a/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift b/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift index c2913f0e..2809688a 100644 --- a/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift +++ b/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift @@ -44,7 +44,7 @@ public enum ToolStatus: Codable { } public enum ToolResultContent: Codable { - // case json([String: Any]) + // case json([String: Any]) // Just Data case text(String) case image(ImageBlock) // currently only supported by Anthropic Claude 3 models case document(DocumentBlock) diff --git a/backend/Sources/BedrockTypes/ConverseContent/VideoBlock.swift b/backend/Sources/BedrockTypes/ConverseContent/VideoBlock.swift index 5aa24c00..ded89b64 100644 --- a/backend/Sources/BedrockTypes/ConverseContent/VideoBlock.swift +++ b/backend/Sources/BedrockTypes/ConverseContent/VideoBlock.swift @@ -16,28 +16,28 @@ import Foundation public struct VideoBlock: Codable { - public let format: VideoFormat - public let source: VideoSource + public let format: Format + public let source: Source - public init(format: VideoFormat, source: VideoSource) { + public init(format: Format, source: Source) { self.format = format self.source = source } -} -public enum VideoSource: Codable { - case bytes(String) // base64 - case s3(S3Location) -} + public enum Source: Codable { + case bytes(String) // base64 + case s3(S3Location) + } -public enum VideoFormat: Codable { - case flv - case mkv - case mov - case mp4 - case mpeg - case mpg - case threeGp - case webm - case wmv + public enum Format: Codable { + case flv + case mkv + case mov + case mp4 + case mpeg + case mpg + case threeGp + case webm + case wmv + } } From 8201a9e3f7be468876765a316ea1854348ff3cf6 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:37:37 +0200 Subject: [PATCH 22/67] backend: converse systemprompts --- backend/Sources/App/Application+build.swift | 3 ++- backend/Sources/App/Types/Chat.swift | 1 + .../Converse/ConverseRequest.swift | 16 +++++++++++--- .../Sources/BedrockService/SwiftBedrock.swift | 21 ++++++++++++------- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/backend/Sources/App/Application+build.swift b/backend/Sources/App/Application+build.swift index bdda426f..b91b30f8 100644 --- a/backend/Sources/App/Application+build.swift +++ b/backend/Sources/App/Application+build.swift @@ -181,7 +181,8 @@ func buildRouter(useSSO: Bool, logger: Logger) async throws -> Router [BedrockRuntimeClientTypes.SystemContentBlock]? { + return systemPrompts?.map { + BedrockRuntimeClientTypes.SystemContentBlock.text($0) + } + } + struct InferenceConfig { let maxTokens: Int? let temperature: Double? diff --git a/backend/Sources/BedrockService/SwiftBedrock.swift b/backend/Sources/BedrockService/SwiftBedrock.swift index 8c97c48c..5bd43a2b 100644 --- a/backend/Sources/BedrockService/SwiftBedrock.swift +++ b/backend/Sources/BedrockService/SwiftBedrock.swift @@ -17,9 +17,9 @@ @preconcurrency import AWSBedrockRuntime import AWSClientRuntime import AWSSDKIdentity +import BedrockTypes import Foundation import Logging -import BedrockTypes public struct BedrockService: Sendable { let region: Region @@ -461,12 +461,13 @@ public struct BedrockService: Sendable { with model: BedrockModel, prompt: String, imageFormat: ImageBlock.Format? = nil, - imageBytes: String? = nil, + imageBytes: String? = nil, history: [Message] = [], maxTokens: Int? = nil, temperature: Double? = nil, topP: Double? = nil, stopSequences: [String]? = nil, + systemPrompts: [String]? = nil, tools: [Tool]? = nil ) async throws -> (String, [Message]) { logger.trace( @@ -482,26 +483,31 @@ public struct BedrockService: Sendable { try validateConverseParams( modality: modality, prompt: prompt, - // FIXME: add image + // FIXME: add image history: history, maxTokens: maxTokens, temperature: temperature, topP: topP, stopSequences: stopSequences + // FIXME: add systemPrompts + // FIXME: add tools ) var messages = history messages.append(Message(from: .user, content: [.text(prompt)])) if let imageFormat: ImageBlock.Format = imageFormat, - let imageBytes: String = imageBytes + let imageBytes: String = imageBytes { guard model.hasConverseModality(.vision) else { throw BedrockServiceError.invalidModality( - model, modality, + model, + modality, "This model does not support converse vision." ) } - messages.append(Message(from: .user, content: [.image(ImageBlock(format: imageFormat, source: imageBytes))])) + messages.append( + Message(from: .user, content: [.image(ImageBlock(format: imageFormat, source: imageBytes))]) + ) } let converseRequest = ConverseRequest( @@ -510,7 +516,8 @@ public struct BedrockService: Sendable { maxTokens: maxTokens, temperature: temperature, topP: topP, - stopSequences: stopSequences + stopSequences: stopSequences, + systemPrompts: systemPrompts ) let input = try converseRequest.getConverseInput() logger.trace( From 74d8c57188298b0d3c6c1fee3209c73bce3bf81d Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Wed, 2 Apr 2025 15:59:20 +0200 Subject: [PATCH 23/67] JSON for tools --- backend/Sources/App/Application+build.swift | 3 +- backend/Sources/App/Types/Chat.swift | 1 + .../ConversionExtensions/DocumentToJSON.swift | 48 +++++++++++++ .../ConversionExtensions/JSONtoDocument.swift | 26 +++++++ .../ToolConversionExtension.swift | 66 +++++++++++++++--- .../BedrockService/Converse/Tool.swift | 54 --------------- .../BedrockTypes/ConverseContent/JSON.swift | 69 +++++++++++++++++++ .../BedrockTypes/ConverseContent/Tool.swift | 28 ++++++++ .../ConverseContent/ToolBlocks.swift | 7 +- 9 files changed, 235 insertions(+), 67 deletions(-) create mode 100644 backend/Sources/BedrockService/Converse/ConversionExtensions/DocumentToJSON.swift create mode 100644 backend/Sources/BedrockService/Converse/ConversionExtensions/JSONtoDocument.swift delete mode 100644 backend/Sources/BedrockService/Converse/Tool.swift create mode 100644 backend/Sources/BedrockTypes/ConverseContent/JSON.swift create mode 100644 backend/Sources/BedrockTypes/ConverseContent/Tool.swift diff --git a/backend/Sources/App/Application+build.swift b/backend/Sources/App/Application+build.swift index b91b30f8..462d4e0d 100644 --- a/backend/Sources/App/Application+build.swift +++ b/backend/Sources/App/Application+build.swift @@ -182,7 +182,8 @@ func buildRouter(useSSO: Bool, logger: Logger) async throws -> Router JSON { + switch self.type { + case .string: + return JSON(try self.asString()) + case .boolean: + return JSON(try self.asBoolean()) + case .integer: + return JSON(try self.asInteger()) + case .double, .float: + return JSON(try self.asDouble()) + case .list: + let array = try self.asList().map { try $0.toJSON() } + return JSON(array) + case .map: + let map = try self.asStringMap() + var result: [String: JSON] = [:] + for (key, value) in map { + result[key] = try value.toJSON() + } + return JSON(result) + case .blob: + let data = try self.asBlob() + return JSON(data) + default: + throw DocumentError.typeMismatch("Unsupported type for JSON conversion: \(self.type)") + } + } +} diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/JSONtoDocument.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/JSONtoDocument.swift new file mode 100644 index 00000000..5d22d981 --- /dev/null +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/JSONtoDocument.swift @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import BedrockTypes +import Foundation +import Smithy + +extension JSON { + func toDocument() throws -> Document { + let encoder = JSONEncoder() + let encoded = try encoder.encode(self) + return try Document.make(from: encoded) + } +} diff --git a/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift b/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift index 4b757120..8a54ed6f 100644 --- a/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift +++ b/backend/Sources/BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift @@ -16,6 +16,41 @@ @preconcurrency import AWSBedrockRuntime import BedrockTypes import Foundation +import Smithy + +extension Tool { + init(from sdkToolSpecification: BedrockRuntimeClientTypes.ToolSpecification) throws { + guard let name = sdkToolSpecification.name else { + throw BedrockServiceError.decodingError( + "Could not extract name from BedrockRuntimeClientTypes.ToolSpecification" + ) + } + guard let sdkInputSchema = sdkToolSpecification.inputSchema else { + throw BedrockServiceError.decodingError( + "Could not extract inputSchema from BedrockRuntimeClientTypes.ToolSpecification" + ) + } + guard case .json(let smithyDocument) = sdkInputSchema else { + throw BedrockServiceError.decodingError( + "Could not extract JSON from BedrockRuntimeClientTypes.ToolSpecification.inputSchema" + ) + } + let inputSchema = try smithyDocument.toJSON() + self = Tool( + name: name, + inputSchema: inputSchema, + description: sdkToolSpecification.description + ) + } + + func getSDKToolSpecification() throws -> BedrockRuntimeClientTypes.ToolSpecification { + BedrockRuntimeClientTypes.ToolSpecification( + description: description, + inputSchema: .json(try inputSchema.toDocument()), + name: name + ) + } +} extension ToolUseBlock { init(from sdkToolUseBlock: BedrockRuntimeClientTypes.ToolUseBlock) throws { @@ -29,18 +64,23 @@ extension ToolUseBlock { "Could not extract name from BedrockRuntimeClientTypes.ToolUseBlock" ) } + guard let sdkInput = sdkToolUseBlock.input else { + throw BedrockServiceError.decodingError( + "Could not extract input from BedrockRuntimeClientTypes.ToolUseBlock" + ) + } self = ToolUseBlock( id: sdkId, - name: sdkName - // input: sdkToolUseBlock.input + name: sdkName, + input: try sdkInput.toJSON() ) } func getSDKToolUseBlock() throws -> BedrockRuntimeClientTypes.ToolUseBlock { .init( + input: try input.toDocument(), name: name, toolUseId: id - // input: input ) } } @@ -72,7 +112,7 @@ extension ToolResultBlock { status: status?.getSDKToolStatus(), toolUseId: id ) - + } } @@ -107,21 +147,27 @@ extension ToolResultContent { self = .text(text) case .video(let sdkVideoBlock): self = .video(try VideoBlock(from: sdkVideoBlock)) - // case .json(let sdkJSON): - // self = .json() + case .json(let document): + self = .json(try document.toJSON()) + // case .json(let document): + // self = .json(document.data(using: .utf8)) case .sdkUnknown(let unknownToolResultContent): throw BedrockServiceError.notImplemented( "ToolResultContentBlock \(unknownToolResultContent) is not implemented by BedrockRuntimeClientTypes" ) - default: - throw BedrockServiceError.notImplemented( - "ToolResultContentBlock \(sdkToolResultContent) is not implemented by BedrockTypes" - ) + // default: + // throw BedrockServiceError.notImplemented( + // "ToolResultContentBlock \(sdkToolResultContent) is not implemented by BedrockTypes" + // ) } } func getSDKToolResultContentBlock() throws -> BedrockRuntimeClientTypes.ToolResultContentBlock { switch self { + // case .json(let data): + // .json(try Document.make(from: data)) + case .json(let json): + .json(try json.toDocument()) case .document(let documentBlock): .document(try documentBlock.getSDKDocumentBlock()) case .image(let imageBlock): diff --git a/backend/Sources/BedrockService/Converse/Tool.swift b/backend/Sources/BedrockService/Converse/Tool.swift deleted file mode 100644 index 9facd50d..00000000 --- a/backend/Sources/BedrockService/Converse/Tool.swift +++ /dev/null @@ -1,54 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import BedrockTypes -import Foundation - -public struct Tool { - public let name: String - public let inputSchema: [String: Any] - public let description: String? - - init(name: String, inputSchema: [String: Any], description: String? = nil) { - self.name = name - self.inputSchema = inputSchema - self.description = description - } - - init(from sdkToolSpecification: BedrockRuntimeClientTypes.ToolSpecification) throws { - guard let name = sdkToolSpecification.name else { - throw BedrockServiceError.decodingError( - "Could not extract name from BedrockRuntimeClientTypes.ToolSpecification" - ) - } - guard let sdkInputSchema = sdkToolSpecification.inputSchema else { - throw BedrockServiceError.decodingError( - "Could not extract inputSchema from BedrockRuntimeClientTypes.ToolSpecification" - ) - } - guard case .json(let smithyDocument) = sdkInputSchema else { - throw BedrockServiceError.decodingError( - "Could not extract JSON from BedrockRuntimeClientTypes.ToolSpecification.inputSchema" - ) - } - let inputSchema = try smithyDocument.asStringMap() - self = Tool( - name: name, - inputSchema: inputSchema, - description: sdkToolSpecification.description - ) - } -} \ No newline at end of file diff --git a/backend/Sources/BedrockTypes/ConverseContent/JSON.swift b/backend/Sources/BedrockTypes/ConverseContent/JSON.swift new file mode 100644 index 00000000..1eb50956 --- /dev/null +++ b/backend/Sources/BedrockTypes/ConverseContent/JSON.swift @@ -0,0 +1,69 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Foundation + +public struct JSON: Codable { + var value: Any? + + public init(_ value: Any?) { + self.value = value + } + + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + if container.decodeNil() { + self.value = nil + } else if let intValue = try? container.decode(Int.self) { + self.value = intValue + } else if let doubleValue = try? container.decode(Double.self) { + self.value = doubleValue + } else if let stringValue = try? container.decode(String.self) { + self.value = stringValue + } else if let boolValue = try? container.decode(Bool.self) { + self.value = boolValue + } else if let arrayValue = try? container.decode([JSON].self) { + self.value = arrayValue.map { $0.value } + } else if let dictionaryValue = try? container.decode([String: JSON].self) { + self.value = dictionaryValue.mapValues { $0.value } + } else { + throw DecodingError.dataCorruptedError(in: container, debugDescription: "Unsupported type") + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + if let intValue = value as? Int { + try container.encode(intValue) + } else if let doubleValue = value as? Double { + try container.encode(doubleValue) + } else if let stringValue = value as? String { + try container.encode(stringValue) + } else if let boolValue = value as? Bool { + try container.encode(boolValue) + } else if let arrayValue = value as? [Any] { + let jsonArray = arrayValue.map { JSON($0) } + try container.encode(jsonArray) + } else if let dictionaryValue = value as? [String: Any] { + let jsonDictionary = dictionaryValue.mapValues { JSON($0) } + try container.encode(jsonDictionary) + } else { + throw EncodingError.invalidValue( + value ?? "nil", + EncodingError.Context(codingPath: encoder.codingPath, debugDescription: "Unsupported type") + ) + } + } +} diff --git a/backend/Sources/BedrockTypes/ConverseContent/Tool.swift b/backend/Sources/BedrockTypes/ConverseContent/Tool.swift new file mode 100644 index 00000000..2d49bced --- /dev/null +++ b/backend/Sources/BedrockTypes/ConverseContent/Tool.swift @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Foundation + +public struct Tool: Codable { + public let name: String + public let inputSchema: JSON + public let description: String? + + public init(name: String, inputSchema: JSON, description: String? = nil) { + self.name = name + self.inputSchema = inputSchema + self.description = description + } +} diff --git a/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift b/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift index 2809688a..616fd104 100644 --- a/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift +++ b/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift @@ -18,11 +18,12 @@ import Foundation public struct ToolUseBlock: Codable { public let id: String public let name: String - // let input: [String: Any] + public let input: JSON - public init(id: String, name: String) { + public init(id: String, name: String, input: JSON) { self.id = id self.name = name + self.input = input } } @@ -45,6 +46,8 @@ public enum ToolStatus: Codable { public enum ToolResultContent: Codable { // case json([String: Any]) // Just Data + // case json(Data) + case json(JSON) case text(String) case image(ImageBlock) // currently only supported by Anthropic Claude 3 models case document(DocumentBlock) From 5376807f32fdea6d36ff759efea01a2a98b9c2fb Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Wed, 2 Apr 2025 16:12:20 +0200 Subject: [PATCH 24/67] backend: tools --- .../Converse/ConverseRequest.swift | 41 +++++++++++-------- .../Sources/BedrockService/SwiftBedrock.swift | 3 +- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/backend/Sources/BedrockService/Converse/ConverseRequest.swift b/backend/Sources/BedrockService/Converse/ConverseRequest.swift index 904522f7..b9a37795 100644 --- a/backend/Sources/BedrockService/Converse/ConverseRequest.swift +++ b/backend/Sources/BedrockService/Converse/ConverseRequest.swift @@ -21,7 +21,7 @@ public struct ConverseRequest { let model: BedrockModel let messages: [Message] let inferenceConfig: InferenceConfig? - // let toolConfig: ToolConfig? + let toolConfig: ToolConfig? let systemPrompts: [String]? init( @@ -31,8 +31,8 @@ public struct ConverseRequest { temperature: Double?, topP: Double?, stopSequences: [String]?, - systemPrompts: [String]? - // tools: [Tool] + systemPrompts: [String]?, + tools: [Tool]? ) { self.messages = messages self.model = model @@ -43,21 +43,20 @@ public struct ConverseRequest { stopSequences: stopSequences ) self.systemPrompts = systemPrompts - // self.toolConfig = ToolConfig(tools: tools) + if tools != nil { + self.toolConfig = ToolConfig(tools: tools!) + } else { + self.toolConfig = nil + } } func getConverseInput() throws -> ConverseInput { - let sdkInferenceConfig: BedrockRuntimeClientTypes.InferenceConfiguration? - if inferenceConfig != nil { - sdkInferenceConfig = inferenceConfig!.getSDKInferenceConfig() - } else { - sdkInferenceConfig = nil - } - return ConverseInput( - inferenceConfig: sdkInferenceConfig, + ConverseInput( + inferenceConfig: inferenceConfig?.getSDKInferenceConfig(), messages: try getSDKMessages(), modelId: model.id, - system: getSDKSystemPrompts() + system: getSDKSystemPrompts(), + toolConfig: try toolConfig?.getSDKToolConfig() ) } @@ -66,7 +65,7 @@ public struct ConverseRequest { } private func getSDKSystemPrompts() -> [BedrockRuntimeClientTypes.SystemContentBlock]? { - return systemPrompts?.map { + systemPrompts?.map { BedrockRuntimeClientTypes.SystemContentBlock.text($0) } } @@ -98,11 +97,17 @@ public struct ConverseRequest { ) } } -} -public struct ToolConfig { - // let toolChoice: ToolChoice? - let tools: [Tool] + public struct ToolConfig { + // let toolChoice: ToolChoice? + let tools: [Tool] + + func getSDKToolConfig() throws -> BedrockRuntimeClientTypes.ToolConfiguration { + BedrockRuntimeClientTypes.ToolConfiguration( + tools: try tools.map { .toolspec(try $0.getSDKToolSpecification()) } + ) + } + } } // public enum ToolChoice { diff --git a/backend/Sources/BedrockService/SwiftBedrock.swift b/backend/Sources/BedrockService/SwiftBedrock.swift index 5bd43a2b..ff28a661 100644 --- a/backend/Sources/BedrockService/SwiftBedrock.swift +++ b/backend/Sources/BedrockService/SwiftBedrock.swift @@ -517,7 +517,8 @@ public struct BedrockService: Sendable { temperature: temperature, topP: topP, stopSequences: stopSequences, - systemPrompts: systemPrompts + systemPrompts: systemPrompts, + tools: tools ) let input = try converseRequest.getConverseInput() logger.trace( From b527ae39c7f486894de052b2ce99ac104b149089 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Wed, 2 Apr 2025 17:25:06 +0200 Subject: [PATCH 25/67] tool use --- backend/Sources/App/Application+build.swift | 21 +++++++++++++------ backend/Sources/App/Types/Chat.swift | 2 +- ...wiftBedrock.swift => BedrockService.swift} | 17 ++++++++++++++- .../BedrockTypes/ConverseContent/JSON.swift | 5 ++++- .../{ => ConverseContent}/Message.swift | 0 5 files changed, 36 insertions(+), 9 deletions(-) rename backend/Sources/BedrockService/{SwiftBedrock.swift => BedrockService.swift} (97%) rename backend/Sources/BedrockTypes/{ => ConverseContent}/Message.swift (100%) diff --git a/backend/Sources/App/Application+build.swift b/backend/Sources/App/Application+build.swift index 462d4e0d..473d2487 100644 --- a/backend/Sources/App/Application+build.swift +++ b/backend/Sources/App/Application+build.swift @@ -13,10 +13,11 @@ // //===----------------------------------------------------------------------===// -import Hummingbird -import Logging import BedrockService import BedrockTypes +import Hummingbird +import Logging +import Foundation /// Application arguments protocol. We use a protocol so we can call /// `buildApplication` inside Tests as well as in the App executable. @@ -175,9 +176,9 @@ func buildRouter(useSSO: Bool, logger: Logger) async throws -> Router Router Router Date: Wed, 2 Apr 2025 17:57:40 +0200 Subject: [PATCH 26/67] Tool result --- backend/Sources/App/Application+build.swift | 13 ++--- backend/Sources/App/Types/Chat.swift | 1 + .../BedrockService/BedrockService.swift | 50 ++++++++++++------- .../BedrockService/ParameterValidation.swift | 10 ++-- .../ConverseContent/ToolBlocks.swift | 48 +++++++++++++++++- 5 files changed, 89 insertions(+), 33 deletions(-) diff --git a/backend/Sources/App/Application+build.swift b/backend/Sources/App/Application+build.swift index 473d2487..2e6536c6 100644 --- a/backend/Sources/App/Application+build.swift +++ b/backend/Sources/App/Application+build.swift @@ -184,17 +184,10 @@ func buildRouter(useSSO: Bool, logger: Logger) async throws -> Router (String, [Message]) { logger.trace( "Conversing", metadata: [ "model.id": .string(model.id), "model.modality": .string(model.modality.getName()), - "prompt": .string(prompt), + "prompt": .string(prompt ?? "No prompt"), ] ) do { @@ -489,35 +490,50 @@ public struct BedrockService: Sendable { temperature: temperature, topP: topP, stopSequences: stopSequences - // FIXME: add systemPrompts - // FIXME: add tools + // FIXME: add systemPrompts + // FIXME: add tools ) var messages = history - messages.append(Message(from: .user, content: [.text(prompt)])) - if let imageFormat: ImageBlock.Format = imageFormat, - let imageBytes: String = imageBytes - { - guard model.hasConverseModality(.vision) else { + + if tools != nil || toolResult != nil { + guard model.hasConverseModality(.toolUse) else { throw BedrockServiceError.invalidModality( model, modality, - "This model does not support converse vision." + "This model does not support converse tool." ) } - messages.append( - Message(from: .user, content: [.image(ImageBlock(format: imageFormat, source: imageBytes))]) - ) } - if tools != nil { - guard model.hasConverseModality(.toolUse) else { + if toolResult != nil { + guard let _: [Tool] = tools else { + throw BedrockServiceError.invalidPrompt("Tool result is defined but tools are not.") + } + guard case .toolUse(_) = messages.last?.content.last else { + throw BedrockServiceError.invalidPrompt("Tool result is defined but last message is not tool use.") + } + messages.append(Message(from: .user, content: [.toolResult(toolResult!)])) + } else { + guard let prompt = prompt else { + throw BedrockServiceError.invalidPrompt("Prompt is not defined.") + } + messages.append(Message(from: .user, content: [.text(prompt)])) + } + + if let imageFormat = imageFormat, + let imageBytes = imageBytes + { + guard model.hasConverseModality(.vision) else { throw BedrockServiceError.invalidModality( model, modality, - "This model does not support converse tool." + "This model does not support converse vision." ) } + messages.append( + Message(from: .user, content: [.image(ImageBlock(format: imageFormat, source: imageBytes))]) + ) } let converseRequest = ConverseRequest( diff --git a/backend/Sources/BedrockService/ParameterValidation.swift b/backend/Sources/BedrockService/ParameterValidation.swift index 429a6849..6989a814 100644 --- a/backend/Sources/BedrockService/ParameterValidation.swift +++ b/backend/Sources/BedrockService/ParameterValidation.swift @@ -13,8 +13,8 @@ // //===----------------------------------------------------------------------===// -import Foundation import BedrockTypes +import Foundation extension BedrockService { @@ -109,7 +109,7 @@ extension BedrockService { /// Validate parameters for a converse request public func validateConverseParams( modality: any ConverseModality, - prompt: String, + prompt: String?, history: [Message], maxTokens: Int?, temperature: Double?, @@ -117,7 +117,9 @@ extension BedrockService { stopSequences: [String]? ) throws { let parameters = modality.getConverseParameters() - try validatePrompt(prompt, maxPromptTokens: parameters.prompt.maxSize) + if prompt != nil { + try validatePrompt(prompt!, maxPromptTokens: parameters.prompt.maxSize) + } if maxTokens != nil { try validateParameterValue(maxTokens!, parameter: parameters.maxTokens) } @@ -177,7 +179,7 @@ extension BedrockService { metadata: [ "parameter": "\(parameter.name)", "value": "\(value)", "value.min": "\(String(describing: parameter.minValue))", - "value.max": "\(String(describing: parameter.maxValue))" + "value.max": "\(String(describing: parameter.maxValue))", ] ) } diff --git a/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift b/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift index 616fd104..cc7eebb3 100644 --- a/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift +++ b/backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift @@ -44,8 +44,8 @@ public enum ToolStatus: Codable { case error } -public enum ToolResultContent: Codable { - // case json([String: Any]) // Just Data +public enum ToolResultContent { + // case json([String: Any]) // Just Data // case json(Data) case json(JSON) case text(String) @@ -53,3 +53,47 @@ public enum ToolResultContent: Codable { case document(DocumentBlock) case video(VideoBlock) } + +extension ToolResultContent: Codable { + private enum CodingKeys: String, CodingKey { + case json, text, image, document, video + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + switch self { + case .json(let json): + try container.encode(json, forKey: .json) + case .text(let text): + try container.encode(text, forKey: .text) + case .image(let image): + try container.encode(image, forKey: .image) + case .document(let doc): + try container.encode(doc, forKey: .document) + case .video(let video): + try container.encode(video, forKey: .video) + } + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + if let json = try container.decodeIfPresent(JSON.self, forKey: .json) { + self = .json(json) + } else if let text = try container.decodeIfPresent(String.self, forKey: .text) { + self = .text(text) + } else if let image = try container.decodeIfPresent(ImageBlock.self, forKey: .image) { + self = .image(image) + } else if let doc = try container.decodeIfPresent(DocumentBlock.self, forKey: .document) { + self = .document(doc) + } else if let video = try container.decodeIfPresent(VideoBlock.self, forKey: .video) { + self = .video(video) + } else { + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Invalid tool result content" + ) + ) + } + } +} From 1b2cf7cd851e44df39560eaf783a5fcb7bc34da7 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Wed, 2 Apr 2025 18:01:10 +0200 Subject: [PATCH 27/67] Tool result --- backend/Sources/App/Types/Chat.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/Sources/App/Types/Chat.swift b/backend/Sources/App/Types/Chat.swift index c5e1f411..c57c5a74 100644 --- a/backend/Sources/App/Types/Chat.swift +++ b/backend/Sources/App/Types/Chat.swift @@ -20,7 +20,7 @@ import BedrockTypes extension Message: ResponseCodable {} struct ChatInput: Codable { - let prompt: String + let prompt: String? let history: [Message]? let imageFormat: ImageBlock.Format? let imageBytes: String? From 97a7beabd35f5878ff6ca3a974ed18d295065123 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Wed, 2 Apr 2025 18:11:35 +0200 Subject: [PATCH 28/67] Deleted Converse only modality for Nova --- README.md | 3 ++- .../BedrockTypes/Models/Amazon/Nova/Nova.swift | 18 +++++++++--------- .../Models/Amazon/Nova/NovaBedrockModels.swift | 10 ++++++---- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index b2a110b4..80f19a20 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ Work in progress, feel free to open issue, but do not use in your projects. + diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift b/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift index 5a30bf3a..b74480c5 100644 --- a/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift +++ b/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift @@ -15,17 +15,17 @@ import Foundation -struct NovaConverse: ConverseModality { - func getName() -> String { "Nova Lite and Nova Pro Converse Modality" } +// struct NovaConverse: ConverseModality { +// func getName() -> String { "Nova Lite and Nova Pro Converse Modality" } - let converseParameters: ConverseParameters - let converseFeatures: [ConverseFeature] +// let converseParameters: ConverseParameters +// let converseFeatures: [ConverseFeature] - init(parameters: ConverseParameters, features: [ConverseFeature]) { - self.converseParameters = parameters - self.converseFeatures = features - } -} +// init(parameters: ConverseParameters, features: [ConverseFeature]) { +// self.converseParameters = parameters +// self.converseFeatures = features +// } +// } struct NovaText: TextModality, ConverseModality { func getName() -> String { "Nova Text Generation" } diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift index 237526c8..59cdb05d 100644 --- a/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift +++ b/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift @@ -39,11 +39,12 @@ extension BedrockModel { public static let nova_lite: BedrockModel = BedrockModel( id: "amazon.nova-lite-v1:0", name: "Nova Lite", - modality: NovaConverse( - parameters: ConverseParameters( + modality: NovaText( + parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0.00001, maxValue: 1, defaultValue: 0.7), maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 5_000, defaultValue: 5_000), topP: Parameter(.topP, minValue: 0, maxValue: 1.0, defaultValue: 0.9), + topK: Parameter(.topK, minValue: 0, maxValue: nil, defaultValue: 50), stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), maxPromptSize: nil ), @@ -53,11 +54,12 @@ extension BedrockModel { public static let nova_pro: BedrockModel = BedrockModel( id: "amazon.nova-pro-v1:0", name: "Nova Pro", - modality: NovaConverse( - parameters: ConverseParameters( + modality: NovaText( + parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0.00001, maxValue: 1, defaultValue: 0.7), maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 5_000, defaultValue: 5_000), topP: Parameter(.topP, minValue: 0, maxValue: 1.0, defaultValue: 0.9), + topK: Parameter(.topK, minValue: 0, maxValue: nil, defaultValue: 50), stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), maxPromptSize: nil ), From ed422139fc8dabdb5cb647f1dab1cbcb2c5a4f24 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Thu, 3 Apr 2025 10:54:00 +0200 Subject: [PATCH 29/67] refactor: moved converse transforamtions to BedrockTypes --- README.md | 4 +- backend/Package.swift | 4 + .../BedrockService/BedrockService.swift | 6 +- .../RoleConversionExtension.swift | 38 ----- .../VideoConversionExtension.swift | 111 -------------- .../Sources/BedrockService/ModelSummary.swift | 6 +- .../Converse/Content.swift} | 14 +- .../Converse/DocumentBlock.swift} | 87 +++++++---- .../Converse}/DocumentToJSON.swift | 4 +- .../Converse/ImageBlock.swift} | 57 ++++--- .../{ConverseContent => Converse}/JSON.swift | 0 .../Converse}/JSONtoDocument.swift | 3 +- .../Converse/Message.swift} | 27 +++- .../Converse/S3Location.swift} | 16 +- .../Sources/BedrockTypes/Converse/Tool.swift | 62 ++++++++ .../Converse/ToolResultBlock.swift} | 145 +++++++++--------- .../BedrockTypes/Converse/ToolUseBlock.swift | 60 ++++++++ .../BedrockTypes/Converse/VideoBlock.swift | 131 ++++++++++++++++ .../ConverseContent/Content.swift | 26 ---- .../ConverseContent/DocumentBlock.swift | 40 ----- .../ConverseContent/ImageBlock.swift | 33 ---- .../ConverseContent/Message.swift | 30 ---- .../ConverseContent/S3Location.swift | 26 ---- .../BedrockTypes/ConverseContent/Tool.swift | 28 ---- .../ConverseContent/ToolBlocks.swift | 99 ------------ .../ConverseContent/VideoBlock.swift | 43 ------ .../{ => InvokeModel}/ContentType.swift | 0 .../ImageGenerationOutput.swift | 0 .../{ => InvokeModel}/ImageResolution.swift | 0 .../{ => InvokeModel}/Protocols.swift | 0 .../{ => InvokeModel}/TextCompletion.swift | 0 backend/Sources/BedrockTypes/Role.swift | 19 +++ 32 files changed, 489 insertions(+), 630 deletions(-) delete mode 100644 backend/Sources/BedrockService/Converse/ConversionExtensions/RoleConversionExtension.swift delete mode 100644 backend/Sources/BedrockService/Converse/ConversionExtensions/VideoConversionExtension.swift rename backend/Sources/{BedrockService/Converse/ConversionExtensions/ContentConversionExtension.swift => BedrockTypes/Converse/Content.swift} (86%) rename backend/Sources/{BedrockService/Converse/ConversionExtensions/DocumentConversionExtension.swift => BedrockTypes/Converse/DocumentBlock.swift} (53%) rename backend/Sources/{BedrockService/Converse/ConversionExtensions => BedrockTypes/Converse}/DocumentToJSON.swift (94%) rename backend/Sources/{BedrockService/Converse/ConversionExtensions/ImageConversionExtension.swift => BedrockTypes/Converse/ImageBlock.swift} (61%) rename backend/Sources/BedrockTypes/{ConverseContent => Converse}/JSON.swift (100%) rename backend/Sources/{BedrockService/Converse/ConversionExtensions => BedrockTypes/Converse}/JSONtoDocument.swift (92%) rename backend/Sources/{BedrockService/Converse/ConversionExtensions/MessageConversionExtension.swift => BedrockTypes/Converse/Message.swift} (67%) rename backend/Sources/{BedrockService/Converse/ConversionExtensions/S3LocationConversionExtension.swift => BedrockTypes/Converse/S3Location.swift} (75%) create mode 100644 backend/Sources/BedrockTypes/Converse/Tool.swift rename backend/Sources/{BedrockService/Converse/ConversionExtensions/ToolConversionExtension.swift => BedrockTypes/Converse/ToolResultBlock.swift} (56%) create mode 100644 backend/Sources/BedrockTypes/Converse/ToolUseBlock.swift create mode 100644 backend/Sources/BedrockTypes/Converse/VideoBlock.swift delete mode 100644 backend/Sources/BedrockTypes/ConverseContent/Content.swift delete mode 100644 backend/Sources/BedrockTypes/ConverseContent/DocumentBlock.swift delete mode 100644 backend/Sources/BedrockTypes/ConverseContent/ImageBlock.swift delete mode 100644 backend/Sources/BedrockTypes/ConverseContent/Message.swift delete mode 100644 backend/Sources/BedrockTypes/ConverseContent/S3Location.swift delete mode 100644 backend/Sources/BedrockTypes/ConverseContent/Tool.swift delete mode 100644 backend/Sources/BedrockTypes/ConverseContent/ToolBlocks.swift delete mode 100644 backend/Sources/BedrockTypes/ConverseContent/VideoBlock.swift rename backend/Sources/BedrockTypes/{ => InvokeModel}/ContentType.swift (100%) rename backend/Sources/BedrockTypes/{ => InvokeModel}/ImageGenerationOutput.swift (100%) rename backend/Sources/BedrockTypes/{ => InvokeModel}/ImageResolution.swift (100%) rename backend/Sources/BedrockTypes/{ => InvokeModel}/Protocols.swift (100%) rename backend/Sources/BedrockTypes/{ => InvokeModel}/TextCompletion.swift (100%) diff --git a/README.md b/README.md index 80f19a20..376fde83 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # SwiftBedrockService Work in progress, feel free to open issue, but do not use in your projects. - +1. How to use the library? +2. Example for every functionality +3. How to add a model(family) +Note that the minimum, maximum and default values for each parameter are model specific and defined when the BedrockModel is created. Some parameters might not be supported by certain models. + +## How to chat using the Converse API + +### Text prompt + +### Vision + +### Tools + +## How to add a BedrockModel + +### Text + +### Image + +### Converse + + diff --git a/backend/Sources/BedrockService/BedrockService.swift b/backend/Sources/BedrockService/BedrockService.swift index e886dd6a..792c528b 100644 --- a/backend/Sources/BedrockService/BedrockService.swift +++ b/backend/Sources/BedrockService/BedrockService.swift @@ -44,7 +44,7 @@ public struct BedrockService: Sendable { bedrockRuntimeClient: BedrockRuntimeClientProtocol? = nil, useSSO: Bool = false ) async throws { - self.logger = logger ?? BedrockService.createLogger("swiftbedrock.service") + self.logger = logger ?? BedrockService.createLogger("bedrock.service") self.logger.trace( "Initializing SwiftBedrock", metadata: ["region": .string(region.rawValue)] diff --git a/backend/Sources/BedrockTypes/BedrockModel.swift b/backend/Sources/BedrockTypes/BedrockModel.swift index 8f8226c6..2c315540 100644 --- a/backend/Sources/BedrockTypes/BedrockModel.swift +++ b/backend/Sources/BedrockTypes/BedrockModel.swift @@ -103,6 +103,10 @@ public struct BedrockModel: Hashable, Sendable, Equatable, RawRepresentable { } } + // MARK: Modality checks + + // MARK - Text completion + /// Checks if the model supports text generation /// - Returns: True if the model supports text generation public func hasTextModality() -> Bool { @@ -122,34 +126,7 @@ public struct BedrockModel: Hashable, Sendable, Equatable, RawRepresentable { return textModality } - /// Checks if the model supports text generation and returns ConverseModality - /// - Returns: ConverseModality if the model supports text modality - public func getConverseModality() throws -> any ConverseModality { - guard let modality = modality as? any ConverseModality else { - throw BedrockServiceError.invalidModality( - self, - modality, - "Model \(id) does not support text generation" - ) - } - return modality - } - - // FIXME: this would be cleaner - // Error: Only concrete types such as structs, enums and classes can conform to protocols - public func getModality() throws -> M { - guard let modality = modality as? M else { - throw BedrockServiceError.invalidModality( - self, - modality, - "Model \(id) does not support \(M.self)" - ) - } - return modality - } - public func hasModality(_ type: M.Type) -> Bool { - modality as? M != nil - } + // MARK - Image generation /// Checks if the model supports image generation /// - Returns: True if the model supports image generation @@ -170,6 +147,12 @@ public struct BedrockModel: Hashable, Sendable, Equatable, RawRepresentable { return imageModality } + /// Checks if the model supports text to image generation + /// - Returns: True if the model supports text to image generation + public func hasTextToImageModality() -> Bool { + modality as? any TextToImageModality != nil + } + /// Checks if the model supports text to image generation and returns TextToImageModality /// - Returns: TextToImageModality if the model supports image modality public func getTextToImageModality() throws -> any TextToImageModality { @@ -183,6 +166,12 @@ public struct BedrockModel: Hashable, Sendable, Equatable, RawRepresentable { return textToImageModality } + /// Checks if the model supports image variation + /// - Returns: True if the model supports image variation + public func hasImageVariationModality() -> Bool { + modality as? any ImageVariationModality != nil + } + /// Checks if the model supports image variation and returns ImageVariationModality /// - Returns: ImageVariationModality if the model supports image modality public func getImageVariationModality() throws -> any ImageVariationModality { @@ -196,32 +185,24 @@ public struct BedrockModel: Hashable, Sendable, Equatable, RawRepresentable { return modality } - /// Checks if the model supports text to image generation - /// - Returns: True if the model supports text to image generation - public func hasTextToImageModality() -> Bool { - modality as? any TextToImageModality != nil - } - - /// Checks if the model supports image variation - /// - Returns: True if the model supports image variation - public func hasImageVariationModality() -> Bool { - modality as? any ImageVariationModality != nil - } - /// Checks if the model supports conditioned text to image generation /// - Returns: True if the model supports conditioned text to image generation public func hasConditionedTextToImageModality() -> Bool { modality as? any ConditionedTextToImageModality != nil } + // MARK - Converse + /// Checks if the model supports converse /// - Returns: True if the model supports converse public func hasConverseModality() -> Bool { modality as? any ConverseModality != nil } - // /// Checks if the model supports converse vision - // /// - Returns: True if the model supports converse vision + /// Checks if the model supports a specific converse feature + /// - Parameters: + /// - feature: the ConverseFeature that will be checked + /// - Returns: True if the model supports the converse feature public func hasConverseModality(_ feature: ConverseFeature = .textGeneration) -> Bool { if let converseModality = modality as? any ConverseModality { let features = converseModality.getConverseFeatures() @@ -229,6 +210,19 @@ public struct BedrockModel: Hashable, Sendable, Equatable, RawRepresentable { } return false } + + /// Checks if the model supports text generation and returns ConverseModality + /// - Returns: ConverseModality if the model supports text modality + public func getConverseModality() throws -> any ConverseModality { + guard let modality = modality as? any ConverseModality else { + throw BedrockServiceError.invalidModality( + self, + modality, + "Model \(id) does not support text generation" + ) + } + return modality + } } extension BedrockModel: Encodable { From d7aa092004782d14795670ffa3d0fa1b9db37d8b Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Thu, 3 Apr 2025 21:33:48 +0200 Subject: [PATCH 35/67] README + StanderdConverse --- README.md | 177 ++++++++++++++++++ backend/Package.swift | 2 +- .../Types/Cohere/CohereBedrockModels.swift | 52 +++++ .../BedrockService/BedrockService.swift | 2 +- .../Sources/BedrockTypes/BedrockModel.swift | 3 + .../StandardConverse.swift} | 13 +- .../Models/Amazon/Nova/Nova.swift | 12 -- .../Models/Mistral/MistralBedrockModels.swift | 2 + 8 files changed, 244 insertions(+), 19 deletions(-) create mode 100644 backend/Sources/App/Types/Cohere/CohereBedrockModels.swift rename backend/Sources/BedrockTypes/{Models/Mistral/Mistral.swift => Modalities/StandardConverse.swift} (60%) diff --git a/README.md b/README.md index dd69ee25..8bc63a69 100644 --- a/README.md +++ b/README.md @@ -141,16 +141,193 @@ Note that the minimum, maximum and default values for each parameter are model s ### Text prompt +```swift +let model = .nova_lite + +guard model.hasConverseModality() else { + print("\(model.name) does not support converse") +} + +var (reply, history) = try await bedrock.converse( + with: model, + prompt: "Tell me about rainbows", + history: [], + maxTokens: 512, +) + +print("Assistant: \(reply)") + +(reply, history) = try await bedrock.converse( + with: model, + prompt: "Do you think birds can see them too?", + history: history, + maxTokens: 512, +) + +print("Assistant: \(reply)") +``` + + ### Vision +```swift +let model = .nova_lite + +guard model.hasConverseModality(.vision) else { + print("\(model.name) does not support converse") +} + +let (reply, history) = try await bedrock.converse( + with model: model, + prompt: "Can you tell me about this plant?", + imageFormat: .jpeg, + imageBytes: base64EncodedImage, + temperature: 0.8, +) + +print("Assistant: \(reply)") +``` + ### Tools +```swift +let model = .nova_lite + +// verify that the model supports tool usage +guard model.hasConverseModality(.toolUse) else { + print("\(model.name) does not support converse tools") +} + +// define the inputschema for your tool +let inputSchema = JSON([ + "type": "object", + "properties": [ + "sign": [ + "type": "string", + "description": "The call sign for the radio station for which you want the most popular song. Example calls signs are WZPZ and WKRP." + ] + ], + "required": [ + "sign" + ] +]) + +// create a Tool object +let tool = Tool(name: "top_song", inputSchema: inputSchema, description: "Get the most popular song played on a radio station.") + +// pass a prompt and the tool to converse +var (reply, history) = try await bedrock.converse( + with model: model, + prompt: "What is the most popular song on WZPZ?", + tools: [tool] +) + +print("Assistant: \(reply)") +// The reply will be similar to this: "I need to use the \"top_song\" tool to find the most popular song on the radio station WZPZ. I will input the call sign \"WZPZ\" into the tool to get the required information." +// The last message in the history will contain the tool use request + +if case .toolUse(let toolUse) = history.last?.content.last { + let id = toolUse.id + let name = toolUse.name + let input = toolUse.input + + // Logic to use the tool here + + let toolResult = ToolResultBlock(id: id, content: [.text("The Best Song Ever")], status: .success) + + // Send the toolResult back to the model + (reply, history) = try await bedrock.converse( + with: model, + history: history, + tools: [tool], + toolResult: toolResult +) +} + +print("Assistant: \(reply)") +// The final reply will be similar to: "The most popular song currently played on WZPZ is \"The Best Song Ever\". If you need more information or have another request, feel free to ask!" +``` + +### Make your own `Message` + +Alternatively use the `converse` function that does not take a `prompt`, `toolResult` or `image` and construct the `Message` yourself. + +```swift +// Message with prompt +let (reply, history) = try await bedrock.converse( + with model: model, + conversation: [Message("What day of the week is it?")], + maxTokens: 512, + temperature: 1, + stopSequences: ["THE END"], + systemPrompts: ["Today is Wednesday, make sure to mention that."] +) + +// Message with an image and prompt +let (reply, history) = try await bedrock.converse( + with model: model, + conversation: [Message(prompt: "What is in the this teacup?", imageFormat: .jpeg, imageBytes: base64EncodedImage)], +) + +// Message with toolResult +let (reply, history) = try await bedrock.converse( + with model: model, + conversation: [Message(toolResult)], + tools: [toolA, toolB] +) +``` + ## How to add a BedrockModel ### Text +-- Under Construction -- + ### Image +-- Under Construction -- + ### Converse +To add a new model that only needs the ConverseModality, simply use the `StandardConverse` and add the correct [inferece parameters](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters.html) and [supported converse features](https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html). + +```swift +extension BedrockModel { + public static let new_bedrock_model = BedrockModel( + id: "family.model-id-v1:0", + name: "New Model Name", + modality: StandardConverse( + parameters: ConverseParameters( + temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.3), + maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: nil, defaultValue: nil), + topP: Parameter(.topP, minValue: 0.01, maxValue: 0.99, defaultValue: 0.75), + stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), + maxPromptSize: nil + ), + features: [.textGeneration, .systemPrompts, .document, .toolUse] + ) + ) +} +``` + +If the model also implements other modalities you might need to create you own `Modality` and make sure it conforms to `ConverseModality` by implementing the `getConverseParameters` and `getConverseFeatures` functions. Note that the `ConverseParameters` can be extracted from `TextGenerationParameters` by using the public initializer. +```swift +struct ModelFamilyModality: TextModality, ConverseModality { + func getName() -> String { "Model Family Text and Converse Modality" } + + let parameters: TextGenerationParameters + let converseFeatures: [ConverseFeature] + let converseParameters: ConverseParameters + + init(parameters: TextGenerationParameters, features: [ConverseFeature] = [.textGeneration]) { + self.parameters = parameters + self.converseFeatures = features + + // public initializer to extract `ConverseParameters` from `TextGenerationParameters` + self.converseParameters = ConverseParameters(textGenerationParameters: parameters) + } + + // ... +} +``` diff --git a/backend/Package.swift b/backend/Package.swift index 9f22df4f..81a719cd 100644 --- a/backend/Package.swift +++ b/backend/Package.swift @@ -42,7 +42,7 @@ let package = Package( .target( name: "BedrockTypes", dependencies: [ - .product(name: "AWSClientRuntime", package: "aws-sdk-swift"), + .product(name: "AWSBedrockRuntime", package: "aws-sdk-swift"), .product(name: "Smithy", package: "smithy-swift"), ], path: "Sources/BedrockTypes" diff --git a/backend/Sources/App/Types/Cohere/CohereBedrockModels.swift b/backend/Sources/App/Types/Cohere/CohereBedrockModels.swift new file mode 100644 index 00000000..580c2c32 --- /dev/null +++ b/backend/Sources/App/Types/Cohere/CohereBedrockModels.swift @@ -0,0 +1,52 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import BedrockTypes +import Foundation + +// https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-cohere-command-r-plus.html +typealias CohereConverse = StandardConverse + +extension BedrockModel { + public static let cohere_command_R_plus = BedrockModel( + id: "cohere.command-r-plus-v1:0", + name: "Cohere Command R+", + modality: CohereConverse( + parameters: ConverseParameters( + temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.3), + maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: nil, defaultValue: nil), + topP: Parameter(.topP, minValue: 0.01, maxValue: 0.99, defaultValue: 0.75), + stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), + maxPromptSize: nil + ), + features: [.textGeneration, .systemPrompts, .document, .toolUse] + ) + ) + + public static let cohere_command_R = BedrockModel( + id: "cohere.command-r-v1:0", + name: "Cohere Command R", + modality: CohereConverse( + parameters: ConverseParameters( + temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.3), + maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: nil, defaultValue: nil), + topP: Parameter(.topP, minValue: 0.01, maxValue: 0.99, defaultValue: 0.75), + stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), + maxPromptSize: nil + ), + features: [.textGeneration, .systemPrompts, .document, .toolUse] + ) + ) +} diff --git a/backend/Sources/BedrockService/BedrockService.swift b/backend/Sources/BedrockService/BedrockService.swift index 792c528b..96285116 100644 --- a/backend/Sources/BedrockService/BedrockService.swift +++ b/backend/Sources/BedrockService/BedrockService.swift @@ -603,7 +603,7 @@ public struct BedrockService: Sendable { /// - Returns: Tuple containing the model's response text and updated message history public func converse( with model: BedrockModel, - prompt: String?, + prompt: String? = nil, imageFormat: ImageBlock.Format? = nil, imageBytes: String? = nil, history: [Message] = [], diff --git a/backend/Sources/BedrockTypes/BedrockModel.swift b/backend/Sources/BedrockTypes/BedrockModel.swift index 2c315540..0d3d778a 100644 --- a/backend/Sources/BedrockTypes/BedrockModel.swift +++ b/backend/Sources/BedrockTypes/BedrockModel.swift @@ -98,6 +98,9 @@ public struct BedrockModel: Hashable, Sendable, Equatable, RawRepresentable { case BedrockModel.mistral_small_2402.id: self = BedrockModel.mistral_small_2402 case BedrockModel.mistral_7B_instruct.id: self = BedrockModel.mistral_7B_instruct case BedrockModel.mistral_8x7B_instruct.id: self = BedrockModel.mistral_8x7B_instruct + //cohere + // case BedrockModel.cohere_command_R_plus.id: self = BedrockModel.cohere_command_R_plus + // case BedrockModel.cohere_command_R.id: self = BedrockModel.cohere_command_R default: return nil } diff --git a/backend/Sources/BedrockTypes/Models/Mistral/Mistral.swift b/backend/Sources/BedrockTypes/Modalities/StandardConverse.swift similarity index 60% rename from backend/Sources/BedrockTypes/Models/Mistral/Mistral.swift rename to backend/Sources/BedrockTypes/Modalities/StandardConverse.swift index f1353a16..2687eba8 100644 --- a/backend/Sources/BedrockTypes/Models/Mistral/Mistral.swift +++ b/backend/Sources/BedrockTypes/Modalities/StandardConverse.swift @@ -15,14 +15,17 @@ import Foundation -struct MistralConverse: ConverseModality { - func getName() -> String { "Mistral Converse Modality" } +public struct StandardConverse: ConverseModality { + public func getName() -> String { "Standard Converse Modality" } - let converseParameters: ConverseParameters - let converseFeatures: [ConverseFeature] + public let converseParameters: ConverseParameters + public let converseFeatures: [ConverseFeature] - init(parameters: ConverseParameters, features: [ConverseFeature]) { + public init(parameters: ConverseParameters, features: [ConverseFeature]) { self.converseParameters = parameters self.converseFeatures = features } + + public func getConverseParameters() -> ConverseParameters { converseParameters } + public func getConverseFeatures() -> [ConverseFeature] { converseFeatures } } diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift b/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift index b74480c5..5386aa91 100644 --- a/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift +++ b/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift @@ -15,18 +15,6 @@ import Foundation -// struct NovaConverse: ConverseModality { -// func getName() -> String { "Nova Lite and Nova Pro Converse Modality" } - -// let converseParameters: ConverseParameters -// let converseFeatures: [ConverseFeature] - -// init(parameters: ConverseParameters, features: [ConverseFeature]) { -// self.converseParameters = parameters -// self.converseFeatures = features -// } -// } - struct NovaText: TextModality, ConverseModality { func getName() -> String { "Nova Text Generation" } diff --git a/backend/Sources/BedrockTypes/Models/Mistral/MistralBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Mistral/MistralBedrockModels.swift index b6b29273..85aaef4e 100644 --- a/backend/Sources/BedrockTypes/Models/Mistral/MistralBedrockModels.swift +++ b/backend/Sources/BedrockTypes/Models/Mistral/MistralBedrockModels.swift @@ -19,6 +19,8 @@ import Foundation // https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-mistral-text-completion.html // https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html +typealias MistralConverse = StandardConverse + extension BedrockModel { public static let mistral_large_2402 = BedrockModel( id: "mistral.mistral-large-2402-v1:0", From faf0d1d11330a3972ed3512bbafc5e8408971227 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Thu, 3 Apr 2025 21:45:52 +0200 Subject: [PATCH 36/67] BedrockModel Cohere --- .../Sources/BedrockTypes/BedrockModel.swift | 4 ++-- .../Models}/Cohere/CohereBedrockModels.swift | 2 +- .../Models/Jamba/JambaBedrockModels.swift | 23 +++++++++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) rename backend/Sources/{App/Types => BedrockTypes/Models}/Cohere/CohereBedrockModels.swift (98%) create mode 100644 backend/Sources/BedrockTypes/Models/Jamba/JambaBedrockModels.swift diff --git a/backend/Sources/BedrockTypes/BedrockModel.swift b/backend/Sources/BedrockTypes/BedrockModel.swift index 0d3d778a..121883a2 100644 --- a/backend/Sources/BedrockTypes/BedrockModel.swift +++ b/backend/Sources/BedrockTypes/BedrockModel.swift @@ -99,8 +99,8 @@ public struct BedrockModel: Hashable, Sendable, Equatable, RawRepresentable { case BedrockModel.mistral_7B_instruct.id: self = BedrockModel.mistral_7B_instruct case BedrockModel.mistral_8x7B_instruct.id: self = BedrockModel.mistral_8x7B_instruct //cohere - // case BedrockModel.cohere_command_R_plus.id: self = BedrockModel.cohere_command_R_plus - // case BedrockModel.cohere_command_R.id: self = BedrockModel.cohere_command_R + case BedrockModel.cohere_command_R_plus.id: self = BedrockModel.cohere_command_R_plus + case BedrockModel.cohere_command_R.id: self = BedrockModel.cohere_command_R default: return nil } diff --git a/backend/Sources/App/Types/Cohere/CohereBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Cohere/CohereBedrockModels.swift similarity index 98% rename from backend/Sources/App/Types/Cohere/CohereBedrockModels.swift rename to backend/Sources/BedrockTypes/Models/Cohere/CohereBedrockModels.swift index 580c2c32..65873215 100644 --- a/backend/Sources/App/Types/Cohere/CohereBedrockModels.swift +++ b/backend/Sources/BedrockTypes/Models/Cohere/CohereBedrockModels.swift @@ -13,7 +13,7 @@ // //===----------------------------------------------------------------------===// -import BedrockTypes +// import BedrockTypes import Foundation // https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-cohere-command-r-plus.html diff --git a/backend/Sources/BedrockTypes/Models/Jamba/JambaBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Jamba/JambaBedrockModels.swift new file mode 100644 index 00000000..06f43d52 --- /dev/null +++ b/backend/Sources/BedrockTypes/Models/Jamba/JambaBedrockModels.swift @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Foundation + +// MARK: converse only +// https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-jamba.html +// https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html + +typealias JambaConverse = StandardConverse + From 2b5d57117c81fde62d4e8201c98b2066798fea6f Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 7 Apr 2025 13:00:29 +0200 Subject: [PATCH 37/67] formatting --- backend/Sources/App/Application+build.swift | 2 +- backend/Sources/App/Types/Chat.swift | 2 +- .../Sources/App/Types/ImageGeneration.swift | 2 +- backend/Sources/App/Types/ListModels.swift | 2 +- .../Sources/App/Types/TextGeneration.swift | 2 +- .../Converse/ConverseResponse.swift | 2 +- .../InvokeModel/InvokeModelRequest.swift | 2 +- .../InvokeModel/InvokeModelResponse.swift | 4 +-- .../BedrockRuntimeClientProtocol.swift | 2 +- .../Nova/NovaImageResolutionValidator.swift | 10 +++++-- .../Models/Amazon/Titan/Titan.swift | 2 +- .../Amazon/Titan/TitanBedrockModels.swift | 15 ++++++---- .../Models/Anthropic/Anthropic.swift | 5 +++- .../Anthropic/AnthropicBedrockModels.swift | 30 ++++++++++++------- .../DeepSeek/DeepSeekBedrockModels.swift | 3 +- .../Models/Jamba/JambaBedrockModels.swift | 1 - .../BedrockTypes/Parameters/Parameters.swift | 3 +- .../Mock/MockBedrockClient.swift | 2 +- .../Mock/MockBedrockRuntimeClient.swift | 4 +-- .../SwiftBedrockServiceTests.swift | 10 +++---- 20 files changed, 65 insertions(+), 40 deletions(-) diff --git a/backend/Sources/App/Application+build.swift b/backend/Sources/App/Application+build.swift index 2e6536c6..08a61676 100644 --- a/backend/Sources/App/Application+build.swift +++ b/backend/Sources/App/Application+build.swift @@ -15,9 +15,9 @@ import BedrockService import BedrockTypes +import Foundation import Hummingbird import Logging -import Foundation /// Application arguments protocol. We use a protocol so we can call /// `buildApplication` inside Tests as well as in the App executable. diff --git a/backend/Sources/App/Types/Chat.swift b/backend/Sources/App/Types/Chat.swift index c57c5a74..5d8e4338 100644 --- a/backend/Sources/App/Types/Chat.swift +++ b/backend/Sources/App/Types/Chat.swift @@ -13,9 +13,9 @@ // //===----------------------------------------------------------------------===// +import BedrockTypes import Foundation import Hummingbird -import BedrockTypes extension Message: ResponseCodable {} diff --git a/backend/Sources/App/Types/ImageGeneration.swift b/backend/Sources/App/Types/ImageGeneration.swift index ea5f77e9..64fa7eda 100644 --- a/backend/Sources/App/Types/ImageGeneration.swift +++ b/backend/Sources/App/Types/ImageGeneration.swift @@ -13,9 +13,9 @@ // //===----------------------------------------------------------------------===// +import BedrockTypes import Foundation import Hummingbird -import BedrockTypes extension ImageGenerationOutput: ResponseCodable {} diff --git a/backend/Sources/App/Types/ListModels.swift b/backend/Sources/App/Types/ListModels.swift index 43bfe10b..ac15bd05 100644 --- a/backend/Sources/App/Types/ListModels.swift +++ b/backend/Sources/App/Types/ListModels.swift @@ -13,8 +13,8 @@ // //===----------------------------------------------------------------------===// +import BedrockTypes import Foundation import Hummingbird -import BedrockTypes extension ModelSummary: ResponseEncodable {} diff --git a/backend/Sources/App/Types/TextGeneration.swift b/backend/Sources/App/Types/TextGeneration.swift index 6768f266..93d78d5e 100644 --- a/backend/Sources/App/Types/TextGeneration.swift +++ b/backend/Sources/App/Types/TextGeneration.swift @@ -13,9 +13,9 @@ // //===----------------------------------------------------------------------===// +import BedrockTypes import Foundation import Hummingbird -import BedrockTypes extension TextCompletion: ResponseCodable {} diff --git a/backend/Sources/BedrockService/Converse/ConverseResponse.swift b/backend/Sources/BedrockService/Converse/ConverseResponse.swift index 819e8e70..791c68ff 100644 --- a/backend/Sources/BedrockService/Converse/ConverseResponse.swift +++ b/backend/Sources/BedrockService/Converse/ConverseResponse.swift @@ -14,8 +14,8 @@ //===----------------------------------------------------------------------===// @preconcurrency import AWSBedrockRuntime -import Foundation import BedrockTypes +import Foundation public struct ConverseResponse { let message: Message diff --git a/backend/Sources/BedrockService/InvokeModel/InvokeModelRequest.swift b/backend/Sources/BedrockService/InvokeModel/InvokeModelRequest.swift index 66e8096e..f64050db 100644 --- a/backend/Sources/BedrockService/InvokeModel/InvokeModelRequest.swift +++ b/backend/Sources/BedrockService/InvokeModel/InvokeModelRequest.swift @@ -14,8 +14,8 @@ //===----------------------------------------------------------------------===// @preconcurrency import AWSBedrockRuntime -import Foundation import BedrockTypes +import Foundation struct InvokeModelRequest { let model: BedrockModel diff --git a/backend/Sources/BedrockService/InvokeModel/InvokeModelResponse.swift b/backend/Sources/BedrockService/InvokeModel/InvokeModelResponse.swift index bbbae7f0..c88c145e 100644 --- a/backend/Sources/BedrockService/InvokeModel/InvokeModelResponse.swift +++ b/backend/Sources/BedrockService/InvokeModel/InvokeModelResponse.swift @@ -14,8 +14,8 @@ //===----------------------------------------------------------------------===// @preconcurrency import AWSBedrockRuntime -import Foundation import BedrockTypes +import Foundation public struct InvokeModelResponse { let model: BedrockModel @@ -82,7 +82,7 @@ public struct InvokeModelResponse { do { guard let textCompletionBody = textCompletionBody else { throw BedrockServiceError.decodingError("No text completion body found in the response") - } + } return try textCompletionBody.getTextCompletion() } catch { throw BedrockServiceError.decodingError( diff --git a/backend/Sources/BedrockService/Protocols/BedrockRuntimeClientProtocol.swift b/backend/Sources/BedrockService/Protocols/BedrockRuntimeClientProtocol.swift index 4de31d56..e7919fa2 100644 --- a/backend/Sources/BedrockService/Protocols/BedrockRuntimeClientProtocol.swift +++ b/backend/Sources/BedrockService/Protocols/BedrockRuntimeClientProtocol.swift @@ -16,8 +16,8 @@ @preconcurrency import AWSBedrockRuntime import AWSClientRuntime import AWSSDKIdentity -import Foundation import BedrockTypes +import Foundation // Protocol allows writing mocks for unit tests public protocol BedrockRuntimeClientProtocol: Sendable { diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaImageResolutionValidator.swift b/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaImageResolutionValidator.swift index c312ab52..782c43a0 100644 --- a/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaImageResolutionValidator.swift +++ b/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaImageResolutionValidator.swift @@ -34,10 +34,16 @@ struct NovaImageResolutionValidator: ImageResolutionValidator { ) } guard width % 16 == 0 else { - throw BedrockServiceError.invalidParameter(.resolution, "Width must be evenly divisible by 16. Width: \(width)") + throw BedrockServiceError.invalidParameter( + .resolution, + "Width must be evenly divisible by 16. Width: \(width)" + ) } guard height % 16 == 0 else { - throw BedrockServiceError.invalidParameter(.resolution, "Height must be evenly divisible by 16. Height: \(height)") + throw BedrockServiceError.invalidParameter( + .resolution, + "Height must be evenly divisible by 16. Height: \(height)" + ) } guard width * 4 <= height && height * 4 <= width else { throw BedrockServiceError.invalidParameter( diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Titan/Titan.swift b/backend/Sources/BedrockTypes/Models/Amazon/Titan/Titan.swift index 109ab7b0..a9a3af91 100644 --- a/backend/Sources/BedrockTypes/Models/Amazon/Titan/Titan.swift +++ b/backend/Sources/BedrockTypes/Models/Amazon/Titan/Titan.swift @@ -17,7 +17,7 @@ import Foundation struct TitanText: TextModality, ConverseModality { func getName() -> String { "Titan Text Generation" } - + let parameters: TextGenerationParameters let converseParameters: ConverseParameters let converseFeatures: [ConverseFeature] diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanBedrockModels.swift index a3c276ba..849fc5dd 100644 --- a/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanBedrockModels.swift +++ b/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanBedrockModels.swift @@ -24,7 +24,8 @@ typealias TitanTextLiteV1 = TitanText extension BedrockModel { public static let titan_text_g1_premier: BedrockModel = BedrockModel( - id: "amazon.titan-text-premier-v1:0", name: "Titan Premier", + id: "amazon.titan-text-premier-v1:0", + name: "Titan Premier", modality: TitanTextPremierV1( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.7), @@ -38,7 +39,8 @@ extension BedrockModel { ) ) public static let titan_text_g1_express: BedrockModel = BedrockModel( - id: "amazon.titan-text-express-v1", name: "Titan Express", + id: "amazon.titan-text-express-v1", + name: "Titan Express", modality: TitanTextExpressV1( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.7), @@ -51,7 +53,8 @@ extension BedrockModel { ) ) public static let titan_text_g1_lite: BedrockModel = BedrockModel( - id: "amazon.titan-text-lite-v1", name: "Titan Lite", + id: "amazon.titan-text-lite-v1", + name: "Titan Lite", modality: TitanTextLiteV1( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.7), @@ -73,7 +76,8 @@ typealias TitanImageG1V2 = AmazonImage extension BedrockModel { public static let titan_image_g1_v1: BedrockModel = BedrockModel( - id: "amazon.titan-image-generator-v1", name: "Titan Image Generator", + id: "amazon.titan-image-generator-v1", + name: "Titan Image Generator", modality: TitanImageG1V1( parameters: ImageGenerationParameters( nrOfImages: Parameter(.nrOfImages, minValue: 1, maxValue: 5, defaultValue: 1), @@ -96,7 +100,8 @@ extension BedrockModel { ) ) public static let titan_image_g1_v2: BedrockModel = BedrockModel( - id: "amazon.titan-image-generator-v2:0", name: "Titan Image Generator V2", + id: "amazon.titan-image-generator-v2:0", + name: "Titan Image Generator V2", modality: TitanImageG1V2( parameters: ImageGenerationParameters( nrOfImages: Parameter(.nrOfImages, minValue: 1, maxValue: 5, defaultValue: 1), diff --git a/backend/Sources/BedrockTypes/Models/Anthropic/Anthropic.swift b/backend/Sources/BedrockTypes/Models/Anthropic/Anthropic.swift index 93eb94b2..ad7037ef 100644 --- a/backend/Sources/BedrockTypes/Models/Anthropic/Anthropic.swift +++ b/backend/Sources/BedrockTypes/Models/Anthropic/Anthropic.swift @@ -22,7 +22,10 @@ struct AnthropicText: TextModality, ConverseModality { func getName() -> String { "Anthropic Text Generation" } - init(parameters: TextGenerationParameters, features: [ConverseFeature] = [.textGeneration, .systemPrompts, .document]) { + init( + parameters: TextGenerationParameters, + features: [ConverseFeature] = [.textGeneration, .systemPrompts, .document] + ) { self.parameters = parameters self.converseFeatures = features self.converseParameters = ConverseParameters(textGenerationParameters: parameters) diff --git a/backend/Sources/BedrockTypes/Models/Anthropic/AnthropicBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Anthropic/AnthropicBedrockModels.swift index dd9d2e90..950e95d5 100644 --- a/backend/Sources/BedrockTypes/Models/Anthropic/AnthropicBedrockModels.swift +++ b/backend/Sources/BedrockTypes/Models/Anthropic/AnthropicBedrockModels.swift @@ -30,7 +30,8 @@ typealias ClaudeV3_7Sonnet = AnthropicText extension BedrockModel { public static let instant: BedrockModel = BedrockModel( - id: "anthropic.claude-instant-v1", name: "Claude Instant", + id: "anthropic.claude-instant-v1", + name: "Claude Instant", modality: ClaudeInstantV1( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), @@ -44,7 +45,8 @@ extension BedrockModel { ) ) public static let claudev1: BedrockModel = BedrockModel( - id: "anthropic.claude-v1", name: "Claude V1", + id: "anthropic.claude-v1", + name: "Claude V1", modality: ClaudeV1( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), @@ -58,7 +60,8 @@ extension BedrockModel { ) ) public static let claudev2: BedrockModel = BedrockModel( - id: "anthropic.claude-v2", name: "Claude V2", + id: "anthropic.claude-v2", + name: "Claude V2", modality: ClaudeV2( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), @@ -72,7 +75,8 @@ extension BedrockModel { ) ) public static let claudev2_1: BedrockModel = BedrockModel( - id: "anthropic.claude-v2:1", name: "Claude V2.1", + id: "anthropic.claude-v2:1", + name: "Claude V2.1", modality: ClaudeV2_1( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), @@ -86,7 +90,8 @@ extension BedrockModel { ) ) public static let claudev3_opus: BedrockModel = BedrockModel( - id: "us.anthropic.claude-3-opus-20240229-v1:0", name: "Claude V3 Opus", + id: "us.anthropic.claude-3-opus-20240229-v1:0", + name: "Claude V3 Opus", modality: ClaudeV3Opus( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), @@ -100,7 +105,8 @@ extension BedrockModel { ) ) public static let claudev3_haiku: BedrockModel = BedrockModel( - id: "anthropic.claude-3-haiku-20240307-v1:0", name: "Claude V3 Haiku", + id: "anthropic.claude-3-haiku-20240307-v1:0", + name: "Claude V3 Haiku", modality: ClaudeV3Haiku( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), @@ -114,7 +120,8 @@ extension BedrockModel { ) ) public static let claudev3_5_haiku: BedrockModel = BedrockModel( - id: "us.anthropic.claude-3-5-haiku-20241022-v1:0", name: "Claude V3.5 Haiku", + id: "us.anthropic.claude-3-5-haiku-20241022-v1:0", + name: "Claude V3.5 Haiku", modality: ClaudeV3_5Haiku( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), @@ -128,7 +135,8 @@ extension BedrockModel { ) ) public static let claudev3_5_sonnet: BedrockModel = BedrockModel( - id: "us.anthropic.claude-3-5-sonnet-20240620-v1:0", name: "Claude V3.5 Sonnet", + id: "us.anthropic.claude-3-5-sonnet-20240620-v1:0", + name: "Claude V3.5 Sonnet", modality: ClaudeV3_5Sonnet( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), @@ -142,7 +150,8 @@ extension BedrockModel { ) ) public static let claudev3_5_sonnet_v2: BedrockModel = BedrockModel( - id: "us.anthropic.claude-3-5-sonnet-20241022-v2:0", name: "Claude V3.5 Sonnet V2", + id: "us.anthropic.claude-3-5-sonnet-20241022-v2:0", + name: "Claude V3.5 Sonnet V2", modality: ClaudeV3_5Sonnet( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), @@ -156,7 +165,8 @@ extension BedrockModel { ) ) public static let claudev3_7_sonnet: BedrockModel = BedrockModel( - id: "us.anthropic.claude-3-7-sonnet-20250219-v1:0", name: "Claude V3.7 Sonnet", + id: "us.anthropic.claude-3-7-sonnet-20250219-v1:0", + name: "Claude V3.7 Sonnet", modality: ClaudeV3_7Sonnet( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), diff --git a/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeekBedrockModels.swift b/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeekBedrockModels.swift index 4b93f99a..83a1a3ec 100644 --- a/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeekBedrockModels.swift +++ b/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeekBedrockModels.swift @@ -19,7 +19,8 @@ typealias DeepSeekR1V1 = DeepSeekText extension BedrockModel { public static let deepseek_r1_v1: BedrockModel = BedrockModel( - id: "us.deepseek.r1-v1:0", name: "DeepSeek R1", + id: "us.deepseek.r1-v1:0", + name: "DeepSeek R1", modality: DeepSeekR1V1( parameters: TextGenerationParameters( temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), diff --git a/backend/Sources/BedrockTypes/Models/Jamba/JambaBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Jamba/JambaBedrockModels.swift index 06f43d52..4bb2a04d 100644 --- a/backend/Sources/BedrockTypes/Models/Jamba/JambaBedrockModels.swift +++ b/backend/Sources/BedrockTypes/Models/Jamba/JambaBedrockModels.swift @@ -20,4 +20,3 @@ import Foundation // https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html typealias JambaConverse = StandardConverse - diff --git a/backend/Sources/BedrockTypes/Parameters/Parameters.swift b/backend/Sources/BedrockTypes/Parameters/Parameters.swift index ddaf1db5..078a679b 100644 --- a/backend/Sources/BedrockTypes/Parameters/Parameters.swift +++ b/backend/Sources/BedrockTypes/Parameters/Parameters.swift @@ -32,7 +32,8 @@ public struct Parameter: Sendable, Self(name: name, minValue: nil, maxValue: nil, defaultValue: nil, isSupported: false) } - private init(name: ParameterName, minValue: T? = nil, maxValue: T? = nil, defaultValue: T? = nil, isSupported: Bool) { + private init(name: ParameterName, minValue: T? = nil, maxValue: T? = nil, defaultValue: T? = nil, isSupported: Bool) + { self.minValue = minValue self.maxValue = maxValue self.defaultValue = defaultValue diff --git a/backend/Tests/BedrockServiceTests/Mock/MockBedrockClient.swift b/backend/Tests/BedrockServiceTests/Mock/MockBedrockClient.swift index 2a4319d3..44fdc0a2 100644 --- a/backend/Tests/BedrockServiceTests/Mock/MockBedrockClient.swift +++ b/backend/Tests/BedrockServiceTests/Mock/MockBedrockClient.swift @@ -16,9 +16,9 @@ @preconcurrency import AWSBedrock import AWSClientRuntime import AWSSDKIdentity -import Foundation import BedrockService import BedrockTypes +import Foundation public struct MockBedrockClient: BedrockClientProtocol { public init() {} diff --git a/backend/Tests/BedrockServiceTests/Mock/MockBedrockRuntimeClient.swift b/backend/Tests/BedrockServiceTests/Mock/MockBedrockRuntimeClient.swift index 53fa8f47..b08dcfea 100644 --- a/backend/Tests/BedrockServiceTests/Mock/MockBedrockRuntimeClient.swift +++ b/backend/Tests/BedrockServiceTests/Mock/MockBedrockRuntimeClient.swift @@ -16,9 +16,9 @@ @preconcurrency import AWSBedrockRuntime import AWSClientRuntime import AWSSDKIdentity -import Foundation import BedrockService import BedrockTypes +import Foundation public struct MockBedrockRuntimeClient: BedrockRuntimeClientProtocol { public init() {} @@ -152,7 +152,7 @@ public struct MockBedrockRuntimeClient: BedrockRuntimeClientProtocol { else { throw AWSBedrockRuntime.ValidationException( message: "Hier is het)" - // message: "Malformed input request, please reformat your input and try again." + // message: "Malformed input request, please reformat your input and try again." ) } if let inputText = json["inputText"] as? String { diff --git a/backend/Tests/BedrockServiceTests/SwiftBedrockServiceTests.swift b/backend/Tests/BedrockServiceTests/SwiftBedrockServiceTests.swift index 61174f48..6d6c740a 100644 --- a/backend/Tests/BedrockServiceTests/SwiftBedrockServiceTests.swift +++ b/backend/Tests/BedrockServiceTests/SwiftBedrockServiceTests.swift @@ -19,7 +19,7 @@ import Testing @testable import BedrockTypes //struct Constants { -// +// //} @Suite("SwiftBedrockService Tests") @@ -59,13 +59,13 @@ struct BedrockServiceTests { ] static let invalidPrompts = [ "", " ", " \n ", "\t", -// String(repeating: "tests", count: 5_000_001), + // String(repeating: "tests", count: 5_000_001), ] static let textCompletionModels = [ BedrockModel.nova_micro, -// BedrockModel.titan_text_g1_lite, -// BedrockModel.titan_text_g1_express, -// BedrockModel.titan_text_g1_premier, + // BedrockModel.titan_text_g1_lite, + // BedrockModel.titan_text_g1_express, + // BedrockModel.titan_text_g1_premier, BedrockModel.claudev1, BedrockModel.claudev2, BedrockModel.claudev2_1, From 1a301f8c110f5d77f6b516970e571d66cb40a8e9 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 7 Apr 2025 16:36:13 +0200 Subject: [PATCH 38/67] github action --- .github/workflows/build_test_soundness.yml | 2 +- ...ift => BedrockService+GenerateImage.swift} | 28 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) rename backend/Sources/BedrockService/InvokeModel/{ImageGeneration.swift => BedrockService+GenerateImage.swift} (86%) diff --git a/.github/workflows/build_test_soundness.yml b/.github/workflows/build_test_soundness.yml index c62e5e9c..2d92d0a9 100644 --- a/.github/workflows/build_test_soundness.yml +++ b/.github/workflows/build_test_soundness.yml @@ -2,7 +2,7 @@ name: Build And Test on EC2 on: push: - branches: ["main", "week12"] + branches: ["main", "week15"] pull_request: branches: ["main"] diff --git a/backend/Sources/BedrockService/InvokeModel/ImageGeneration.swift b/backend/Sources/BedrockService/InvokeModel/BedrockService+GenerateImage.swift similarity index 86% rename from backend/Sources/BedrockService/InvokeModel/ImageGeneration.swift rename to backend/Sources/BedrockService/InvokeModel/BedrockService+GenerateImage.swift index e695cfb1..c2894496 100644 --- a/backend/Sources/BedrockService/InvokeModel/ImageGeneration.swift +++ b/backend/Sources/BedrockService/InvokeModel/BedrockService+GenerateImage.swift @@ -1,17 +1,17 @@ -// //===----------------------------------------------------------------------===// -// // -// // This source file is part of the Swift Foundation Models Playground open source project -// // -// // Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// // and the Swift Foundation Models Playground project authors -// // Licensed under Apache License v2.0 -// // -// // See LICENSE.txt for license information -// // See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// // -// // SPDX-License-Identifier: Apache-2.0 -// // -// //===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// // @preconcurrency import AWSBedrockRuntime // import BedrockTypes From aad32932d837cc343654b9b3dd945c15ad26efe5 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 7 Apr 2025 17:30:01 +0200 Subject: [PATCH 39/67] formatting --- backend/Sources/BedrockTypes/Converse/Message.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/Sources/BedrockTypes/Converse/Message.swift b/backend/Sources/BedrockTypes/Converse/Message.swift index af8967d2..6d82aeae 100644 --- a/backend/Sources/BedrockTypes/Converse/Message.swift +++ b/backend/Sources/BedrockTypes/Converse/Message.swift @@ -69,7 +69,7 @@ public struct Message: Codable { public func getSDKMessage() throws -> BedrockRuntimeClientTypes.Message { let contentBlocks: [BedrockRuntimeClientTypes.ContentBlock] = try content.map { content -> BedrockRuntimeClientTypes.ContentBlock in - return try content.getSDKContentBlock() + try content.getSDKContentBlock() } return BedrockRuntimeClientTypes.Message( content: contentBlocks, From 967bdbe1cc6b2ee8432f2dd2e37f93cdfb3b0a1c Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 7 Apr 2025 17:30:34 +0200 Subject: [PATCH 40/67] formatting --- openapi.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openapi.yaml b/openapi.yaml index 99a2103c..12d5ee48 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -75,7 +75,7 @@ components: type: string enum: [user, assistant, unknown] content: - type: string # FIXME: will not always be a string + type: string # FIXME: will not always be a string enum: [text, unknown] maxTokens: type: integer @@ -104,7 +104,7 @@ components: type: string enum: [user, assistant, unknown] content: - type: string # FIXME: will not always be a string + type: string # FIXME: will not always be a string enum: [text, unknown] paths: From b4539ffc5de0435b2f582ab1672ad046a41cfd11 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 7 Apr 2025 17:42:37 +0200 Subject: [PATCH 41/67] SSO profile name --- .../BedrockService/BedrockService.swift | 19 +- frontend/package-lock.json | 1202 ++++++++++++----- frontend/package.json | 4 +- 3 files changed, 896 insertions(+), 329 deletions(-) diff --git a/backend/Sources/BedrockService/BedrockService.swift b/backend/Sources/BedrockService/BedrockService.swift index 96285116..0e963ca7 100644 --- a/backend/Sources/BedrockService/BedrockService.swift +++ b/backend/Sources/BedrockService/BedrockService.swift @@ -42,7 +42,8 @@ public struct BedrockService: Sendable { logger: Logger? = nil, bedrockClient: BedrockClientProtocol? = nil, bedrockRuntimeClient: BedrockRuntimeClientProtocol? = nil, - useSSO: Bool = false + useSSO: Bool = false, + ssoProfileName: String? = nil ) async throws { self.logger = logger ?? BedrockService.createLogger("bedrock.service") self.logger.trace( @@ -58,7 +59,8 @@ public struct BedrockService: Sendable { self.logger.trace("Creating bedrockClient") self.bedrockClient = try await BedrockService.createBedrockClient( region: region, - useSSO: useSSO + useSSO: useSSO, + ssoProfileName: ssoProfileName ) self.logger.trace( "Created bedrockClient", @@ -72,7 +74,8 @@ public struct BedrockService: Sendable { self.logger.trace("Creating bedrockRuntimeClient") self.bedrockRuntimeClient = try await BedrockService.createBedrockRuntimeClient( region: region, - useSSO: useSSO + useSSO: useSSO, + ssoProfileName: ssoProfileName ) self.logger.trace( "Created bedrockRuntimeClient", @@ -107,7 +110,8 @@ public struct BedrockService: Sendable { /// - Throws: Error if client creation fails static private func createBedrockClient( region: Region, - useSSO: Bool = false + useSSO: Bool = false, + ssoProfileName: String? = nil ) async throws -> BedrockClientProtocol { @@ -115,7 +119,7 @@ public struct BedrockService: Sendable { region: region.rawValue ) if useSSO { - config.awsCredentialIdentityResolver = try SSOAWSCredentialIdentityResolver() + config.awsCredentialIdentityResolver = try SSOAWSCredentialIdentityResolver(profileName: ssoProfileName) } return BedrockClient(config: config) } @@ -128,7 +132,8 @@ public struct BedrockService: Sendable { /// - Throws: Error if client creation fails static private func createBedrockRuntimeClient( region: Region, - useSSO: Bool = false + useSSO: Bool = false, + ssoProfileName: String? = nil ) async throws -> BedrockRuntimeClientProtocol @@ -138,7 +143,7 @@ public struct BedrockService: Sendable { region: region.rawValue ) if useSSO { - config.awsCredentialIdentityResolver = try SSOAWSCredentialIdentityResolver() + config.awsCredentialIdentityResolver = try SSOAWSCredentialIdentityResolver(profileName: ssoProfileName) } return BedrockRuntimeClient(config: config) } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e867be41..bf244f67 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,7 +8,7 @@ "name": "frontend", "version": "0.1.0", "dependencies": { - "next": "14.0.0", + "next": "^14.2.26", "react": "^18", "react-dom": "^18" }, @@ -23,6 +23,7 @@ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -30,66 +31,91 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@next/env": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.0.tgz", - "integrity": "sha512-cIKhxkfVELB6hFjYsbtEeTus2mwrTC+JissfZYM0n+8Fv+g8ucUfOlm3VEDtwtwydZ0Nuauv3bl0qF82nnCAqA==" + "version": "14.2.26", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.26.tgz", + "integrity": "sha512-vO//GJ/YBco+H7xdQhzJxF7ub3SUwft76jwaeOyVVQFHCi5DCnkP16WHB+JBylo4vOKPoZBlR94Z8xBxNBdNJA==", + "license": "MIT" }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.0.0.tgz", - "integrity": "sha512-HQKi159jCz4SRsPesVCiNN6tPSAFUkOuSkpJsqYTIlbHLKr1mD6be/J0TvWV6fwJekj81bZV9V/Tgx3C2HO9lA==", + "version": "14.2.26", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.26.tgz", + "integrity": "sha512-zDJY8gsKEseGAxG+C2hTMT0w9Nk9N1Sk1qV7vXYz9MEiyRoF5ogQX2+vplyUMIfygnjn9/A04I6yrUTRTuRiyQ==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -99,12 +125,13 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.0.tgz", - "integrity": "sha512-4YyQLMSaCgX/kgC1jjF3s3xSoBnwHuDhnF6WA1DWNEYRsbOOPWjcYhv8TKhRe2ApdOam+VfQSffC4ZD+X4u1Cg==", + "version": "14.2.26", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.26.tgz", + "integrity": "sha512-U0adH5ryLfmTDkahLwG9sUQG2L0a9rYux8crQeC92rPhi3jGQEY47nByQHrVrt3prZigadwj/2HZ1LUUimuSbg==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -114,12 +141,13 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.0.0.tgz", - "integrity": "sha512-io7fMkJ28Glj7SH8yvnlD6naIhRDnDxeE55CmpQkj3+uaA2Hko6WGY2pT5SzpQLTnGGnviK85cy8EJ2qsETj/g==", + "version": "14.2.26", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.26.tgz", + "integrity": "sha512-SINMl1I7UhfHGM7SoRiw0AbwnLEMUnJ/3XXVmhyptzriHbWvPPbbm0OEVG24uUKhuS1t0nvN/DBvm5kz6ZIqpg==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -129,12 +157,13 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.0.tgz", - "integrity": "sha512-nC2h0l1Jt8LEzyQeSs/BKpXAMe0mnHIMykYALWaeddTqCv5UEN8nGO3BG8JAqW/Y8iutqJsaMe2A9itS0d/r8w==", + "version": "14.2.26", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.26.tgz", + "integrity": "sha512-s6JaezoyJK2DxrwHWxLWtJKlqKqTdi/zaYigDXUJ/gmx/72CrzdVZfMvUc6VqnZ7YEvRijvYo+0o4Z9DencduA==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -144,12 +173,13 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.0.tgz", - "integrity": "sha512-Wf+WjXibJQ7hHXOdNOmSMW5bxeJHVf46Pwb3eLSD2L76NrytQlif9NH7JpHuFlYKCQGfKfgSYYre5rIfmnSwQw==", + "version": "14.2.26", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.26.tgz", + "integrity": "sha512-FEXeUQi8/pLr/XI0hKbe0tgbLmHFRhgXOUiPScz2hk0hSmbGiU8aUqVslj/6C6KA38RzXnWoJXo4FMo6aBxjzg==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -159,12 +189,13 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.0.tgz", - "integrity": "sha512-WTZb2G7B+CTsdigcJVkRxfcAIQj7Lf0ipPNRJ3vlSadU8f0CFGv/ST+sJwF5eSwIe6dxKoX0DG6OljDBaad+rg==", + "version": "14.2.26", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.26.tgz", + "integrity": "sha512-BUsomaO4d2DuXhXhgQCVt2jjX4B4/Thts8nDoIruEJkhE5ifeQFtvW5c9JkdOtYvE5p2G0hcwQ0UbRaQmQwaVg==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -174,12 +205,13 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.0.tgz", - "integrity": "sha512-7R8/x6oQODmNpnWVW00rlWX90sIlwluJwcvMT6GXNIBOvEf01t3fBg0AGURNKdTJg2xNuP7TyLchCL7Lh2DTiw==", + "version": "14.2.26", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.26.tgz", + "integrity": "sha512-5auwsMVzT7wbB2CZXQxDctpWbdEnEW/e66DyXO1DcgHxIyhP06awu+rHKshZE+lPLIGiwtjo7bsyeuubewwxMw==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -189,12 +221,13 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.0.tgz", - "integrity": "sha512-RLK1nELvhCnxaWPF07jGU4x3tjbyx2319q43loZELqF0+iJtKutZ+Lk8SVmf/KiJkYBc7Cragadz7hb3uQvz4g==", + "version": "14.2.26", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.26.tgz", + "integrity": "sha512-GQWg/Vbz9zUGi9X80lOeGsz1rMH/MtFO/XqigDznhhhTfDlDoynCM6982mPCbSlxJ/aveZcKtTlwfAjwhyxDpg==", "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -204,12 +237,13 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.0.tgz", - "integrity": "sha512-g6hLf1SUko+hnnaywQQZzzb3BRecQsoKkF3o/C+F+dOA4w/noVAJngUVkfwF0+2/8FzNznM7ofM6TGZO9svn7w==", + "version": "14.2.26", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.26.tgz", + "integrity": "sha512-2rdB3T1/Gp7bv1eQTTm9d1Y1sv9UuJ2LAwOE0Pe2prHKe32UNscj7YS13fRB37d0GAiGNR+Y7ZcW8YjDI8Ns0w==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -223,6 +257,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -236,6 +271,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } @@ -245,6 +281,7 @@ "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": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -253,25 +290,72 @@ "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/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, "node_modules/@swc/helpers": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", - "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "license": "Apache-2.0", "dependencies": { + "@swc/counter": "^0.1.3", "tslib": "^2.4.0" } }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "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/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, + "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -284,12 +368,13 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/autoprefixer": { - "version": "10.4.16", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", - "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", "dev": true, "funding": [ { @@ -305,12 +390,13 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "browserslist": "^4.21.10", - "caniuse-lite": "^1.0.30001538", - "fraction.js": "^4.3.6", + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -327,43 +413,49 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "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==", + "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", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "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": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, "node_modules/browserslist": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", - "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "dev": true, "funding": [ { @@ -379,11 +471,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001541", - "electron-to-chromium": "^1.4.535", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -408,14 +501,15 @@ "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001557", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001557.tgz", - "integrity": "sha512-91oR7hLNUP3gG6MLU+n96em322a8Xzes8wWdBKhLgUoiJsAF5irZnxSUCbc+qUZXNnPCfUwLOi9ZCZpkvjQajw==", + "version": "1.0.30001712", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001712.tgz", + "integrity": "sha512-MBqPpGYYdQ7/hfKiet9SCI+nmN5/hp4ZzveOJubl5DTAMa5oggjAuoi0Z4onBpKPFI2ePGnQuQIzF3VxDjDJig==", "funding": [ { "type": "opencollective", @@ -429,19 +523,15 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -454,6 +544,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } @@ -463,6 +556,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -473,28 +567,60 @@ "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "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": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "node_modules/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", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, + "license": "MIT", "bin": { "cssesc": "bin/cssesc" }, @@ -506,40 +632,59 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.4.569", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.569.tgz", - "integrity": "sha512-LsrJjZ0IbVy12ApW3gpYpcmHS3iRxH4bkKOW98y1/D+3cvDUWGcbzbsFinfUS8knpcZk/PG/2p/RnkMCYN7PVg==", - "dev": true + "version": "1.5.132", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.132.tgz", + "integrity": "sha512-QgX9EBvWGmvSRa74zqfnG7+Eno0Ak0vftBll0Pt2/z5b3bEGYL6OUXLgKPtvx73dn3dvwrlyVkjPKRRlhLYTEg==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "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": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -550,6 +695,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -558,19 +704,21 @@ } }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "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": { "to-regex-range": "^5.0.1" }, @@ -578,11 +726,29 @@ "node": ">=8" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "dev": true, + "license": "MIT", "engines": { "node": "*" }, @@ -591,18 +757,13 @@ "url": "https://github.com/sponsors/rawify" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -616,25 +777,27 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, + "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": "*" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -645,6 +808,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -652,21 +816,18 @@ "node": ">=10.13.0" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -674,27 +835,12 @@ "node": ">= 0.4" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, + "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, @@ -703,12 +849,16 @@ } }, "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, + "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -719,15 +869,27 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -740,15 +902,40 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, + "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": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jiti": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.20.0.tgz", - "integrity": "sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==", + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, + "license": "MIT", "bin": { "jiti": "bin/jiti.js" } @@ -756,27 +943,34 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" }, "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -784,22 +978,31 @@ "loose-envify": "cli.js" } }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "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": { "node": ">= 8" } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "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": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -807,15 +1010,29 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "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": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" } }, "node_modules/mz": { @@ -823,6 +1040,7 @@ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", "dev": true, + "license": "MIT", "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", @@ -830,15 +1048,16 @@ } }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -847,17 +1066,18 @@ } }, "node_modules/next": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/next/-/next-14.0.0.tgz", - "integrity": "sha512-J0jHKBJpB9zd4+c153sair0sz44mbaCHxggs8ryVXSFBuBqJ8XdE9/ozoV85xGh2VnSjahwntBZZgsihL9QznA==", + "version": "14.2.26", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.26.tgz", + "integrity": "sha512-b81XSLihMwCfwiUVRRja3LphLo4uBBMZEzBBWMaISbKTwOmq3wPknIETy/8000tr7Gq4WmbuFYPS7jOYIf+ZJw==", + "license": "MIT", "dependencies": { - "@next/env": "14.0.0", - "@swc/helpers": "0.5.2", + "@next/env": "14.2.26", + "@swc/helpers": "0.5.5", "busboy": "1.6.0", - "caniuse-lite": "^1.0.30001406", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", "postcss": "8.4.31", - "styled-jsx": "5.1.1", - "watchpack": "2.4.0" + "styled-jsx": "5.1.1" }, "bin": { "next": "dist/bin/next" @@ -866,18 +1086,19 @@ "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.0.0", - "@next/swc-darwin-x64": "14.0.0", - "@next/swc-linux-arm64-gnu": "14.0.0", - "@next/swc-linux-arm64-musl": "14.0.0", - "@next/swc-linux-x64-gnu": "14.0.0", - "@next/swc-linux-x64-musl": "14.0.0", - "@next/swc-win32-arm64-msvc": "14.0.0", - "@next/swc-win32-ia32-msvc": "14.0.0", - "@next/swc-win32-x64-msvc": "14.0.0" + "@next/swc-darwin-arm64": "14.2.26", + "@next/swc-darwin-x64": "14.2.26", + "@next/swc-linux-arm64-gnu": "14.2.26", + "@next/swc-linux-arm64-musl": "14.2.26", + "@next/swc-linux-x64-gnu": "14.2.26", + "@next/swc-linux-x64-musl": "14.2.26", + "@next/swc-win32-arm64-msvc": "14.2.26", + "@next/swc-win32-ia32-msvc": "14.2.26", + "@next/swc-win32-x64-msvc": "14.2.26" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", "react": "^18.2.0", "react-dom": "^18.2.0", "sass": "^1.3.0" @@ -886,22 +1107,55 @@ "@opentelemetry/api": { "optional": true }, + "@playwright/test": { + "optional": true + }, "sass": { "optional": true } } }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", - "dev": true + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -911,6 +1165,7 @@ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -920,6 +1175,7 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -929,44 +1185,64 @@ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, - "dependencies": { - "wrappy": "1" - } + "license": "BlueOak-1.0.0" }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "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": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -979,23 +1255,26 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "dev": true, "funding": [ { "type": "opencollective", @@ -1010,10 +1289,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -1024,6 +1304,7 @@ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", @@ -1041,6 +1322,7 @@ "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", "dev": true, + "license": "MIT", "dependencies": { "camelcase-css": "^2.0.1" }, @@ -1056,21 +1338,28 @@ } }, "node_modules/postcss-load-config": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", - "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^2.1.1" + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" }, "engines": { "node": ">= 14" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" @@ -1085,29 +1374,37 @@ } }, "node_modules/postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.11" + "postcss-selector-parser": "^6.1.1" }, "engines": { "node": ">=12.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, "peerDependencies": { "postcss": "^8.2.14" } }, "node_modules/postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, + "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -1120,7 +1417,8 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/queue-microtask": { "version": "1.2.3", @@ -1140,12 +1438,14 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" }, @@ -1154,15 +1454,16 @@ } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.3.1" } }, "node_modules/read-cache": { @@ -1170,6 +1471,7 @@ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", "dev": true, + "license": "MIT", "dependencies": { "pify": "^2.3.0" } @@ -1179,6 +1481,7 @@ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, + "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -1187,27 +1490,32 @@ } }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, + "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -1232,22 +1540,61 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" } }, + "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": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -1260,10 +1607,115 @@ "node": ">=10.0.0" } }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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/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": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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", + "engines": { + "node": ">=8" + } + }, "node_modules/styled-jsx": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "license": "MIT", "dependencies": { "client-only": "0.0.1" }, @@ -1283,14 +1735,15 @@ } }, "node_modules/sucrase": { - "version": "3.34.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", - "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==", + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", - "glob": "7.1.6", + "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", @@ -1301,7 +1754,7 @@ "sucrase-node": "bin/sucrase-node" }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/supports-preserve-symlinks-flag": { @@ -1309,6 +1762,7 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1317,33 +1771,34 @@ } }, "node_modules/tailwindcss": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.5.tgz", - "integrity": "sha512-5SEZU4J7pxZgSkv7FP1zY8i2TIAOooNZ1e/OGtxIEv6GltpoiXUqWvLy89+a10qYTB1N5Ifkuw9lqQkN9sscvA==", + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "dev": true, + "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", - "chokidar": "^3.5.3", + "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "fast-glob": "^3.3.0", + "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.19.1", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", @@ -1358,6 +1813,7 @@ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", "dev": true, + "license": "MIT", "dependencies": { "any-promise": "^1.0.0" } @@ -1367,6 +1823,7 @@ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", "dev": true, + "license": "MIT", "dependencies": { "thenify": ">= 3.1.0 < 4" }, @@ -1379,6 +1836,7 @@ "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": { "is-number": "^7.0.0" }, @@ -1390,17 +1848,19 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dev": true, "funding": [ { @@ -1416,9 +1876,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -1431,31 +1892,132 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "dev": true, + "license": "MIT" }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "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": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" }, "engines": { - "node": ">=10.13.0" + "node": ">= 8" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/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", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } }, "node_modules/yaml": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.3.tgz", - "integrity": "sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } diff --git a/frontend/package.json b/frontend/package.json index f025bd79..eb60bed0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,9 +9,9 @@ "lint": "next lint" }, "dependencies": { + "next": "^14.2.26", "react": "^18", - "react-dom": "^18", - "next": "14.0.0" + "react-dom": "^18" }, "devDependencies": { "autoprefixer": "^10", From f07f0851bc931b7ee19348f8ad6837be93405084 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 7 Apr 2025 17:58:25 +0200 Subject: [PATCH 42/67] Package updated to add libraries --- .vscode/launch.json | 18 ++++++++ backend/Package.swift | 20 +++------ .../Sources/{App => PlaygroundAPI}/App.swift | 0 .../Application+build.swift | 0 .../{App => PlaygroundAPI}/Types/Chat.swift | 0 .../Types/ImageGeneration.swift | 0 .../Types/ListModels.swift | 0 .../Types/TextGeneration.swift | 0 backend/Tests/AppTests/AppTests.swift | 44 ------------------- 9 files changed, 25 insertions(+), 57 deletions(-) rename backend/Sources/{App => PlaygroundAPI}/App.swift (100%) rename backend/Sources/{App => PlaygroundAPI}/Application+build.swift (100%) rename backend/Sources/{App => PlaygroundAPI}/Types/Chat.swift (100%) rename backend/Sources/{App => PlaygroundAPI}/Types/ImageGeneration.swift (100%) rename backend/Sources/{App => PlaygroundAPI}/Types/ListModels.swift (100%) rename backend/Sources/{App => PlaygroundAPI}/Types/TextGeneration.swift (100%) delete mode 100644 backend/Tests/AppTests/AppTests.swift diff --git a/.vscode/launch.json b/.vscode/launch.json index 6da29e05..504f596c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -71,6 +71,24 @@ "name": "Release SwiftBedrockService (backend)", "program": "${workspaceFolder:swift-bedrock-playground}/backend/.build/release/SwiftBedrockService", "preLaunchTask": "swift: Build Release SwiftBedrockService (backend)" + }, + { + "type": "lldb", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:swift-fm-playground}/backend", + "name": "Debug PlaygroundAPI (backend)", + "program": "${workspaceFolder:swift-fm-playground}/backend/.build/debug/PlaygroundAPI", + "preLaunchTask": "swift: Build Debug PlaygroundAPI (backend)" + }, + { + "type": "lldb", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:swift-fm-playground}/backend", + "name": "Release PlaygroundAPI (backend)", + "program": "${workspaceFolder:swift-fm-playground}/backend/.build/release/PlaygroundAPI", + "preLaunchTask": "swift: Build Release PlaygroundAPI (backend)" } ] } \ No newline at end of file diff --git a/backend/Package.swift b/backend/Package.swift index 81a719cd..e730c47a 100644 --- a/backend/Package.swift +++ b/backend/Package.swift @@ -4,10 +4,12 @@ import PackageDescription let package = Package( - name: "HummingbirdBackend", // FIXME: better name + name: "SwiftBedrock", platforms: [.macOS(.v14), .iOS(.v17), .tvOS(.v17)], products: [ - .executable(name: "App", targets: ["App"]) // FIXME: better name + .executable(name: "PlaygroundAPI", targets: ["PlaygroundAPI"]), + .library(name: "BedrockService", targets: ["BedrockService"]), + .library(name: "BedrockTypes", targets: ["BedrockTypes"]) ], dependencies: [ .package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.0.0"), @@ -19,13 +21,13 @@ let package = Package( ], targets: [ .executableTarget( - name: "App", + name: "PlaygroundAPI", dependencies: [ .target(name: "BedrockService"), .product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "Hummingbird", package: "hummingbird"), ], - path: "Sources/App" + path: "Sources/PlaygroundAPI" ), .target( name: "BedrockService", @@ -54,14 +56,6 @@ let package = Package( .product(name: "Testing", package: "swift-testing"), ], path: "Tests/BedrockServiceTests" - ), - // .testTarget(name: "AppTests", - // dependencies: [ - // .byName(name: "App"), - // .target(name: "SwiftBedrockService"), - // .product(name: "HummingbirdTesting", package: "hummingbird") - // ], - // path: "Tests/AppTests" - // ) + ) ] ) diff --git a/backend/Sources/App/App.swift b/backend/Sources/PlaygroundAPI/App.swift similarity index 100% rename from backend/Sources/App/App.swift rename to backend/Sources/PlaygroundAPI/App.swift diff --git a/backend/Sources/App/Application+build.swift b/backend/Sources/PlaygroundAPI/Application+build.swift similarity index 100% rename from backend/Sources/App/Application+build.swift rename to backend/Sources/PlaygroundAPI/Application+build.swift diff --git a/backend/Sources/App/Types/Chat.swift b/backend/Sources/PlaygroundAPI/Types/Chat.swift similarity index 100% rename from backend/Sources/App/Types/Chat.swift rename to backend/Sources/PlaygroundAPI/Types/Chat.swift diff --git a/backend/Sources/App/Types/ImageGeneration.swift b/backend/Sources/PlaygroundAPI/Types/ImageGeneration.swift similarity index 100% rename from backend/Sources/App/Types/ImageGeneration.swift rename to backend/Sources/PlaygroundAPI/Types/ImageGeneration.swift diff --git a/backend/Sources/App/Types/ListModels.swift b/backend/Sources/PlaygroundAPI/Types/ListModels.swift similarity index 100% rename from backend/Sources/App/Types/ListModels.swift rename to backend/Sources/PlaygroundAPI/Types/ListModels.swift diff --git a/backend/Sources/App/Types/TextGeneration.swift b/backend/Sources/PlaygroundAPI/Types/TextGeneration.swift similarity index 100% rename from backend/Sources/App/Types/TextGeneration.swift rename to backend/Sources/PlaygroundAPI/Types/TextGeneration.swift diff --git a/backend/Tests/AppTests/AppTests.swift b/backend/Tests/AppTests/AppTests.swift deleted file mode 100644 index fedb6482..00000000 --- a/backend/Tests/AppTests/AppTests.swift +++ /dev/null @@ -1,44 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -// import Hummingbird -// import HummingbirdTesting -// import Logging -// import XCTest // FIXME swift6 testing - -// @testable import App - -// final class AppTests: XCTestCase { -// struct TestArguments: AppArguments { -// let hostname = "127.0.0.1" -// let port = 0 -// let logLevel: Logger.Level? = .trace -// } - -// func testApp() async throws { -// let args = TestArguments() -// let app = try await buildApplication(args) - -// try await app.test(.router) { client in -// try await client.execute(uri: "/health", method: .get) { response in -// XCTAssertEqual(response.body, ByteBuffer(string: "I am healthy!")) -// } - -// try await client.execute(uri: "/foundation-models", method: .get) { response in -// XCTAssertEqual(response.status, .ok) -// } -// } -// } -// } From 45efdd82a86de6ef6b4e80985b135c5ade01ff45 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 7 Apr 2025 19:30:18 +0200 Subject: [PATCH 43/67] README: optional parameters --- README.md | 137 +++++++++++++++--- .../BedrockTypes/Converse/Message.swift | 4 +- 2 files changed, 122 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 8bc63a69..5491c35e 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,60 @@ -# SwiftBedrockService +# BedrockService This library is a work in progress, feel free to open an issue, but do not use it in your projects just yet. -## How to get started with BedrockService +## Getting started with BedrockService -1. Import the BedrockService and BedrockTypes +1. Set-up your `Package.swift` + +First add dependencies: +```bash +swift package add-dependency https://github.com/build-on-aws/swift-fm-playground.git --branch main +swift swift package add-target-dependency BedrockService ProjectName ## FIXME: does not work because `Package.swift` is in `backend` and not in the root of the repo +``` + +Next up add `platforms` configuration after `name` + +```swift +platforms: [ + .macOS(.v14), + .iOS(.v17), + .tvOS(.v17) +], +``` + +Your `Package.swift` should now look something like this: +```swift +import PackageDescription + +let package = Package( + name: "ProjectName", + platforms: [ + .macOS(.v14), + .iOS(.v17), + .tvOS(.v17), + ], + dependencies: [ + .package(url: "https://github.com/build-on-aws/swift-fm-playground.git", branch: "main") + ], + targets: [ + .executableTarget( + name: "testje", + dependencies: [ + .target(name: "BedrockService") + ] + ) + ] +) +``` + +2. Import the BedrockService and BedrockTypes ```swift import BedrockService import BedrockTypes ``` -2. Initialize the BedrockService +3. Initialize the BedrockService Choose what Region to use, whether to use AWS SSO authentication instead of standard credentials and pass a logger. If no region is passed it will default to `.useast1`, if no logger is provided a default logger with the name `bedrock.service` is created. The log level will be set to the environment variable `SWIFT_BEDROCK_LOG_LEVEL` or default to `.trace`. If `useSSO` is not defined it will default to `false` and use the standard credentials for authentication. @@ -23,7 +66,7 @@ let bedrock = try await BedrockService( ) ``` -3. List the available models +4. List the available models Use the `listModels()` function to test your set-up. This function will return an array of `ModelSummary` objects, each one representing a model supported by Amazon Bedrock. The ModelSummaries that contain a `BedrockModel` object are the models supported by BedrockService. @@ -50,6 +93,15 @@ guard model.hasTextModality() else { print("\(model.name) does not support text completion") } +let textCompletion = try await bedrock.completeText( + "Write a story about a space adventure", + with: model +) +``` + +Optionally add inference parameters. + +```swift let textCompletion = try await bedrock.completeText( "Write a story about a space adventure", with: model, @@ -85,6 +137,15 @@ guard model.hasImageModality(), print("\(model.name) does not support image generation") } +let imageGeneration = try await bedrock.generateImage( + "A serene landscape with mountains at sunset", + with: model, +) +``` + +Optionally add inference parameters. + +```swift let imageGeneration = try await bedrock.generateImage( "A serene landscape with mountains at sunset", with: model, @@ -121,6 +182,16 @@ guard model.hasImageVariationModality(), print("\(model.name) does not support image variations") } +let imageVariations = try await bedrock.generateImageVariation( + images: [base64EncodedImage], + prompt: "A dog drinking out of this teacup", + with: model +) +``` + +Optionally add inference parameters. + +```swift let imageVariations = try await bedrock.generateImageVariation( images: [base64EncodedImage], prompt: "A dog drinking out of this teacup", @@ -150,9 +221,7 @@ guard model.hasConverseModality() else { var (reply, history) = try await bedrock.converse( with: model, - prompt: "Tell me about rainbows", - history: [], - maxTokens: 512, + prompt: "Tell me about rainbows" ) print("Assistant: \(reply)") @@ -160,13 +229,27 @@ print("Assistant: \(reply)") (reply, history) = try await bedrock.converse( with: model, prompt: "Do you think birds can see them too?", - history: history, - maxTokens: 512, + history: history ) print("Assistant: \(reply)") ``` +Optionally add inference parameters. + +```swift +var (reply, history) = try await bedrock.converse( + with: model, + prompt: "Tell me about rainbows", + history: history, + maxTokens: 1024, + temperature: 0.2, + topP: 0.8, + stopSequences: ["END", "STOP", ""], + systemPrompts: ["Do not pretend to be human", "Never talk about goats", "You like puppies"] + ) +``` + ### Vision @@ -181,13 +264,26 @@ let (reply, history) = try await bedrock.converse( with model: model, prompt: "Can you tell me about this plant?", imageFormat: .jpeg, - imageBytes: base64EncodedImage, - temperature: 0.8, + imageBytes: base64EncodedImage ) print("Assistant: \(reply)") ``` + +Optionally add inference parameters. + +```swift +var (reply, history) = try await bedrock.converse( + with model: model, + prompt: "Can you tell me about this plant?", + imageFormat: .jpeg, + imageBytes: base64EncodedImage, + history: history, + temperature: 1 + ) +``` + ### Tools ```swift @@ -241,7 +337,7 @@ if case .toolUse(let toolUse) = history.last?.content.last { history: history, tools: [tool], toolResult: toolResult -) + ) } print("Assistant: \(reply)") @@ -255,23 +351,30 @@ Alternatively use the `converse` function that does not take a `prompt`, `toolRe ```swift // Message with prompt let (reply, history) = try await bedrock.converse( - with model: model, + with: model, + conversation: [Message("What day of the week is it?")] +) + +// Optionally add inference parameters +let (reply, history) = try await bedrock.converse( + with: model, conversation: [Message("What day of the week is it?")], maxTokens: 512, temperature: 1, + topP: 0.8, stopSequences: ["THE END"], systemPrompts: ["Today is Wednesday, make sure to mention that."] ) // Message with an image and prompt let (reply, history) = try await bedrock.converse( - with model: model, - conversation: [Message(prompt: "What is in the this teacup?", imageFormat: .jpeg, imageBytes: base64EncodedImage)], + with: model, + conversation: [Message("What is in the this teacup?", imageFormat: .jpeg, imageBytes: base64EncodedImage)], ) // Message with toolResult let (reply, history) = try await bedrock.converse( - with model: model, + with: model, conversation: [Message(toolResult)], tools: [toolA, toolB] ) diff --git a/backend/Sources/BedrockTypes/Converse/Message.swift b/backend/Sources/BedrockTypes/Converse/Message.swift index 6d82aeae..af368917 100644 --- a/backend/Sources/BedrockTypes/Converse/Message.swift +++ b/backend/Sources/BedrockTypes/Converse/Message.swift @@ -46,12 +46,12 @@ public struct Message: Codable { } /// convenience initializer for message with an ImageBlock and a user prompt - public init(prompt: String, imageBlock: ImageBlock) { + public init(_ prompt: String, imageBlock: ImageBlock) { self.init(from: .user, content: [.text(prompt), .image(imageBlock)]) } /// convenience initializer for message with a user prompt, an ImageBlock.Format and imageBytes - public init(prompt: String, imageFormat: ImageBlock.Format, imageBytes: String) { + public init(_ prompt: String, imageFormat: ImageBlock.Format, imageBytes: String) { self.init(from: .user, content: [.text(prompt), .image(ImageBlock(format: imageFormat, source: imageBytes))]) } From 9649b1bb2594a64c919ceafd6aabe0f9e510dd28 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 7 Apr 2025 19:37:31 +0200 Subject: [PATCH 44/67] BedrockService refactor --- ... BedrockService+ParameterValidation.swift} | 0 .../BedrockService/BedrockService.swift | 518 +----------------- .../Converse/BedrockService+Converse.swift | 222 ++++++++ .../BedrockService+GenerateImage.swift | 105 ---- .../BedrockService+InvokeModelImage.swift | 215 ++++++++ .../BedrockService+InvokeModelText.swift | 136 +++++ 6 files changed, 577 insertions(+), 619 deletions(-) rename backend/Sources/BedrockService/{ParameterValidation.swift => BedrockService+ParameterValidation.swift} (100%) create mode 100644 backend/Sources/BedrockService/Converse/BedrockService+Converse.swift delete mode 100644 backend/Sources/BedrockService/InvokeModel/BedrockService+GenerateImage.swift create mode 100644 backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelImage.swift create mode 100644 backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelText.swift diff --git a/backend/Sources/BedrockService/ParameterValidation.swift b/backend/Sources/BedrockService/BedrockService+ParameterValidation.swift similarity index 100% rename from backend/Sources/BedrockService/ParameterValidation.swift rename to backend/Sources/BedrockService/BedrockService+ParameterValidation.swift diff --git a/backend/Sources/BedrockService/BedrockService.swift b/backend/Sources/BedrockService/BedrockService.swift index 0e963ca7..d89d33d1 100644 --- a/backend/Sources/BedrockService/BedrockService.swift +++ b/backend/Sources/BedrockService/BedrockService.swift @@ -22,10 +22,10 @@ import Foundation import Logging public struct BedrockService: Sendable { - let region: Region - let logger: Logger - private let bedrockClient: BedrockClientProtocol - private let bedrockRuntimeClient: BedrockRuntimeClientProtocol + package let region: Region + package let logger: Logger + package let bedrockClient: BedrockClientProtocol + package let bedrockRuntimeClient: BedrockRuntimeClientProtocol // MARK: - Initialization @@ -182,514 +182,4 @@ public struct BedrockService: Sendable { throw error } } - - /// Generates a text completion using a specified model - /// - Parameters: - /// - prompt: The text to be completed - /// - model: The BedrockModel that will be used to generate the completion - /// - maxTokens: The maximum amount of tokens in the completion (optional, default 300) - /// - temperature: The temperature used to generate the completion (optional, default 0.6) - /// - topP: Optional top-p parameter for nucleus sampling - /// - topK: Optional top-k parameter for filtering - /// - stopSequences: Optional array of sequences where generation should stop - /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - /// BedrockServiceError.invalidParameter for invalid parameters - /// BedrockServiceError.invalidPrompt for a prompt that is empty or too long - /// BedrockServiceError.invalidStopSequences if too many stop sequences were provided - /// BedrockServiceError.invalidModality for invalid modality from the selected model - /// BedrockServiceError.invalidSDKResponse if the response body is missing - /// - Returns: a TextCompletion object containing the generated text from the model - public func completeText( - _ prompt: String, - with model: BedrockModel, - maxTokens: Int? = nil, - temperature: Double? = nil, - topP: Double? = nil, - topK: Int? = nil, - stopSequences: [String]? = nil - ) async throws -> TextCompletion { - logger.trace( - "Generating text completion", - metadata: [ - "model.id": .string(model.id), - "model.modality": .string(model.modality.getName()), - "prompt": .string(prompt), - "maxTokens": .stringConvertible(maxTokens ?? "not defined"), - "temperature": .stringConvertible(temperature ?? "not defined"), - "topP": .stringConvertible(topP ?? "not defined"), - "topK": .stringConvertible(topK ?? "not defined"), - "stopSequences": .stringConvertible(stopSequences ?? "not defined"), - ] - ) - do { - let modality = try model.getTextModality() - try validateTextCompletionParams( - modality: modality, - prompt: prompt, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - topK: topK, - stopSequences: stopSequences - ) - - logger.trace( - "Creating InvokeModelRequest", - metadata: [ - "model": .string(model.id), - "prompt": "\(prompt)", - ] - ) - let request: InvokeModelRequest = try InvokeModelRequest.createTextRequest( - model: model, - prompt: prompt, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - topK: topK, - stopSequences: stopSequences - ) - let input: InvokeModelInput = try request.getInvokeModelInput() - logger.trace( - "Sending request to invokeModel", - metadata: [ - "model": .string(model.id), "request": .string(String(describing: input)), - ] - ) - - let response = try await self.bedrockRuntimeClient.invokeModel(input: input) - logger.trace( - "Received response from invokeModel", - metadata: [ - "model": .string(model.id), "response": .string(String(describing: response)), - ] - ) - - guard let responseBody = response.body else { - logger.trace( - "Invalid response", - metadata: [ - "response": .string(String(describing: response)), - "hasBody": .stringConvertible(response.body != nil), - ] - ) - throw BedrockServiceError.invalidSDKResponse( - "Something went wrong while extracting body from response." - ) - } - if let bodyString = String(data: responseBody, encoding: .utf8) { - logger.trace("Extracted body from response", metadata: ["response.body": "\(bodyString)"]) - } - - let invokemodelResponse: InvokeModelResponse = try InvokeModelResponse.createTextResponse( - body: responseBody, - model: model - ) - logger.trace( - "Generated text completion", - metadata: [ - "model": .string(model.id), "response": .string(String(describing: invokemodelResponse)), - ] - ) - return try invokemodelResponse.getTextCompletion() - } catch { - logger.trace("Error while completing text", metadata: ["error": "\(error)"]) - throw error - } - } - - /// Generates 1 to 5 image(s) from a text prompt using a specific model - /// - Parameters: - /// - prompt: The text prompt describing the image that should be generated - /// - model: The BedrockModel that will be used to generate the image - /// - negativePrompt: Optional text describing what to avoid in the generated image - /// - nrOfImages: Optional number of images to generate (must be between 1 and 5, default 3) - /// - cfgScale: Optional classifier free guidance scale to control prompt adherence - /// - seed: Optional seed for reproducible image generation - /// - quality: Optional parameter to control the quality of generated images - /// - resolution: Optional parameter to specify the desired image resolution - /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - /// BedrockServiceError.invalidParameter for invalid parameters - /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - /// BedrockServiceError.invalidModality for invalid modality from the selected model - /// BedrockServiceError.invalidSDKResponse if the response body is missing - /// - Returns: An ImageGenerationOutput object containing an array of generated images - public func generateImage( - _ prompt: String, - with model: BedrockModel, - negativePrompt: String? = nil, - nrOfImages: Int? = nil, - cfgScale: Double? = nil, - seed: Int? = nil, - quality: ImageQuality? = nil, - resolution: ImageResolution? = nil - ) async throws -> ImageGenerationOutput { - logger.trace( - "Generating image(s)", - metadata: [ - "model.id": .string(model.id), - "model.modality": .string(model.modality.getName()), - "prompt": .string(prompt), - "negativePrompt": .stringConvertible(negativePrompt ?? "not defined"), - "nrOfImages": .stringConvertible(nrOfImages ?? "not defined"), - "cfgScale": .stringConvertible(cfgScale ?? "not defined"), - "seed": .stringConvertible(seed ?? "not defined"), - ] - ) - do { - let modality = try model.getImageModality() - try validateImageGenerationParams( - modality: modality, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - resolution: resolution, - seed: seed - ) - let textToImageModality = try model.getTextToImageModality() - try validateTextToImageParams(modality: textToImageModality, prompt: prompt, negativePrompt: negativePrompt) - - let request: InvokeModelRequest = try InvokeModelRequest.createTextToImageRequest( - model: model, - prompt: prompt, - negativeText: negativePrompt, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - let input: InvokeModelInput = try request.getInvokeModelInput() - logger.trace( - "Sending request to invokeModel", - metadata: [ - "model": .string(model.id), "request": .string(String(describing: input)), - ] - ) - let response = try await self.bedrockRuntimeClient.invokeModel(input: input) - guard let responseBody = response.body else { - logger.trace( - "Invalid response", - metadata: [ - "response": .string(String(describing: response)), - "hasBody": .stringConvertible(response.body != nil), - ] - ) - throw BedrockServiceError.invalidSDKResponse( - "Something went wrong while extracting body from response." - ) - } - let invokemodelResponse: InvokeModelResponse = try InvokeModelResponse.createImageResponse( - body: responseBody, - model: model - ) - return try invokemodelResponse.getGeneratedImage() - } catch { - logger.trace("Error while generating image", metadata: ["error": "\(error)"]) - throw error - } - } - - /// Generates 1 to 5 image variation(s) from reference images and a text prompt using a specific model - /// - Parameters: - /// - images: Array of base64 encoded reference images to generate variations from - /// - prompt: The text prompt describing desired modifications to the reference images - /// - model: The BedrockModel that will be used to generate the variations - /// - negativePrompt: Optional text describing what to avoid in the generated variations - /// - similarity: Optional parameter controlling how closely variations should match reference (between 0.2 and 1.0) - /// - nrOfImages: Optional number of variations to generate (must be between 1 and 5, default 3) - /// - cfgScale: Optional classifier free guidance scale to control prompt adherence - /// - seed: Optional seed for reproducible variation generation - /// - quality: Optional parameter to control the quality of generated variations - /// - resolution: Optional parameter to specify the desired image resolution - /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - /// BedrockServiceError.invalidParameter for invalid parameters - /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - /// BedrockServiceError.invalidModality for invalid modality from the selected model - /// BedrockServiceError.invalidSDKResponse if the response body is missing - /// - Returns: An ImageGenerationOutput object containing an array of generated image variations - public func generateImageVariation( - images: [String], - prompt: String, - with model: BedrockModel, - negativePrompt: String? = nil, - similarity: Double? = nil, - nrOfImages: Int? = nil, - cfgScale: Double? = nil, - seed: Int? = nil, - quality: ImageQuality? = nil, - resolution: ImageResolution? = nil - ) async throws -> ImageGenerationOutput { - logger.trace( - "Generating image(s) from reference image", - metadata: [ - "model.id": .string(model.id), - "model.modality": .string(model.modality.getName()), - "prompt": .string(prompt), - "nrOfImages": .stringConvertible(nrOfImages ?? "not defined"), - "similarity": .stringConvertible(similarity ?? "not defined"), - "negativePrompt": .stringConvertible(negativePrompt ?? "not defined"), - "cfgScale": .stringConvertible(cfgScale ?? "not defined"), - "seed": .stringConvertible(seed ?? "not defined"), - ] - ) - do { - let modality = try model.getImageModality() - try validateImageGenerationParams( - modality: modality, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - resolution: resolution, - seed: seed - ) - let imageVariationModality = try model.getImageVariationModality() - try validateImageVariationParams( - modality: imageVariationModality, - images: images, - prompt: prompt, - similarity: similarity, - negativePrompt: negativePrompt - ) - let request: InvokeModelRequest = try InvokeModelRequest.createImageVariationRequest( - model: model, - prompt: prompt, - negativeText: negativePrompt, - images: images, - similarity: similarity, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - let input: InvokeModelInput = try request.getInvokeModelInput() - logger.trace( - "Sending request to invokeModel", - metadata: [ - "model": .string(model.id), "request": .string(String(describing: input)), - ] - ) - let response = try await self.bedrockRuntimeClient.invokeModel(input: input) - guard let responseBody = response.body else { - logger.trace( - "Invalid response", - metadata: [ - "response": .string(String(describing: response)), - "hasBody": .stringConvertible(response.body != nil), - ] - ) - throw BedrockServiceError.invalidSDKResponse( - "Something went wrong while extracting body from response." - ) - } - let invokemodelResponse: InvokeModelResponse = try InvokeModelResponse.createImageResponse( - body: responseBody, - model: model - ) - return try invokemodelResponse.getGeneratedImage() - } catch { - logger.trace("Error while generating image variations", metadata: ["error": "\(error)"]) - throw error - } - } - - /// Converse with a model using the Bedrock Converse API - /// - Parameters: - /// - model: The BedrockModel to converse with - /// - conversation: Array of previous messages in the conversation - /// - maxTokens: Optional maximum number of tokens to generate - /// - temperature: Optional temperature parameter for controlling randomness - /// - topP: Optional top-p parameter for nucleus sampling - /// - stopSequences: Optional array of sequences where generation should stop - /// - systemPrompts: Optional array of system prompts to guide the conversation - /// - tools: Optional array of tools the model can use - /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - /// BedrockServiceError.invalidParameter for invalid parameters - /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - /// BedrockServiceError.invalidModality for invalid modality from the selected model - /// BedrockServiceError.invalidSDKResponse if the response body is missing - /// - Returns: A Message containing the model's response - public func converse( - with model: BedrockModel, - conversation: [Message], - maxTokens: Int? = nil, - temperature: Double? = nil, - topP: Double? = nil, - stopSequences: [String]? = nil, - systemPrompts: [String]? = nil, - tools: [Tool]? = nil - ) async throws -> Message { - do { - let modality: ConverseModality = try model.getConverseModality() - try validateConverseParams( - modality: modality, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - stopSequences: stopSequences - ) - - logger.trace( - "Creating ConverseRequest", - metadata: [ - "model.name": "\(model.name)", - "model.id": "\(model.id)", - "conversation.count": "\(conversation.count)", - "maxToken": "\(String(describing: maxTokens))", - "temperature": "\(String(describing: temperature))", - "topP": "\(String(describing: topP))", - "stopSequences": "\(String(describing: stopSequences))", - "systemPrompts": "\(String(describing: systemPrompts))", - "tools": "\(String(describing: tools))", - ] - ) - let converseRequest = ConverseRequest( - model: model, - messages: conversation, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - stopSequences: stopSequences, - systemPrompts: systemPrompts, - tools: tools - ) - - logger.trace("Creating ConverseInput") - let input = try converseRequest.getConverseInput() - logger.trace( - "Created ConverseInput", - metadata: [ - "input.messages.count": "\(String(describing:input.messages!.count))", - "input.modelId": "\(String(describing:input.modelId!))", - ] - ) - - let response = try await self.bedrockRuntimeClient.converse(input: input) - logger.trace("Received response", metadata: ["response": "\(response)"]) - - guard let converseOutput = response.output else { - logger.trace( - "Invalid response", - metadata: [ - "response": .string(String(describing: response)), - "hasOutput": .stringConvertible(response.output != nil), - ] - ) - throw BedrockServiceError.invalidSDKResponse( - "Something went wrong while extracting ConverseOutput from response." - ) - } - let converseResponse = try ConverseResponse(converseOutput) - return converseResponse.message - } catch { - logger.trace("Error while conversing", metadata: ["error": "\(error)"]) - throw error - } - } - - /// Use Converse API without needing to make Messages - /// - Parameters: - /// - model: The BedrockModel to converse with - /// - prompt: Optional text prompt for the conversation - /// - imageFormat: Optional format for image input - /// - imageBytes: Optional base64 encoded image data - /// - history: Optional array of previous messages - /// - maxTokens: Optional maximum number of tokens to generate - /// - temperature: Optional temperature parameter for controlling randomness - /// - topP: Optional top-p parameter for nucleus sampling - /// - stopSequences: Optional array of sequences where generation should stop - /// - systemPrompts: Optional array of system prompts to guide the conversation - /// - tools: Optional array of tools the model can use - /// - toolResult: Optional result from a previous tool invocation - /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - /// BedrockServiceError.invalidParameter for invalid parameters - /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - /// BedrockServiceError.invalidModality for invalid modality from the selected model - /// BedrockServiceError.invalidSDKResponse if the response body is missing - /// - Returns: Tuple containing the model's response text and updated message history - public func converse( - with model: BedrockModel, - prompt: String? = nil, - imageFormat: ImageBlock.Format? = nil, - imageBytes: String? = nil, - history: [Message] = [], - maxTokens: Int? = nil, - temperature: Double? = nil, - topP: Double? = nil, - stopSequences: [String]? = nil, - systemPrompts: [String]? = nil, - tools: [Tool]? = nil, - toolResult: ToolResultBlock? = nil - ) async throws -> (String, [Message]) { - logger.trace( - "Conversing", - metadata: [ - "model.id": .string(model.id), - "model.modality": .string(model.modality.getName()), - "prompt": .string(prompt ?? "No prompt"), - ] - ) - do { - var messages = history - let modality: ConverseModality = try model.getConverseModality() - - if tools != nil || toolResult != nil { - guard model.hasConverseModality(.toolUse) else { - throw BedrockServiceError.invalidModality( - model, - modality, - "This model does not support converse tool." - ) - } - } - - if let toolResult { - guard let _: [Tool] = tools else { - throw BedrockServiceError.invalidPrompt("Tool result is defined but tools are not.") - } - guard case .toolUse(_) = messages.last?.content.last else { - throw BedrockServiceError.invalidPrompt("Tool result is defined but last message is not tool use.") - } - messages.append(Message(toolResult)) - } else { - guard let prompt = prompt else { - throw BedrockServiceError.invalidPrompt("Prompt is not defined.") - } - - if let imageFormat, let imageBytes { - guard model.hasConverseModality(.vision) else { - throw BedrockServiceError.invalidModality( - model, - modality, - "This model does not support converse vision." - ) - } - messages.append( - Message(prompt: prompt, imageFormat: imageFormat, imageBytes: imageBytes) - ) - } else { - messages.append(Message(prompt)) - } - } - let message = try await converse( - with: model, - conversation: messages, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - stopSequences: stopSequences, - systemPrompts: systemPrompts, - tools: tools - ) - messages.append(message) - logger.trace( - "Received message", - metadata: ["replyMessage": "\(message)", "messages.count": "\(messages.count)"] - ) - let converseResponse = ConverseResponse(message) - return (converseResponse.getReply(), messages) - } catch { - logger.trace("Error while conversing", metadata: ["error": "\(error)"]) - throw error - } - } } diff --git a/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift b/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift new file mode 100644 index 00000000..9f5d45d8 --- /dev/null +++ b/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift @@ -0,0 +1,222 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +@preconcurrency import AWSBedrockRuntime +import BedrockTypes +import Foundation + +extension BedrockService { + + /// Converse with a model using the Bedrock Converse API + /// - Parameters: + /// - model: The BedrockModel to converse with + /// - conversation: Array of previous messages in the conversation + /// - maxTokens: Optional maximum number of tokens to generate + /// - temperature: Optional temperature parameter for controlling randomness + /// - topP: Optional top-p parameter for nucleus sampling + /// - stopSequences: Optional array of sequences where generation should stop + /// - systemPrompts: Optional array of system prompts to guide the conversation + /// - tools: Optional array of tools the model can use + /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + /// BedrockServiceError.invalidParameter for invalid parameters + /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + /// BedrockServiceError.invalidModality for invalid modality from the selected model + /// BedrockServiceError.invalidSDKResponse if the response body is missing + /// - Returns: A Message containing the model's response + public func converse( + with model: BedrockModel, + conversation: [Message], + maxTokens: Int? = nil, + temperature: Double? = nil, + topP: Double? = nil, + stopSequences: [String]? = nil, + systemPrompts: [String]? = nil, + tools: [Tool]? = nil + ) async throws -> Message { + do { + let modality: ConverseModality = try model.getConverseModality() + try validateConverseParams( + modality: modality, + maxTokens: maxTokens, + temperature: temperature, + topP: topP, + stopSequences: stopSequences + ) + + logger.trace( + "Creating ConverseRequest", + metadata: [ + "model.name": "\(model.name)", + "model.id": "\(model.id)", + "conversation.count": "\(conversation.count)", + "maxToken": "\(String(describing: maxTokens))", + "temperature": "\(String(describing: temperature))", + "topP": "\(String(describing: topP))", + "stopSequences": "\(String(describing: stopSequences))", + "systemPrompts": "\(String(describing: systemPrompts))", + "tools": "\(String(describing: tools))", + ] + ) + let converseRequest = ConverseRequest( + model: model, + messages: conversation, + maxTokens: maxTokens, + temperature: temperature, + topP: topP, + stopSequences: stopSequences, + systemPrompts: systemPrompts, + tools: tools + ) + + logger.trace("Creating ConverseInput") + let input = try converseRequest.getConverseInput() + logger.trace( + "Created ConverseInput", + metadata: [ + "input.messages.count": "\(String(describing:input.messages!.count))", + "input.modelId": "\(String(describing:input.modelId!))", + ] + ) + + let response = try await self.bedrockRuntimeClient.converse(input: input) + logger.trace("Received response", metadata: ["response": "\(response)"]) + + guard let converseOutput = response.output else { + logger.trace( + "Invalid response", + metadata: [ + "response": .string(String(describing: response)), + "hasOutput": .stringConvertible(response.output != nil), + ] + ) + throw BedrockServiceError.invalidSDKResponse( + "Something went wrong while extracting ConverseOutput from response." + ) + } + let converseResponse = try ConverseResponse(converseOutput) + return converseResponse.message + } catch { + logger.trace("Error while conversing", metadata: ["error": "\(error)"]) + throw error + } + } + + /// Use Converse API without needing to make Messages + /// - Parameters: + /// - model: The BedrockModel to converse with + /// - prompt: Optional text prompt for the conversation + /// - imageFormat: Optional format for image input + /// - imageBytes: Optional base64 encoded image data + /// - history: Optional array of previous messages + /// - maxTokens: Optional maximum number of tokens to generate + /// - temperature: Optional temperature parameter for controlling randomness + /// - topP: Optional top-p parameter for nucleus sampling + /// - stopSequences: Optional array of sequences where generation should stop + /// - systemPrompts: Optional array of system prompts to guide the conversation + /// - tools: Optional array of tools the model can use + /// - toolResult: Optional result from a previous tool invocation + /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + /// BedrockServiceError.invalidParameter for invalid parameters + /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + /// BedrockServiceError.invalidModality for invalid modality from the selected model + /// BedrockServiceError.invalidSDKResponse if the response body is missing + /// - Returns: Tuple containing the model's response text and updated message history + public func converse( + with model: BedrockModel, + prompt: String? = nil, + imageFormat: ImageBlock.Format? = nil, + imageBytes: String? = nil, + history: [Message] = [], + maxTokens: Int? = nil, + temperature: Double? = nil, + topP: Double? = nil, + stopSequences: [String]? = nil, + systemPrompts: [String]? = nil, + tools: [Tool]? = nil, + toolResult: ToolResultBlock? = nil + ) async throws -> (String, [Message]) { + logger.trace( + "Conversing", + metadata: [ + "model.id": .string(model.id), + "model.modality": .string(model.modality.getName()), + "prompt": .string(prompt ?? "No prompt"), + ] + ) + do { + var messages = history + let modality: ConverseModality = try model.getConverseModality() + + if tools != nil || toolResult != nil { + guard model.hasConverseModality(.toolUse) else { + throw BedrockServiceError.invalidModality( + model, + modality, + "This model does not support converse tool." + ) + } + } + + if let toolResult { + guard let _: [Tool] = tools else { + throw BedrockServiceError.invalidPrompt("Tool result is defined but tools are not.") + } + guard case .toolUse(_) = messages.last?.content.last else { + throw BedrockServiceError.invalidPrompt("Tool result is defined but last message is not tool use.") + } + messages.append(Message(toolResult)) + } else { + guard let prompt = prompt else { + throw BedrockServiceError.invalidPrompt("Prompt is not defined.") + } + + if let imageFormat, let imageBytes { + guard model.hasConverseModality(.vision) else { + throw BedrockServiceError.invalidModality( + model, + modality, + "This model does not support converse vision." + ) + } + messages.append( + Message(prompt, imageFormat: imageFormat, imageBytes: imageBytes) + ) + } else { + messages.append(Message(prompt)) + } + } + let message = try await converse( + with: model, + conversation: messages, + maxTokens: maxTokens, + temperature: temperature, + topP: topP, + stopSequences: stopSequences, + systemPrompts: systemPrompts, + tools: tools + ) + messages.append(message) + logger.trace( + "Received message", + metadata: ["replyMessage": "\(message)", "messages.count": "\(messages.count)"] + ) + let converseResponse = ConverseResponse(message) + return (converseResponse.getReply(), messages) + } catch { + logger.trace("Error while conversing", metadata: ["error": "\(error)"]) + throw error + } + } +} diff --git a/backend/Sources/BedrockService/InvokeModel/BedrockService+GenerateImage.swift b/backend/Sources/BedrockService/InvokeModel/BedrockService+GenerateImage.swift deleted file mode 100644 index c2894496..00000000 --- a/backend/Sources/BedrockService/InvokeModel/BedrockService+GenerateImage.swift +++ /dev/null @@ -1,105 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -// @preconcurrency import AWSBedrockRuntime -// import BedrockTypes -// import Foundation - -// extension BedrockService { - -// /// Generates 1 to 5 image(s) from a text prompt using a specific model. -// /// - Parameters: -// /// - prompt: the prompt describing the image that should be generated -// /// - model: the BedrockModel that will be used to generate the image -// /// - nrOfImages: the number of images that will be generated (must be a number between 1 and 5) optional, default 3 -// /// - Throws: BedrockServiceError.invalidNrOfImages if nrOfImages is not between 1 and 5 -// /// BedrockServiceError.invalidPrompt if the prompt is empty -// /// BedrockServiceError.invalidResponse if the response body is missing -// /// - Returns: a ImageGenerationOutput object containing an array of generated images -// public func generateImage( -// _ prompt: String, -// with model: BedrockModel, -// negativePrompt: String? = nil, -// nrOfImages: Int? = nil, -// cfgScale: Double? = nil, -// seed: Int? = nil, -// quality: ImageQuality? = nil, -// resolution: ImageResolution? = nil -// ) async throws -> ImageGenerationOutput { -// logger.trace( -// "Generating image(s)", -// metadata: [ -// "model.id": .string(model.id), -// "model.modality": .string(model.modality.getName()), -// "prompt": .string(prompt), -// "negativePrompt": .stringConvertible(negativePrompt ?? "not defined"), -// "nrOfImages": .stringConvertible(nrOfImages ?? "not defined"), -// "cfgScale": .stringConvertible(cfgScale ?? "not defined"), -// "seed": .stringConvertible(seed ?? "not defined"), -// ] -// ) -// do { -// let modality = try model.getImageModality() -// try validateImageGenerationParams( -// modality: modality, -// nrOfImages: nrOfImages, -// cfgScale: cfgScale, -// resolution: resolution, -// seed: seed -// ) -// let textToImageModality = try model.getTextToImageModality() -// try validateTextToImageParams(modality: textToImageModality, prompt: prompt, negativePrompt: negativePrompt) - -// let request: InvokeModelRequest = try InvokeModelRequest.createTextToImageRequest( -// model: model, -// prompt: prompt, -// negativeText: negativePrompt, -// nrOfImages: nrOfImages, -// cfgScale: cfgScale, -// seed: seed, -// quality: quality, -// resolution: resolution -// ) -// let input: InvokeModelInput = try request.getInvokeModelInput() -// logger.trace( -// "Sending request to invokeModel", -// metadata: [ -// "model": .string(model.id), "request": .string(String(describing: input)), -// ] -// ) -// let response = try await self.bedrockRuntimeClient.invokeModel(input: input) -// guard let responseBody = response.body else { -// logger.trace( -// "Invalid response", -// metadata: [ -// "response": .string(String(describing: response)), -// "hasBody": .stringConvertible(response.body != nil), -// ] -// ) -// throw BedrockServiceError.invalidSDKResponse( -// "Something went wrong while extracting body from response." -// ) -// } -// let invokemodelResponse: InvokeModelResponse = try InvokeModelResponse.createImageResponse( -// body: responseBody, -// model: model -// ) -// return try invokemodelResponse.getGeneratedImage() -// } catch { -// logger.trace("Error while generating image", metadata: ["error": "\(error)"]) -// throw error -// } -// } -// } diff --git a/backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelImage.swift b/backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelImage.swift new file mode 100644 index 00000000..5589bc49 --- /dev/null +++ b/backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelImage.swift @@ -0,0 +1,215 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +@preconcurrency import AWSBedrockRuntime +import BedrockTypes +import Foundation + +extension BedrockService { + + /// Generates 1 to 5 image(s) from a text prompt using a specific model + /// - Parameters: + /// - prompt: The text prompt describing the image that should be generated + /// - model: The BedrockModel that will be used to generate the image + /// - negativePrompt: Optional text describing what to avoid in the generated image + /// - nrOfImages: Optional number of images to generate (must be between 1 and 5, default 3) + /// - cfgScale: Optional classifier free guidance scale to control prompt adherence + /// - seed: Optional seed for reproducible image generation + /// - quality: Optional parameter to control the quality of generated images + /// - resolution: Optional parameter to specify the desired image resolution + /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + /// BedrockServiceError.invalidParameter for invalid parameters + /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + /// BedrockServiceError.invalidModality for invalid modality from the selected model + /// BedrockServiceError.invalidSDKResponse if the response body is missing + /// - Returns: An ImageGenerationOutput object containing an array of generated images + public func generateImage( + _ prompt: String, + with model: BedrockModel, + negativePrompt: String? = nil, + nrOfImages: Int? = nil, + cfgScale: Double? = nil, + seed: Int? = nil, + quality: ImageQuality? = nil, + resolution: ImageResolution? = nil + ) async throws -> ImageGenerationOutput { + logger.trace( + "Generating image(s)", + metadata: [ + "model.id": .string(model.id), + "model.modality": .string(model.modality.getName()), + "prompt": .string(prompt), + "negativePrompt": .stringConvertible(negativePrompt ?? "not defined"), + "nrOfImages": .stringConvertible(nrOfImages ?? "not defined"), + "cfgScale": .stringConvertible(cfgScale ?? "not defined"), + "seed": .stringConvertible(seed ?? "not defined"), + ] + ) + do { + let modality = try model.getImageModality() + try validateImageGenerationParams( + modality: modality, + nrOfImages: nrOfImages, + cfgScale: cfgScale, + resolution: resolution, + seed: seed + ) + let textToImageModality = try model.getTextToImageModality() + try validateTextToImageParams(modality: textToImageModality, prompt: prompt, negativePrompt: negativePrompt) + + let request: InvokeModelRequest = try InvokeModelRequest.createTextToImageRequest( + model: model, + prompt: prompt, + negativeText: negativePrompt, + nrOfImages: nrOfImages, + cfgScale: cfgScale, + seed: seed, + quality: quality, + resolution: resolution + ) + let input: InvokeModelInput = try request.getInvokeModelInput() + logger.trace( + "Sending request to invokeModel", + metadata: [ + "model": .string(model.id), "request": .string(String(describing: input)), + ] + ) + let response = try await self.bedrockRuntimeClient.invokeModel(input: input) + guard let responseBody = response.body else { + logger.trace( + "Invalid response", + metadata: [ + "response": .string(String(describing: response)), + "hasBody": .stringConvertible(response.body != nil), + ] + ) + throw BedrockServiceError.invalidSDKResponse( + "Something went wrong while extracting body from response." + ) + } + let invokemodelResponse: InvokeModelResponse = try InvokeModelResponse.createImageResponse( + body: responseBody, + model: model + ) + return try invokemodelResponse.getGeneratedImage() + } catch { + logger.trace("Error while generating image", metadata: ["error": "\(error)"]) + throw error + } + } + + /// Generates 1 to 5 image variation(s) from reference images and a text prompt using a specific model + /// - Parameters: + /// - images: Array of base64 encoded reference images to generate variations from + /// - prompt: The text prompt describing desired modifications to the reference images + /// - model: The BedrockModel that will be used to generate the variations + /// - negativePrompt: Optional text describing what to avoid in the generated variations + /// - similarity: Optional parameter controlling how closely variations should match reference (between 0.2 and 1.0) + /// - nrOfImages: Optional number of variations to generate (must be between 1 and 5, default 3) + /// - cfgScale: Optional classifier free guidance scale to control prompt adherence + /// - seed: Optional seed for reproducible variation generation + /// - quality: Optional parameter to control the quality of generated variations + /// - resolution: Optional parameter to specify the desired image resolution + /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + /// BedrockServiceError.invalidParameter for invalid parameters + /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + /// BedrockServiceError.invalidModality for invalid modality from the selected model + /// BedrockServiceError.invalidSDKResponse if the response body is missing + /// - Returns: An ImageGenerationOutput object containing an array of generated image variations + public func generateImageVariation( + images: [String], + prompt: String, + with model: BedrockModel, + negativePrompt: String? = nil, + similarity: Double? = nil, + nrOfImages: Int? = nil, + cfgScale: Double? = nil, + seed: Int? = nil, + quality: ImageQuality? = nil, + resolution: ImageResolution? = nil + ) async throws -> ImageGenerationOutput { + logger.trace( + "Generating image(s) from reference image", + metadata: [ + "model.id": .string(model.id), + "model.modality": .string(model.modality.getName()), + "prompt": .string(prompt), + "nrOfImages": .stringConvertible(nrOfImages ?? "not defined"), + "similarity": .stringConvertible(similarity ?? "not defined"), + "negativePrompt": .stringConvertible(negativePrompt ?? "not defined"), + "cfgScale": .stringConvertible(cfgScale ?? "not defined"), + "seed": .stringConvertible(seed ?? "not defined"), + ] + ) + do { + let modality = try model.getImageModality() + try validateImageGenerationParams( + modality: modality, + nrOfImages: nrOfImages, + cfgScale: cfgScale, + resolution: resolution, + seed: seed + ) + let imageVariationModality = try model.getImageVariationModality() + try validateImageVariationParams( + modality: imageVariationModality, + images: images, + prompt: prompt, + similarity: similarity, + negativePrompt: negativePrompt + ) + let request: InvokeModelRequest = try InvokeModelRequest.createImageVariationRequest( + model: model, + prompt: prompt, + negativeText: negativePrompt, + images: images, + similarity: similarity, + nrOfImages: nrOfImages, + cfgScale: cfgScale, + seed: seed, + quality: quality, + resolution: resolution + ) + let input: InvokeModelInput = try request.getInvokeModelInput() + logger.trace( + "Sending request to invokeModel", + metadata: [ + "model": .string(model.id), "request": .string(String(describing: input)), + ] + ) + let response = try await self.bedrockRuntimeClient.invokeModel(input: input) + guard let responseBody = response.body else { + logger.trace( + "Invalid response", + metadata: [ + "response": .string(String(describing: response)), + "hasBody": .stringConvertible(response.body != nil), + ] + ) + throw BedrockServiceError.invalidSDKResponse( + "Something went wrong while extracting body from response." + ) + } + let invokemodelResponse: InvokeModelResponse = try InvokeModelResponse.createImageResponse( + body: responseBody, + model: model + ) + return try invokemodelResponse.getGeneratedImage() + } catch { + logger.trace("Error while generating image variations", metadata: ["error": "\(error)"]) + throw error + } + } +} diff --git a/backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelText.swift b/backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelText.swift new file mode 100644 index 00000000..cc92285c --- /dev/null +++ b/backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelText.swift @@ -0,0 +1,136 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +@preconcurrency import AWSBedrockRuntime +import BedrockTypes +import Foundation + +extension BedrockService { + + /// Generates a text completion using a specified model + /// - Parameters: + /// - prompt: The text to be completed + /// - model: The BedrockModel that will be used to generate the completion + /// - maxTokens: The maximum amount of tokens in the completion (optional, default 300) + /// - temperature: The temperature used to generate the completion (optional, default 0.6) + /// - topP: Optional top-p parameter for nucleus sampling + /// - topK: Optional top-k parameter for filtering + /// - stopSequences: Optional array of sequences where generation should stop + /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + /// BedrockServiceError.invalidParameter for invalid parameters + /// BedrockServiceError.invalidPrompt for a prompt that is empty or too long + /// BedrockServiceError.invalidStopSequences if too many stop sequences were provided + /// BedrockServiceError.invalidModality for invalid modality from the selected model + /// BedrockServiceError.invalidSDKResponse if the response body is missing + /// - Returns: a TextCompletion object containing the generated text from the model + public func completeText( + _ prompt: String, + with model: BedrockModel, + maxTokens: Int? = nil, + temperature: Double? = nil, + topP: Double? = nil, + topK: Int? = nil, + stopSequences: [String]? = nil + ) async throws -> TextCompletion { + logger.trace( + "Generating text completion", + metadata: [ + "model.id": .string(model.id), + "model.modality": .string(model.modality.getName()), + "prompt": .string(prompt), + "maxTokens": .stringConvertible(maxTokens ?? "not defined"), + "temperature": .stringConvertible(temperature ?? "not defined"), + "topP": .stringConvertible(topP ?? "not defined"), + "topK": .stringConvertible(topK ?? "not defined"), + "stopSequences": .stringConvertible(stopSequences ?? "not defined"), + ] + ) + do { + let modality = try model.getTextModality() + try validateTextCompletionParams( + modality: modality, + prompt: prompt, + maxTokens: maxTokens, + temperature: temperature, + topP: topP, + topK: topK, + stopSequences: stopSequences + ) + + logger.trace( + "Creating InvokeModelRequest", + metadata: [ + "model": .string(model.id), + "prompt": "\(prompt)", + ] + ) + let request: InvokeModelRequest = try InvokeModelRequest.createTextRequest( + model: model, + prompt: prompt, + maxTokens: maxTokens, + temperature: temperature, + topP: topP, + topK: topK, + stopSequences: stopSequences + ) + let input: InvokeModelInput = try request.getInvokeModelInput() + logger.trace( + "Sending request to invokeModel", + metadata: [ + "model": .string(model.id), "request": .string(String(describing: input)), + ] + ) + + let response = try await self.bedrockRuntimeClient.invokeModel(input: input) + logger.trace( + "Received response from invokeModel", + metadata: [ + "model": .string(model.id), "response": .string(String(describing: response)), + ] + ) + + guard let responseBody = response.body else { + logger.trace( + "Invalid response", + metadata: [ + "response": .string(String(describing: response)), + "hasBody": .stringConvertible(response.body != nil), + ] + ) + throw BedrockServiceError.invalidSDKResponse( + "Something went wrong while extracting body from response." + ) + } + if let bodyString = String(data: responseBody, encoding: .utf8) { + logger.trace("Extracted body from response", metadata: ["response.body": "\(bodyString)"]) + } + + let invokemodelResponse: InvokeModelResponse = try InvokeModelResponse.createTextResponse( + body: responseBody, + model: model + ) + logger.trace( + "Generated text completion", + metadata: [ + "model": .string(model.id), "response": .string(String(describing: invokemodelResponse)), + ] + ) + return try invokemodelResponse.getTextCompletion() + } catch { + logger.trace("Error while completing text", metadata: ["error": "\(error)"]) + throw error + } + } +} From 453e0970464d8eeb158b0748654cf499c7038505 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 7 Apr 2025 19:58:01 +0200 Subject: [PATCH 45/67] convenience initializers for ToolResultBlock --- README.md | 2 +- .../Converse/ToolResultBlock.swift | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5491c35e..a2e0098b 100644 --- a/README.md +++ b/README.md @@ -329,7 +329,7 @@ if case .toolUse(let toolUse) = history.last?.content.last { // Logic to use the tool here - let toolResult = ToolResultBlock(id: id, content: [.text("The Best Song Ever")], status: .success) + let toolResult = ToolResultBlock("The Best Song Ever", id: id) // Send the toolResult back to the model (reply, history) = try await bedrock.converse( diff --git a/backend/Sources/BedrockTypes/Converse/ToolResultBlock.swift b/backend/Sources/BedrockTypes/Converse/ToolResultBlock.swift index d615c893..a54757bb 100644 --- a/backend/Sources/BedrockTypes/Converse/ToolResultBlock.swift +++ b/backend/Sources/BedrockTypes/Converse/ToolResultBlock.swift @@ -27,6 +27,36 @@ public struct ToolResultBlock: Codable { self.status = status } + /// convenience initializer for ToolResultBlock with only an id and a String + public init(_ prompt: String, id: String) { + self.init(id: id, content: [.text(prompt)], status: .success) + } + + /// convenience initializer for ToolResultBlock with only an id and a JSON + public init(_ json: JSON, id: String) { + self.init(id: id, content: [.json(json)], status: .success) + } + + /// convenience initializer for ToolResultBlock with only an id and a ImageBlock + public init(_ image: ImageBlock, id: String) { + self.init(id: id, content: [.image(image)], status: .success) + } + + /// convenience initializer for ToolResultBlock with only an id and a DocumentBlock + public init(_ document: DocumentBlock, id: String) { + self.init(id: id, content: [.document(document)], status: .success) + } + + /// convenience initializer for ToolResultBlock with only an id and a VideoBlock + public init(_ video: VideoBlock, id: String) { + self.init(id: id, content: [.video(video)], status: .success) + } + + /// convenience initializer for ToolResultBlock with failed request + public static func failed(_ id: String) -> Self { + self.init(id: id, content: [], status: .error) + } + public init(from sdkToolResultBlock: BedrockRuntimeClientTypes.ToolResultBlock) throws { guard let sdkToolResultContent = sdkToolResultBlock.content else { throw BedrockServiceError.decodingError( From b131b2284e4437e0b43877a12898170de6dc8b14 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 7 Apr 2025 20:14:18 +0200 Subject: [PATCH 46/67] getToolUse --- .../Sources/BedrockService/Converse/ConverseResponse.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/Sources/BedrockService/Converse/ConverseResponse.swift b/backend/Sources/BedrockService/Converse/ConverseResponse.swift index 791c68ff..1189e3f0 100644 --- a/backend/Sources/BedrockService/Converse/ConverseResponse.swift +++ b/backend/Sources/BedrockService/Converse/ConverseResponse.swift @@ -39,4 +39,10 @@ public struct ConverseResponse { return "Not found" // FIXME } } + + func getToolUse() -> ToolUseBlock? { + if case .toolUse(let toolUse) = history.last?.content.last { + return toolUse + } + } } From 0a395939c4b92fa71604e0fb0148eef5da6e7c69 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 7 Apr 2025 21:04:58 +0200 Subject: [PATCH 47/67] convenience converse functions --- .../Converse/BedrockService+Converse.swift | 353 ++++++++++++++++++ .../Converse/ConverseResponse.swift | 3 +- 2 files changed, 355 insertions(+), 1 deletion(-) diff --git a/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift b/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift index 9f5d45d8..9acb650d 100644 --- a/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift +++ b/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift @@ -219,4 +219,357 @@ extension BedrockService { throw error } } + + // MARK: convenience functions to avoid arrays + + /// Use Converse API without needing to make Messages + /// - Parameters: + /// - model: The BedrockModel to converse with + /// - prompt: Optional text prompt for the conversation + /// - imageFormat: Optional format for image input + /// - imageBytes: Optional base64 encoded image data + /// - history: Optional array of previous messages + /// - maxTokens: Optional maximum number of tokens to generate + /// - temperature: Optional temperature parameter for controlling randomness + /// - topP: Optional top-p parameter for nucleus sampling + /// - stopSequences: Optional array of sequences where generation should stop + /// - systemPrompts: Optional array of system prompts to guide the conversation + /// - tool: Optional tool the model can use + /// - toolResult: Optional result from a previous tool invocation + /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + /// BedrockServiceError.invalidParameter for invalid parameters + /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + /// BedrockServiceError.invalidModality for invalid modality from the selected model + /// BedrockServiceError.invalidSDKResponse if the response body is missing + /// - Returns: Tuple containing the model's response text and updated message history + public func converse( + with model: BedrockModel, + prompt: String? = nil, + imageFormat: ImageBlock.Format? = nil, + imageBytes: String? = nil, + history: [Message] = [], + maxTokens: Int? = nil, + temperature: Double? = nil, + topP: Double? = nil, + stopSequences: [String]? = nil, + systemPrompts: [String]? = nil, + tool: Tool? = nil, + toolResult: ToolResultBlock? = nil + ) async throws -> (String, [Message]) { + converse( + with: model, + prompt: prompt, + imageFormat: imageFormat, + imageBytes: imageBytes, + history: history, + maxTokens: maxTokens, + temperature: temperature, + topP: topP, + stopSequences: stopSequences, + systemPrompts: systemPrompts, + tools: tool != nil ? [tool!] : nil, + toolResult: toolResult + ) + } + + /// Use Converse API without needing to make Messages + /// - Parameters: + /// - model: The BedrockModel to converse with + /// - prompt: Optional text prompt for the conversation + /// - imageFormat: Optional format for image input + /// - imageBytes: Optional base64 encoded image data + /// - history: Optional array of previous messages + /// - maxTokens: Optional maximum number of tokens to generate + /// - temperature: Optional temperature parameter for controlling randomness + /// - topP: Optional top-p parameter for nucleus sampling + /// - stopSequence: Optional sequence where generation should stop + /// - systemPrompts: Optional array of system prompts to guide the conversation + /// - tool: Optional tool the model can use + /// - toolResult: Optional result from a previous tool invocation + /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + /// BedrockServiceError.invalidParameter for invalid parameters + /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + /// BedrockServiceError.invalidModality for invalid modality from the selected model + /// BedrockServiceError.invalidSDKResponse if the response body is missing + /// - Returns: Tuple containing the model's response text and updated message history + public func converse( + with model: BedrockModel, + prompt: String? = nil, + imageFormat: ImageBlock.Format? = nil, + imageBytes: String? = nil, + history: [Message] = [], + maxTokens: Int? = nil, + temperature: Double? = nil, + topP: Double? = nil, + stopSequence: String? = nil, + systemPrompts: [String]? = nil, + tool: Tool? = nil, + toolResult: ToolResultBlock? = nil + ) async throws -> (String, [Message]) { + converse( + with: model, + prompt: prompt, + imageFormat: imageFormat, + imageBytes: imageBytes, + history: history, + maxTokens: maxTokens, + temperature: temperature, + topP: topP, + stopSequences: stopSequence != nil ? [stopSequence!] : nil, + systemPrompts: systemPrompts, + tools: tool != nil ? [tool!] : nil, + toolResult: toolResult + ) + } + + /// Use Converse API without needing to make Messages + /// - Parameters: + /// - model: The BedrockModel to converse with + /// - prompt: Optional text prompt for the conversation + /// - imageFormat: Optional format for image input + /// - imageBytes: Optional base64 encoded image data + /// - history: Optional array of previous messages + /// - maxTokens: Optional maximum number of tokens to generate + /// - temperature: Optional temperature parameter for controlling randomness + /// - topP: Optional top-p parameter for nucleus sampling + /// - stopSequences: Optional array of sequences where generation should stop + /// - systemPrompt: Optional system prompt to guide the conversation + /// - tool: Optional tool the model can use + /// - toolResult: Optional result from a previous tool invocation + /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + /// BedrockServiceError.invalidParameter for invalid parameters + /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + /// BedrockServiceError.invalidModality for invalid modality from the selected model + /// BedrockServiceError.invalidSDKResponse if the response body is missing + /// - Returns: Tuple containing the model's response text and updated message history + public func converse( + with model: BedrockModel, + prompt: String? = nil, + imageFormat: ImageBlock.Format? = nil, + imageBytes: String? = nil, + history: [Message] = [], + maxTokens: Int? = nil, + temperature: Double? = nil, + topP: Double? = nil, + stopSequences: [String]? = nil, + systemPrompt: String? = nil, + tool: Tool? = nil, + toolResult: ToolResultBlock? = nil + ) async throws -> (String, [Message]) { + converse( + with: model, + prompt: prompt, + imageFormat: imageFormat, + imageBytes: imageBytes, + history: history, + maxTokens: maxTokens, + temperature: temperature, + topP: topP, + stopSequences: stopSequences, + systemPrompts: systemPrompt != nil ? [systemPrompt!] : nil, + tools: tool != nil ? [tool!] : nil, + toolResult: toolResult + ) + } + + /// Use Converse API without needing to make Messages + /// - Parameters: + /// - model: The BedrockModel to converse with + /// - prompt: Optional text prompt for the conversation + /// - imageFormat: Optional format for image input + /// - imageBytes: Optional base64 encoded image data + /// - history: Optional array of previous messages + /// - maxTokens: Optional maximum number of tokens to generate + /// - temperature: Optional temperature parameter for controlling randomness + /// - topP: Optional top-p parameter for nucleus sampling + /// - stopSequence: Optional sequence where generation should stop + /// - systemPrompt: Optional system prompt to guide the conversation + /// - tool: Optional tool the model can use + /// - toolResult: Optional result from a previous tool invocation + /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + /// BedrockServiceError.invalidParameter for invalid parameters + /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + /// BedrockServiceError.invalidModality for invalid modality from the selected model + /// BedrockServiceError.invalidSDKResponse if the response body is missing + /// - Returns: Tuple containing the model's response text and updated message history + public func converse( + with model: BedrockModel, + prompt: String? = nil, + imageFormat: ImageBlock.Format? = nil, + imageBytes: String? = nil, + history: [Message] = [], + maxTokens: Int? = nil, + temperature: Double? = nil, + topP: Double? = nil, + stopSequence: String? = nil, + systemPrompt: String? = nil, + tool: Tool? = nil, + toolResult: ToolResultBlock? = nil + ) async throws -> (String, [Message]) { + converse( + with: model, + prompt: prompt, + imageFormat: imageFormat, + imageBytes: imageBytes, + history: history, + maxTokens: maxTokens, + temperature: temperature, + topP: topP, + stopSequences: stopSequence != nil ? [stopSequence!] : nil, + systemPrompts: systemPrompt != nil ? [systemPrompt!] : nil, + tools: tool != nil ? [tool!] : nil, + toolResult: toolResult + ) + } + + /// Use Converse API without needing to make Messages + /// - Parameters: + /// - model: The BedrockModel to converse with + /// - prompt: Optional text prompt for the conversation + /// - imageFormat: Optional format for image input + /// - imageBytes: Optional base64 encoded image data + /// - history: Optional array of previous messages + /// - maxTokens: Optional maximum number of tokens to generate + /// - temperature: Optional temperature parameter for controlling randomness + /// - topP: Optional top-p parameter for nucleus sampling + /// - stopSequences: Optional array of sequences where generation should stop + /// - systemPrompt: Optional system prompt to guide the conversation + /// - tools: Optional array of tools the model can use + /// - toolResult: Optional result from a previous tool invocation + /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + /// BedrockServiceError.invalidParameter for invalid parameters + /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + /// BedrockServiceError.invalidModality for invalid modality from the selected model + /// BedrockServiceError.invalidSDKResponse if the response body is missing + /// - Returns: Tuple containing the model's response text and updated message history + public func converse( + with model: BedrockModel, + prompt: String? = nil, + imageFormat: ImageBlock.Format? = nil, + imageBytes: String? = nil, + history: [Message] = [], + maxTokens: Int? = nil, + temperature: Double? = nil, + topP: Double? = nil, + stopSequences: [String]? = nil, + systemPrompt: String? = nil, + tools: [Tool]? = nil, + toolResult: ToolResultBlock? = nil + ) async throws -> (String, [Message]) { + converse( + with: model, + prompt: prompt, + imageFormat: imageFormat, + imageBytes: imageBytes, + history: history, + maxTokens: maxTokens, + temperature: temperature, + topP: topP, + stopSequences: stopSequences, + systemPrompts: systemPrompt != nil ? [systemPrompt!] : nil, + tools: tools, + toolResult: toolResult + ) + } + + /// Use Converse API without needing to make Messages + /// - Parameters: + /// - model: The BedrockModel to converse with + /// - prompt: Optional text prompt for the conversation + /// - imageFormat: Optional format for image input + /// - imageBytes: Optional base64 encoded image data + /// - history: Optional array of previous messages + /// - maxTokens: Optional maximum number of tokens to generate + /// - temperature: Optional temperature parameter for controlling randomness + /// - topP: Optional top-p parameter for nucleus sampling + /// - stopSequence: Optional sequence where generation should stop + /// - systemPrompts: Optional array of system prompts to guide the conversation + /// - tools: Optional array of tools the model can use + /// - toolResult: Optional result from a previous tool invocation + /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + /// BedrockServiceError.invalidParameter for invalid parameters + /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + /// BedrockServiceError.invalidModality for invalid modality from the selected model + /// BedrockServiceError.invalidSDKResponse if the response body is missing + /// - Returns: Tuple containing the model's response text and updated message history + public func converse( + with model: BedrockModel, + prompt: String? = nil, + imageFormat: ImageBlock.Format? = nil, + imageBytes: String? = nil, + history: [Message] = [], + maxTokens: Int? = nil, + temperature: Double? = nil, + topP: Double? = nil, + stopSequence: String? = nil, + systemPrompts: [String]? = nil, + tools: [Tool]? = nil, + toolResult: ToolResultBlock? = nil + ) async throws -> (String, [Message]) { + converse( + with: model, + prompt: prompt, + imageFormat: imageFormat, + imageBytes: imageBytes, + history: history, + maxTokens: maxTokens, + temperature: temperature, + topP: topP, + stopSequences: stopSequence != nil ? [stopSequence!] : nil, + systemPrompts: systemPrompts, + tools: tools, + toolResult: toolResult + ) + } + + /// Use Converse API without needing to make Messages + /// - Parameters: + /// - model: The BedrockModel to converse with + /// - prompt: Optional text prompt for the conversation + /// - imageFormat: Optional format for image input + /// - imageBytes: Optional base64 encoded image data + /// - history: Optional array of previous messages + /// - maxTokens: Optional maximum number of tokens to generate + /// - temperature: Optional temperature parameter for controlling randomness + /// - topP: Optional top-p parameter for nucleus sampling + /// - stopSequence: Optional sequence where generation should stop + /// - systemPrompt: Optional system prompt to guide the conversation + /// - tools: Optional array of tools the model can use + /// - toolResult: Optional result from a previous tool invocation + /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + /// BedrockServiceError.invalidParameter for invalid parameters + /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + /// BedrockServiceError.invalidModality for invalid modality from the selected model + /// BedrockServiceError.invalidSDKResponse if the response body is missing + /// - Returns: Tuple containing the model's response text and updated message history + public func converse( + with model: BedrockModel, + prompt: String? = nil, + imageFormat: ImageBlock.Format? = nil, + imageBytes: String? = nil, + history: [Message] = [], + maxTokens: Int? = nil, + temperature: Double? = nil, + topP: Double? = nil, + stopSequence: String? = nil, + systemPrompt: String? = nil, + tools: [Tool]? = nil, + toolResult: ToolResultBlock? = nil + ) async throws -> (String, [Message]) { + converse( + with: model, + prompt: prompt, + imageFormat: imageFormat, + imageBytes: imageBytes, + history: history, + maxTokens: maxTokens, + temperature: temperature, + topP: topP, + stopSequences: stopSequence != nil ? [stopSequence!] : nil, + systemPrompts: systemPrompt != nil ? [systemPrompt!] : nil, + tools: tools, + toolResult: toolResult + ) + } + } diff --git a/backend/Sources/BedrockService/Converse/ConverseResponse.swift b/backend/Sources/BedrockService/Converse/ConverseResponse.swift index 1189e3f0..f642c369 100644 --- a/backend/Sources/BedrockService/Converse/ConverseResponse.swift +++ b/backend/Sources/BedrockService/Converse/ConverseResponse.swift @@ -41,8 +41,9 @@ public struct ConverseResponse { } func getToolUse() -> ToolUseBlock? { - if case .toolUse(let toolUse) = history.last?.content.last { + if case .toolUse(let toolUse) = message.content.last { return toolUse } + return nil } } From 29f5054bb772f7084573887873aa0339d1610d99 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 7 Apr 2025 21:06:08 +0200 Subject: [PATCH 48/67] convenience converse functions --- .../Converse/BedrockService+Converse.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift b/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift index 9acb650d..2ce68631 100644 --- a/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift +++ b/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift @@ -256,7 +256,7 @@ extension BedrockService { tool: Tool? = nil, toolResult: ToolResultBlock? = nil ) async throws -> (String, [Message]) { - converse( + try await converse( with: model, prompt: prompt, imageFormat: imageFormat, @@ -306,7 +306,7 @@ extension BedrockService { tool: Tool? = nil, toolResult: ToolResultBlock? = nil ) async throws -> (String, [Message]) { - converse( + try await converse( with: model, prompt: prompt, imageFormat: imageFormat, @@ -356,7 +356,7 @@ extension BedrockService { tool: Tool? = nil, toolResult: ToolResultBlock? = nil ) async throws -> (String, [Message]) { - converse( + try await converse( with: model, prompt: prompt, imageFormat: imageFormat, @@ -406,7 +406,7 @@ extension BedrockService { tool: Tool? = nil, toolResult: ToolResultBlock? = nil ) async throws -> (String, [Message]) { - converse( + try await converse( with: model, prompt: prompt, imageFormat: imageFormat, @@ -456,7 +456,7 @@ extension BedrockService { tools: [Tool]? = nil, toolResult: ToolResultBlock? = nil ) async throws -> (String, [Message]) { - converse( + try await converse( with: model, prompt: prompt, imageFormat: imageFormat, @@ -506,7 +506,7 @@ extension BedrockService { tools: [Tool]? = nil, toolResult: ToolResultBlock? = nil ) async throws -> (String, [Message]) { - converse( + try await converse( with: model, prompt: prompt, imageFormat: imageFormat, @@ -556,7 +556,7 @@ extension BedrockService { tools: [Tool]? = nil, toolResult: ToolResultBlock? = nil ) async throws -> (String, [Message]) { - converse( + try await converse( with: model, prompt: prompt, imageFormat: imageFormat, From d3cdd4d27291152ff9569b35135f9942df2b94e4 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 7 Apr 2025 21:07:05 +0200 Subject: [PATCH 49/67] formatting --- backend/Package.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/Package.swift b/backend/Package.swift index e730c47a..64bceba4 100644 --- a/backend/Package.swift +++ b/backend/Package.swift @@ -9,7 +9,7 @@ let package = Package( products: [ .executable(name: "PlaygroundAPI", targets: ["PlaygroundAPI"]), .library(name: "BedrockService", targets: ["BedrockService"]), - .library(name: "BedrockTypes", targets: ["BedrockTypes"]) + .library(name: "BedrockTypes", targets: ["BedrockTypes"]), ], dependencies: [ .package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.0.0"), @@ -56,6 +56,6 @@ let package = Package( .product(name: "Testing", package: "swift-testing"), ], path: "Tests/BedrockServiceTests" - ) + ), ] ) From f9fcc715f91378301948a812a8831b18276d7f42 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Mon, 7 Apr 2025 21:10:00 +0200 Subject: [PATCH 50/67] formatting --- .../BedrockService/Converse/BedrockService+Converse.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift b/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift index 2ce68631..dd2cc057 100644 --- a/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift +++ b/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift @@ -471,7 +471,7 @@ extension BedrockService { toolResult: toolResult ) } - + /// Use Converse API without needing to make Messages /// - Parameters: /// - model: The BedrockModel to converse with @@ -521,7 +521,7 @@ extension BedrockService { toolResult: toolResult ) } - + /// Use Converse API without needing to make Messages /// - Parameters: /// - model: The BedrockModel to converse with @@ -571,5 +571,5 @@ extension BedrockService { toolResult: toolResult ) } - + } From 5d5b31846dedb3ff41b4c6f45805b5eacf68b65b Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Tue, 8 Apr 2025 11:53:11 +0200 Subject: [PATCH 51/67] Parameter tests --- backend/Package.swift | 2 +- .../Converse/BedrockService+Converse.swift | 691 +++++++++--------- .../BedrockService+InvokeModelImage.swift | 44 ++ .../BedrockServiceParameterTests.swift | 416 +++++++++++ ...eTests.swift => BedrockServiceTests.swift} | 39 +- .../Mock/MockBedrockClient.swift | 18 +- .../Mock/MockBedrockRuntimeClient.swift | 14 +- 7 files changed, 850 insertions(+), 374 deletions(-) create mode 100644 backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift rename backend/Tests/BedrockServiceTests/{SwiftBedrockServiceTests.swift => BedrockServiceTests.swift} (94%) diff --git a/backend/Package.swift b/backend/Package.swift index 64bceba4..ce0eea06 100644 --- a/backend/Package.swift +++ b/backend/Package.swift @@ -14,7 +14,7 @@ let package = Package( dependencies: [ .package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.0.0"), .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0"), - .package(url: "https://github.com/awslabs/aws-sdk-swift", from: "1.2.45"), + .package(url: "https://github.com/awslabs/aws-sdk-swift", from: "1.2.54"), .package(url: "https://github.com/smithy-lang/smithy-swift", from: "0.118.0"), .package(url: "https://github.com/swiftlang/swift-testing", branch: "main"), .package(url: "https://github.com/apple/swift-log.git", from: "1.5.3"), diff --git a/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift b/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift index dd2cc057..1ccb57b8 100644 --- a/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift +++ b/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift @@ -159,6 +159,8 @@ extension BedrockService { var messages = history let modality: ConverseModality = try model.getConverseModality() + try validateConverseParams(modality: modality, prompt: prompt) + if tools != nil || toolResult != nil { guard model.hasConverseModality(.toolUse) else { throw BedrockServiceError.invalidModality( @@ -220,356 +222,355 @@ extension BedrockService { } } - // MARK: convenience functions to avoid arrays + // // MARK: convenience functions to avoid arrays - /// Use Converse API without needing to make Messages - /// - Parameters: - /// - model: The BedrockModel to converse with - /// - prompt: Optional text prompt for the conversation - /// - imageFormat: Optional format for image input - /// - imageBytes: Optional base64 encoded image data - /// - history: Optional array of previous messages - /// - maxTokens: Optional maximum number of tokens to generate - /// - temperature: Optional temperature parameter for controlling randomness - /// - topP: Optional top-p parameter for nucleus sampling - /// - stopSequences: Optional array of sequences where generation should stop - /// - systemPrompts: Optional array of system prompts to guide the conversation - /// - tool: Optional tool the model can use - /// - toolResult: Optional result from a previous tool invocation - /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - /// BedrockServiceError.invalidParameter for invalid parameters - /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - /// BedrockServiceError.invalidModality for invalid modality from the selected model - /// BedrockServiceError.invalidSDKResponse if the response body is missing - /// - Returns: Tuple containing the model's response text and updated message history - public func converse( - with model: BedrockModel, - prompt: String? = nil, - imageFormat: ImageBlock.Format? = nil, - imageBytes: String? = nil, - history: [Message] = [], - maxTokens: Int? = nil, - temperature: Double? = nil, - topP: Double? = nil, - stopSequences: [String]? = nil, - systemPrompts: [String]? = nil, - tool: Tool? = nil, - toolResult: ToolResultBlock? = nil - ) async throws -> (String, [Message]) { - try await converse( - with: model, - prompt: prompt, - imageFormat: imageFormat, - imageBytes: imageBytes, - history: history, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - stopSequences: stopSequences, - systemPrompts: systemPrompts, - tools: tool != nil ? [tool!] : nil, - toolResult: toolResult - ) - } + // /// Use Converse API without needing to make Messages + // /// - Parameters: + // /// - model: The BedrockModel to converse with + // /// - prompt: Optional text prompt for the conversation + // /// - imageFormat: Optional format for image input + // /// - imageBytes: Optional base64 encoded image data + // /// - history: Optional array of previous messages + // /// - maxTokens: Optional maximum number of tokens to generate + // /// - temperature: Optional temperature parameter for controlling randomness + // /// - topP: Optional top-p parameter for nucleus sampling + // /// - stopSequences: Optional array of sequences where generation should stop + // /// - systemPrompts: Optional array of system prompts to guide the conversation + // /// - tool: Optional tool the model can use + // /// - toolResult: Optional result from a previous tool invocation + // /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + // /// BedrockServiceError.invalidParameter for invalid parameters + // /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + // /// BedrockServiceError.invalidModality for invalid modality from the selected model + // /// BedrockServiceError.invalidSDKResponse if the response body is missing + // /// - Returns: Tuple containing the model's response text and updated message history + // public func converse( + // with model: BedrockModel, + // prompt: String? = nil, + // imageFormat: ImageBlock.Format? = nil, + // imageBytes: String? = nil, + // history: [Message] = [], + // maxTokens: Int? = nil, + // temperature: Double? = nil, + // topP: Double? = nil, + // stopSequences: [String]? = nil, + // systemPrompts: [String]? = nil, + // tool: Tool? = nil, + // toolResult: ToolResultBlock? = nil + // ) async throws -> (String, [Message]) { + // try await converse( + // with: model, + // prompt: prompt, + // imageFormat: imageFormat, + // imageBytes: imageBytes, + // history: history, + // maxTokens: maxTokens, + // temperature: temperature, + // topP: topP, + // stopSequences: stopSequences, + // systemPrompts: systemPrompts, + // tools: tool != nil ? [tool!] : nil, + // toolResult: toolResult + // ) + // } - /// Use Converse API without needing to make Messages - /// - Parameters: - /// - model: The BedrockModel to converse with - /// - prompt: Optional text prompt for the conversation - /// - imageFormat: Optional format for image input - /// - imageBytes: Optional base64 encoded image data - /// - history: Optional array of previous messages - /// - maxTokens: Optional maximum number of tokens to generate - /// - temperature: Optional temperature parameter for controlling randomness - /// - topP: Optional top-p parameter for nucleus sampling - /// - stopSequence: Optional sequence where generation should stop - /// - systemPrompts: Optional array of system prompts to guide the conversation - /// - tool: Optional tool the model can use - /// - toolResult: Optional result from a previous tool invocation - /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - /// BedrockServiceError.invalidParameter for invalid parameters - /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - /// BedrockServiceError.invalidModality for invalid modality from the selected model - /// BedrockServiceError.invalidSDKResponse if the response body is missing - /// - Returns: Tuple containing the model's response text and updated message history - public func converse( - with model: BedrockModel, - prompt: String? = nil, - imageFormat: ImageBlock.Format? = nil, - imageBytes: String? = nil, - history: [Message] = [], - maxTokens: Int? = nil, - temperature: Double? = nil, - topP: Double? = nil, - stopSequence: String? = nil, - systemPrompts: [String]? = nil, - tool: Tool? = nil, - toolResult: ToolResultBlock? = nil - ) async throws -> (String, [Message]) { - try await converse( - with: model, - prompt: prompt, - imageFormat: imageFormat, - imageBytes: imageBytes, - history: history, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - stopSequences: stopSequence != nil ? [stopSequence!] : nil, - systemPrompts: systemPrompts, - tools: tool != nil ? [tool!] : nil, - toolResult: toolResult - ) - } + // /// Use Converse API without needing to make Messages + // /// - Parameters: + // /// - model: The BedrockModel to converse with + // /// - prompt: Optional text prompt for the conversation + // /// - imageFormat: Optional format for image input + // /// - imageBytes: Optional base64 encoded image data + // /// - history: Optional array of previous messages + // /// - maxTokens: Optional maximum number of tokens to generate + // /// - temperature: Optional temperature parameter for controlling randomness + // /// - topP: Optional top-p parameter for nucleus sampling + // /// - stopSequence: Optional sequence where generation should stop + // /// - systemPrompts: Optional array of system prompts to guide the conversation + // /// - tool: Optional tool the model can use + // /// - toolResult: Optional result from a previous tool invocation + // /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + // /// BedrockServiceError.invalidParameter for invalid parameters + // /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + // /// BedrockServiceError.invalidModality for invalid modality from the selected model + // /// BedrockServiceError.invalidSDKResponse if the response body is missing + // /// - Returns: Tuple containing the model's response text and updated message history + // public func converse( + // with model: BedrockModel, + // prompt: String? = nil, + // imageFormat: ImageBlock.Format? = nil, + // imageBytes: String? = nil, + // history: [Message] = [], + // maxTokens: Int? = nil, + // temperature: Double? = nil, + // topP: Double? = nil, + // stopSequence: String? = nil, + // systemPrompts: [String]? = nil, + // tool: Tool? = nil, + // toolResult: ToolResultBlock? = nil + // ) async throws -> (String, [Message]) { + // try await converse( + // with: model, + // prompt: prompt, + // imageFormat: imageFormat, + // imageBytes: imageBytes, + // history: history, + // maxTokens: maxTokens, + // temperature: temperature, + // topP: topP, + // stopSequences: stopSequence != nil ? [stopSequence!] : nil, + // systemPrompts: systemPrompts, + // tools: tool != nil ? [tool!] : nil, + // toolResult: toolResult + // ) + // } - /// Use Converse API without needing to make Messages - /// - Parameters: - /// - model: The BedrockModel to converse with - /// - prompt: Optional text prompt for the conversation - /// - imageFormat: Optional format for image input - /// - imageBytes: Optional base64 encoded image data - /// - history: Optional array of previous messages - /// - maxTokens: Optional maximum number of tokens to generate - /// - temperature: Optional temperature parameter for controlling randomness - /// - topP: Optional top-p parameter for nucleus sampling - /// - stopSequences: Optional array of sequences where generation should stop - /// - systemPrompt: Optional system prompt to guide the conversation - /// - tool: Optional tool the model can use - /// - toolResult: Optional result from a previous tool invocation - /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - /// BedrockServiceError.invalidParameter for invalid parameters - /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - /// BedrockServiceError.invalidModality for invalid modality from the selected model - /// BedrockServiceError.invalidSDKResponse if the response body is missing - /// - Returns: Tuple containing the model's response text and updated message history - public func converse( - with model: BedrockModel, - prompt: String? = nil, - imageFormat: ImageBlock.Format? = nil, - imageBytes: String? = nil, - history: [Message] = [], - maxTokens: Int? = nil, - temperature: Double? = nil, - topP: Double? = nil, - stopSequences: [String]? = nil, - systemPrompt: String? = nil, - tool: Tool? = nil, - toolResult: ToolResultBlock? = nil - ) async throws -> (String, [Message]) { - try await converse( - with: model, - prompt: prompt, - imageFormat: imageFormat, - imageBytes: imageBytes, - history: history, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - stopSequences: stopSequences, - systemPrompts: systemPrompt != nil ? [systemPrompt!] : nil, - tools: tool != nil ? [tool!] : nil, - toolResult: toolResult - ) - } - - /// Use Converse API without needing to make Messages - /// - Parameters: - /// - model: The BedrockModel to converse with - /// - prompt: Optional text prompt for the conversation - /// - imageFormat: Optional format for image input - /// - imageBytes: Optional base64 encoded image data - /// - history: Optional array of previous messages - /// - maxTokens: Optional maximum number of tokens to generate - /// - temperature: Optional temperature parameter for controlling randomness - /// - topP: Optional top-p parameter for nucleus sampling - /// - stopSequence: Optional sequence where generation should stop - /// - systemPrompt: Optional system prompt to guide the conversation - /// - tool: Optional tool the model can use - /// - toolResult: Optional result from a previous tool invocation - /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - /// BedrockServiceError.invalidParameter for invalid parameters - /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - /// BedrockServiceError.invalidModality for invalid modality from the selected model - /// BedrockServiceError.invalidSDKResponse if the response body is missing - /// - Returns: Tuple containing the model's response text and updated message history - public func converse( - with model: BedrockModel, - prompt: String? = nil, - imageFormat: ImageBlock.Format? = nil, - imageBytes: String? = nil, - history: [Message] = [], - maxTokens: Int? = nil, - temperature: Double? = nil, - topP: Double? = nil, - stopSequence: String? = nil, - systemPrompt: String? = nil, - tool: Tool? = nil, - toolResult: ToolResultBlock? = nil - ) async throws -> (String, [Message]) { - try await converse( - with: model, - prompt: prompt, - imageFormat: imageFormat, - imageBytes: imageBytes, - history: history, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - stopSequences: stopSequence != nil ? [stopSequence!] : nil, - systemPrompts: systemPrompt != nil ? [systemPrompt!] : nil, - tools: tool != nil ? [tool!] : nil, - toolResult: toolResult - ) - } + // /// Use Converse API without needing to make Messages + // /// - Parameters: + // /// - model: The BedrockModel to converse with + // /// - prompt: Optional text prompt for the conversation + // /// - imageFormat: Optional format for image input + // /// - imageBytes: Optional base64 encoded image data + // /// - history: Optional array of previous messages + // /// - maxTokens: Optional maximum number of tokens to generate + // /// - temperature: Optional temperature parameter for controlling randomness + // /// - topP: Optional top-p parameter for nucleus sampling + // /// - stopSequences: Optional array of sequences where generation should stop + // /// - systemPrompt: Optional system prompt to guide the conversation + // /// - tool: Optional tool the model can use + // /// - toolResult: Optional result from a previous tool invocation + // /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + // /// BedrockServiceError.invalidParameter for invalid parameters + // /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + // /// BedrockServiceError.invalidModality for invalid modality from the selected model + // /// BedrockServiceError.invalidSDKResponse if the response body is missing + // /// - Returns: Tuple containing the model's response text and updated message history + // public func converse( + // with model: BedrockModel, + // prompt: String? = nil, + // imageFormat: ImageBlock.Format? = nil, + // imageBytes: String? = nil, + // history: [Message] = [], + // maxTokens: Int? = nil, + // temperature: Double? = nil, + // topP: Double? = nil, + // stopSequences: [String]? = nil, + // systemPrompt: String? = nil, + // tool: Tool? = nil, + // toolResult: ToolResultBlock? = nil + // ) async throws -> (String, [Message]) { + // try await converse( + // with: model, + // prompt: prompt, + // imageFormat: imageFormat, + // imageBytes: imageBytes, + // history: history, + // maxTokens: maxTokens, + // temperature: temperature, + // topP: topP, + // stopSequences: stopSequences, + // systemPrompts: systemPrompt != nil ? [systemPrompt!] : nil, + // tools: tool != nil ? [tool!] : nil, + // toolResult: toolResult + // ) + // } - /// Use Converse API without needing to make Messages - /// - Parameters: - /// - model: The BedrockModel to converse with - /// - prompt: Optional text prompt for the conversation - /// - imageFormat: Optional format for image input - /// - imageBytes: Optional base64 encoded image data - /// - history: Optional array of previous messages - /// - maxTokens: Optional maximum number of tokens to generate - /// - temperature: Optional temperature parameter for controlling randomness - /// - topP: Optional top-p parameter for nucleus sampling - /// - stopSequences: Optional array of sequences where generation should stop - /// - systemPrompt: Optional system prompt to guide the conversation - /// - tools: Optional array of tools the model can use - /// - toolResult: Optional result from a previous tool invocation - /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - /// BedrockServiceError.invalidParameter for invalid parameters - /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - /// BedrockServiceError.invalidModality for invalid modality from the selected model - /// BedrockServiceError.invalidSDKResponse if the response body is missing - /// - Returns: Tuple containing the model's response text and updated message history - public func converse( - with model: BedrockModel, - prompt: String? = nil, - imageFormat: ImageBlock.Format? = nil, - imageBytes: String? = nil, - history: [Message] = [], - maxTokens: Int? = nil, - temperature: Double? = nil, - topP: Double? = nil, - stopSequences: [String]? = nil, - systemPrompt: String? = nil, - tools: [Tool]? = nil, - toolResult: ToolResultBlock? = nil - ) async throws -> (String, [Message]) { - try await converse( - with: model, - prompt: prompt, - imageFormat: imageFormat, - imageBytes: imageBytes, - history: history, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - stopSequences: stopSequences, - systemPrompts: systemPrompt != nil ? [systemPrompt!] : nil, - tools: tools, - toolResult: toolResult - ) - } + // /// Use Converse API without needing to make Messages + // /// - Parameters: + // /// - model: The BedrockModel to converse with + // /// - prompt: Optional text prompt for the conversation + // /// - imageFormat: Optional format for image input + // /// - imageBytes: Optional base64 encoded image data + // /// - history: Optional array of previous messages + // /// - maxTokens: Optional maximum number of tokens to generate + // /// - temperature: Optional temperature parameter for controlling randomness + // /// - topP: Optional top-p parameter for nucleus sampling + // /// - stopSequence: Optional sequence where generation should stop + // /// - systemPrompt: Optional system prompt to guide the conversation + // /// - tool: Optional tool the model can use + // /// - toolResult: Optional result from a previous tool invocation + // /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + // /// BedrockServiceError.invalidParameter for invalid parameters + // /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + // /// BedrockServiceError.invalidModality for invalid modality from the selected model + // /// BedrockServiceError.invalidSDKResponse if the response body is missing + // /// - Returns: Tuple containing the model's response text and updated message history + // public func converse( + // with model: BedrockModel, + // prompt: String? = nil, + // imageFormat: ImageBlock.Format? = nil, + // imageBytes: String? = nil, + // history: [Message] = [], + // maxTokens: Int? = nil, + // temperature: Double? = nil, + // topP: Double? = nil, + // stopSequence: String? = nil, + // systemPrompt: String? = nil, + // tool: Tool? = nil, + // toolResult: ToolResultBlock? = nil + // ) async throws -> (String, [Message]) { + // try await converse( + // with: model, + // prompt: prompt, + // imageFormat: imageFormat, + // imageBytes: imageBytes, + // history: history, + // maxTokens: maxTokens, + // temperature: temperature, + // topP: topP, + // stopSequences: stopSequence != nil ? [stopSequence!] : nil, + // systemPrompts: systemPrompt != nil ? [systemPrompt!] : nil, + // tools: tool != nil ? [tool!] : nil, + // toolResult: toolResult + // ) + // } - /// Use Converse API without needing to make Messages - /// - Parameters: - /// - model: The BedrockModel to converse with - /// - prompt: Optional text prompt for the conversation - /// - imageFormat: Optional format for image input - /// - imageBytes: Optional base64 encoded image data - /// - history: Optional array of previous messages - /// - maxTokens: Optional maximum number of tokens to generate - /// - temperature: Optional temperature parameter for controlling randomness - /// - topP: Optional top-p parameter for nucleus sampling - /// - stopSequence: Optional sequence where generation should stop - /// - systemPrompts: Optional array of system prompts to guide the conversation - /// - tools: Optional array of tools the model can use - /// - toolResult: Optional result from a previous tool invocation - /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - /// BedrockServiceError.invalidParameter for invalid parameters - /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - /// BedrockServiceError.invalidModality for invalid modality from the selected model - /// BedrockServiceError.invalidSDKResponse if the response body is missing - /// - Returns: Tuple containing the model's response text and updated message history - public func converse( - with model: BedrockModel, - prompt: String? = nil, - imageFormat: ImageBlock.Format? = nil, - imageBytes: String? = nil, - history: [Message] = [], - maxTokens: Int? = nil, - temperature: Double? = nil, - topP: Double? = nil, - stopSequence: String? = nil, - systemPrompts: [String]? = nil, - tools: [Tool]? = nil, - toolResult: ToolResultBlock? = nil - ) async throws -> (String, [Message]) { - try await converse( - with: model, - prompt: prompt, - imageFormat: imageFormat, - imageBytes: imageBytes, - history: history, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - stopSequences: stopSequence != nil ? [stopSequence!] : nil, - systemPrompts: systemPrompts, - tools: tools, - toolResult: toolResult - ) - } + // /// Use Converse API without needing to make Messages + // /// - Parameters: + // /// - model: The BedrockModel to converse with + // /// - prompt: Optional text prompt for the conversation + // /// - imageFormat: Optional format for image input + // /// - imageBytes: Optional base64 encoded image data + // /// - history: Optional array of previous messages + // /// - maxTokens: Optional maximum number of tokens to generate + // /// - temperature: Optional temperature parameter for controlling randomness + // /// - topP: Optional top-p parameter for nucleus sampling + // /// - stopSequences: Optional array of sequences where generation should stop + // /// - systemPrompt: Optional system prompt to guide the conversation + // /// - tools: Optional array of tools the model can use + // /// - toolResult: Optional result from a previous tool invocation + // /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + // /// BedrockServiceError.invalidParameter for invalid parameters + // /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + // /// BedrockServiceError.invalidModality for invalid modality from the selected model + // /// BedrockServiceError.invalidSDKResponse if the response body is missing + // /// - Returns: Tuple containing the model's response text and updated message history + // public func converse( + // with model: BedrockModel, + // prompt: String? = nil, + // imageFormat: ImageBlock.Format? = nil, + // imageBytes: String? = nil, + // history: [Message] = [], + // maxTokens: Int? = nil, + // temperature: Double? = nil, + // topP: Double? = nil, + // stopSequences: [String]? = nil, + // systemPrompt: String? = nil, + // tools: [Tool]? = nil, + // toolResult: ToolResultBlock? = nil + // ) async throws -> (String, [Message]) { + // try await converse( + // with: model, + // prompt: prompt, + // imageFormat: imageFormat, + // imageBytes: imageBytes, + // history: history, + // maxTokens: maxTokens, + // temperature: temperature, + // topP: topP, + // stopSequences: stopSequences, + // systemPrompts: systemPrompt != nil ? [systemPrompt!] : nil, + // tools: tools, + // toolResult: toolResult + // ) + // } - /// Use Converse API without needing to make Messages - /// - Parameters: - /// - model: The BedrockModel to converse with - /// - prompt: Optional text prompt for the conversation - /// - imageFormat: Optional format for image input - /// - imageBytes: Optional base64 encoded image data - /// - history: Optional array of previous messages - /// - maxTokens: Optional maximum number of tokens to generate - /// - temperature: Optional temperature parameter for controlling randomness - /// - topP: Optional top-p parameter for nucleus sampling - /// - stopSequence: Optional sequence where generation should stop - /// - systemPrompt: Optional system prompt to guide the conversation - /// - tools: Optional array of tools the model can use - /// - toolResult: Optional result from a previous tool invocation - /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - /// BedrockServiceError.invalidParameter for invalid parameters - /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - /// BedrockServiceError.invalidModality for invalid modality from the selected model - /// BedrockServiceError.invalidSDKResponse if the response body is missing - /// - Returns: Tuple containing the model's response text and updated message history - public func converse( - with model: BedrockModel, - prompt: String? = nil, - imageFormat: ImageBlock.Format? = nil, - imageBytes: String? = nil, - history: [Message] = [], - maxTokens: Int? = nil, - temperature: Double? = nil, - topP: Double? = nil, - stopSequence: String? = nil, - systemPrompt: String? = nil, - tools: [Tool]? = nil, - toolResult: ToolResultBlock? = nil - ) async throws -> (String, [Message]) { - try await converse( - with: model, - prompt: prompt, - imageFormat: imageFormat, - imageBytes: imageBytes, - history: history, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - stopSequences: stopSequence != nil ? [stopSequence!] : nil, - systemPrompts: systemPrompt != nil ? [systemPrompt!] : nil, - tools: tools, - toolResult: toolResult - ) - } + // /// Use Converse API without needing to make Messages + // /// - Parameters: + // /// - model: The BedrockModel to converse with + // /// - prompt: Optional text prompt for the conversation + // /// - imageFormat: Optional format for image input + // /// - imageBytes: Optional base64 encoded image data + // /// - history: Optional array of previous messages + // /// - maxTokens: Optional maximum number of tokens to generate + // /// - temperature: Optional temperature parameter for controlling randomness + // /// - topP: Optional top-p parameter for nucleus sampling + // /// - stopSequence: Optional sequence where generation should stop + // /// - systemPrompts: Optional array of system prompts to guide the conversation + // /// - tools: Optional array of tools the model can use + // /// - toolResult: Optional result from a previous tool invocation + // /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + // /// BedrockServiceError.invalidParameter for invalid parameters + // /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + // /// BedrockServiceError.invalidModality for invalid modality from the selected model + // /// BedrockServiceError.invalidSDKResponse if the response body is missing + // /// - Returns: Tuple containing the model's response text and updated message history + // public func converse( + // with model: BedrockModel, + // prompt: String? = nil, + // imageFormat: ImageBlock.Format? = nil, + // imageBytes: String? = nil, + // history: [Message] = [], + // maxTokens: Int? = nil, + // temperature: Double? = nil, + // topP: Double? = nil, + // stopSequence: String? = nil, + // systemPrompts: [String]? = nil, + // tools: [Tool]? = nil, + // toolResult: ToolResultBlock? = nil + // ) async throws -> (String, [Message]) { + // try await converse( + // with: model, + // prompt: prompt, + // imageFormat: imageFormat, + // imageBytes: imageBytes, + // history: history, + // maxTokens: maxTokens, + // temperature: temperature, + // topP: topP, + // stopSequences: stopSequence != nil ? [stopSequence!] : nil, + // systemPrompts: systemPrompts, + // tools: tools, + // toolResult: toolResult + // ) + // } + // /// Use Converse API without needing to make Messages + // /// - Parameters: + // /// - model: The BedrockModel to converse with + // /// - prompt: Optional text prompt for the conversation + // /// - imageFormat: Optional format for image input + // /// - imageBytes: Optional base64 encoded image data + // /// - history: Optional array of previous messages + // /// - maxTokens: Optional maximum number of tokens to generate + // /// - temperature: Optional temperature parameter for controlling randomness + // /// - topP: Optional top-p parameter for nucleus sampling + // /// - stopSequence: Optional sequence where generation should stop + // /// - systemPrompt: Optional system prompt to guide the conversation + // /// - tools: Optional array of tools the model can use + // /// - toolResult: Optional result from a previous tool invocation + // /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + // /// BedrockServiceError.invalidParameter for invalid parameters + // /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + // /// BedrockServiceError.invalidModality for invalid modality from the selected model + // /// BedrockServiceError.invalidSDKResponse if the response body is missing + // /// - Returns: Tuple containing the model's response text and updated message history + // public func converse( + // with model: BedrockModel, + // prompt: String? = nil, + // imageFormat: ImageBlock.Format? = nil, + // imageBytes: String? = nil, + // history: [Message] = [], + // maxTokens: Int? = nil, + // temperature: Double? = nil, + // topP: Double? = nil, + // stopSequence: String? = nil, + // systemPrompt: String? = nil, + // tools: [Tool]? = nil, + // toolResult: ToolResultBlock? = nil + // ) async throws -> (String, [Message]) { + // try await converse( + // with: model, + // prompt: prompt, + // imageFormat: imageFormat, + // imageBytes: imageBytes, + // history: history, + // maxTokens: maxTokens, + // temperature: temperature, + // topP: topP, + // stopSequences: stopSequence != nil ? [stopSequence!] : nil, + // systemPrompts: systemPrompt != nil ? [systemPrompt!] : nil, + // tools: tools, + // toolResult: toolResult + // ) + // } } diff --git a/backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelImage.swift b/backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelImage.swift index 5589bc49..6007e145 100644 --- a/backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelImage.swift +++ b/backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelImage.swift @@ -212,4 +212,48 @@ extension BedrockService { throw error } } + + /// Generates 1 to 5 image variation(s) from reference images and a text prompt using a specific model + /// - Parameters: + /// - image: A base64 encoded reference image to generate variations from + /// - prompt: The text prompt describing desired modifications to the reference images + /// - model: The BedrockModel that will be used to generate the variations + /// - negativePrompt: Optional text describing what to avoid in the generated variations + /// - similarity: Optional parameter controlling how closely variations should match reference (between 0.2 and 1.0) + /// - nrOfImages: Optional number of variations to generate (must be between 1 and 5, default 3) + /// - cfgScale: Optional classifier free guidance scale to control prompt adherence + /// - seed: Optional seed for reproducible variation generation + /// - quality: Optional parameter to control the quality of generated variations + /// - resolution: Optional parameter to specify the desired image resolution + /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported + /// BedrockServiceError.invalidParameter for invalid parameters + /// BedrockServiceError.invalidPrompt if the prompt is empty or too long + /// BedrockServiceError.invalidModality for invalid modality from the selected model + /// BedrockServiceError.invalidSDKResponse if the response body is missing + /// - Returns: An ImageGenerationOutput object containing an array of generated image variations + public func generateImageVariation( + image: String, + prompt: String, + with model: BedrockModel, + negativePrompt: String? = nil, + similarity: Double? = nil, + nrOfImages: Int? = nil, + cfgScale: Double? = nil, + seed: Int? = nil, + quality: ImageQuality? = nil, + resolution: ImageResolution? = nil + ) async throws -> ImageGenerationOutput { + try await generateImageVariation( + images: [image], + prompt: prompt, + with: model, + negativePrompt: negativePrompt, + similarity: similarity, + nrOfImages: nrOfImages, + cfgScale: cfgScale, + seed: seed, + quality: quality, + resolution: resolution + ) + } } diff --git a/backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift b/backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift new file mode 100644 index 00000000..913c1e99 --- /dev/null +++ b/backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift @@ -0,0 +1,416 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Testing + +@testable import BedrockService +@testable import BedrockTypes + +// For the parameter tests the Nova models are used as an example +@Suite("BedrockService Model Parameter Tests") +struct BedrockServiceParameterTests { + let bedrock: BedrockService + + init() async throws { + self.bedrock = try await BedrockService( + bedrockClient: MockBedrockClient(), + bedrockRuntimeClient: MockBedrockRuntimeClient() + ) + } + + // MARK: constants based on the Nova parameters + static let validTemperature = [0.00001, 0.2, 0.6, 1] + static let invalidTemperature = [-2.5, -1, 0, 1.00001, 2] + static let validMaxTokens = [1, 10, 100, 5_000] + static let invalidMaxTokens = [0, -2, 5_001] + static let validTopP = [0.00001, 0.2, 0.6, 1] + static let invalidTopP = [-2.5, -1, 0, 1.00001, 2] + static let validTopK = [0, 50] + static let invalidTopK = [-1] + static let validStopSequences = [ + ["\n\nHuman:"], + ["\n\nHuman:", "\n\nAI:"], + ["\n\nHuman:", "\n\nAI:", "\n\nHuman:"], + ] + static let validPrompts = [ + "This is a test", + "!@#$%^&*()_+{}|:<>?", + String(repeating: "test ", count: 10), + ] + static let invalidPrompts = [ + "", " ", " \n ", "\t", + ] + + static let validNrOfImages = [1, 2, 3, 4, 5] + static let invalidNrOfImages = [-4, 0, 6, 20] + static let validSimilarity = [0.2, 0.5, 1] + static let invalidSimilarity = [-4, 0, 0.1, 1.1, 2] + + static let textCompletionModels = [ + BedrockModel.nova_micro + ] + static let imageGenerationModels = [ + BedrockModel.titan_image_g1_v1, + BedrockModel.titan_image_g1_v2, + BedrockModel.nova_canvas, + ] + + // MARK: completeText + + @Test( + "Complete text using an implemented model", + arguments: textCompletionModels + ) + func completeTextWithValidModel(model: BedrockModel) async throws { + let completion: TextCompletion = try await bedrock.completeText( + "This is a test", + with: model + ) + #expect(completion.completion == "This is the textcompletion for: This is a test") + } + + @Test( + "Complete text using an invalid model", + arguments: imageGenerationModels + ) + func completeTextWithInvalidModel(model: BedrockModel) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: TextCompletion = try await bedrock.completeText( + "This is a test", + with: model, + temperature: 0.8 + ) + } + } + + @Test("Complete text using a valid temperature", arguments: validTemperature) + func completeTextWithValidTemperature(temperature: Double) async throws { + let completion: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + temperature: temperature + ) + #expect(completion.completion == "This is the textcompletion for: This is a test") + } + + @Test("Complete text using an invalid temperature", arguments: invalidTemperature) + func completeTextWithInvalidTemperature(temperature: Double) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + temperature: temperature + ) + } + } + + @Test( + "Complete text using a valid maxTokens", + arguments: validMaxTokens + ) + func completeTextWithValidMaxTokens(maxTokens: Int) async throws { + let completion: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + maxTokens: maxTokens + ) + #expect(completion.completion == "This is the textcompletion for: This is a test") + } + + @Test( + "Complete text using an invalid maxTokens", + arguments: invalidMaxTokens + ) + func completeTextWithInvalidMaxTokens(maxTokens: Int) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + maxTokens: maxTokens + ) + } + } + + @Test( + "Complete text using a valid prompt", + arguments: validPrompts + ) + func completeTextWithValidPrompt(prompt: String) async throws { + let completion: TextCompletion = try await bedrock.completeText( + prompt, + with: BedrockModel.nova_micro, + maxTokens: 200 + ) + #expect(completion.completion == "This is the textcompletion for: \(prompt)") + } + + @Test( + "Complete text using an invalid prompt", + arguments: invalidPrompts + ) + func completeTextWithInvalidPrompt(prompt: String) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: TextCompletion = try await bedrock.completeText( + prompt, + with: BedrockModel.nova_micro, + maxTokens: 10 + ) + } + } + + // MARK: generateImage + + @Test( + "Generate image using an implemented model", + arguments: imageGenerationModels + ) + func generateImageWithValidModel(model: BedrockModel) async throws { + let output: ImageGenerationOutput = try await bedrock.generateImage( + "This is a test", + with: model + ) + #expect(output.images.count == 1) + } + + @Test( + "Generate image using an implemented model", + arguments: textCompletionModels + ) + func generateImageWithInvalidModel(model: BedrockModel) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: ImageGenerationOutput = try await bedrock.generateImage( + "This is a test", + with: model, + nrOfImages: 3 + ) + } + } + + @Test( + "Generate image using a valid nrOfImages", + arguments: validNrOfImages + ) + func generateImageWithValidNrOfImages(nrOfImages: Int) async throws { + let output: ImageGenerationOutput = try await bedrock.generateImage( + "This is a test", + with: BedrockModel.nova_canvas, + nrOfImages: nrOfImages + ) + #expect(output.images.count == nrOfImages) + } + + @Test( + "Generate image using an invalid nrOfImages", + arguments: invalidNrOfImages + ) + func generateImageWithInvalidNrOfImages(nrOfImages: Int) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: ImageGenerationOutput = try await bedrock.generateImage( + "This is a test", + with: BedrockModel.nova_canvas, + nrOfImages: nrOfImages + ) + } + } + + @Test( + "Generate image using a valid prompt", + arguments: validPrompts + ) + func generateImageWithValidPrompt(prompt: String) async throws { + let output: ImageGenerationOutput = try await bedrock.generateImage( + prompt, + with: BedrockModel.nova_canvas, + nrOfImages: 3 + ) + #expect(output.images.count == 3) + } + + @Test( + "Generate image using an invalid prompt", + arguments: invalidPrompts + ) + func generateImageWithInvalidPrompt(prompt: String) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: ImageGenerationOutput = try await bedrock.generateImage( + prompt, + with: BedrockModel.nova_canvas + ) + } + } + + // MARK: generateImageVariation + + @Test( + "Generate image variation using an implemented model", + arguments: imageGenerationModels + ) + func generateImageVariationWithValidModel(model: BedrockModel) async throws { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let output: ImageGenerationOutput = try await bedrock.generateImageVariation( + image: mockBase64Image, + prompt: "This is a test", + with: model, + nrOfImages: 3 + ) + #expect(output.images.count == 3) + } + + @Test( + "Generate image variation using an invalid model", + arguments: textCompletionModels + ) + func generateImageVariationWithInvalidModel(model: BedrockModel) async throws { + await #expect(throws: BedrockServiceError.self) { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let _: ImageGenerationOutput = try await bedrock.generateImageVariation( + image: mockBase64Image, + prompt: "This is a test", + with: model, + nrOfImages: 3 + ) + } + } + + @Test( + "Generate image variation using a valid nrOfImages", + arguments: validNrOfImages + ) + func generateImageVariationWithValidNrOfImages(nrOfImages: Int) async throws { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let output: ImageGenerationOutput = try await bedrock.generateImageVariation( + image: mockBase64Image, + prompt: "This is a test", + with: BedrockModel.nova_canvas, + nrOfImages: nrOfImages + ) + #expect(output.images.count == nrOfImages) + } + + @Test( + "Generate image variation using an invalid nrOfImages", + arguments: invalidNrOfImages + ) + func generateImageVariationWithInvalidNrOfImages(nrOfImages: Int) async throws { + await #expect(throws: BedrockServiceError.self) { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let _: ImageGenerationOutput = try await bedrock.generateImageVariation( + image: mockBase64Image, + prompt: "This is a test", + with: BedrockModel.nova_canvas, + nrOfImages: nrOfImages + ) + } + } + + @Test( + "Generate image variation using a valid similarity", + arguments: validSimilarity + ) + func generateImageVariationWithValidNr(similarity: Double) async throws { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let output: ImageGenerationOutput = try await bedrock.generateImageVariation( + image: mockBase64Image, + prompt: "This is a test", + with: BedrockModel.nova_canvas, + similarity: similarity, + nrOfImages: 3 + ) + #expect(output.images.count == 3) + } + + @Test( + "Generate image variation using an invalid similarity", + arguments: invalidSimilarity + ) + func generateImageVariationWithInvalidNrOfImages(similarity: Double) async throws { + await #expect(throws: BedrockServiceError.self) { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let _: ImageGenerationOutput = try await bedrock.generateImageVariation( + image: mockBase64Image, + prompt: "This is a test", + with: BedrockModel.nova_canvas, + similarity: similarity, + nrOfImages: 3 + ) + } + } + + @Test( + "Generate image variation using a valid prompt", + arguments: validPrompts + ) + func generateImageVariationWithValidPrompt(prompt: String) async throws { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let output: ImageGenerationOutput = try await bedrock.generateImageVariation( + image: mockBase64Image, + prompt: prompt, + with: BedrockModel.nova_canvas, + similarity: 0.6 + ) + #expect(output.images.count == 1) + } + + @Test( + "Generate image variation using an invalid prompt", + arguments: invalidPrompts + ) + func generateImageVariationWithInvalidPrompt(prompt: String) async throws { + await #expect(throws: BedrockServiceError.self) { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let _: ImageGenerationOutput = try await bedrock.generateImageVariation( + image: mockBase64Image, + prompt: prompt, + with: BedrockModel.nova_canvas, + similarity: 0.6 + ) + } + } + + // MARK: converse + + @Test( + "Continue conversation using a valid prompt", + arguments: validPrompts + ) + func converseWithValidPrompt(prompt: String) async throws { + let (output, _) = try await bedrock.converse( + with: BedrockModel.nova_micro, + prompt: prompt + ) + #expect(output == "Your prompt was: \(prompt)") + } + + @Test( + "Continue conversation variation using an invalid prompt", + arguments: invalidPrompts + ) + func converseWithInvalidPrompt(prompt: String) async throws { + await #expect(throws: BedrockServiceError.self) { + let _ = try await bedrock.converse( + with: BedrockModel.nova_micro, + prompt: prompt + ) + } + } +} diff --git a/backend/Tests/BedrockServiceTests/SwiftBedrockServiceTests.swift b/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift similarity index 94% rename from backend/Tests/BedrockServiceTests/SwiftBedrockServiceTests.swift rename to backend/Tests/BedrockServiceTests/BedrockServiceTests.swift index 6d6c740a..f1fdbff4 100644 --- a/backend/Tests/BedrockServiceTests/SwiftBedrockServiceTests.swift +++ b/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift @@ -18,16 +18,12 @@ import Testing @testable import BedrockService @testable import BedrockTypes -//struct Constants { -// -//} - @Suite("SwiftBedrockService Tests") struct BedrockServiceTests { - let bedrock: SwiftBedrock + let bedrock: BedrockService init() async throws { - self.bedrock = try await SwiftBedrock( + self.bedrock = try await BedrockService( bedrockClient: MockBedrockClient(), bedrockRuntimeClient: MockBedrockRuntimeClient() ) @@ -36,8 +32,8 @@ struct BedrockServiceTests { // MARK: listModels @Test("List all models") func listModels() async throws { - let models = try await bedrock.listModels() - #expect(models.count == 2) + let models: [ModelSummary] = try await bedrock.listModels() + #expect(models.count == 3) #expect(models[0].modelId == "anthropic.claude-instant-v1") #expect(models[0].modelName == "Claude Instant") #expect(models[0].providerName == "Anthropic") @@ -58,19 +54,18 @@ struct BedrockServiceTests { String(repeating: "test ", count: 1000), ] static let invalidPrompts = [ - "", " ", " \n ", "\t", - // String(repeating: "tests", count: 5_000_001), + "", " ", " \n ", "\t" ] static let textCompletionModels = [ BedrockModel.nova_micro, - // BedrockModel.titan_text_g1_lite, - // BedrockModel.titan_text_g1_express, - // BedrockModel.titan_text_g1_premier, - BedrockModel.claudev1, - BedrockModel.claudev2, - BedrockModel.claudev2_1, - BedrockModel.claudev3_haiku, - BedrockModel.claudev3_5_haiku, + // BedrockModel.titan_text_g1_lite, + // BedrockModel.titan_text_g1_express, + // BedrockModel.titan_text_g1_premier, + // BedrockModel.claudev1, + // BedrockModel.claudev2, + // BedrockModel.claudev2_1, + // BedrockModel.claudev3_haiku, + // BedrockModel.claudev3_5_haiku, ] static let imageGenerationModels = [ BedrockModel.titan_image_g1_v1, @@ -87,9 +82,7 @@ struct BedrockServiceTests { func completeTextWithValidModel(model: BedrockModel) async throws { let completion: TextCompletion = try await bedrock.completeText( "This is a test", - with: model, - maxTokens: 100, - temperature: 0.5 + with: model ) #expect(completion.completion == "This is the textcompletion for: This is a test") } @@ -407,7 +400,7 @@ struct BedrockServiceTests { arguments: validPrompts ) func converseWithValidPrompt(prompt: String) async throws { - let output: String = try await bedrock.converse( + let (output, _) = try await bedrock.converse( with: BedrockModel.nova_micro, prompt: prompt ) @@ -420,7 +413,7 @@ struct BedrockServiceTests { ) func converseWithInvalidPrompt(prompt: String) async throws { await #expect(throws: BedrockServiceError.self) { - let _: String = try await bedrock.converse( + let _: (String, [Message]) = try await bedrock.converse( with: BedrockModel.nova_micro, prompt: prompt ) diff --git a/backend/Tests/BedrockServiceTests/Mock/MockBedrockClient.swift b/backend/Tests/BedrockServiceTests/Mock/MockBedrockClient.swift index 44fdc0a2..51d523c0 100644 --- a/backend/Tests/BedrockServiceTests/Mock/MockBedrockClient.swift +++ b/backend/Tests/BedrockServiceTests/Mock/MockBedrockClient.swift @@ -29,23 +29,39 @@ public struct MockBedrockClient: BedrockClientProtocol { -> ListFoundationModelsOutput { ListFoundationModelsOutput( + // customizationsSupported: [BedrockClientTypes.ModelCustomization]? = nil, + // inferenceTypesSupported: [BedrockClientTypes.InferenceType]? = nil, + // inputModalities: [BedrockClientTypes.ModelModality]? = nil, + // modelArn: Swift.String? = nil, + // modelId: Swift.String? = nil, + // modelLifecycle: BedrockClientTypes.FoundationModelLifecycle? = nil, + // modelName: Swift.String? = nil, + // outputModalities: [BedrockClientTypes.ModelModality]? = nil, + // providerName: Swift.String? = nil, + // responseStreamingSupported: Swift.Bool? = nil modelSummaries: [ BedrockClientTypes.FoundationModelSummary( + modelArn: "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-instant-v1", modelId: "anthropic.claude-instant-v1", + modelLifecycle: BedrockClientTypes.FoundationModelLifecycle(status: .active), modelName: "Claude Instant", providerName: "Anthropic", responseStreamingSupported: false ), BedrockClientTypes.FoundationModelSummary( + modelArn: "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-instant-v2", modelId: "anthropic.claude-instant-v2", + modelLifecycle: BedrockClientTypes.FoundationModelLifecycle(status: .active), modelName: "Claude Instant 2", providerName: "Anthropic", responseStreamingSupported: true ), BedrockClientTypes.FoundationModelSummary( + modelArn: "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-instant-v3", modelId: "unknownID", + modelLifecycle: BedrockClientTypes.FoundationModelLifecycle(status: .active), modelName: "Claude Instant 3", - providerName: nil, + providerName: "Anthropic", responseStreamingSupported: false ), ]) diff --git a/backend/Tests/BedrockServiceTests/Mock/MockBedrockRuntimeClient.swift b/backend/Tests/BedrockServiceTests/Mock/MockBedrockRuntimeClient.swift index b08dcfea..fcd6f236 100644 --- a/backend/Tests/BedrockServiceTests/Mock/MockBedrockRuntimeClient.swift +++ b/backend/Tests/BedrockServiceTests/Mock/MockBedrockRuntimeClient.swift @@ -42,20 +42,24 @@ public struct MockBedrockRuntimeClient: BedrockRuntimeClientProtocol { // MARK: invokeModel public func invokeModel(input: InvokeModelInput) async throws -> InvokeModelOutput { + print("checkpoint 1") guard let modelId = input.modelId else { throw AWSBedrockRuntime.ValidationException( message: "Malformed input request, please reformat your input and try again." ) } + print("checkpoint 2") guard let inputBody = input.body else { throw AWSBedrockRuntime.ValidationException( message: "Malformed input request, please reformat your input and try again." ) } let model: BedrockModel = BedrockModel(rawValue: modelId)! + print("checkpoint 3") switch model.modality.getName() { case "Amazon Image Generation": + print("checkpoint 3.1") return InvokeModelOutput(body: try getImageGeneration(body: inputBody)) case "Nova Text Generation": return InvokeModelOutput(body: try invokeNovaModel(body: inputBody)) @@ -65,27 +69,29 @@ public struct MockBedrockRuntimeClient: BedrockRuntimeClientProtocol { return InvokeModelOutput(body: try invokeAnthropicModel(body: inputBody)) default: throw AWSBedrockRuntime.ValidationException( - // message: "Malformed input request, please reformat your input and try again." - message: "Hier in de default! model: \(String(describing: model))" + message: "Malformed input request, please reformat your input and try again." + // message: "Hier in de default! model: \(String(describing: model))" ) } } private func getImageGeneration(body: Data) throws -> Data { + print("Checking image generation input") guard let json: [String: Any] = try? JSONSerialization.jsonObject( with: body, options: [] ) as? [String: Any], - let imageGenerationConfig = json["imageGenerationConfig"] as? [String: Any], - let nrOfImages = imageGenerationConfig["numberOfImages"] as? Int + let imageGenerationConfig = json["imageGenerationConfig"] as? [String: Any] else { throw AWSBedrockRuntime.ValidationException( message: "Malformed input request, please reformat your input and try again." ) } + print("Returning mockimage(s)") + let nrOfImages = imageGenerationConfig["numberOfImages"] as? Int ?? 1 let mockBase64Image = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" let imageArray = Array(repeating: "\"\(mockBase64Image)\"", count: nrOfImages) From 546ab7800f1639b353f65efc1e4c218fbab6d895 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Tue, 8 Apr 2025 12:58:34 +0200 Subject: [PATCH 52/67] parameter tests: text, image generation, image variation --- .../Amazon/Nova/NovaBedrockModels.swift | 2 +- .../BedrockServiceParameterTests.swift | 198 +++++++- .../BedrockServiceTests.swift | 462 +++++++++--------- 3 files changed, 419 insertions(+), 243 deletions(-) diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift index 59cdb05d..dcb1f6fd 100644 --- a/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift +++ b/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift @@ -90,7 +90,7 @@ extension BedrockModel { similarity: Parameter(.similarity, minValue: 0, maxValue: 1.0, defaultValue: 0.7) ), imageVariationParameters: ImageVariationParameters( - images: Parameter(.images, minValue: 1, maxValue: 10, defaultValue: 1), + images: Parameter(.images, minValue: 1, maxValue: 5, defaultValue: 1), maxPromptSize: 1024, maxNegativePromptSize: 1024, similarity: Parameter(.similarity, minValue: 0.2, maxValue: 1.0, defaultValue: 0.6) diff --git a/backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift b/backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift index 913c1e99..0856e76e 100644 --- a/backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift +++ b/backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift @@ -31,12 +31,13 @@ struct BedrockServiceParameterTests { } // MARK: constants based on the Nova parameters + // tText generation static let validTemperature = [0.00001, 0.2, 0.6, 1] static let invalidTemperature = [-2.5, -1, 0, 1.00001, 2] static let validMaxTokens = [1, 10, 100, 5_000] static let invalidMaxTokens = [0, -2, 5_001] - static let validTopP = [0.00001, 0.2, 0.6, 1] - static let invalidTopP = [-2.5, -1, 0, 1.00001, 2] + static let validTopP = [0, 0.2, 0.6, 1] + static let invalidTopP = [-1, 1.00001, 2] static let validTopK = [0, 50] static let invalidTopK = [-1] static let validStopSequences = [ @@ -53,13 +54,34 @@ struct BedrockServiceParameterTests { "", " ", " \n ", "\t", ] + // image generation static let validNrOfImages = [1, 2, 3, 4, 5] static let invalidNrOfImages = [-4, 0, 6, 20] + static let validCfgScale = [1.1, 6, 10] + static let invalidCfgScale = [-4, 0, 1.0, 11, 20] + static let validSeed = [0, 12, 900, 858_993_459] + static let invalidSeed = [-4, 1_000_000_000] + static let validImagePrompts = [ + "This is a test", + "!@#$%^&*()_+{}|:<>?", + String(repeating: "x", count: 1_024), + ] + static let invalidImagePrompts = [ + "", " ", " \n ", "\t", + String(repeating: "x", count: 1_025), + ] + + // image variation static let validSimilarity = [0.2, 0.5, 1] static let invalidSimilarity = [-4, 0, 0.1, 1.1, 2] + static let validNrOfReferenceImages = [1, 3, 5] + static let invalidNrOfReferenceImages = [0, 6, 10] + // models static let textCompletionModels = [ - BedrockModel.nova_micro + BedrockModel.nova_micro, + BedrockModel.nova_lite, + BedrockModel.nova_pro, ] static let imageGenerationModels = [ BedrockModel.titan_image_g1_v1, @@ -143,6 +165,73 @@ struct BedrockServiceParameterTests { } } + @Test( + "Complete text using a valid topP", + arguments: validTopP + ) + func completeTextWithValidTopP(topP: Double) async throws { + let completion: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + topP: topP + ) + #expect(completion.completion == "This is the textcompletion for: This is a test") + } + + @Test( + "Complete text using an invalid topP", + arguments: invalidTopP + ) + func completeTextWithInvalidMaxTokens(topP: Double) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + topP: topP + ) + } + } + + @Test( + "Complete text using a valid topK", + arguments: validTopK + ) + func completeTextWithValidTopK(topK: Int) async throws { + let completion: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + topK: topK + ) + #expect(completion.completion == "This is the textcompletion for: This is a test") + } + + @Test( + "Complete text using an invalid topK", + arguments: invalidTopK + ) + func completeTextWithInvalidTopK(topK: Int) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + topK: topK + ) + } + } + + @Test( + "Complete text using valid stopSequences", + arguments: validStopSequences + ) + func completeTextWithValidMaxTokens(stopSequences: [String]) async throws { + let completion: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + stopSequences: stopSequences + ) + #expect(completion.completion == "This is the textcompletion for: This is a test") + } + @Test( "Complete text using a valid prompt", arguments: validPrompts @@ -164,7 +253,7 @@ struct BedrockServiceParameterTests { await #expect(throws: BedrockServiceError.self) { let _: TextCompletion = try await bedrock.completeText( prompt, - with: BedrockModel.nova_micro, + with: BedrockModel.nova_canvas, maxTokens: 10 ) } @@ -185,7 +274,7 @@ struct BedrockServiceParameterTests { } @Test( - "Generate image using an implemented model", + "Generate image using a wrong model", arguments: textCompletionModels ) func generateImageWithInvalidModel(model: BedrockModel) async throws { @@ -225,9 +314,63 @@ struct BedrockServiceParameterTests { } } + @Test( + "Generate image using a valid cfgScale", + arguments: validCfgScale + ) + func generateImageWithValidCfgScale(cfgScale: Double) async throws { + let output: ImageGenerationOutput = try await bedrock.generateImage( + "This is a test", + with: BedrockModel.nova_canvas, + cfgScale: cfgScale + ) + #expect(output.images.count == 1) + } + + @Test( + "Generate image using an invalid cfgScale", + arguments: invalidCfgScale + ) + func generateImageWithInvalidCfgScale(cfgScale: Double) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: ImageGenerationOutput = try await bedrock.generateImage( + "This is a test", + with: BedrockModel.nova_canvas, + cfgScale: cfgScale + ) + } + } + + @Test( + "Generate image using a valid seed", + arguments: validSeed + ) + func generateImageWithValidSeed(seed: Int) async throws { + let output: ImageGenerationOutput = try await bedrock.generateImage( + "This is a test", + with: BedrockModel.nova_canvas, + seed: seed + ) + #expect(output.images.count == 1) + } + + @Test( + "Generate image using an invalid seed", + arguments: invalidSeed + ) + func generateImageWithInvalidSeed(seed: Int) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: ImageGenerationOutput = try await bedrock.generateImage( + "This is a test", + with: BedrockModel.nova_canvas, + seed: seed + ) + } + } + @Test( "Generate image using a valid prompt", - arguments: validPrompts + arguments: validImagePrompts ) func generateImageWithValidPrompt(prompt: String) async throws { let output: ImageGenerationOutput = try await bedrock.generateImage( @@ -240,7 +383,7 @@ struct BedrockServiceParameterTests { @Test( "Generate image using an invalid prompt", - arguments: invalidPrompts + arguments: invalidImagePrompts ) func generateImageWithInvalidPrompt(prompt: String) async throws { await #expect(throws: BedrockServiceError.self) { @@ -323,7 +466,7 @@ struct BedrockServiceParameterTests { "Generate image variation using a valid similarity", arguments: validSimilarity ) - func generateImageVariationWithValidNr(similarity: Double) async throws { + func generateImageVariationWithValidSimilarity(similarity: Double) async throws { let mockBase64Image = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" let output: ImageGenerationOutput = try await bedrock.generateImageVariation( @@ -340,7 +483,7 @@ struct BedrockServiceParameterTests { "Generate image variation using an invalid similarity", arguments: invalidSimilarity ) - func generateImageVariationWithInvalidNrOfImages(similarity: Double) async throws { + func generateImageVariationWithInvalidSimilarity(similarity: Double) async throws { await #expect(throws: BedrockServiceError.self) { let mockBase64Image = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" @@ -354,9 +497,42 @@ struct BedrockServiceParameterTests { } } + @Test( + "Generate image variation using a valid number of reference images", + arguments: validNrOfReferenceImages + ) + func generateImageVariationWithValidNrOfReferenceImages(nrOfReferenceImages: Int) async throws { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let mockImages = Array(repeating: mockBase64Image, count: nrOfReferenceImages) + let output: ImageGenerationOutput = try await bedrock.generateImageVariation( + images: mockImages, + prompt: "This is a test", + with: BedrockModel.nova_canvas + ) + #expect(output.images.count == 1) + } + + @Test( + "Generate image variation using an invalid number of reference images", + arguments: invalidNrOfReferenceImages + ) + func generateImageVariationWithInvalidNrOfReferenceImages(nrOfReferenceImages: Int) async throws { + await #expect(throws: BedrockServiceError.self) { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let mockImages = Array(repeating: mockBase64Image, count: nrOfReferenceImages) + let _: ImageGenerationOutput = try await bedrock.generateImageVariation( + images: mockImages, + prompt: "This is a test", + with: BedrockModel.nova_canvas + ) + } + } + @Test( "Generate image variation using a valid prompt", - arguments: validPrompts + arguments: validImagePrompts ) func generateImageVariationWithValidPrompt(prompt: String) async throws { let mockBase64Image = @@ -372,7 +548,7 @@ struct BedrockServiceParameterTests { @Test( "Generate image variation using an invalid prompt", - arguments: invalidPrompts + arguments: invalidImagePrompts ) func generateImageVariationWithInvalidPrompt(prompt: String) async throws { await #expect(throws: BedrockServiceError.self) { diff --git a/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift b/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift index f1fdbff4..337a2342 100644 --- a/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift +++ b/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift @@ -40,22 +40,22 @@ struct BedrockServiceTests { } // MARK: constants - static let validNrOfImages = [1, 2, 3, 4, 5] - static let invalidNrOfImages = [-4, 0, 6, 20] - static let validTemperature = [0, 0.2, 0.6, 1] - static let invalidTemperature = [-2.5, -1, 1.00001, 2] - static let validMaxTokens = [1, 10, 100, 5000] - static let invalidMaxTokens = [0, -1, -2] - static let validSimilarity = [0.5] - static let invalidSimilarity = [-4, 0.02, 1.1, 2] - static let validPrompts = [ - "This is a test", - "!@#$%^&*()_+{}|:<>?", - String(repeating: "test ", count: 1000), - ] - static let invalidPrompts = [ - "", " ", " \n ", "\t" - ] + // static let validNrOfImages = [1, 2, 3, 4, 5] + // static let invalidNrOfImages = [-4, 0, 6, 20] + // static let validTemperature = [0, 0.2, 0.6, 1] + // static let invalidTemperature = [-2.5, -1, 1.00001, 2] + // static let validMaxTokens = [1, 10, 100, 5000] + // static let invalidMaxTokens = [0, -1, -2] + // static let validSimilarity = [0.5] + // static let invalidSimilarity = [-4, 0.02, 1.1, 2] + // static let validPrompts = [ + // "This is a test", + // "!@#$%^&*()_+{}|:<>?", + // String(repeating: "test ", count: 1000), + // ] + // static let invalidPrompts = [ + // "", " ", " \n ", "\t" + // ] static let textCompletionModels = [ BedrockModel.nova_micro, // BedrockModel.titan_text_g1_lite, @@ -101,80 +101,80 @@ struct BedrockServiceTests { } } - @Test("Complete text using a valid temperature", arguments: validTemperature) - func completeTextWithValidTemperature(temperature: Double) async throws { - let completion: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - temperature: temperature - ) - #expect(completion.completion == "This is the textcompletion for: This is a test") - } - - @Test("Complete text using an invalid temperature", arguments: invalidTemperature) - func completeTextWithInvalidTemperature(temperature: Double) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - temperature: temperature - ) - } - } - - @Test( - "Complete text using a valid maxTokens", - arguments: validMaxTokens - ) - func completeTextWithValidMaxTokens(maxTokens: Int) async throws { - let completion: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - maxTokens: maxTokens - ) - #expect(completion.completion == "This is the textcompletion for: This is a test") - } - - @Test( - "Complete text using an invalid maxTokens", - arguments: invalidMaxTokens - ) - func completeTextWithInvalidMaxTokens(maxTokens: Int) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - maxTokens: maxTokens - ) - } - } - - @Test( - "Complete text using a valid prompt", - arguments: validPrompts - ) - func completeTextWithValidPrompt(prompt: String) async throws { - let completion: TextCompletion = try await bedrock.completeText( - prompt, - with: BedrockModel.nova_micro, - maxTokens: 200 - ) - #expect(completion.completion == "This is the textcompletion for: \(prompt)") - } - - @Test( - "Complete text using an invalid prompt", - arguments: invalidPrompts - ) - func completeTextWithInvalidPrompt(prompt: String) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: TextCompletion = try await bedrock.completeText( - prompt, - with: BedrockModel.nova_micro, - maxTokens: 10 - ) - } - } + // @Test("Complete text using a valid temperature", arguments: validTemperature) + // func completeTextWithValidTemperature(temperature: Double) async throws { + // let completion: TextCompletion = try await bedrock.completeText( + // "This is a test", + // with: BedrockModel.nova_micro, + // temperature: temperature + // ) + // #expect(completion.completion == "This is the textcompletion for: This is a test") + // } + + // @Test("Complete text using an invalid temperature", arguments: invalidTemperature) + // func completeTextWithInvalidTemperature(temperature: Double) async throws { + // await #expect(throws: BedrockServiceError.self) { + // let _: TextCompletion = try await bedrock.completeText( + // "This is a test", + // with: BedrockModel.nova_micro, + // temperature: temperature + // ) + // } + // } + + // @Test( + // "Complete text using a valid maxTokens", + // arguments: validMaxTokens + // ) + // func completeTextWithValidMaxTokens(maxTokens: Int) async throws { + // let completion: TextCompletion = try await bedrock.completeText( + // "This is a test", + // with: BedrockModel.nova_micro, + // maxTokens: maxTokens + // ) + // #expect(completion.completion == "This is the textcompletion for: This is a test") + // } + + // @Test( + // "Complete text using an invalid maxTokens", + // arguments: invalidMaxTokens + // ) + // func completeTextWithInvalidMaxTokens(maxTokens: Int) async throws { + // await #expect(throws: BedrockServiceError.self) { + // let _: TextCompletion = try await bedrock.completeText( + // "This is a test", + // with: BedrockModel.nova_micro, + // maxTokens: maxTokens + // ) + // } + // } + + // @Test( + // "Complete text using a valid prompt", + // arguments: validPrompts + // ) + // func completeTextWithValidPrompt(prompt: String) async throws { + // let completion: TextCompletion = try await bedrock.completeText( + // prompt, + // with: BedrockModel.nova_micro, + // maxTokens: 200 + // ) + // #expect(completion.completion == "This is the textcompletion for: \(prompt)") + // } + + // @Test( + // "Complete text using an invalid prompt", + // arguments: invalidPrompts + // ) + // func completeTextWithInvalidPrompt(prompt: String) async throws { + // await #expect(throws: BedrockServiceError.self) { + // let _: TextCompletion = try await bedrock.completeText( + // prompt, + // with: BedrockModel.nova_micro, + // maxTokens: 10 + // ) + // } + // } // MARK: generateImage @@ -191,7 +191,7 @@ struct BedrockServiceTests { } @Test( - "Generate image using an implemented model", + "Generate image using a wrong model", arguments: textCompletionModels ) func generateImageWithInvalidModel(model: BedrockModel) async throws { @@ -204,58 +204,58 @@ struct BedrockServiceTests { } } - @Test( - "Generate image using a valid nrOfImages", - arguments: validNrOfImages - ) - func generateImageWithValidNrOfImages(nrOfImages: Int) async throws { - let output: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: BedrockModel.nova_canvas, - nrOfImages: nrOfImages - ) - #expect(output.images.count == nrOfImages) - } - - @Test( - "Generate image using an invalid nrOfImages", - arguments: invalidNrOfImages - ) - func generateImageWithInvalidNrOfImages(nrOfImages: Int) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: BedrockModel.nova_canvas, - nrOfImages: nrOfImages - ) - } - } - - @Test( - "Generate image using a valid prompt", - arguments: validPrompts - ) - func generateImageWithValidPrompt(prompt: String) async throws { - let output: ImageGenerationOutput = try await bedrock.generateImage( - prompt, - with: BedrockModel.nova_canvas, - nrOfImages: 3 - ) - #expect(output.images.count == 3) - } - - @Test( - "Generate image using an invalid prompt", - arguments: invalidPrompts - ) - func generateImageWithInvalidPrompt(prompt: String) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: ImageGenerationOutput = try await bedrock.generateImage( - prompt, - with: BedrockModel.nova_canvas - ) - } - } + // @Test( + // "Generate image using a valid nrOfImages", + // arguments: validNrOfImages + // ) + // func generateImageWithValidNrOfImages(nrOfImages: Int) async throws { + // let output: ImageGenerationOutput = try await bedrock.generateImage( + // "This is a test", + // with: BedrockModel.nova_canvas, + // nrOfImages: nrOfImages + // ) + // #expect(output.images.count == nrOfImages) + // } + + // @Test( + // "Generate image using an invalid nrOfImages", + // arguments: invalidNrOfImages + // ) + // func generateImageWithInvalidNrOfImages(nrOfImages: Int) async throws { + // await #expect(throws: BedrockServiceError.self) { + // let _: ImageGenerationOutput = try await bedrock.generateImage( + // "This is a test", + // with: BedrockModel.nova_canvas, + // nrOfImages: nrOfImages + // ) + // } + // } + + // @Test( + // "Generate image using a valid prompt", + // arguments: validPrompts + // ) + // func generateImageWithValidPrompt(prompt: String) async throws { + // let output: ImageGenerationOutput = try await bedrock.generateImage( + // prompt, + // with: BedrockModel.nova_canvas, + // nrOfImages: 3 + // ) + // #expect(output.images.count == 3) + // } + + // @Test( + // "Generate image using an invalid prompt", + // arguments: invalidPrompts + // ) + // func generateImageWithInvalidPrompt(prompt: String) async throws { + // await #expect(throws: BedrockServiceError.self) { + // let _: ImageGenerationOutput = try await bedrock.generateImage( + // prompt, + // with: BedrockModel.nova_canvas + // ) + // } + // } // MARK: generateImageVariation @@ -292,38 +292,38 @@ struct BedrockServiceTests { } } - @Test( - "Generate image variation using a valid nrOfImages", - arguments: validNrOfImages - ) - func generateImageVariationWithValidNrOfImages(nrOfImages: Int) async throws { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let output: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: "This is a test", - with: BedrockModel.nova_canvas, - nrOfImages: nrOfImages - ) - #expect(output.images.count == nrOfImages) - } - - @Test( - "Generate image variation using an invalid nrOfImages", - arguments: invalidNrOfImages - ) - func generateImageVariationWithInvalidNrOfImages(nrOfImages: Int) async throws { - await #expect(throws: BedrockServiceError.self) { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let _: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: "This is a test", - with: BedrockModel.nova_canvas, - nrOfImages: nrOfImages - ) - } - } + // @Test( + // "Generate image variation using a valid nrOfImages", + // arguments: validNrOfImages + // ) + // func generateImageVariationWithValidNrOfImages(nrOfImages: Int) async throws { + // let mockBase64Image = + // "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + // let output: ImageGenerationOutput = try await bedrock.generateImageVariation( + // image: mockBase64Image, + // prompt: "This is a test", + // with: BedrockModel.nova_canvas, + // nrOfImages: nrOfImages + // ) + // #expect(output.images.count == nrOfImages) + // } + + // @Test( + // "Generate image variation using an invalid nrOfImages", + // arguments: invalidNrOfImages + // ) + // func generateImageVariationWithInvalidNrOfImages(nrOfImages: Int) async throws { + // await #expect(throws: BedrockServiceError.self) { + // let mockBase64Image = + // "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + // let _: ImageGenerationOutput = try await bedrock.generateImageVariation( + // image: mockBase64Image, + // prompt: "This is a test", + // with: BedrockModel.nova_canvas, + // nrOfImages: nrOfImages + // ) + // } + // } // @Test( // "Generate image variation using a valid similarity", @@ -360,63 +360,63 @@ struct BedrockServiceTests { // } // } - @Test( - "Generate image variation using a valid prompt", - arguments: validPrompts - ) - func generateImageVariationWithValidPrompt(prompt: String) async throws { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let output: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: prompt, - with: BedrockModel.nova_canvas, - similarity: 0.6 - ) - #expect(output.images.count == 1) - } - - @Test( - "Generate image variation using an invalid prompt", - arguments: invalidPrompts - ) - func generateImageVariationWithInvalidPrompt(prompt: String) async throws { - await #expect(throws: BedrockServiceError.self) { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let _: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: prompt, - with: BedrockModel.nova_canvas, - similarity: 0.6 - ) - } - } + // @Test( + // "Generate image variation using a valid prompt", + // arguments: validPrompts + // ) + // func generateImageVariationWithValidPrompt(prompt: String) async throws { + // let mockBase64Image = + // "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + // let output: ImageGenerationOutput = try await bedrock.generateImageVariation( + // image: mockBase64Image, + // prompt: prompt, + // with: BedrockModel.nova_canvas, + // similarity: 0.6 + // ) + // #expect(output.images.count == 1) + // } + + // @Test( + // "Generate image variation using an invalid prompt", + // arguments: invalidPrompts + // ) + // func generateImageVariationWithInvalidPrompt(prompt: String) async throws { + // await #expect(throws: BedrockServiceError.self) { + // let mockBase64Image = + // "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + // let _: ImageGenerationOutput = try await bedrock.generateImageVariation( + // image: mockBase64Image, + // prompt: prompt, + // with: BedrockModel.nova_canvas, + // similarity: 0.6 + // ) + // } + // } // MARK: converse - @Test( - "Continue conversation using a valid prompt", - arguments: validPrompts - ) - func converseWithValidPrompt(prompt: String) async throws { - let (output, _) = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt - ) - #expect(output == "Your prompt was: \(prompt)") - } - - @Test( - "Continue conversation variation using an invalid prompt", - arguments: invalidPrompts - ) - func converseWithInvalidPrompt(prompt: String) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: (String, [Message]) = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt - ) - } - } + // @Test( + // "Continue conversation using a valid prompt", + // arguments: validPrompts + // ) + // func converseWithValidPrompt(prompt: String) async throws { + // let (output, _) = try await bedrock.converse( + // with: BedrockModel.nova_micro, + // prompt: prompt + // ) + // #expect(output == "Your prompt was: \(prompt)") + // } + + // @Test( + // "Continue conversation variation using an invalid prompt", + // arguments: invalidPrompts + // ) + // func converseWithInvalidPrompt(prompt: String) async throws { + // await #expect(throws: BedrockServiceError.self) { + // let _: (String, [Message]) = try await bedrock.converse( + // with: BedrockModel.nova_micro, + // prompt: prompt + // ) + // } + // } } From b57b6cb4dd5da22221fa515cd6b35c9884f91402 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Tue, 8 Apr 2025 13:12:21 +0200 Subject: [PATCH 53/67] parameter tests: converse text --- .../BedrockServiceParameterTests.swift | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift b/backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift index 0856e76e..f33aebff 100644 --- a/backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift +++ b/backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift @@ -589,4 +589,109 @@ struct BedrockServiceParameterTests { ) } } + + // Temperature + @Test( + "Continue conversation using a valid temperature", + arguments: validTemperature + ) + func converseWithValidTemperature(temperature: Double) async throws { + let prompt = "This is a test" + let (output, _) = try await bedrock.converse( + with: BedrockModel.nova_micro, + prompt: prompt, + temperature: temperature + ) + #expect(output == "Your prompt was: \(prompt)") + } + + @Test( + "Continue conversation variation using an invalid temperature", + arguments: invalidTemperature + ) + func converseWithInvalidTemperature(temperature: Double) async throws { + await #expect(throws: BedrockServiceError.self) { + let prompt = "This is a test" + let _ = try await bedrock.converse( + with: BedrockModel.nova_micro, + prompt: prompt, + temperature: temperature + ) + } + } + + // MaxTokens + @Test( + "Continue conversation using a valid maxTokens", + arguments: validMaxTokens + ) + func converseWithValidMaxTokens(maxTokens: Int) async throws { + let prompt = "This is a test" + let (output, _) = try await bedrock.converse( + with: BedrockModel.nova_micro, + prompt: prompt, + maxTokens: maxTokens + ) + #expect(output == "Your prompt was: \(prompt)") + } + + @Test( + "Continue conversation variation using an invalid maxTokens", + arguments: invalidMaxTokens + ) + func converseWithInvalidMaxTokens(maxTokens: Int) async throws { + await #expect(throws: BedrockServiceError.self) { + let prompt = "This is a test" + let _ = try await bedrock.converse( + with: BedrockModel.nova_micro, + prompt: prompt, + maxTokens: maxTokens + ) + } + } + + // TopP + @Test( + "Continue conversation using a valid temperature", + arguments: validTopP + ) + func converseWithValidTopP(topP: Double) async throws { + let prompt = "This is a test" + let (output, _) = try await bedrock.converse( + with: BedrockModel.nova_micro, + prompt: prompt, + topP: topP + ) + #expect(output == "Your prompt was: \(prompt)") + } + + @Test( + "Continue conversation variation using an invalid temperature", + arguments: invalidTopP + ) + func converseWithInvalidTopP(topP: Double) async throws { + await #expect(throws: BedrockServiceError.self) { + let prompt = "This is a test" + let _ = try await bedrock.converse( + with: BedrockModel.nova_micro, + prompt: prompt, + topP: topP + ) + } + } + + // StopSequences + @Test( + "Continue conversation using a valid stopSequences", + arguments: validStopSequences + ) + func converseWithValidTopK(stopSequences: [String]) async throws { + let prompt = "This is a test" + let (output, _) = try await bedrock.converse( + with: BedrockModel.nova_micro, + prompt: prompt, + stopSequences: stopSequences + ) + #expect(output == "Your prompt was: \(prompt)") + } } From f44e20e3bd189e6185359d119321247e9de78f79 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Tue, 8 Apr 2025 13:13:25 +0200 Subject: [PATCH 54/67] formatting --- backend/Tests/BedrockServiceTests/BedrockServiceTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift b/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift index 337a2342..965a4547 100644 --- a/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift +++ b/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift @@ -57,7 +57,7 @@ struct BedrockServiceTests { // "", " ", " \n ", "\t" // ] static let textCompletionModels = [ - BedrockModel.nova_micro, + BedrockModel.nova_micro // BedrockModel.titan_text_g1_lite, // BedrockModel.titan_text_g1_express, // BedrockModel.titan_text_g1_premier, From 9e9eb48167f90b54cd2c6d9100dff943d0707009 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Tue, 8 Apr 2025 13:14:56 +0200 Subject: [PATCH 55/67] formatting --- .../BedrockServiceTests/Mock/MockBedrockRuntimeClient.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/Tests/BedrockServiceTests/Mock/MockBedrockRuntimeClient.swift b/backend/Tests/BedrockServiceTests/Mock/MockBedrockRuntimeClient.swift index fcd6f236..c3063c59 100644 --- a/backend/Tests/BedrockServiceTests/Mock/MockBedrockRuntimeClient.swift +++ b/backend/Tests/BedrockServiceTests/Mock/MockBedrockRuntimeClient.swift @@ -70,7 +70,7 @@ public struct MockBedrockRuntimeClient: BedrockRuntimeClientProtocol { default: throw AWSBedrockRuntime.ValidationException( message: "Malformed input request, please reformat your input and try again." - // message: "Hier in de default! model: \(String(describing: model))" + // message: "Hier in de default! model: \(String(describing: model))" ) } } From 2869e67cae0a143504d29853763b7aed6c6336fa Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Tue, 8 Apr 2025 14:05:43 +0200 Subject: [PATCH 56/67] formatting --- .../BedrockServiceParameterTests.swift | 165 ++++---- .../BedrockServiceTests.swift | 382 +----------------- .../BedrockServiceTests/ConverseTests.swift | 25 ++ .../ImageGenerationTests.swift | 51 +++ .../ImageVariationTests.swift | 57 +++ .../NovaTestConstants.swift | 78 ++++ .../TextGenerationTests.swift | 79 ++++ 7 files changed, 375 insertions(+), 462 deletions(-) create mode 100644 backend/Tests/BedrockServiceTests/ConverseTests.swift create mode 100644 backend/Tests/BedrockServiceTests/ImageGenerationTests.swift create mode 100644 backend/Tests/BedrockServiceTests/ImageVariationTests.swift create mode 100644 backend/Tests/BedrockServiceTests/NovaTestConstants.swift create mode 100644 backend/Tests/BedrockServiceTests/TextGenerationTests.swift diff --git a/backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift b/backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift index f33aebff..e05c0ca7 100644 --- a/backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift +++ b/backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift @@ -31,51 +31,6 @@ struct BedrockServiceParameterTests { } // MARK: constants based on the Nova parameters - // tText generation - static let validTemperature = [0.00001, 0.2, 0.6, 1] - static let invalidTemperature = [-2.5, -1, 0, 1.00001, 2] - static let validMaxTokens = [1, 10, 100, 5_000] - static let invalidMaxTokens = [0, -2, 5_001] - static let validTopP = [0, 0.2, 0.6, 1] - static let invalidTopP = [-1, 1.00001, 2] - static let validTopK = [0, 50] - static let invalidTopK = [-1] - static let validStopSequences = [ - ["\n\nHuman:"], - ["\n\nHuman:", "\n\nAI:"], - ["\n\nHuman:", "\n\nAI:", "\n\nHuman:"], - ] - static let validPrompts = [ - "This is a test", - "!@#$%^&*()_+{}|:<>?", - String(repeating: "test ", count: 10), - ] - static let invalidPrompts = [ - "", " ", " \n ", "\t", - ] - - // image generation - static let validNrOfImages = [1, 2, 3, 4, 5] - static let invalidNrOfImages = [-4, 0, 6, 20] - static let validCfgScale = [1.1, 6, 10] - static let invalidCfgScale = [-4, 0, 1.0, 11, 20] - static let validSeed = [0, 12, 900, 858_993_459] - static let invalidSeed = [-4, 1_000_000_000] - static let validImagePrompts = [ - "This is a test", - "!@#$%^&*()_+{}|:<>?", - String(repeating: "x", count: 1_024), - ] - static let invalidImagePrompts = [ - "", " ", " \n ", "\t", - String(repeating: "x", count: 1_025), - ] - - // image variation - static let validSimilarity = [0.2, 0.5, 1] - static let invalidSimilarity = [-4, 0, 0.1, 1.1, 2] - static let validNrOfReferenceImages = [1, 3, 5] - static let invalidNrOfReferenceImages = [0, 6, 10] // models static let textCompletionModels = [ @@ -91,6 +46,7 @@ struct BedrockServiceParameterTests { // MARK: completeText + // Models @Test( "Complete text using an implemented model", arguments: textCompletionModels @@ -117,7 +73,38 @@ struct BedrockServiceParameterTests { } } - @Test("Complete text using a valid temperature", arguments: validTemperature) + // Parameter combinations + @Test( + "Complete text using an implemented model and a valid combination of parameters" + ) + func completeTextWithValidModelValidParameters() async throws { + let completion: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + maxTokens: 512, + temperature: 0.5, + topK: 10, + stopSequences: ["END", "\n\nHuman:"] + ) + #expect(completion.completion == "This is the textcompletion for: This is a test") + } + + @Test( + "Complete text using an implemented model and an invalid combination of parameters" + ) + func completeTextWithInvalidModelInvalidParameters() async throws { + await #expect(throws: BedrockServiceError.self) { + let _: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_lite, + temperature: 0.5, + topP: 0.5 + ) + } + } + + // Temperature + @Test("Complete text using a valid temperature", arguments: NovaTestConstants.TextGeneration.validTemperature) func completeTextWithValidTemperature(temperature: Double) async throws { let completion: TextCompletion = try await bedrock.completeText( "This is a test", @@ -127,7 +114,7 @@ struct BedrockServiceParameterTests { #expect(completion.completion == "This is the textcompletion for: This is a test") } - @Test("Complete text using an invalid temperature", arguments: invalidTemperature) + @Test("Complete text using an invalid temperature", arguments: NovaTestConstants.TextGeneration.invalidTemperature) func completeTextWithInvalidTemperature(temperature: Double) async throws { await #expect(throws: BedrockServiceError.self) { let _: TextCompletion = try await bedrock.completeText( @@ -138,9 +125,10 @@ struct BedrockServiceParameterTests { } } + // MaxTokens @Test( "Complete text using a valid maxTokens", - arguments: validMaxTokens + arguments: NovaTestConstants.TextGeneration.validMaxTokens ) func completeTextWithValidMaxTokens(maxTokens: Int) async throws { let completion: TextCompletion = try await bedrock.completeText( @@ -153,7 +141,7 @@ struct BedrockServiceParameterTests { @Test( "Complete text using an invalid maxTokens", - arguments: invalidMaxTokens + arguments: NovaTestConstants.TextGeneration.invalidMaxTokens ) func completeTextWithInvalidMaxTokens(maxTokens: Int) async throws { await #expect(throws: BedrockServiceError.self) { @@ -165,9 +153,10 @@ struct BedrockServiceParameterTests { } } + // TopP @Test( "Complete text using a valid topP", - arguments: validTopP + arguments: NovaTestConstants.TextGeneration.validTopP ) func completeTextWithValidTopP(topP: Double) async throws { let completion: TextCompletion = try await bedrock.completeText( @@ -180,7 +169,7 @@ struct BedrockServiceParameterTests { @Test( "Complete text using an invalid topP", - arguments: invalidTopP + arguments: NovaTestConstants.TextGeneration.invalidTopP ) func completeTextWithInvalidMaxTokens(topP: Double) async throws { await #expect(throws: BedrockServiceError.self) { @@ -192,9 +181,10 @@ struct BedrockServiceParameterTests { } } + // TopK @Test( "Complete text using a valid topK", - arguments: validTopK + arguments: NovaTestConstants.TextGeneration.validTopK ) func completeTextWithValidTopK(topK: Int) async throws { let completion: TextCompletion = try await bedrock.completeText( @@ -207,7 +197,7 @@ struct BedrockServiceParameterTests { @Test( "Complete text using an invalid topK", - arguments: invalidTopK + arguments: NovaTestConstants.TextGeneration.invalidTopK ) func completeTextWithInvalidTopK(topK: Int) async throws { await #expect(throws: BedrockServiceError.self) { @@ -219,9 +209,10 @@ struct BedrockServiceParameterTests { } } + // StopSequences @Test( "Complete text using valid stopSequences", - arguments: validStopSequences + arguments: NovaTestConstants.TextGeneration.validStopSequences ) func completeTextWithValidMaxTokens(stopSequences: [String]) async throws { let completion: TextCompletion = try await bedrock.completeText( @@ -232,9 +223,10 @@ struct BedrockServiceParameterTests { #expect(completion.completion == "This is the textcompletion for: This is a test") } + // Prompt @Test( "Complete text using a valid prompt", - arguments: validPrompts + arguments: NovaTestConstants.TextGeneration.validPrompts ) func completeTextWithValidPrompt(prompt: String) async throws { let completion: TextCompletion = try await bedrock.completeText( @@ -247,7 +239,7 @@ struct BedrockServiceParameterTests { @Test( "Complete text using an invalid prompt", - arguments: invalidPrompts + arguments: NovaTestConstants.TextGeneration.invalidPrompts ) func completeTextWithInvalidPrompt(prompt: String) async throws { await #expect(throws: BedrockServiceError.self) { @@ -261,6 +253,7 @@ struct BedrockServiceParameterTests { // MARK: generateImage + // Models @Test( "Generate image using an implemented model", arguments: imageGenerationModels @@ -287,9 +280,10 @@ struct BedrockServiceParameterTests { } } + // NrOfmages @Test( "Generate image using a valid nrOfImages", - arguments: validNrOfImages + arguments: NovaTestConstants.ImageGeneration.validNrOfImages ) func generateImageWithValidNrOfImages(nrOfImages: Int) async throws { let output: ImageGenerationOutput = try await bedrock.generateImage( @@ -302,7 +296,7 @@ struct BedrockServiceParameterTests { @Test( "Generate image using an invalid nrOfImages", - arguments: invalidNrOfImages + arguments: NovaTestConstants.ImageGeneration.invalidNrOfImages ) func generateImageWithInvalidNrOfImages(nrOfImages: Int) async throws { await #expect(throws: BedrockServiceError.self) { @@ -314,9 +308,10 @@ struct BedrockServiceParameterTests { } } + // CfgScale @Test( "Generate image using a valid cfgScale", - arguments: validCfgScale + arguments: NovaTestConstants.ImageGeneration.validCfgScale ) func generateImageWithValidCfgScale(cfgScale: Double) async throws { let output: ImageGenerationOutput = try await bedrock.generateImage( @@ -329,7 +324,7 @@ struct BedrockServiceParameterTests { @Test( "Generate image using an invalid cfgScale", - arguments: invalidCfgScale + arguments: NovaTestConstants.ImageGeneration.invalidCfgScale ) func generateImageWithInvalidCfgScale(cfgScale: Double) async throws { await #expect(throws: BedrockServiceError.self) { @@ -341,9 +336,10 @@ struct BedrockServiceParameterTests { } } + // Seed @Test( "Generate image using a valid seed", - arguments: validSeed + arguments: NovaTestConstants.ImageGeneration.validSeed ) func generateImageWithValidSeed(seed: Int) async throws { let output: ImageGenerationOutput = try await bedrock.generateImage( @@ -356,7 +352,7 @@ struct BedrockServiceParameterTests { @Test( "Generate image using an invalid seed", - arguments: invalidSeed + arguments: NovaTestConstants.ImageGeneration.invalidSeed ) func generateImageWithInvalidSeed(seed: Int) async throws { await #expect(throws: BedrockServiceError.self) { @@ -368,9 +364,10 @@ struct BedrockServiceParameterTests { } } + // Prompt @Test( "Generate image using a valid prompt", - arguments: validImagePrompts + arguments: NovaTestConstants.ImageGeneration.validImagePrompts ) func generateImageWithValidPrompt(prompt: String) async throws { let output: ImageGenerationOutput = try await bedrock.generateImage( @@ -383,7 +380,7 @@ struct BedrockServiceParameterTests { @Test( "Generate image using an invalid prompt", - arguments: invalidImagePrompts + arguments: NovaTestConstants.ImageGeneration.invalidImagePrompts ) func generateImageWithInvalidPrompt(prompt: String) async throws { await #expect(throws: BedrockServiceError.self) { @@ -396,6 +393,7 @@ struct BedrockServiceParameterTests { // MARK: generateImageVariation + // Models @Test( "Generate image variation using an implemented model", arguments: imageGenerationModels @@ -429,9 +427,10 @@ struct BedrockServiceParameterTests { } } + // NrOfImages @Test( "Generate image variation using a valid nrOfImages", - arguments: validNrOfImages + arguments: NovaTestConstants.ImageGeneration.validNrOfImages ) func generateImageVariationWithValidNrOfImages(nrOfImages: Int) async throws { let mockBase64Image = @@ -447,7 +446,7 @@ struct BedrockServiceParameterTests { @Test( "Generate image variation using an invalid nrOfImages", - arguments: invalidNrOfImages + arguments: NovaTestConstants.ImageGeneration.invalidNrOfImages ) func generateImageVariationWithInvalidNrOfImages(nrOfImages: Int) async throws { await #expect(throws: BedrockServiceError.self) { @@ -462,9 +461,10 @@ struct BedrockServiceParameterTests { } } + // Similarity @Test( "Generate image variation using a valid similarity", - arguments: validSimilarity + arguments: NovaTestConstants.ImageVariation.validSimilarity ) func generateImageVariationWithValidSimilarity(similarity: Double) async throws { let mockBase64Image = @@ -481,7 +481,7 @@ struct BedrockServiceParameterTests { @Test( "Generate image variation using an invalid similarity", - arguments: invalidSimilarity + arguments: NovaTestConstants.ImageVariation.invalidSimilarity ) func generateImageVariationWithInvalidSimilarity(similarity: Double) async throws { await #expect(throws: BedrockServiceError.self) { @@ -497,9 +497,10 @@ struct BedrockServiceParameterTests { } } + // Number of reference images @Test( "Generate image variation using a valid number of reference images", - arguments: validNrOfReferenceImages + arguments: NovaTestConstants.ImageVariation.validNrOfReferenceImages ) func generateImageVariationWithValidNrOfReferenceImages(nrOfReferenceImages: Int) async throws { let mockBase64Image = @@ -515,7 +516,7 @@ struct BedrockServiceParameterTests { @Test( "Generate image variation using an invalid number of reference images", - arguments: invalidNrOfReferenceImages + arguments: NovaTestConstants.ImageVariation.invalidNrOfReferenceImages ) func generateImageVariationWithInvalidNrOfReferenceImages(nrOfReferenceImages: Int) async throws { await #expect(throws: BedrockServiceError.self) { @@ -530,9 +531,10 @@ struct BedrockServiceParameterTests { } } + // Prompt @Test( "Generate image variation using a valid prompt", - arguments: validImagePrompts + arguments: NovaTestConstants.ImageGeneration.validImagePrompts ) func generateImageVariationWithValidPrompt(prompt: String) async throws { let mockBase64Image = @@ -548,7 +550,7 @@ struct BedrockServiceParameterTests { @Test( "Generate image variation using an invalid prompt", - arguments: invalidImagePrompts + arguments: NovaTestConstants.ImageGeneration.invalidImagePrompts ) func generateImageVariationWithInvalidPrompt(prompt: String) async throws { await #expect(throws: BedrockServiceError.self) { @@ -565,9 +567,10 @@ struct BedrockServiceParameterTests { // MARK: converse + // Prompt @Test( "Continue conversation using a valid prompt", - arguments: validPrompts + arguments: NovaTestConstants.TextGeneration.validPrompts ) func converseWithValidPrompt(prompt: String) async throws { let (output, _) = try await bedrock.converse( @@ -579,7 +582,7 @@ struct BedrockServiceParameterTests { @Test( "Continue conversation variation using an invalid prompt", - arguments: invalidPrompts + arguments: NovaTestConstants.TextGeneration.invalidPrompts ) func converseWithInvalidPrompt(prompt: String) async throws { await #expect(throws: BedrockServiceError.self) { @@ -593,7 +596,7 @@ struct BedrockServiceParameterTests { // Temperature @Test( "Continue conversation using a valid temperature", - arguments: validTemperature + arguments: NovaTestConstants.TextGeneration.validTemperature ) func converseWithValidTemperature(temperature: Double) async throws { let prompt = "This is a test" @@ -607,7 +610,7 @@ struct BedrockServiceParameterTests { @Test( "Continue conversation variation using an invalid temperature", - arguments: invalidTemperature + arguments: NovaTestConstants.TextGeneration.invalidTemperature ) func converseWithInvalidTemperature(temperature: Double) async throws { await #expect(throws: BedrockServiceError.self) { @@ -623,7 +626,7 @@ struct BedrockServiceParameterTests { // MaxTokens @Test( "Continue conversation using a valid maxTokens", - arguments: validMaxTokens + arguments: NovaTestConstants.TextGeneration.validMaxTokens ) func converseWithValidMaxTokens(maxTokens: Int) async throws { let prompt = "This is a test" @@ -637,7 +640,7 @@ struct BedrockServiceParameterTests { @Test( "Continue conversation variation using an invalid maxTokens", - arguments: invalidMaxTokens + arguments: NovaTestConstants.TextGeneration.invalidMaxTokens ) func converseWithInvalidMaxTokens(maxTokens: Int) async throws { await #expect(throws: BedrockServiceError.self) { @@ -653,7 +656,7 @@ struct BedrockServiceParameterTests { // TopP @Test( "Continue conversation using a valid temperature", - arguments: validTopP + arguments: NovaTestConstants.TextGeneration.validTopP ) func converseWithValidTopP(topP: Double) async throws { let prompt = "This is a test" @@ -667,7 +670,7 @@ struct BedrockServiceParameterTests { @Test( "Continue conversation variation using an invalid temperature", - arguments: invalidTopP + arguments: NovaTestConstants.TextGeneration.invalidTopP ) func converseWithInvalidTopP(topP: Double) async throws { await #expect(throws: BedrockServiceError.self) { @@ -683,7 +686,7 @@ struct BedrockServiceParameterTests { // StopSequences @Test( "Continue conversation using a valid stopSequences", - arguments: validStopSequences + arguments: NovaTestConstants.TextGeneration.validStopSequences ) func converseWithValidTopK(stopSequences: [String]) async throws { let prompt = "This is a test" diff --git a/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift b/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift index 965a4547..cd695e15 100644 --- a/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift +++ b/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift @@ -30,6 +30,7 @@ struct BedrockServiceTests { } // MARK: listModels + @Test("List all models") func listModels() async throws { let models: [ModelSummary] = try await bedrock.listModels() @@ -38,385 +39,4 @@ struct BedrockServiceTests { #expect(models[0].modelName == "Claude Instant") #expect(models[0].providerName == "Anthropic") } - - // MARK: constants - // static let validNrOfImages = [1, 2, 3, 4, 5] - // static let invalidNrOfImages = [-4, 0, 6, 20] - // static let validTemperature = [0, 0.2, 0.6, 1] - // static let invalidTemperature = [-2.5, -1, 1.00001, 2] - // static let validMaxTokens = [1, 10, 100, 5000] - // static let invalidMaxTokens = [0, -1, -2] - // static let validSimilarity = [0.5] - // static let invalidSimilarity = [-4, 0.02, 1.1, 2] - // static let validPrompts = [ - // "This is a test", - // "!@#$%^&*()_+{}|:<>?", - // String(repeating: "test ", count: 1000), - // ] - // static let invalidPrompts = [ - // "", " ", " \n ", "\t" - // ] - static let textCompletionModels = [ - BedrockModel.nova_micro - // BedrockModel.titan_text_g1_lite, - // BedrockModel.titan_text_g1_express, - // BedrockModel.titan_text_g1_premier, - // BedrockModel.claudev1, - // BedrockModel.claudev2, - // BedrockModel.claudev2_1, - // BedrockModel.claudev3_haiku, - // BedrockModel.claudev3_5_haiku, - ] - static let imageGenerationModels = [ - BedrockModel.titan_image_g1_v1, - BedrockModel.titan_image_g1_v2, - BedrockModel.nova_canvas, - ] - - // MARK: completeText - - @Test( - "Complete text using an implemented model", - arguments: textCompletionModels - ) - func completeTextWithValidModel(model: BedrockModel) async throws { - let completion: TextCompletion = try await bedrock.completeText( - "This is a test", - with: model - ) - #expect(completion.completion == "This is the textcompletion for: This is a test") - } - - @Test( - "Complete text using an invalid model", - arguments: imageGenerationModels - ) - func completeTextWithInvalidModel(model: BedrockModel) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: TextCompletion = try await bedrock.completeText( - "This is a test", - with: model, - temperature: 0.8 - ) - } - } - - // @Test("Complete text using a valid temperature", arguments: validTemperature) - // func completeTextWithValidTemperature(temperature: Double) async throws { - // let completion: TextCompletion = try await bedrock.completeText( - // "This is a test", - // with: BedrockModel.nova_micro, - // temperature: temperature - // ) - // #expect(completion.completion == "This is the textcompletion for: This is a test") - // } - - // @Test("Complete text using an invalid temperature", arguments: invalidTemperature) - // func completeTextWithInvalidTemperature(temperature: Double) async throws { - // await #expect(throws: BedrockServiceError.self) { - // let _: TextCompletion = try await bedrock.completeText( - // "This is a test", - // with: BedrockModel.nova_micro, - // temperature: temperature - // ) - // } - // } - - // @Test( - // "Complete text using a valid maxTokens", - // arguments: validMaxTokens - // ) - // func completeTextWithValidMaxTokens(maxTokens: Int) async throws { - // let completion: TextCompletion = try await bedrock.completeText( - // "This is a test", - // with: BedrockModel.nova_micro, - // maxTokens: maxTokens - // ) - // #expect(completion.completion == "This is the textcompletion for: This is a test") - // } - - // @Test( - // "Complete text using an invalid maxTokens", - // arguments: invalidMaxTokens - // ) - // func completeTextWithInvalidMaxTokens(maxTokens: Int) async throws { - // await #expect(throws: BedrockServiceError.self) { - // let _: TextCompletion = try await bedrock.completeText( - // "This is a test", - // with: BedrockModel.nova_micro, - // maxTokens: maxTokens - // ) - // } - // } - - // @Test( - // "Complete text using a valid prompt", - // arguments: validPrompts - // ) - // func completeTextWithValidPrompt(prompt: String) async throws { - // let completion: TextCompletion = try await bedrock.completeText( - // prompt, - // with: BedrockModel.nova_micro, - // maxTokens: 200 - // ) - // #expect(completion.completion == "This is the textcompletion for: \(prompt)") - // } - - // @Test( - // "Complete text using an invalid prompt", - // arguments: invalidPrompts - // ) - // func completeTextWithInvalidPrompt(prompt: String) async throws { - // await #expect(throws: BedrockServiceError.self) { - // let _: TextCompletion = try await bedrock.completeText( - // prompt, - // with: BedrockModel.nova_micro, - // maxTokens: 10 - // ) - // } - // } - - // MARK: generateImage - - @Test( - "Generate image using an implemented model", - arguments: imageGenerationModels - ) - func generateImageWithValidModel(model: BedrockModel) async throws { - let output: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: model - ) - #expect(output.images.count == 1) - } - - @Test( - "Generate image using a wrong model", - arguments: textCompletionModels - ) - func generateImageWithInvalidModel(model: BedrockModel) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: model, - nrOfImages: 3 - ) - } - } - - // @Test( - // "Generate image using a valid nrOfImages", - // arguments: validNrOfImages - // ) - // func generateImageWithValidNrOfImages(nrOfImages: Int) async throws { - // let output: ImageGenerationOutput = try await bedrock.generateImage( - // "This is a test", - // with: BedrockModel.nova_canvas, - // nrOfImages: nrOfImages - // ) - // #expect(output.images.count == nrOfImages) - // } - - // @Test( - // "Generate image using an invalid nrOfImages", - // arguments: invalidNrOfImages - // ) - // func generateImageWithInvalidNrOfImages(nrOfImages: Int) async throws { - // await #expect(throws: BedrockServiceError.self) { - // let _: ImageGenerationOutput = try await bedrock.generateImage( - // "This is a test", - // with: BedrockModel.nova_canvas, - // nrOfImages: nrOfImages - // ) - // } - // } - - // @Test( - // "Generate image using a valid prompt", - // arguments: validPrompts - // ) - // func generateImageWithValidPrompt(prompt: String) async throws { - // let output: ImageGenerationOutput = try await bedrock.generateImage( - // prompt, - // with: BedrockModel.nova_canvas, - // nrOfImages: 3 - // ) - // #expect(output.images.count == 3) - // } - - // @Test( - // "Generate image using an invalid prompt", - // arguments: invalidPrompts - // ) - // func generateImageWithInvalidPrompt(prompt: String) async throws { - // await #expect(throws: BedrockServiceError.self) { - // let _: ImageGenerationOutput = try await bedrock.generateImage( - // prompt, - // with: BedrockModel.nova_canvas - // ) - // } - // } - - // MARK: generateImageVariation - - @Test( - "Generate image variation using an implemented model", - arguments: imageGenerationModels - ) - func generateImageVariationWithValidModel(model: BedrockModel) async throws { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let output: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: "This is a test", - with: model, - nrOfImages: 3 - ) - #expect(output.images.count == 3) - } - - @Test( - "Generate image variation using an invalid model", - arguments: textCompletionModels - ) - func generateImageVariationWithInvalidModel(model: BedrockModel) async throws { - await #expect(throws: BedrockServiceError.self) { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let _: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: "This is a test", - with: model, - nrOfImages: 3 - ) - } - } - - // @Test( - // "Generate image variation using a valid nrOfImages", - // arguments: validNrOfImages - // ) - // func generateImageVariationWithValidNrOfImages(nrOfImages: Int) async throws { - // let mockBase64Image = - // "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - // let output: ImageGenerationOutput = try await bedrock.generateImageVariation( - // image: mockBase64Image, - // prompt: "This is a test", - // with: BedrockModel.nova_canvas, - // nrOfImages: nrOfImages - // ) - // #expect(output.images.count == nrOfImages) - // } - - // @Test( - // "Generate image variation using an invalid nrOfImages", - // arguments: invalidNrOfImages - // ) - // func generateImageVariationWithInvalidNrOfImages(nrOfImages: Int) async throws { - // await #expect(throws: BedrockServiceError.self) { - // let mockBase64Image = - // "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - // let _: ImageGenerationOutput = try await bedrock.generateImageVariation( - // image: mockBase64Image, - // prompt: "This is a test", - // with: BedrockModel.nova_canvas, - // nrOfImages: nrOfImages - // ) - // } - // } - - // @Test( - // "Generate image variation using a valid similarity", - // arguments: validSimilarity - // ) - // func generateImageVariationWithValidNr(similarity: Double) async throws { - // let mockBase64Image = - // "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - // let output: ImageGenerationOutput = try await bedrock.generateImageVariation( - // image: mockBase64Image, - // prompt: "This is a test", - // with: BedrockModel.nova_canvas, - // similarity: similarity, - // nrOfImages: 3 - // ) - // #expect(output.images.count == 3) - // } - - // @Test( - // "Generate image variation using an invalid similarity", - // arguments: invalidSimilarity - // ) - // func generateImageVariationWithInvalidNrOfImages(similarity: Double) async throws { - // await #expect(throws: BedrockServiceError.self) { - // let mockBase64Image = - // "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - // let _: ImageGenerationOutput = try await bedrock.generateImageVariation( - // image: mockBase64Image, - // prompt: "This is a test", - // with: BedrockModel.nova_canvas, - // similarity: similarity, - // nrOfImages: 3 - // ) - // } - // } - - // @Test( - // "Generate image variation using a valid prompt", - // arguments: validPrompts - // ) - // func generateImageVariationWithValidPrompt(prompt: String) async throws { - // let mockBase64Image = - // "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - // let output: ImageGenerationOutput = try await bedrock.generateImageVariation( - // image: mockBase64Image, - // prompt: prompt, - // with: BedrockModel.nova_canvas, - // similarity: 0.6 - // ) - // #expect(output.images.count == 1) - // } - - // @Test( - // "Generate image variation using an invalid prompt", - // arguments: invalidPrompts - // ) - // func generateImageVariationWithInvalidPrompt(prompt: String) async throws { - // await #expect(throws: BedrockServiceError.self) { - // let mockBase64Image = - // "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - // let _: ImageGenerationOutput = try await bedrock.generateImageVariation( - // image: mockBase64Image, - // prompt: prompt, - // with: BedrockModel.nova_canvas, - // similarity: 0.6 - // ) - // } - // } - - // MARK: converse - - // @Test( - // "Continue conversation using a valid prompt", - // arguments: validPrompts - // ) - // func converseWithValidPrompt(prompt: String) async throws { - // let (output, _) = try await bedrock.converse( - // with: BedrockModel.nova_micro, - // prompt: prompt - // ) - // #expect(output == "Your prompt was: \(prompt)") - // } - - // @Test( - // "Continue conversation variation using an invalid prompt", - // arguments: invalidPrompts - // ) - // func converseWithInvalidPrompt(prompt: String) async throws { - // await #expect(throws: BedrockServiceError.self) { - // let _: (String, [Message]) = try await bedrock.converse( - // with: BedrockModel.nova_micro, - // prompt: prompt - // ) - // } - // } } diff --git a/backend/Tests/BedrockServiceTests/ConverseTests.swift b/backend/Tests/BedrockServiceTests/ConverseTests.swift new file mode 100644 index 00000000..72aa5038 --- /dev/null +++ b/backend/Tests/BedrockServiceTests/ConverseTests.swift @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Testing + +@testable import BedrockService +@testable import BedrockTypes + +// Converse text + +extension BedrockServiceTests { + +} \ No newline at end of file diff --git a/backend/Tests/BedrockServiceTests/ImageGenerationTests.swift b/backend/Tests/BedrockServiceTests/ImageGenerationTests.swift new file mode 100644 index 00000000..7aff60db --- /dev/null +++ b/backend/Tests/BedrockServiceTests/ImageGenerationTests.swift @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Testing + +@testable import BedrockService +@testable import BedrockTypes + +// Image generation + +extension BedrockServiceTests { + + @Test( + "Generate image using an implemented model", + arguments: NovaTestConstants.imageGenerationModels + ) + func generateImageWithValidModel(model: BedrockModel) async throws { + let output: ImageGenerationOutput = try await bedrock.generateImage( + "This is a test", + with: model + ) + #expect(output.images.count == 1) + } + + @Test( + "Generate image using a wrong model", + arguments: NovaTestConstants.textCompletionModels + ) + func generateImageWithInvalidModel(model: BedrockModel) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: ImageGenerationOutput = try await bedrock.generateImage( + "This is a test", + with: model, + nrOfImages: 3 + ) + } + } + +} \ No newline at end of file diff --git a/backend/Tests/BedrockServiceTests/ImageVariationTests.swift b/backend/Tests/BedrockServiceTests/ImageVariationTests.swift new file mode 100644 index 00000000..e9e16103 --- /dev/null +++ b/backend/Tests/BedrockServiceTests/ImageVariationTests.swift @@ -0,0 +1,57 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Testing + +@testable import BedrockService +@testable import BedrockTypes + +// Image variation + +extension BedrockServiceTests { + + @Test( + "Generate image variation using an implemented model", + arguments: NovaTestConstants.imageGenerationModels + ) + func generateImageVariationWithValidModel(model: BedrockModel) async throws { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let output: ImageGenerationOutput = try await bedrock.generateImageVariation( + image: mockBase64Image, + prompt: "This is a test", + with: model, + nrOfImages: 3 + ) + #expect(output.images.count == 3) + } + + @Test( + "Generate image variation using an invalid model", + arguments: NovaTestConstants.textCompletionModels + ) + func generateImageVariationWithInvalidModel(model: BedrockModel) async throws { + await #expect(throws: BedrockServiceError.self) { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let _: ImageGenerationOutput = try await bedrock.generateImageVariation( + image: mockBase64Image, + prompt: "This is a test", + with: model, + nrOfImages: 3 + ) + } + } +} \ No newline at end of file diff --git a/backend/Tests/BedrockServiceTests/NovaTestConstants.swift b/backend/Tests/BedrockServiceTests/NovaTestConstants.swift new file mode 100644 index 00000000..0a6dd52e --- /dev/null +++ b/backend/Tests/BedrockServiceTests/NovaTestConstants.swift @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import BedrockTypes + +/// Constants for testing based on the Nova parameters +enum NovaTestConstants { + + static let textCompletionModels = [ + BedrockModel.nova_micro, + BedrockModel.nova_lite, + BedrockModel.nova_pro, + ] + static let imageGenerationModels = [ + BedrockModel.titan_image_g1_v1, + BedrockModel.titan_image_g1_v2, + BedrockModel.nova_canvas, + ] + + enum TextGeneration { + static let validTemperature = [0.00001, 0.2, 0.6, 1] + static let invalidTemperature = [-2.5, -1, 0, 1.00001, 2] + static let validMaxTokens = [1, 10, 100, 5_000] + static let invalidMaxTokens = [0, -2, 5_001] + static let validTopP = [0, 0.2, 0.6, 1] + static let invalidTopP = [-1, 1.00001, 2] + static let validTopK = [0, 50] + static let invalidTopK = [-1] + static let validStopSequences = [ + ["\n\nHuman:"], + ["\n\nHuman:", "\n\nAI:"], + ["\n\nHuman:", "\n\nAI:", "\n\nHuman:"], + ] + static let validPrompts = [ + "This is a test", + "!@#$%^&*()_+{}|:<>?", + String(repeating: "test ", count: 10), + ] + static let invalidPrompts = [ + "", " ", " \n ", "\t", + ] + } + enum ImageGeneration { + static let validNrOfImages = [1, 2, 3, 4, 5] + static let invalidNrOfImages = [-4, 0, 6, 20] + static let validCfgScale = [1.1, 6, 10] + static let invalidCfgScale = [-4, 0, 1.0, 11, 20] + static let validSeed = [0, 12, 900, 858_993_459] + static let invalidSeed = [-4, 1_000_000_000] + static let validImagePrompts = [ + "This is a test", + "!@#$%^&*()_+{}|:<>?", + String(repeating: "x", count: 1_024), + ] + static let invalidImagePrompts = [ + "", " ", " \n ", "\t", + String(repeating: "x", count: 1_025), + ] + } + enum ImageVariation { + static let validSimilarity = [0.2, 0.5, 1] + static let invalidSimilarity = [-4, 0, 0.1, 1.1, 2] + static let validNrOfReferenceImages = [1, 3, 5] + static let invalidNrOfReferenceImages = [0, 6, 10] + } +} diff --git a/backend/Tests/BedrockServiceTests/TextGenerationTests.swift b/backend/Tests/BedrockServiceTests/TextGenerationTests.swift new file mode 100644 index 00000000..9252ca56 --- /dev/null +++ b/backend/Tests/BedrockServiceTests/TextGenerationTests.swift @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Testing + +@testable import BedrockService +@testable import BedrockTypes + +// Text completion + +extension BedrockServiceTests { + + @Test( + "Complete text using an implemented model and no parameters", + arguments: NovaTestConstants.textCompletionModels + ) + func completeTextWithValidModel(model: BedrockModel) async throws { + let completion: TextCompletion = try await bedrock.completeText( + "This is a test", + with: model + ) + #expect(completion.completion == "This is the textcompletion for: This is a test") + } + + @Test( + "Complete text using an invalid model", + arguments: NovaTestConstants.imageGenerationModels + ) + func completeTextWithInvalidModel(model: BedrockModel) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: TextCompletion = try await bedrock.completeText( + "This is a test", + with: model + ) + } + } + + @Test( + "Complete text using an implemented model and a valid combination of parameters" + ) + func completeTextWithValidModelValidParameters() async throws { + let completion: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + maxTokens: 512, + temperature: 0.5, + topK: 10, + stopSequences: ["END", "\n\nHuman:"] + ) + #expect(completion.completion == "This is the textcompletion for: This is a test") + } + + @Test( + "Complete text using an implemented model and an invalid combination of parameters" + ) + func completeTextWithInvalidModelInvalidParameters() async throws { + await #expect(throws: BedrockServiceError.self) { + let _: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_lite, + temperature: 0.5, + topP: 0.5 + ) + } + } + +} From d80837b9f48b5254124803d6ec848a68531257ea Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Tue, 8 Apr 2025 14:14:28 +0200 Subject: [PATCH 57/67] tests refactor --- .../BedrockServiceParameterTests.swift | 700 ------------------ .../BedrockServiceTests/ConverseTests.swift | 25 - .../ConverseTextTests.swift | 155 ++++ .../ImageGenerationTests.swift | 113 ++- .../ImageVariationTests.swift | 141 +++- .../TextGenerationTests.swift | 154 +++- 6 files changed, 559 insertions(+), 729 deletions(-) delete mode 100644 backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift delete mode 100644 backend/Tests/BedrockServiceTests/ConverseTests.swift create mode 100644 backend/Tests/BedrockServiceTests/ConverseTextTests.swift diff --git a/backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift b/backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift deleted file mode 100644 index e05c0ca7..00000000 --- a/backend/Tests/BedrockServiceTests/BedrockServiceParameterTests.swift +++ /dev/null @@ -1,700 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Testing - -@testable import BedrockService -@testable import BedrockTypes - -// For the parameter tests the Nova models are used as an example -@Suite("BedrockService Model Parameter Tests") -struct BedrockServiceParameterTests { - let bedrock: BedrockService - - init() async throws { - self.bedrock = try await BedrockService( - bedrockClient: MockBedrockClient(), - bedrockRuntimeClient: MockBedrockRuntimeClient() - ) - } - - // MARK: constants based on the Nova parameters - - // models - static let textCompletionModels = [ - BedrockModel.nova_micro, - BedrockModel.nova_lite, - BedrockModel.nova_pro, - ] - static let imageGenerationModels = [ - BedrockModel.titan_image_g1_v1, - BedrockModel.titan_image_g1_v2, - BedrockModel.nova_canvas, - ] - - // MARK: completeText - - // Models - @Test( - "Complete text using an implemented model", - arguments: textCompletionModels - ) - func completeTextWithValidModel(model: BedrockModel) async throws { - let completion: TextCompletion = try await bedrock.completeText( - "This is a test", - with: model - ) - #expect(completion.completion == "This is the textcompletion for: This is a test") - } - - @Test( - "Complete text using an invalid model", - arguments: imageGenerationModels - ) - func completeTextWithInvalidModel(model: BedrockModel) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: TextCompletion = try await bedrock.completeText( - "This is a test", - with: model, - temperature: 0.8 - ) - } - } - - // Parameter combinations - @Test( - "Complete text using an implemented model and a valid combination of parameters" - ) - func completeTextWithValidModelValidParameters() async throws { - let completion: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - maxTokens: 512, - temperature: 0.5, - topK: 10, - stopSequences: ["END", "\n\nHuman:"] - ) - #expect(completion.completion == "This is the textcompletion for: This is a test") - } - - @Test( - "Complete text using an implemented model and an invalid combination of parameters" - ) - func completeTextWithInvalidModelInvalidParameters() async throws { - await #expect(throws: BedrockServiceError.self) { - let _: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_lite, - temperature: 0.5, - topP: 0.5 - ) - } - } - - // Temperature - @Test("Complete text using a valid temperature", arguments: NovaTestConstants.TextGeneration.validTemperature) - func completeTextWithValidTemperature(temperature: Double) async throws { - let completion: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - temperature: temperature - ) - #expect(completion.completion == "This is the textcompletion for: This is a test") - } - - @Test("Complete text using an invalid temperature", arguments: NovaTestConstants.TextGeneration.invalidTemperature) - func completeTextWithInvalidTemperature(temperature: Double) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - temperature: temperature - ) - } - } - - // MaxTokens - @Test( - "Complete text using a valid maxTokens", - arguments: NovaTestConstants.TextGeneration.validMaxTokens - ) - func completeTextWithValidMaxTokens(maxTokens: Int) async throws { - let completion: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - maxTokens: maxTokens - ) - #expect(completion.completion == "This is the textcompletion for: This is a test") - } - - @Test( - "Complete text using an invalid maxTokens", - arguments: NovaTestConstants.TextGeneration.invalidMaxTokens - ) - func completeTextWithInvalidMaxTokens(maxTokens: Int) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - maxTokens: maxTokens - ) - } - } - - // TopP - @Test( - "Complete text using a valid topP", - arguments: NovaTestConstants.TextGeneration.validTopP - ) - func completeTextWithValidTopP(topP: Double) async throws { - let completion: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - topP: topP - ) - #expect(completion.completion == "This is the textcompletion for: This is a test") - } - - @Test( - "Complete text using an invalid topP", - arguments: NovaTestConstants.TextGeneration.invalidTopP - ) - func completeTextWithInvalidMaxTokens(topP: Double) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - topP: topP - ) - } - } - - // TopK - @Test( - "Complete text using a valid topK", - arguments: NovaTestConstants.TextGeneration.validTopK - ) - func completeTextWithValidTopK(topK: Int) async throws { - let completion: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - topK: topK - ) - #expect(completion.completion == "This is the textcompletion for: This is a test") - } - - @Test( - "Complete text using an invalid topK", - arguments: NovaTestConstants.TextGeneration.invalidTopK - ) - func completeTextWithInvalidTopK(topK: Int) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - topK: topK - ) - } - } - - // StopSequences - @Test( - "Complete text using valid stopSequences", - arguments: NovaTestConstants.TextGeneration.validStopSequences - ) - func completeTextWithValidMaxTokens(stopSequences: [String]) async throws { - let completion: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - stopSequences: stopSequences - ) - #expect(completion.completion == "This is the textcompletion for: This is a test") - } - - // Prompt - @Test( - "Complete text using a valid prompt", - arguments: NovaTestConstants.TextGeneration.validPrompts - ) - func completeTextWithValidPrompt(prompt: String) async throws { - let completion: TextCompletion = try await bedrock.completeText( - prompt, - with: BedrockModel.nova_micro, - maxTokens: 200 - ) - #expect(completion.completion == "This is the textcompletion for: \(prompt)") - } - - @Test( - "Complete text using an invalid prompt", - arguments: NovaTestConstants.TextGeneration.invalidPrompts - ) - func completeTextWithInvalidPrompt(prompt: String) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: TextCompletion = try await bedrock.completeText( - prompt, - with: BedrockModel.nova_canvas, - maxTokens: 10 - ) - } - } - - // MARK: generateImage - - // Models - @Test( - "Generate image using an implemented model", - arguments: imageGenerationModels - ) - func generateImageWithValidModel(model: BedrockModel) async throws { - let output: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: model - ) - #expect(output.images.count == 1) - } - - @Test( - "Generate image using a wrong model", - arguments: textCompletionModels - ) - func generateImageWithInvalidModel(model: BedrockModel) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: model, - nrOfImages: 3 - ) - } - } - - // NrOfmages - @Test( - "Generate image using a valid nrOfImages", - arguments: NovaTestConstants.ImageGeneration.validNrOfImages - ) - func generateImageWithValidNrOfImages(nrOfImages: Int) async throws { - let output: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: BedrockModel.nova_canvas, - nrOfImages: nrOfImages - ) - #expect(output.images.count == nrOfImages) - } - - @Test( - "Generate image using an invalid nrOfImages", - arguments: NovaTestConstants.ImageGeneration.invalidNrOfImages - ) - func generateImageWithInvalidNrOfImages(nrOfImages: Int) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: BedrockModel.nova_canvas, - nrOfImages: nrOfImages - ) - } - } - - // CfgScale - @Test( - "Generate image using a valid cfgScale", - arguments: NovaTestConstants.ImageGeneration.validCfgScale - ) - func generateImageWithValidCfgScale(cfgScale: Double) async throws { - let output: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: BedrockModel.nova_canvas, - cfgScale: cfgScale - ) - #expect(output.images.count == 1) - } - - @Test( - "Generate image using an invalid cfgScale", - arguments: NovaTestConstants.ImageGeneration.invalidCfgScale - ) - func generateImageWithInvalidCfgScale(cfgScale: Double) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: BedrockModel.nova_canvas, - cfgScale: cfgScale - ) - } - } - - // Seed - @Test( - "Generate image using a valid seed", - arguments: NovaTestConstants.ImageGeneration.validSeed - ) - func generateImageWithValidSeed(seed: Int) async throws { - let output: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: BedrockModel.nova_canvas, - seed: seed - ) - #expect(output.images.count == 1) - } - - @Test( - "Generate image using an invalid seed", - arguments: NovaTestConstants.ImageGeneration.invalidSeed - ) - func generateImageWithInvalidSeed(seed: Int) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: BedrockModel.nova_canvas, - seed: seed - ) - } - } - - // Prompt - @Test( - "Generate image using a valid prompt", - arguments: NovaTestConstants.ImageGeneration.validImagePrompts - ) - func generateImageWithValidPrompt(prompt: String) async throws { - let output: ImageGenerationOutput = try await bedrock.generateImage( - prompt, - with: BedrockModel.nova_canvas, - nrOfImages: 3 - ) - #expect(output.images.count == 3) - } - - @Test( - "Generate image using an invalid prompt", - arguments: NovaTestConstants.ImageGeneration.invalidImagePrompts - ) - func generateImageWithInvalidPrompt(prompt: String) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: ImageGenerationOutput = try await bedrock.generateImage( - prompt, - with: BedrockModel.nova_canvas - ) - } - } - - // MARK: generateImageVariation - - // Models - @Test( - "Generate image variation using an implemented model", - arguments: imageGenerationModels - ) - func generateImageVariationWithValidModel(model: BedrockModel) async throws { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let output: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: "This is a test", - with: model, - nrOfImages: 3 - ) - #expect(output.images.count == 3) - } - - @Test( - "Generate image variation using an invalid model", - arguments: textCompletionModels - ) - func generateImageVariationWithInvalidModel(model: BedrockModel) async throws { - await #expect(throws: BedrockServiceError.self) { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let _: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: "This is a test", - with: model, - nrOfImages: 3 - ) - } - } - - // NrOfImages - @Test( - "Generate image variation using a valid nrOfImages", - arguments: NovaTestConstants.ImageGeneration.validNrOfImages - ) - func generateImageVariationWithValidNrOfImages(nrOfImages: Int) async throws { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let output: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: "This is a test", - with: BedrockModel.nova_canvas, - nrOfImages: nrOfImages - ) - #expect(output.images.count == nrOfImages) - } - - @Test( - "Generate image variation using an invalid nrOfImages", - arguments: NovaTestConstants.ImageGeneration.invalidNrOfImages - ) - func generateImageVariationWithInvalidNrOfImages(nrOfImages: Int) async throws { - await #expect(throws: BedrockServiceError.self) { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let _: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: "This is a test", - with: BedrockModel.nova_canvas, - nrOfImages: nrOfImages - ) - } - } - - // Similarity - @Test( - "Generate image variation using a valid similarity", - arguments: NovaTestConstants.ImageVariation.validSimilarity - ) - func generateImageVariationWithValidSimilarity(similarity: Double) async throws { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let output: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: "This is a test", - with: BedrockModel.nova_canvas, - similarity: similarity, - nrOfImages: 3 - ) - #expect(output.images.count == 3) - } - - @Test( - "Generate image variation using an invalid similarity", - arguments: NovaTestConstants.ImageVariation.invalidSimilarity - ) - func generateImageVariationWithInvalidSimilarity(similarity: Double) async throws { - await #expect(throws: BedrockServiceError.self) { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let _: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: "This is a test", - with: BedrockModel.nova_canvas, - similarity: similarity, - nrOfImages: 3 - ) - } - } - - // Number of reference images - @Test( - "Generate image variation using a valid number of reference images", - arguments: NovaTestConstants.ImageVariation.validNrOfReferenceImages - ) - func generateImageVariationWithValidNrOfReferenceImages(nrOfReferenceImages: Int) async throws { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let mockImages = Array(repeating: mockBase64Image, count: nrOfReferenceImages) - let output: ImageGenerationOutput = try await bedrock.generateImageVariation( - images: mockImages, - prompt: "This is a test", - with: BedrockModel.nova_canvas - ) - #expect(output.images.count == 1) - } - - @Test( - "Generate image variation using an invalid number of reference images", - arguments: NovaTestConstants.ImageVariation.invalidNrOfReferenceImages - ) - func generateImageVariationWithInvalidNrOfReferenceImages(nrOfReferenceImages: Int) async throws { - await #expect(throws: BedrockServiceError.self) { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let mockImages = Array(repeating: mockBase64Image, count: nrOfReferenceImages) - let _: ImageGenerationOutput = try await bedrock.generateImageVariation( - images: mockImages, - prompt: "This is a test", - with: BedrockModel.nova_canvas - ) - } - } - - // Prompt - @Test( - "Generate image variation using a valid prompt", - arguments: NovaTestConstants.ImageGeneration.validImagePrompts - ) - func generateImageVariationWithValidPrompt(prompt: String) async throws { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let output: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: prompt, - with: BedrockModel.nova_canvas, - similarity: 0.6 - ) - #expect(output.images.count == 1) - } - - @Test( - "Generate image variation using an invalid prompt", - arguments: NovaTestConstants.ImageGeneration.invalidImagePrompts - ) - func generateImageVariationWithInvalidPrompt(prompt: String) async throws { - await #expect(throws: BedrockServiceError.self) { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let _: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: prompt, - with: BedrockModel.nova_canvas, - similarity: 0.6 - ) - } - } - - // MARK: converse - - // Prompt - @Test( - "Continue conversation using a valid prompt", - arguments: NovaTestConstants.TextGeneration.validPrompts - ) - func converseWithValidPrompt(prompt: String) async throws { - let (output, _) = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt - ) - #expect(output == "Your prompt was: \(prompt)") - } - - @Test( - "Continue conversation variation using an invalid prompt", - arguments: NovaTestConstants.TextGeneration.invalidPrompts - ) - func converseWithInvalidPrompt(prompt: String) async throws { - await #expect(throws: BedrockServiceError.self) { - let _ = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt - ) - } - } - - // Temperature - @Test( - "Continue conversation using a valid temperature", - arguments: NovaTestConstants.TextGeneration.validTemperature - ) - func converseWithValidTemperature(temperature: Double) async throws { - let prompt = "This is a test" - let (output, _) = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt, - temperature: temperature - ) - #expect(output == "Your prompt was: \(prompt)") - } - - @Test( - "Continue conversation variation using an invalid temperature", - arguments: NovaTestConstants.TextGeneration.invalidTemperature - ) - func converseWithInvalidTemperature(temperature: Double) async throws { - await #expect(throws: BedrockServiceError.self) { - let prompt = "This is a test" - let _ = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt, - temperature: temperature - ) - } - } - - // MaxTokens - @Test( - "Continue conversation using a valid maxTokens", - arguments: NovaTestConstants.TextGeneration.validMaxTokens - ) - func converseWithValidMaxTokens(maxTokens: Int) async throws { - let prompt = "This is a test" - let (output, _) = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt, - maxTokens: maxTokens - ) - #expect(output == "Your prompt was: \(prompt)") - } - - @Test( - "Continue conversation variation using an invalid maxTokens", - arguments: NovaTestConstants.TextGeneration.invalidMaxTokens - ) - func converseWithInvalidMaxTokens(maxTokens: Int) async throws { - await #expect(throws: BedrockServiceError.self) { - let prompt = "This is a test" - let _ = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt, - maxTokens: maxTokens - ) - } - } - - // TopP - @Test( - "Continue conversation using a valid temperature", - arguments: NovaTestConstants.TextGeneration.validTopP - ) - func converseWithValidTopP(topP: Double) async throws { - let prompt = "This is a test" - let (output, _) = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt, - topP: topP - ) - #expect(output == "Your prompt was: \(prompt)") - } - - @Test( - "Continue conversation variation using an invalid temperature", - arguments: NovaTestConstants.TextGeneration.invalidTopP - ) - func converseWithInvalidTopP(topP: Double) async throws { - await #expect(throws: BedrockServiceError.self) { - let prompt = "This is a test" - let _ = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt, - topP: topP - ) - } - } - - // StopSequences - @Test( - "Continue conversation using a valid stopSequences", - arguments: NovaTestConstants.TextGeneration.validStopSequences - ) - func converseWithValidTopK(stopSequences: [String]) async throws { - let prompt = "This is a test" - let (output, _) = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt, - stopSequences: stopSequences - ) - #expect(output == "Your prompt was: \(prompt)") - } -} diff --git a/backend/Tests/BedrockServiceTests/ConverseTests.swift b/backend/Tests/BedrockServiceTests/ConverseTests.swift deleted file mode 100644 index 72aa5038..00000000 --- a/backend/Tests/BedrockServiceTests/ConverseTests.swift +++ /dev/null @@ -1,25 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Testing - -@testable import BedrockService -@testable import BedrockTypes - -// Converse text - -extension BedrockServiceTests { - -} \ No newline at end of file diff --git a/backend/Tests/BedrockServiceTests/ConverseTextTests.swift b/backend/Tests/BedrockServiceTests/ConverseTextTests.swift new file mode 100644 index 00000000..01974a62 --- /dev/null +++ b/backend/Tests/BedrockServiceTests/ConverseTextTests.swift @@ -0,0 +1,155 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Testing + +@testable import BedrockService +@testable import BedrockTypes + +// Converse text + +extension BedrockServiceTests { + + // Prompt + @Test( + "Continue conversation using a valid prompt", + arguments: NovaTestConstants.TextGeneration.validPrompts + ) + func converseWithValidPrompt(prompt: String) async throws { + let (output, _) = try await bedrock.converse( + with: BedrockModel.nova_micro, + prompt: prompt + ) + #expect(output == "Your prompt was: \(prompt)") + } + + @Test( + "Continue conversation variation using an invalid prompt", + arguments: NovaTestConstants.TextGeneration.invalidPrompts + ) + func converseWithInvalidPrompt(prompt: String) async throws { + await #expect(throws: BedrockServiceError.self) { + let _ = try await bedrock.converse( + with: BedrockModel.nova_micro, + prompt: prompt + ) + } + } + + // Temperature + @Test( + "Continue conversation using a valid temperature", + arguments: NovaTestConstants.TextGeneration.validTemperature + ) + func converseWithValidTemperature(temperature: Double) async throws { + let prompt = "This is a test" + let (output, _) = try await bedrock.converse( + with: BedrockModel.nova_micro, + prompt: prompt, + temperature: temperature + ) + #expect(output == "Your prompt was: \(prompt)") + } + + @Test( + "Continue conversation variation using an invalid temperature", + arguments: NovaTestConstants.TextGeneration.invalidTemperature + ) + func converseWithInvalidTemperature(temperature: Double) async throws { + await #expect(throws: BedrockServiceError.self) { + let prompt = "This is a test" + let _ = try await bedrock.converse( + with: BedrockModel.nova_micro, + prompt: prompt, + temperature: temperature + ) + } + } + + // MaxTokens + @Test( + "Continue conversation using a valid maxTokens", + arguments: NovaTestConstants.TextGeneration.validMaxTokens + ) + func converseWithValidMaxTokens(maxTokens: Int) async throws { + let prompt = "This is a test" + let (output, _) = try await bedrock.converse( + with: BedrockModel.nova_micro, + prompt: prompt, + maxTokens: maxTokens + ) + #expect(output == "Your prompt was: \(prompt)") + } + + @Test( + "Continue conversation variation using an invalid maxTokens", + arguments: NovaTestConstants.TextGeneration.invalidMaxTokens + ) + func converseWithInvalidMaxTokens(maxTokens: Int) async throws { + await #expect(throws: BedrockServiceError.self) { + let prompt = "This is a test" + let _ = try await bedrock.converse( + with: BedrockModel.nova_micro, + prompt: prompt, + maxTokens: maxTokens + ) + } + } + + // TopP + @Test( + "Continue conversation using a valid temperature", + arguments: NovaTestConstants.TextGeneration.validTopP + ) + func converseWithValidTopP(topP: Double) async throws { + let prompt = "This is a test" + let (output, _) = try await bedrock.converse( + with: BedrockModel.nova_micro, + prompt: prompt, + topP: topP + ) + #expect(output == "Your prompt was: \(prompt)") + } + + @Test( + "Continue conversation variation using an invalid temperature", + arguments: NovaTestConstants.TextGeneration.invalidTopP + ) + func converseWithInvalidTopP(topP: Double) async throws { + await #expect(throws: BedrockServiceError.self) { + let prompt = "This is a test" + let _ = try await bedrock.converse( + with: BedrockModel.nova_micro, + prompt: prompt, + topP: topP + ) + } + } + + // StopSequences + @Test( + "Continue conversation using a valid stopSequences", + arguments: NovaTestConstants.TextGeneration.validStopSequences + ) + func converseWithValidTopK(stopSequences: [String]) async throws { + let prompt = "This is a test" + let (output, _) = try await bedrock.converse( + with: BedrockModel.nova_micro, + prompt: prompt, + stopSequences: stopSequences + ) + #expect(output == "Your prompt was: \(prompt)") + } +} diff --git a/backend/Tests/BedrockServiceTests/ImageGenerationTests.swift b/backend/Tests/BedrockServiceTests/ImageGenerationTests.swift index 7aff60db..12088380 100644 --- a/backend/Tests/BedrockServiceTests/ImageGenerationTests.swift +++ b/backend/Tests/BedrockServiceTests/ImageGenerationTests.swift @@ -22,6 +22,7 @@ import Testing extension BedrockServiceTests { + // Models @Test( "Generate image using an implemented model", arguments: NovaTestConstants.imageGenerationModels @@ -48,4 +49,114 @@ extension BedrockServiceTests { } } -} \ No newline at end of file + // NrOfmages + @Test( + "Generate image using a valid nrOfImages", + arguments: NovaTestConstants.ImageGeneration.validNrOfImages + ) + func generateImageWithValidNrOfImages(nrOfImages: Int) async throws { + let output: ImageGenerationOutput = try await bedrock.generateImage( + "This is a test", + with: BedrockModel.nova_canvas, + nrOfImages: nrOfImages + ) + #expect(output.images.count == nrOfImages) + } + + @Test( + "Generate image using an invalid nrOfImages", + arguments: NovaTestConstants.ImageGeneration.invalidNrOfImages + ) + func generateImageWithInvalidNrOfImages(nrOfImages: Int) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: ImageGenerationOutput = try await bedrock.generateImage( + "This is a test", + with: BedrockModel.nova_canvas, + nrOfImages: nrOfImages + ) + } + } + + // CfgScale + @Test( + "Generate image using a valid cfgScale", + arguments: NovaTestConstants.ImageGeneration.validCfgScale + ) + func generateImageWithValidCfgScale(cfgScale: Double) async throws { + let output: ImageGenerationOutput = try await bedrock.generateImage( + "This is a test", + with: BedrockModel.nova_canvas, + cfgScale: cfgScale + ) + #expect(output.images.count == 1) + } + + @Test( + "Generate image using an invalid cfgScale", + arguments: NovaTestConstants.ImageGeneration.invalidCfgScale + ) + func generateImageWithInvalidCfgScale(cfgScale: Double) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: ImageGenerationOutput = try await bedrock.generateImage( + "This is a test", + with: BedrockModel.nova_canvas, + cfgScale: cfgScale + ) + } + } + + // Seed + @Test( + "Generate image using a valid seed", + arguments: NovaTestConstants.ImageGeneration.validSeed + ) + func generateImageWithValidSeed(seed: Int) async throws { + let output: ImageGenerationOutput = try await bedrock.generateImage( + "This is a test", + with: BedrockModel.nova_canvas, + seed: seed + ) + #expect(output.images.count == 1) + } + + @Test( + "Generate image using an invalid seed", + arguments: NovaTestConstants.ImageGeneration.invalidSeed + ) + func generateImageWithInvalidSeed(seed: Int) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: ImageGenerationOutput = try await bedrock.generateImage( + "This is a test", + with: BedrockModel.nova_canvas, + seed: seed + ) + } + } + + // Prompt + @Test( + "Generate image using a valid prompt", + arguments: NovaTestConstants.ImageGeneration.validImagePrompts + ) + func generateImageWithValidPrompt(prompt: String) async throws { + let output: ImageGenerationOutput = try await bedrock.generateImage( + prompt, + with: BedrockModel.nova_canvas, + nrOfImages: 3 + ) + #expect(output.images.count == 3) + } + + @Test( + "Generate image using an invalid prompt", + arguments: NovaTestConstants.ImageGeneration.invalidImagePrompts + ) + func generateImageWithInvalidPrompt(prompt: String) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: ImageGenerationOutput = try await bedrock.generateImage( + prompt, + with: BedrockModel.nova_canvas + ) + } + } +} diff --git a/backend/Tests/BedrockServiceTests/ImageVariationTests.swift b/backend/Tests/BedrockServiceTests/ImageVariationTests.swift index e9e16103..c596d342 100644 --- a/backend/Tests/BedrockServiceTests/ImageVariationTests.swift +++ b/backend/Tests/BedrockServiceTests/ImageVariationTests.swift @@ -22,6 +22,7 @@ import Testing extension BedrockServiceTests { + // Models @Test( "Generate image variation using an implemented model", arguments: NovaTestConstants.imageGenerationModels @@ -54,4 +55,142 @@ extension BedrockServiceTests { ) } } -} \ No newline at end of file + + // NrOfImages + @Test( + "Generate image variation using a valid nrOfImages", + arguments: NovaTestConstants.ImageGeneration.validNrOfImages + ) + func generateImageVariationWithValidNrOfImages(nrOfImages: Int) async throws { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let output: ImageGenerationOutput = try await bedrock.generateImageVariation( + image: mockBase64Image, + prompt: "This is a test", + with: BedrockModel.nova_canvas, + nrOfImages: nrOfImages + ) + #expect(output.images.count == nrOfImages) + } + + @Test( + "Generate image variation using an invalid nrOfImages", + arguments: NovaTestConstants.ImageGeneration.invalidNrOfImages + ) + func generateImageVariationWithInvalidNrOfImages(nrOfImages: Int) async throws { + await #expect(throws: BedrockServiceError.self) { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let _: ImageGenerationOutput = try await bedrock.generateImageVariation( + image: mockBase64Image, + prompt: "This is a test", + with: BedrockModel.nova_canvas, + nrOfImages: nrOfImages + ) + } + } + + // Similarity + @Test( + "Generate image variation using a valid similarity", + arguments: NovaTestConstants.ImageVariation.validSimilarity + ) + func generateImageVariationWithValidSimilarity(similarity: Double) async throws { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let output: ImageGenerationOutput = try await bedrock.generateImageVariation( + image: mockBase64Image, + prompt: "This is a test", + with: BedrockModel.nova_canvas, + similarity: similarity, + nrOfImages: 3 + ) + #expect(output.images.count == 3) + } + + @Test( + "Generate image variation using an invalid similarity", + arguments: NovaTestConstants.ImageVariation.invalidSimilarity + ) + func generateImageVariationWithInvalidSimilarity(similarity: Double) async throws { + await #expect(throws: BedrockServiceError.self) { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let _: ImageGenerationOutput = try await bedrock.generateImageVariation( + image: mockBase64Image, + prompt: "This is a test", + with: BedrockModel.nova_canvas, + similarity: similarity, + nrOfImages: 3 + ) + } + } + + // Number of reference images + @Test( + "Generate image variation using a valid number of reference images", + arguments: NovaTestConstants.ImageVariation.validNrOfReferenceImages + ) + func generateImageVariationWithValidNrOfReferenceImages(nrOfReferenceImages: Int) async throws { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let mockImages = Array(repeating: mockBase64Image, count: nrOfReferenceImages) + let output: ImageGenerationOutput = try await bedrock.generateImageVariation( + images: mockImages, + prompt: "This is a test", + with: BedrockModel.nova_canvas + ) + #expect(output.images.count == 1) + } + + @Test( + "Generate image variation using an invalid number of reference images", + arguments: NovaTestConstants.ImageVariation.invalidNrOfReferenceImages + ) + func generateImageVariationWithInvalidNrOfReferenceImages(nrOfReferenceImages: Int) async throws { + await #expect(throws: BedrockServiceError.self) { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let mockImages = Array(repeating: mockBase64Image, count: nrOfReferenceImages) + let _: ImageGenerationOutput = try await bedrock.generateImageVariation( + images: mockImages, + prompt: "This is a test", + with: BedrockModel.nova_canvas + ) + } + } + + // Prompt + @Test( + "Generate image variation using a valid prompt", + arguments: NovaTestConstants.ImageGeneration.validImagePrompts + ) + func generateImageVariationWithValidPrompt(prompt: String) async throws { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let output: ImageGenerationOutput = try await bedrock.generateImageVariation( + image: mockBase64Image, + prompt: prompt, + with: BedrockModel.nova_canvas, + similarity: 0.6 + ) + #expect(output.images.count == 1) + } + + @Test( + "Generate image variation using an invalid prompt", + arguments: NovaTestConstants.ImageGeneration.invalidImagePrompts + ) + func generateImageVariationWithInvalidPrompt(prompt: String) async throws { + await #expect(throws: BedrockServiceError.self) { + let mockBase64Image = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" + let _: ImageGenerationOutput = try await bedrock.generateImageVariation( + image: mockBase64Image, + prompt: prompt, + with: BedrockModel.nova_canvas, + similarity: 0.6 + ) + } + } +} diff --git a/backend/Tests/BedrockServiceTests/TextGenerationTests.swift b/backend/Tests/BedrockServiceTests/TextGenerationTests.swift index 9252ca56..0f36ece4 100644 --- a/backend/Tests/BedrockServiceTests/TextGenerationTests.swift +++ b/backend/Tests/BedrockServiceTests/TextGenerationTests.swift @@ -22,8 +22,9 @@ import Testing extension BedrockServiceTests { + // Models @Test( - "Complete text using an implemented model and no parameters", + "Complete text using an implemented model", arguments: NovaTestConstants.textCompletionModels ) func completeTextWithValidModel(model: BedrockModel) async throws { @@ -42,11 +43,13 @@ extension BedrockServiceTests { await #expect(throws: BedrockServiceError.self) { let _: TextCompletion = try await bedrock.completeText( "This is a test", - with: model + with: model, + temperature: 0.8 ) } } + // Parameter combinations @Test( "Complete text using an implemented model and a valid combination of parameters" ) @@ -76,4 +79,151 @@ extension BedrockServiceTests { } } + // Temperature + @Test("Complete text using a valid temperature", arguments: NovaTestConstants.TextGeneration.validTemperature) + func completeTextWithValidTemperature(temperature: Double) async throws { + let completion: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + temperature: temperature + ) + #expect(completion.completion == "This is the textcompletion for: This is a test") + } + + @Test("Complete text using an invalid temperature", arguments: NovaTestConstants.TextGeneration.invalidTemperature) + func completeTextWithInvalidTemperature(temperature: Double) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + temperature: temperature + ) + } + } + + // MaxTokens + @Test( + "Complete text using a valid maxTokens", + arguments: NovaTestConstants.TextGeneration.validMaxTokens + ) + func completeTextWithValidMaxTokens(maxTokens: Int) async throws { + let completion: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + maxTokens: maxTokens + ) + #expect(completion.completion == "This is the textcompletion for: This is a test") + } + + @Test( + "Complete text using an invalid maxTokens", + arguments: NovaTestConstants.TextGeneration.invalidMaxTokens + ) + func completeTextWithInvalidMaxTokens(maxTokens: Int) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + maxTokens: maxTokens + ) + } + } + + // TopP + @Test( + "Complete text using a valid topP", + arguments: NovaTestConstants.TextGeneration.validTopP + ) + func completeTextWithValidTopP(topP: Double) async throws { + let completion: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + topP: topP + ) + #expect(completion.completion == "This is the textcompletion for: This is a test") + } + + @Test( + "Complete text using an invalid topP", + arguments: NovaTestConstants.TextGeneration.invalidTopP + ) + func completeTextWithInvalidMaxTokens(topP: Double) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + topP: topP + ) + } + } + + // TopK + @Test( + "Complete text using a valid topK", + arguments: NovaTestConstants.TextGeneration.validTopK + ) + func completeTextWithValidTopK(topK: Int) async throws { + let completion: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + topK: topK + ) + #expect(completion.completion == "This is the textcompletion for: This is a test") + } + + @Test( + "Complete text using an invalid topK", + arguments: NovaTestConstants.TextGeneration.invalidTopK + ) + func completeTextWithInvalidTopK(topK: Int) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + topK: topK + ) + } + } + + // StopSequences + @Test( + "Complete text using valid stopSequences", + arguments: NovaTestConstants.TextGeneration.validStopSequences + ) + func completeTextWithValidMaxTokens(stopSequences: [String]) async throws { + let completion: TextCompletion = try await bedrock.completeText( + "This is a test", + with: BedrockModel.nova_micro, + stopSequences: stopSequences + ) + #expect(completion.completion == "This is the textcompletion for: This is a test") + } + + // Prompt + @Test( + "Complete text using a valid prompt", + arguments: NovaTestConstants.TextGeneration.validPrompts + ) + func completeTextWithValidPrompt(prompt: String) async throws { + let completion: TextCompletion = try await bedrock.completeText( + prompt, + with: BedrockModel.nova_micro, + maxTokens: 200 + ) + #expect(completion.completion == "This is the textcompletion for: \(prompt)") + } + + @Test( + "Complete text using an invalid prompt", + arguments: NovaTestConstants.TextGeneration.invalidPrompts + ) + func completeTextWithInvalidPrompt(prompt: String) async throws { + await #expect(throws: BedrockServiceError.self) { + let _: TextCompletion = try await bedrock.completeText( + prompt, + with: BedrockModel.nova_canvas, + maxTokens: 10 + ) + } + } } From 82ea5ded4af35ac6d2ca4d5be564e0c95927d3a1 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Fri, 11 Apr 2025 11:32:21 +0200 Subject: [PATCH 58/67] testing --- backend/Package.swift | 8 +++---- .../BedrockServiceTests.swift | 2 +- .../ConverseDocumentTests.swift | 23 +++++++++++++++++++ .../ConverseToolTests.swift | 23 +++++++++++++++++++ .../ConverseVisionTests.swift | 23 +++++++++++++++++++ 5 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 backend/Tests/BedrockServiceTests/ConverseDocumentTests.swift create mode 100644 backend/Tests/BedrockServiceTests/ConverseToolTests.swift create mode 100644 backend/Tests/BedrockServiceTests/ConverseVisionTests.swift diff --git a/backend/Package.swift b/backend/Package.swift index ce0eea06..48c22087 100644 --- a/backend/Package.swift +++ b/backend/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "SwiftBedrock", - platforms: [.macOS(.v14), .iOS(.v17), .tvOS(.v17)], + platforms: [.macOS(.v15), .iOS(.v18), .tvOS(.v18)], products: [ .executable(name: "PlaygroundAPI", targets: ["PlaygroundAPI"]), .library(name: "BedrockService", targets: ["BedrockService"]), @@ -16,8 +16,7 @@ let package = Package( .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0"), .package(url: "https://github.com/awslabs/aws-sdk-swift", from: "1.2.54"), .package(url: "https://github.com/smithy-lang/smithy-swift", from: "0.118.0"), - .package(url: "https://github.com/swiftlang/swift-testing", branch: "main"), - .package(url: "https://github.com/apple/swift-log.git", from: "1.5.3"), + .package(url: "https://github.com/apple/swift-log.git", from: "1.5.0"), ], targets: [ .executableTarget( @@ -52,8 +51,7 @@ let package = Package( .testTarget( name: "BedrockServiceTests", dependencies: [ - .target(name: "BedrockService"), - .product(name: "Testing", package: "swift-testing"), + .target(name: "BedrockService") ], path: "Tests/BedrockServiceTests" ), diff --git a/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift b/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift index cd695e15..f5d15744 100644 --- a/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift +++ b/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift @@ -18,7 +18,7 @@ import Testing @testable import BedrockService @testable import BedrockTypes -@Suite("SwiftBedrockService Tests") +@Suite("BedrockService Tests") struct BedrockServiceTests { let bedrock: BedrockService diff --git a/backend/Tests/BedrockServiceTests/ConverseDocumentTests.swift b/backend/Tests/BedrockServiceTests/ConverseDocumentTests.swift new file mode 100644 index 00000000..5092df80 --- /dev/null +++ b/backend/Tests/BedrockServiceTests/ConverseDocumentTests.swift @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Testing + +@testable import BedrockService +@testable import BedrockTypes + +// Converse document + +// extension BedrockServiceTests \ No newline at end of file diff --git a/backend/Tests/BedrockServiceTests/ConverseToolTests.swift b/backend/Tests/BedrockServiceTests/ConverseToolTests.swift new file mode 100644 index 00000000..3c0c442a --- /dev/null +++ b/backend/Tests/BedrockServiceTests/ConverseToolTests.swift @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Testing + +@testable import BedrockService +@testable import BedrockTypes + +// Converse tools + +// extension BedrockServiceTests diff --git a/backend/Tests/BedrockServiceTests/ConverseVisionTests.swift b/backend/Tests/BedrockServiceTests/ConverseVisionTests.swift new file mode 100644 index 00000000..7ce92f66 --- /dev/null +++ b/backend/Tests/BedrockServiceTests/ConverseVisionTests.swift @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Foundation Models Playground open source project +// +// Copyright (c) 2025 Amazon.com, Inc. or its affiliates +// and the Swift Foundation Models Playground project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Testing + +@testable import BedrockService +@testable import BedrockTypes + +// Converse vision + +// extension BedrockServiceTests From c39fb2a3acddfcb16382e15ba7bf04d896c45667 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Fri, 11 Apr 2025 11:33:55 +0200 Subject: [PATCH 59/67] formatting --- backend/Tests/BedrockServiceTests/ConverseDocumentTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/Tests/BedrockServiceTests/ConverseDocumentTests.swift b/backend/Tests/BedrockServiceTests/ConverseDocumentTests.swift index 5092df80..8a702673 100644 --- a/backend/Tests/BedrockServiceTests/ConverseDocumentTests.swift +++ b/backend/Tests/BedrockServiceTests/ConverseDocumentTests.swift @@ -20,4 +20,4 @@ import Testing // Converse document -// extension BedrockServiceTests \ No newline at end of file +// extension BedrockServiceTests From fb4899a31ed11741b1e55d3f85ed50e37b4d285a Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Fri, 11 Apr 2025 12:05:10 +0200 Subject: [PATCH 60/67] actions --- .github/workflows/build_test_soundness.yml | 7 ++----- backend/Package.swift | 4 +++- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_test_soundness.yml b/.github/workflows/build_test_soundness.yml index 2d92d0a9..06991c16 100644 --- a/.github/workflows/build_test_soundness.yml +++ b/.github/workflows/build_test_soundness.yml @@ -1,10 +1,7 @@ name: Build And Test on EC2 -on: - push: - branches: ["main", "week15"] - pull_request: - branches: ["main"] + +on: [push, pull_request] jobs: build: diff --git a/backend/Package.swift b/backend/Package.swift index 48c22087..6fad4033 100644 --- a/backend/Package.swift +++ b/backend/Package.swift @@ -16,6 +16,7 @@ let package = Package( .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0"), .package(url: "https://github.com/awslabs/aws-sdk-swift", from: "1.2.54"), .package(url: "https://github.com/smithy-lang/smithy-swift", from: "0.118.0"), + .package(url: "https://github.com/swiftlang/swift-testing", branch: "main"), .package(url: "https://github.com/apple/swift-log.git", from: "1.5.0"), ], targets: [ @@ -51,7 +52,8 @@ let package = Package( .testTarget( name: "BedrockServiceTests", dependencies: [ - .target(name: "BedrockService") + .target(name: "BedrockService"), + .product(name: "Testing", package: "swift-testing"), ], path: "Tests/BedrockServiceTests" ), From 1f6756d69791ddf9776b817edf1ce48aa42b3dac Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Fri, 11 Apr 2025 17:31:59 +0200 Subject: [PATCH 61/67] testing --- backend/Package.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/Package.swift b/backend/Package.swift index 6fad4033..82f9bb7d 100644 --- a/backend/Package.swift +++ b/backend/Package.swift @@ -45,6 +45,7 @@ let package = Package( name: "BedrockTypes", dependencies: [ .product(name: "AWSBedrockRuntime", package: "aws-sdk-swift"), + .product(name: "AWSBedrock", package: "aws-sdk-swift"), .product(name: "Smithy", package: "smithy-swift"), ], path: "Sources/BedrockTypes" From a4c6c0142beac359e67a73138a12d6582c02a9e6 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Fri, 11 Apr 2025 17:57:02 +0200 Subject: [PATCH 62/67] moved libraries to different repository --- backend/Package.swift | 40 +- .../BedrockService+ParameterValidation.swift | 279 --------- .../BedrockService/BedrockService.swift | 185 ------ .../Converse/BedrockService+Converse.swift | 576 ------------------ .../Converse/ConverseRequest.swift | 120 ---- .../Converse/ConverseResponse.swift | 49 -- .../BedrockService+InvokeModelImage.swift | 259 -------- .../BedrockService+InvokeModelText.swift | 136 ----- .../InvokeModel/InvokeModelRequest.swift | 224 ------- .../InvokeModel/InvokeModelResponse.swift | 109 ---- .../Protocols/BedrockClientProtocol.swift | 29 - .../BedrockRuntimeClientProtocol.swift | 28 - .../Sources/BedrockTypes/BedrockModel.swift | 338 ---------- .../BedrockTypes/BedrockServiceError.swift | 32 - .../BedrockTypes/Converse/Content.swift | 72 --- .../BedrockTypes/Converse/DocumentBlock.swift | 113 ---- .../Converse/DocumentToJSON.swift | 48 -- .../BedrockTypes/Converse/ImageBlock.swift | 90 --- .../Sources/BedrockTypes/Converse/JSON.swift | 72 --- .../Converse/JSONtoDocument.swift | 25 - .../BedrockTypes/Converse/Message.swift | 79 --- .../BedrockTypes/Converse/S3Location.swift | 43 -- .../Sources/BedrockTypes/Converse/Tool.swift | 62 -- .../Converse/ToolResultBlock.swift | 200 ------ .../BedrockTypes/Converse/ToolUseBlock.swift | 60 -- .../BedrockTypes/Converse/VideoBlock.swift | 131 ---- .../InvokeModel/ContentType.swift | 27 - .../InvokeModel/ImageGenerationOutput.swift | 20 - .../InvokeModel/ImageResolution.swift | 21 - .../BedrockTypes/InvokeModel/Protocols.swift | 26 - .../InvokeModel/TextCompletion.swift | 24 - .../ListModels/ModelSummary.swift | 74 --- .../Modalities/ConverseModality.swift | 46 -- .../Modalities/ImageModality.swift | 67 -- .../BedrockTypes/Modalities/Modality.swift | 20 - .../Modalities/StandardConverse.swift | 31 - .../Modalities/TextModality.swift | 32 - .../Models/Amazon/AmazonImage.swift | 118 ---- .../Amazon/AmazonImageRequestBody.swift | 338 ---------- .../Amazon/AmazonImageResponseBody.swift | 24 - .../Models/Amazon/Nova/Nova.swift | 60 -- .../Amazon/Nova/NovaBedrockModels.swift | 100 --- .../Nova/NovaImageResolutionValidator.swift | 62 -- .../Models/Amazon/Nova/NovaRequestBody.swift | 56 -- .../Models/Amazon/Nova/NovaResponseBody.swift | 53 -- .../BedrockTypes/Models/Amazon/TaskType.swift | 25 - .../Models/Amazon/Titan/Titan.swift | 75 --- .../Amazon/Titan/TitanBedrockModels.swift | 126 ---- .../Titan/TitanImageResolutionValidator.swift | 60 -- .../Amazon/Titan/TitanRequestBody.swift | 44 -- .../Amazon/Titan/TitanResponseBody.swift | 34 -- .../Models/Anthropic/Anthropic.swift | 70 --- .../Anthropic/AnthropicBedrockModels.swift | 182 ------ .../Anthropic/AnthropicRequestBody.swift | 60 -- .../Anthropic/AnthropicResponseBody.swift | 48 -- .../Models/Cohere/CohereBedrockModels.swift | 52 -- .../Models/DeepSeek/DeepSeek.swift | 74 --- .../DeepSeek/DeepSeekBedrockModels.swift | 35 -- .../Models/DeepSeek/DeepSeekRequestBody.swift | 38 -- .../DeepSeek/DeepSeekResponseBody.swift | 33 - .../Models/Jamba/JambaBedrockModels.swift | 22 - .../BedrockTypes/Models/Llama/Llama.swift | 64 -- .../Models/Llama/LlamaBedrockModels.swift | 126 ---- .../Models/Llama/LlamaRequestBody.swift | 36 -- .../Models/Llama/LlamaResponseBody.swift | 28 - .../Models/Mistral/MistralBedrockModels.swift | 81 --- .../Parameters/ConverseParameters.swift | 46 -- .../ImageGenerationParameters.swift | 97 --- .../BedrockTypes/Parameters/Parameters.swift | 80 --- .../Parameters/TextGenerationParameters.swift | 41 -- backend/Sources/BedrockTypes/Region.swift | 305 ---------- backend/Sources/BedrockTypes/Role.swift | 40 -- .../BedrockServiceTests.swift | 42 -- .../ConverseDocumentTests.swift | 23 - .../ConverseTextTests.swift | 155 ----- .../ConverseToolTests.swift | 23 - .../ConverseVisionTests.swift | 23 - .../ImageGenerationTests.swift | 162 ----- .../ImageVariationTests.swift | 196 ------ .../Mock/MockBedrockClient.swift | 69 --- .../Mock/MockBedrockRuntimeClient.swift | 226 ------- .../NovaTestConstants.swift | 78 --- .../TextGenerationTests.swift | 229 ------- 83 files changed, 3 insertions(+), 7743 deletions(-) delete mode 100644 backend/Sources/BedrockService/BedrockService+ParameterValidation.swift delete mode 100644 backend/Sources/BedrockService/BedrockService.swift delete mode 100644 backend/Sources/BedrockService/Converse/BedrockService+Converse.swift delete mode 100644 backend/Sources/BedrockService/Converse/ConverseRequest.swift delete mode 100644 backend/Sources/BedrockService/Converse/ConverseResponse.swift delete mode 100644 backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelImage.swift delete mode 100644 backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelText.swift delete mode 100644 backend/Sources/BedrockService/InvokeModel/InvokeModelRequest.swift delete mode 100644 backend/Sources/BedrockService/InvokeModel/InvokeModelResponse.swift delete mode 100644 backend/Sources/BedrockService/Protocols/BedrockClientProtocol.swift delete mode 100644 backend/Sources/BedrockService/Protocols/BedrockRuntimeClientProtocol.swift delete mode 100644 backend/Sources/BedrockTypes/BedrockModel.swift delete mode 100644 backend/Sources/BedrockTypes/BedrockServiceError.swift delete mode 100644 backend/Sources/BedrockTypes/Converse/Content.swift delete mode 100644 backend/Sources/BedrockTypes/Converse/DocumentBlock.swift delete mode 100644 backend/Sources/BedrockTypes/Converse/DocumentToJSON.swift delete mode 100644 backend/Sources/BedrockTypes/Converse/ImageBlock.swift delete mode 100644 backend/Sources/BedrockTypes/Converse/JSON.swift delete mode 100644 backend/Sources/BedrockTypes/Converse/JSONtoDocument.swift delete mode 100644 backend/Sources/BedrockTypes/Converse/Message.swift delete mode 100644 backend/Sources/BedrockTypes/Converse/S3Location.swift delete mode 100644 backend/Sources/BedrockTypes/Converse/Tool.swift delete mode 100644 backend/Sources/BedrockTypes/Converse/ToolResultBlock.swift delete mode 100644 backend/Sources/BedrockTypes/Converse/ToolUseBlock.swift delete mode 100644 backend/Sources/BedrockTypes/Converse/VideoBlock.swift delete mode 100644 backend/Sources/BedrockTypes/InvokeModel/ContentType.swift delete mode 100644 backend/Sources/BedrockTypes/InvokeModel/ImageGenerationOutput.swift delete mode 100644 backend/Sources/BedrockTypes/InvokeModel/ImageResolution.swift delete mode 100644 backend/Sources/BedrockTypes/InvokeModel/Protocols.swift delete mode 100644 backend/Sources/BedrockTypes/InvokeModel/TextCompletion.swift delete mode 100644 backend/Sources/BedrockTypes/ListModels/ModelSummary.swift delete mode 100644 backend/Sources/BedrockTypes/Modalities/ConverseModality.swift delete mode 100644 backend/Sources/BedrockTypes/Modalities/ImageModality.swift delete mode 100644 backend/Sources/BedrockTypes/Modalities/Modality.swift delete mode 100644 backend/Sources/BedrockTypes/Modalities/StandardConverse.swift delete mode 100644 backend/Sources/BedrockTypes/Modalities/TextModality.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Amazon/AmazonImage.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Amazon/AmazonImageRequestBody.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Amazon/AmazonImageResponseBody.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaImageResolutionValidator.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaRequestBody.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaResponseBody.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Amazon/TaskType.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Amazon/Titan/Titan.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanBedrockModels.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanImageResolutionValidator.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanRequestBody.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanResponseBody.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Anthropic/Anthropic.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Anthropic/AnthropicBedrockModels.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Anthropic/AnthropicRequestBody.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Anthropic/AnthropicResponseBody.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Cohere/CohereBedrockModels.swift delete mode 100644 backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeek.swift delete mode 100644 backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeekBedrockModels.swift delete mode 100644 backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeekRequestBody.swift delete mode 100644 backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeekResponseBody.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Jamba/JambaBedrockModels.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Llama/Llama.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Llama/LlamaBedrockModels.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Llama/LlamaRequestBody.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Llama/LlamaResponseBody.swift delete mode 100644 backend/Sources/BedrockTypes/Models/Mistral/MistralBedrockModels.swift delete mode 100644 backend/Sources/BedrockTypes/Parameters/ConverseParameters.swift delete mode 100644 backend/Sources/BedrockTypes/Parameters/ImageGenerationParameters.swift delete mode 100644 backend/Sources/BedrockTypes/Parameters/Parameters.swift delete mode 100644 backend/Sources/BedrockTypes/Parameters/TextGenerationParameters.swift delete mode 100644 backend/Sources/BedrockTypes/Region.swift delete mode 100644 backend/Sources/BedrockTypes/Role.swift delete mode 100644 backend/Tests/BedrockServiceTests/BedrockServiceTests.swift delete mode 100644 backend/Tests/BedrockServiceTests/ConverseDocumentTests.swift delete mode 100644 backend/Tests/BedrockServiceTests/ConverseTextTests.swift delete mode 100644 backend/Tests/BedrockServiceTests/ConverseToolTests.swift delete mode 100644 backend/Tests/BedrockServiceTests/ConverseVisionTests.swift delete mode 100644 backend/Tests/BedrockServiceTests/ImageGenerationTests.swift delete mode 100644 backend/Tests/BedrockServiceTests/ImageVariationTests.swift delete mode 100644 backend/Tests/BedrockServiceTests/Mock/MockBedrockClient.swift delete mode 100644 backend/Tests/BedrockServiceTests/Mock/MockBedrockRuntimeClient.swift delete mode 100644 backend/Tests/BedrockServiceTests/NovaTestConstants.swift delete mode 100644 backend/Tests/BedrockServiceTests/TextGenerationTests.swift diff --git a/backend/Package.swift b/backend/Package.swift index 82f9bb7d..b9422601 100644 --- a/backend/Package.swift +++ b/backend/Package.swift @@ -8,55 +8,21 @@ let package = Package( platforms: [.macOS(.v15), .iOS(.v18), .tvOS(.v18)], products: [ .executable(name: "PlaygroundAPI", targets: ["PlaygroundAPI"]), - .library(name: "BedrockService", targets: ["BedrockService"]), - .library(name: "BedrockTypes", targets: ["BedrockTypes"]), ], dependencies: [ .package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.0.0"), .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0"), - .package(url: "https://github.com/awslabs/aws-sdk-swift", from: "1.2.54"), - .package(url: "https://github.com/smithy-lang/smithy-swift", from: "0.118.0"), - .package(url: "https://github.com/swiftlang/swift-testing", branch: "main"), - .package(url: "https://github.com/apple/swift-log.git", from: "1.5.0"), + .package(url: "https://github.com/monadierickx/swift-bedrock-library.git", branch: "main"), ], targets: [ .executableTarget( name: "PlaygroundAPI", dependencies: [ - .target(name: "BedrockService"), .product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "Hummingbird", package: "hummingbird"), + .product(name: "BedrockService", package: "swift-bedrock-library"), ], path: "Sources/PlaygroundAPI" - ), - .target( - name: "BedrockService", - dependencies: [ - .target(name: "BedrockTypes"), - .product(name: "AWSClientRuntime", package: "aws-sdk-swift"), - .product(name: "AWSBedrock", package: "aws-sdk-swift"), - .product(name: "AWSBedrockRuntime", package: "aws-sdk-swift"), - .product(name: "Smithy", package: "smithy-swift"), - .product(name: "Logging", package: "swift-log"), - ], - path: "Sources/BedrockService" - ), - .target( - name: "BedrockTypes", - dependencies: [ - .product(name: "AWSBedrockRuntime", package: "aws-sdk-swift"), - .product(name: "AWSBedrock", package: "aws-sdk-swift"), - .product(name: "Smithy", package: "smithy-swift"), - ], - path: "Sources/BedrockTypes" - ), - .testTarget( - name: "BedrockServiceTests", - dependencies: [ - .target(name: "BedrockService"), - .product(name: "Testing", package: "swift-testing"), - ], - path: "Tests/BedrockServiceTests" - ), + ) ] ) diff --git a/backend/Sources/BedrockService/BedrockService+ParameterValidation.swift b/backend/Sources/BedrockService/BedrockService+ParameterValidation.swift deleted file mode 100644 index 65259064..00000000 --- a/backend/Sources/BedrockService/BedrockService+ParameterValidation.swift +++ /dev/null @@ -1,279 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import BedrockTypes -import Foundation - -extension BedrockService { - - /// Validates text completion parameters against the model's capabilities and constraints - /// - Parameters: - /// - modality: The text modality of the model - /// - prompt: The input text prompt to validate - /// - maxTokens: Optional maximum number of tokens to generate - /// - temperature: Optional temperature parameter for controlling randomness - /// - topP: Optional top-p parameter for nucleus sampling - /// - topK: Optional top-k parameter for filtering - /// - stopSequences: Optional array of sequences where generation should stop - /// - Throws: BedrockServiceError for invalid parameters - public func validateTextCompletionParams( - modality: any TextModality, - prompt: String? = nil, - maxTokens: Int? = nil, - temperature: Double? = nil, - topP: Double? = nil, - topK: Int? = nil, - stopSequences: [String]? = nil - ) throws { - let parameters = modality.getParameters() - if maxTokens != nil { - try validateParameterValue(maxTokens!, parameter: parameters.maxTokens) - } - if temperature != nil { - try validateParameterValue(temperature!, parameter: parameters.temperature) - } - if topP != nil { - try validateParameterValue(topP!, parameter: parameters.topP) - } - if topK != nil { - try validateParameterValue(topK!, parameter: parameters.topK) - } - if stopSequences != nil { - try validateStopSequences(stopSequences!, maxNrOfStopSequences: parameters.stopSequences.maxSequences) - } - if prompt != nil { - try validatePrompt(prompt!, maxPromptTokens: parameters.prompt.maxSize) - } - } - - /// Validates image generation parameters against the model's capabilities and constraints - /// - Parameters: - /// - modality: The image modality of the model - /// - nrOfImages: Optional number of images to generate - /// - cfgScale: Optional classifier free guidance scale - /// - resolution: Optional image resolution settings - /// - seed: Optional seed for reproducible generation - /// - Throws: BedrockServiceError for invalid parameters - public func validateImageGenerationParams( - modality: any ImageModality, - nrOfImages: Int? = nil, - cfgScale: Double? = nil, - resolution: ImageResolution? = nil, - seed: Int? = nil - ) throws { - let parameters = modality.getParameters() - if nrOfImages != nil { - try validateParameterValue(nrOfImages!, parameter: parameters.nrOfImages) - } - if cfgScale != nil { - try validateParameterValue(cfgScale!, parameter: parameters.cfgScale) - } - if seed != nil { - try validateParameterValue(seed!, parameter: parameters.seed) - } - if resolution != nil { - try modality.validateResolution(resolution!) - } - } - - /// Validates parameters for text-to-image generation requests - /// - Parameters: - /// - modality: The text-to-image modality of the model to use - /// - prompt: The input text prompt describing the desired image - /// - negativePrompt: Optional text describing what to avoid in the generated image - /// - Throws: BedrockServiceError if the parameters are invalid or exceed model constraints - public func validateTextToImageParams( - modality: any TextToImageModality, - prompt: String, - negativePrompt: String? = nil - ) throws { - let textToImageParameters = modality.getTextToImageParameters() - try validatePrompt(prompt, maxPromptTokens: textToImageParameters.prompt.maxSize) - if negativePrompt != nil { - try validatePrompt(negativePrompt!, maxPromptTokens: textToImageParameters.negativePrompt.maxSize) - } - } - - /// Validates image variation generation parameters - /// - Parameters: - /// - modality: The image variation modality of the model - /// - images: Array of base64 encoded images to use as reference - /// - prompt: Text prompt describing desired variations - /// - similarity: Optional parameter controlling variation similarity - /// - negativePrompt: Optional text describing what to avoid - /// - Throws: BedrockServiceError for invalid parameters - public func validateImageVariationParams( - modality: any ImageVariationModality, - images: [String], - prompt: String? = nil, - similarity: Double? = nil, - negativePrompt: String? = nil - ) throws { - let imageVariationParameters = modality.getImageVariationParameters() - try validateParameterValue(images.count, parameter: imageVariationParameters.images) - if prompt != nil { - try validatePrompt(prompt!, maxPromptTokens: imageVariationParameters.prompt.maxSize) - } - if similarity != nil { - try validateParameterValue(similarity!, parameter: imageVariationParameters.similarity) - } - if negativePrompt != nil { - try validatePrompt(negativePrompt!, maxPromptTokens: imageVariationParameters.negativePrompt.maxSize) - } - } - - /// Validates conversation parameters for the model - /// - Parameters: - /// - modality: The conversation modality of the model - /// - maxTokens: Optional maximum number of tokens to generate - /// - temperature: Optional temperature parameter for controlling randomness - /// - topP: Optional top-p parameter for nucleus sampling - /// - stopSequences: Optional array of sequences where generation should stop - /// - Throws: BedrockServiceError for invalid parameters - public func validateConverseParams( - modality: any ConverseModality, - prompt: String? = nil, - maxTokens: Int? = nil, - temperature: Double? = nil, - topP: Double? = nil, - stopSequences: [String]? = nil - ) throws { - let parameters = modality.getConverseParameters() - if prompt != nil { - try validatePrompt(prompt!, maxPromptTokens: parameters.prompt.maxSize) - } - if maxTokens != nil { - try validateParameterValue(maxTokens!, parameter: parameters.maxTokens) - } - if temperature != nil { - try validateParameterValue(temperature!, parameter: parameters.temperature) - } - if topP != nil { - try validateParameterValue(topP!, parameter: parameters.topP) - } - if stopSequences != nil { - try validateStopSequences(stopSequences!, maxNrOfStopSequences: parameters.stopSequences.maxSequences) - } - } - - // MARK: private helpers - - // Validate a parameter value with the min and max value saved in the Parameter - private func validateParameterValue(_ value: T, parameter: Parameter) throws { - guard parameter.isSupported else { - logger.trace("Unsupported parameter", metadata: ["parameter": "\(parameter.name)"]) - throw BedrockServiceError.notSupported("Parameter \(parameter.name) is not supported.") - } - if let min = parameter.minValue { - guard value >= min else { - logger.trace( - "Invalid parameter", - metadata: [ - "parameter": "\(parameter.name)", - "value": "\(value)", - "value.min": "\(min)", - ] - ) - throw BedrockServiceError.invalidParameter( - parameter.name, - "Parameter \(parameter.name) should be at least \(min). Value: \(value)" - ) - } - } - if let max = parameter.maxValue { - guard value <= max else { - logger.trace( - "Invalid parameter", - metadata: [ - "parameter": "\(parameter.name)", - "value": "\(value)", - "value.max": "\(max)", - ] - ) - throw BedrockServiceError.invalidParameter( - parameter.name, - "Parameter \(parameter.name) should be at most \(max). Value: \(value)" - ) - } - } - logger.trace( - "Valid parameter", - metadata: [ - "parameter": "\(parameter.name)", "value": "\(value)", - "value.min": "\(String(describing: parameter.minValue))", - "value.max": "\(String(describing: parameter.maxValue))", - ] - ) - } - - /// Validate prompt is not empty and does not consist of only whitespaces, tabs or newlines - /// Additionally validates that the prompt is not longer than the maxPromptTokens - private func validatePrompt(_ prompt: String, maxPromptTokens: Int?) throws { - guard !prompt.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).isEmpty else { - logger.trace("Invalid prompt", metadata: ["prompt": .string(prompt)]) - throw BedrockServiceError.invalidPrompt("Prompt is not allowed to be empty.") - } - if let maxPromptTokens = maxPromptTokens { - let length = prompt.utf8.count - guard length <= maxPromptTokens else { - logger.trace( - "Invalid prompt", - metadata: [ - "prompt": .string(prompt), - "prompt.length": "\(length)", - "maxPromptTokens": "\(maxPromptTokens)", - ] - ) - throw BedrockServiceError.invalidPrompt( - "Prompt is not allowed to be longer than \(maxPromptTokens) tokens. Prompt lengt \(length)" - ) - } - } - logger.trace( - "Valid prompt", - metadata: [ - "prompt": "\(prompt)", - "prompt.maxPromptTokens": "\(String(describing: maxPromptTokens))", - ] - ) - } - - /// Validate that not more stopsequences than allowed were given - private func validateStopSequences(_ stopSequences: [String], maxNrOfStopSequences: Int?) throws { - if let maxNrOfStopSequences = maxNrOfStopSequences { - guard stopSequences.count <= maxNrOfStopSequences else { - logger.trace( - "Invalid stopSequences", - metadata: [ - "stopSequences": "\(stopSequences)", - "stopSequences.count": "\(stopSequences.count)", - "maxNrOfStopSequences": "\(maxNrOfStopSequences)", - ] - ) - throw BedrockServiceError.invalidStopSequences( - stopSequences, - "You can only provide up to \(maxNrOfStopSequences) stop sequences. Number of stop sequences: \(stopSequences.count)" - ) - } - } - logger.trace( - "Valid stop sequences", - metadata: [ - "stopSequences": "\(stopSequences)", - "stopSequences.maxNrOfStopSequences": "\(String(describing: maxNrOfStopSequences))", - ] - ) - } -} diff --git a/backend/Sources/BedrockService/BedrockService.swift b/backend/Sources/BedrockService/BedrockService.swift deleted file mode 100644 index d89d33d1..00000000 --- a/backend/Sources/BedrockService/BedrockService.swift +++ /dev/null @@ -1,185 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrock -@preconcurrency import AWSBedrockRuntime -import AWSClientRuntime -import AWSSDKIdentity -import BedrockTypes -import Foundation -import Logging - -public struct BedrockService: Sendable { - package let region: Region - package let logger: Logger - package let bedrockClient: BedrockClientProtocol - package let bedrockRuntimeClient: BedrockRuntimeClientProtocol - - // MARK: - Initialization - - /// Initializes a new SwiftBedrock instance - /// - Parameters: - /// - region: The AWS region to use (defaults to .useast1) - /// - logger: Optional custom logger instance - /// - bedrockClient: Optional custom Bedrock client - /// - bedrockRuntimeClient: Optional custom Bedrock Runtime client - /// - useSSO: Whether to use SSO authentication (defaults to false) - /// - Throws: Error if client initialization fails - public init( - region: Region = .useast1, - logger: Logger? = nil, - bedrockClient: BedrockClientProtocol? = nil, - bedrockRuntimeClient: BedrockRuntimeClientProtocol? = nil, - useSSO: Bool = false, - ssoProfileName: String? = nil - ) async throws { - self.logger = logger ?? BedrockService.createLogger("bedrock.service") - self.logger.trace( - "Initializing SwiftBedrock", - metadata: ["region": .string(region.rawValue)] - ) - self.region = region - - if bedrockClient != nil { - self.logger.trace("Using supplied bedrockClient") - self.bedrockClient = bedrockClient! - } else { - self.logger.trace("Creating bedrockClient") - self.bedrockClient = try await BedrockService.createBedrockClient( - region: region, - useSSO: useSSO, - ssoProfileName: ssoProfileName - ) - self.logger.trace( - "Created bedrockClient", - metadata: ["useSSO": "\(useSSO)"] - ) - } - if bedrockRuntimeClient != nil { - self.logger.trace("Using supplied bedrockRuntimeClient") - self.bedrockRuntimeClient = bedrockRuntimeClient! - } else { - self.logger.trace("Creating bedrockRuntimeClient") - self.bedrockRuntimeClient = try await BedrockService.createBedrockRuntimeClient( - region: region, - useSSO: useSSO, - ssoProfileName: ssoProfileName - ) - self.logger.trace( - "Created bedrockRuntimeClient", - metadata: ["useSSO": "\(useSSO)"] - ) - } - self.logger.trace( - "Initialized SwiftBedrock", - metadata: ["region": .string(region.rawValue)] - ) - } - - // MARK: - Private Helpers - - /// Creates Logger using either the loglevel saved as environment variable `SWIFT_BEDROCK_LOG_LEVEL` or with default `.trace` - /// - Parameter name: The name/label for the logger - /// - Returns: Configured Logger instance - static private func createLogger(_ name: String) -> Logger { - var logger: Logger = Logger(label: name) - logger.logLevel = - ProcessInfo.processInfo.environment["SWIFT_BEDROCK_LOG_LEVEL"].flatMap { - Logger.Level(rawValue: $0.lowercased()) - } ?? .trace // FIXME: trace for me, later .info - return logger - } - - /// Creates a BedrockClient - /// - Parameters: - /// - region: The AWS region to configure the client for - /// - useSSO: Whether to use SSO authentication - /// - Returns: Configured BedrockClientProtocol instance - /// - Throws: Error if client creation fails - static private func createBedrockClient( - region: Region, - useSSO: Bool = false, - ssoProfileName: String? = nil - ) async throws - -> BedrockClientProtocol - { - let config = try await BedrockClient.BedrockClientConfiguration( - region: region.rawValue - ) - if useSSO { - config.awsCredentialIdentityResolver = try SSOAWSCredentialIdentityResolver(profileName: ssoProfileName) - } - return BedrockClient(config: config) - } - - /// Creates a BedrockRuntimeClient - /// - Parameters: - /// - region: The AWS region to configure the client for - /// - useSSO: Whether to use SSO authentication - /// - Returns: Configured BedrockRuntimeClientProtocol instance - /// - Throws: Error if client creation fails - static private func createBedrockRuntimeClient( - region: Region, - useSSO: Bool = false, - ssoProfileName: String? = nil - ) - async throws - -> BedrockRuntimeClientProtocol - { - let config = - try await BedrockRuntimeClient.BedrockRuntimeClientConfiguration( - region: region.rawValue - ) - if useSSO { - config.awsCredentialIdentityResolver = try SSOAWSCredentialIdentityResolver(profileName: ssoProfileName) - } - return BedrockRuntimeClient(config: config) - } - - // MARK: Public Methods - - /// Lists all available foundation models from Amazon Bedrock - /// - Throws: BedrockServiceError.invalidResponse - /// - Returns: An array of ModelSummary objects containing details about each available model - public func listModels() async throws -> [ModelSummary] { - logger.trace("Fetching foundation models") - do { - let response = try await bedrockClient.listFoundationModels( - input: ListFoundationModelsInput() - ) - guard let models = response.modelSummaries else { - logger.trace("Failed to extract modelSummaries from response") - throw BedrockServiceError.invalidSDKResponse( - "Something went wrong while extracting the modelSummaries from the response." - ) - } - var modelsInfo: [ModelSummary] = [] - modelsInfo = try models.compactMap { (sdkModelSummary) -> ModelSummary? in - try ModelSummary.getModelSummary(from: sdkModelSummary) - } - logger.trace( - "Fetched foundation models", - metadata: [ - "models.count": "\(modelsInfo.count)", - "models.content": .string(String(describing: modelsInfo)), - ] - ) - return modelsInfo - } catch { - logger.trace("Error while listing foundation models", metadata: ["error": "\(error)"]) - throw error - } - } -} diff --git a/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift b/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift deleted file mode 100644 index 1ccb57b8..00000000 --- a/backend/Sources/BedrockService/Converse/BedrockService+Converse.swift +++ /dev/null @@ -1,576 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import BedrockTypes -import Foundation - -extension BedrockService { - - /// Converse with a model using the Bedrock Converse API - /// - Parameters: - /// - model: The BedrockModel to converse with - /// - conversation: Array of previous messages in the conversation - /// - maxTokens: Optional maximum number of tokens to generate - /// - temperature: Optional temperature parameter for controlling randomness - /// - topP: Optional top-p parameter for nucleus sampling - /// - stopSequences: Optional array of sequences where generation should stop - /// - systemPrompts: Optional array of system prompts to guide the conversation - /// - tools: Optional array of tools the model can use - /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - /// BedrockServiceError.invalidParameter for invalid parameters - /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - /// BedrockServiceError.invalidModality for invalid modality from the selected model - /// BedrockServiceError.invalidSDKResponse if the response body is missing - /// - Returns: A Message containing the model's response - public func converse( - with model: BedrockModel, - conversation: [Message], - maxTokens: Int? = nil, - temperature: Double? = nil, - topP: Double? = nil, - stopSequences: [String]? = nil, - systemPrompts: [String]? = nil, - tools: [Tool]? = nil - ) async throws -> Message { - do { - let modality: ConverseModality = try model.getConverseModality() - try validateConverseParams( - modality: modality, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - stopSequences: stopSequences - ) - - logger.trace( - "Creating ConverseRequest", - metadata: [ - "model.name": "\(model.name)", - "model.id": "\(model.id)", - "conversation.count": "\(conversation.count)", - "maxToken": "\(String(describing: maxTokens))", - "temperature": "\(String(describing: temperature))", - "topP": "\(String(describing: topP))", - "stopSequences": "\(String(describing: stopSequences))", - "systemPrompts": "\(String(describing: systemPrompts))", - "tools": "\(String(describing: tools))", - ] - ) - let converseRequest = ConverseRequest( - model: model, - messages: conversation, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - stopSequences: stopSequences, - systemPrompts: systemPrompts, - tools: tools - ) - - logger.trace("Creating ConverseInput") - let input = try converseRequest.getConverseInput() - logger.trace( - "Created ConverseInput", - metadata: [ - "input.messages.count": "\(String(describing:input.messages!.count))", - "input.modelId": "\(String(describing:input.modelId!))", - ] - ) - - let response = try await self.bedrockRuntimeClient.converse(input: input) - logger.trace("Received response", metadata: ["response": "\(response)"]) - - guard let converseOutput = response.output else { - logger.trace( - "Invalid response", - metadata: [ - "response": .string(String(describing: response)), - "hasOutput": .stringConvertible(response.output != nil), - ] - ) - throw BedrockServiceError.invalidSDKResponse( - "Something went wrong while extracting ConverseOutput from response." - ) - } - let converseResponse = try ConverseResponse(converseOutput) - return converseResponse.message - } catch { - logger.trace("Error while conversing", metadata: ["error": "\(error)"]) - throw error - } - } - - /// Use Converse API without needing to make Messages - /// - Parameters: - /// - model: The BedrockModel to converse with - /// - prompt: Optional text prompt for the conversation - /// - imageFormat: Optional format for image input - /// - imageBytes: Optional base64 encoded image data - /// - history: Optional array of previous messages - /// - maxTokens: Optional maximum number of tokens to generate - /// - temperature: Optional temperature parameter for controlling randomness - /// - topP: Optional top-p parameter for nucleus sampling - /// - stopSequences: Optional array of sequences where generation should stop - /// - systemPrompts: Optional array of system prompts to guide the conversation - /// - tools: Optional array of tools the model can use - /// - toolResult: Optional result from a previous tool invocation - /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - /// BedrockServiceError.invalidParameter for invalid parameters - /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - /// BedrockServiceError.invalidModality for invalid modality from the selected model - /// BedrockServiceError.invalidSDKResponse if the response body is missing - /// - Returns: Tuple containing the model's response text and updated message history - public func converse( - with model: BedrockModel, - prompt: String? = nil, - imageFormat: ImageBlock.Format? = nil, - imageBytes: String? = nil, - history: [Message] = [], - maxTokens: Int? = nil, - temperature: Double? = nil, - topP: Double? = nil, - stopSequences: [String]? = nil, - systemPrompts: [String]? = nil, - tools: [Tool]? = nil, - toolResult: ToolResultBlock? = nil - ) async throws -> (String, [Message]) { - logger.trace( - "Conversing", - metadata: [ - "model.id": .string(model.id), - "model.modality": .string(model.modality.getName()), - "prompt": .string(prompt ?? "No prompt"), - ] - ) - do { - var messages = history - let modality: ConverseModality = try model.getConverseModality() - - try validateConverseParams(modality: modality, prompt: prompt) - - if tools != nil || toolResult != nil { - guard model.hasConverseModality(.toolUse) else { - throw BedrockServiceError.invalidModality( - model, - modality, - "This model does not support converse tool." - ) - } - } - - if let toolResult { - guard let _: [Tool] = tools else { - throw BedrockServiceError.invalidPrompt("Tool result is defined but tools are not.") - } - guard case .toolUse(_) = messages.last?.content.last else { - throw BedrockServiceError.invalidPrompt("Tool result is defined but last message is not tool use.") - } - messages.append(Message(toolResult)) - } else { - guard let prompt = prompt else { - throw BedrockServiceError.invalidPrompt("Prompt is not defined.") - } - - if let imageFormat, let imageBytes { - guard model.hasConverseModality(.vision) else { - throw BedrockServiceError.invalidModality( - model, - modality, - "This model does not support converse vision." - ) - } - messages.append( - Message(prompt, imageFormat: imageFormat, imageBytes: imageBytes) - ) - } else { - messages.append(Message(prompt)) - } - } - let message = try await converse( - with: model, - conversation: messages, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - stopSequences: stopSequences, - systemPrompts: systemPrompts, - tools: tools - ) - messages.append(message) - logger.trace( - "Received message", - metadata: ["replyMessage": "\(message)", "messages.count": "\(messages.count)"] - ) - let converseResponse = ConverseResponse(message) - return (converseResponse.getReply(), messages) - } catch { - logger.trace("Error while conversing", metadata: ["error": "\(error)"]) - throw error - } - } - - // // MARK: convenience functions to avoid arrays - - // /// Use Converse API without needing to make Messages - // /// - Parameters: - // /// - model: The BedrockModel to converse with - // /// - prompt: Optional text prompt for the conversation - // /// - imageFormat: Optional format for image input - // /// - imageBytes: Optional base64 encoded image data - // /// - history: Optional array of previous messages - // /// - maxTokens: Optional maximum number of tokens to generate - // /// - temperature: Optional temperature parameter for controlling randomness - // /// - topP: Optional top-p parameter for nucleus sampling - // /// - stopSequences: Optional array of sequences where generation should stop - // /// - systemPrompts: Optional array of system prompts to guide the conversation - // /// - tool: Optional tool the model can use - // /// - toolResult: Optional result from a previous tool invocation - // /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - // /// BedrockServiceError.invalidParameter for invalid parameters - // /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - // /// BedrockServiceError.invalidModality for invalid modality from the selected model - // /// BedrockServiceError.invalidSDKResponse if the response body is missing - // /// - Returns: Tuple containing the model's response text and updated message history - // public func converse( - // with model: BedrockModel, - // prompt: String? = nil, - // imageFormat: ImageBlock.Format? = nil, - // imageBytes: String? = nil, - // history: [Message] = [], - // maxTokens: Int? = nil, - // temperature: Double? = nil, - // topP: Double? = nil, - // stopSequences: [String]? = nil, - // systemPrompts: [String]? = nil, - // tool: Tool? = nil, - // toolResult: ToolResultBlock? = nil - // ) async throws -> (String, [Message]) { - // try await converse( - // with: model, - // prompt: prompt, - // imageFormat: imageFormat, - // imageBytes: imageBytes, - // history: history, - // maxTokens: maxTokens, - // temperature: temperature, - // topP: topP, - // stopSequences: stopSequences, - // systemPrompts: systemPrompts, - // tools: tool != nil ? [tool!] : nil, - // toolResult: toolResult - // ) - // } - - // /// Use Converse API without needing to make Messages - // /// - Parameters: - // /// - model: The BedrockModel to converse with - // /// - prompt: Optional text prompt for the conversation - // /// - imageFormat: Optional format for image input - // /// - imageBytes: Optional base64 encoded image data - // /// - history: Optional array of previous messages - // /// - maxTokens: Optional maximum number of tokens to generate - // /// - temperature: Optional temperature parameter for controlling randomness - // /// - topP: Optional top-p parameter for nucleus sampling - // /// - stopSequence: Optional sequence where generation should stop - // /// - systemPrompts: Optional array of system prompts to guide the conversation - // /// - tool: Optional tool the model can use - // /// - toolResult: Optional result from a previous tool invocation - // /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - // /// BedrockServiceError.invalidParameter for invalid parameters - // /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - // /// BedrockServiceError.invalidModality for invalid modality from the selected model - // /// BedrockServiceError.invalidSDKResponse if the response body is missing - // /// - Returns: Tuple containing the model's response text and updated message history - // public func converse( - // with model: BedrockModel, - // prompt: String? = nil, - // imageFormat: ImageBlock.Format? = nil, - // imageBytes: String? = nil, - // history: [Message] = [], - // maxTokens: Int? = nil, - // temperature: Double? = nil, - // topP: Double? = nil, - // stopSequence: String? = nil, - // systemPrompts: [String]? = nil, - // tool: Tool? = nil, - // toolResult: ToolResultBlock? = nil - // ) async throws -> (String, [Message]) { - // try await converse( - // with: model, - // prompt: prompt, - // imageFormat: imageFormat, - // imageBytes: imageBytes, - // history: history, - // maxTokens: maxTokens, - // temperature: temperature, - // topP: topP, - // stopSequences: stopSequence != nil ? [stopSequence!] : nil, - // systemPrompts: systemPrompts, - // tools: tool != nil ? [tool!] : nil, - // toolResult: toolResult - // ) - // } - - // /// Use Converse API without needing to make Messages - // /// - Parameters: - // /// - model: The BedrockModel to converse with - // /// - prompt: Optional text prompt for the conversation - // /// - imageFormat: Optional format for image input - // /// - imageBytes: Optional base64 encoded image data - // /// - history: Optional array of previous messages - // /// - maxTokens: Optional maximum number of tokens to generate - // /// - temperature: Optional temperature parameter for controlling randomness - // /// - topP: Optional top-p parameter for nucleus sampling - // /// - stopSequences: Optional array of sequences where generation should stop - // /// - systemPrompt: Optional system prompt to guide the conversation - // /// - tool: Optional tool the model can use - // /// - toolResult: Optional result from a previous tool invocation - // /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - // /// BedrockServiceError.invalidParameter for invalid parameters - // /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - // /// BedrockServiceError.invalidModality for invalid modality from the selected model - // /// BedrockServiceError.invalidSDKResponse if the response body is missing - // /// - Returns: Tuple containing the model's response text and updated message history - // public func converse( - // with model: BedrockModel, - // prompt: String? = nil, - // imageFormat: ImageBlock.Format? = nil, - // imageBytes: String? = nil, - // history: [Message] = [], - // maxTokens: Int? = nil, - // temperature: Double? = nil, - // topP: Double? = nil, - // stopSequences: [String]? = nil, - // systemPrompt: String? = nil, - // tool: Tool? = nil, - // toolResult: ToolResultBlock? = nil - // ) async throws -> (String, [Message]) { - // try await converse( - // with: model, - // prompt: prompt, - // imageFormat: imageFormat, - // imageBytes: imageBytes, - // history: history, - // maxTokens: maxTokens, - // temperature: temperature, - // topP: topP, - // stopSequences: stopSequences, - // systemPrompts: systemPrompt != nil ? [systemPrompt!] : nil, - // tools: tool != nil ? [tool!] : nil, - // toolResult: toolResult - // ) - // } - - // /// Use Converse API without needing to make Messages - // /// - Parameters: - // /// - model: The BedrockModel to converse with - // /// - prompt: Optional text prompt for the conversation - // /// - imageFormat: Optional format for image input - // /// - imageBytes: Optional base64 encoded image data - // /// - history: Optional array of previous messages - // /// - maxTokens: Optional maximum number of tokens to generate - // /// - temperature: Optional temperature parameter for controlling randomness - // /// - topP: Optional top-p parameter for nucleus sampling - // /// - stopSequence: Optional sequence where generation should stop - // /// - systemPrompt: Optional system prompt to guide the conversation - // /// - tool: Optional tool the model can use - // /// - toolResult: Optional result from a previous tool invocation - // /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - // /// BedrockServiceError.invalidParameter for invalid parameters - // /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - // /// BedrockServiceError.invalidModality for invalid modality from the selected model - // /// BedrockServiceError.invalidSDKResponse if the response body is missing - // /// - Returns: Tuple containing the model's response text and updated message history - // public func converse( - // with model: BedrockModel, - // prompt: String? = nil, - // imageFormat: ImageBlock.Format? = nil, - // imageBytes: String? = nil, - // history: [Message] = [], - // maxTokens: Int? = nil, - // temperature: Double? = nil, - // topP: Double? = nil, - // stopSequence: String? = nil, - // systemPrompt: String? = nil, - // tool: Tool? = nil, - // toolResult: ToolResultBlock? = nil - // ) async throws -> (String, [Message]) { - // try await converse( - // with: model, - // prompt: prompt, - // imageFormat: imageFormat, - // imageBytes: imageBytes, - // history: history, - // maxTokens: maxTokens, - // temperature: temperature, - // topP: topP, - // stopSequences: stopSequence != nil ? [stopSequence!] : nil, - // systemPrompts: systemPrompt != nil ? [systemPrompt!] : nil, - // tools: tool != nil ? [tool!] : nil, - // toolResult: toolResult - // ) - // } - - // /// Use Converse API without needing to make Messages - // /// - Parameters: - // /// - model: The BedrockModel to converse with - // /// - prompt: Optional text prompt for the conversation - // /// - imageFormat: Optional format for image input - // /// - imageBytes: Optional base64 encoded image data - // /// - history: Optional array of previous messages - // /// - maxTokens: Optional maximum number of tokens to generate - // /// - temperature: Optional temperature parameter for controlling randomness - // /// - topP: Optional top-p parameter for nucleus sampling - // /// - stopSequences: Optional array of sequences where generation should stop - // /// - systemPrompt: Optional system prompt to guide the conversation - // /// - tools: Optional array of tools the model can use - // /// - toolResult: Optional result from a previous tool invocation - // /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - // /// BedrockServiceError.invalidParameter for invalid parameters - // /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - // /// BedrockServiceError.invalidModality for invalid modality from the selected model - // /// BedrockServiceError.invalidSDKResponse if the response body is missing - // /// - Returns: Tuple containing the model's response text and updated message history - // public func converse( - // with model: BedrockModel, - // prompt: String? = nil, - // imageFormat: ImageBlock.Format? = nil, - // imageBytes: String? = nil, - // history: [Message] = [], - // maxTokens: Int? = nil, - // temperature: Double? = nil, - // topP: Double? = nil, - // stopSequences: [String]? = nil, - // systemPrompt: String? = nil, - // tools: [Tool]? = nil, - // toolResult: ToolResultBlock? = nil - // ) async throws -> (String, [Message]) { - // try await converse( - // with: model, - // prompt: prompt, - // imageFormat: imageFormat, - // imageBytes: imageBytes, - // history: history, - // maxTokens: maxTokens, - // temperature: temperature, - // topP: topP, - // stopSequences: stopSequences, - // systemPrompts: systemPrompt != nil ? [systemPrompt!] : nil, - // tools: tools, - // toolResult: toolResult - // ) - // } - - // /// Use Converse API without needing to make Messages - // /// - Parameters: - // /// - model: The BedrockModel to converse with - // /// - prompt: Optional text prompt for the conversation - // /// - imageFormat: Optional format for image input - // /// - imageBytes: Optional base64 encoded image data - // /// - history: Optional array of previous messages - // /// - maxTokens: Optional maximum number of tokens to generate - // /// - temperature: Optional temperature parameter for controlling randomness - // /// - topP: Optional top-p parameter for nucleus sampling - // /// - stopSequence: Optional sequence where generation should stop - // /// - systemPrompts: Optional array of system prompts to guide the conversation - // /// - tools: Optional array of tools the model can use - // /// - toolResult: Optional result from a previous tool invocation - // /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - // /// BedrockServiceError.invalidParameter for invalid parameters - // /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - // /// BedrockServiceError.invalidModality for invalid modality from the selected model - // /// BedrockServiceError.invalidSDKResponse if the response body is missing - // /// - Returns: Tuple containing the model's response text and updated message history - // public func converse( - // with model: BedrockModel, - // prompt: String? = nil, - // imageFormat: ImageBlock.Format? = nil, - // imageBytes: String? = nil, - // history: [Message] = [], - // maxTokens: Int? = nil, - // temperature: Double? = nil, - // topP: Double? = nil, - // stopSequence: String? = nil, - // systemPrompts: [String]? = nil, - // tools: [Tool]? = nil, - // toolResult: ToolResultBlock? = nil - // ) async throws -> (String, [Message]) { - // try await converse( - // with: model, - // prompt: prompt, - // imageFormat: imageFormat, - // imageBytes: imageBytes, - // history: history, - // maxTokens: maxTokens, - // temperature: temperature, - // topP: topP, - // stopSequences: stopSequence != nil ? [stopSequence!] : nil, - // systemPrompts: systemPrompts, - // tools: tools, - // toolResult: toolResult - // ) - // } - - // /// Use Converse API without needing to make Messages - // /// - Parameters: - // /// - model: The BedrockModel to converse with - // /// - prompt: Optional text prompt for the conversation - // /// - imageFormat: Optional format for image input - // /// - imageBytes: Optional base64 encoded image data - // /// - history: Optional array of previous messages - // /// - maxTokens: Optional maximum number of tokens to generate - // /// - temperature: Optional temperature parameter for controlling randomness - // /// - topP: Optional top-p parameter for nucleus sampling - // /// - stopSequence: Optional sequence where generation should stop - // /// - systemPrompt: Optional system prompt to guide the conversation - // /// - tools: Optional array of tools the model can use - // /// - toolResult: Optional result from a previous tool invocation - // /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - // /// BedrockServiceError.invalidParameter for invalid parameters - // /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - // /// BedrockServiceError.invalidModality for invalid modality from the selected model - // /// BedrockServiceError.invalidSDKResponse if the response body is missing - // /// - Returns: Tuple containing the model's response text and updated message history - // public func converse( - // with model: BedrockModel, - // prompt: String? = nil, - // imageFormat: ImageBlock.Format? = nil, - // imageBytes: String? = nil, - // history: [Message] = [], - // maxTokens: Int? = nil, - // temperature: Double? = nil, - // topP: Double? = nil, - // stopSequence: String? = nil, - // systemPrompt: String? = nil, - // tools: [Tool]? = nil, - // toolResult: ToolResultBlock? = nil - // ) async throws -> (String, [Message]) { - // try await converse( - // with: model, - // prompt: prompt, - // imageFormat: imageFormat, - // imageBytes: imageBytes, - // history: history, - // maxTokens: maxTokens, - // temperature: temperature, - // topP: topP, - // stopSequences: stopSequence != nil ? [stopSequence!] : nil, - // systemPrompts: systemPrompt != nil ? [systemPrompt!] : nil, - // tools: tools, - // toolResult: toolResult - // ) - // } -} diff --git a/backend/Sources/BedrockService/Converse/ConverseRequest.swift b/backend/Sources/BedrockService/Converse/ConverseRequest.swift deleted file mode 100644 index b9a37795..00000000 --- a/backend/Sources/BedrockService/Converse/ConverseRequest.swift +++ /dev/null @@ -1,120 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import BedrockTypes -import Foundation - -public struct ConverseRequest { - let model: BedrockModel - let messages: [Message] - let inferenceConfig: InferenceConfig? - let toolConfig: ToolConfig? - let systemPrompts: [String]? - - init( - model: BedrockModel, - messages: [Message] = [], - maxTokens: Int?, - temperature: Double?, - topP: Double?, - stopSequences: [String]?, - systemPrompts: [String]?, - tools: [Tool]? - ) { - self.messages = messages - self.model = model - self.inferenceConfig = InferenceConfig( - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - stopSequences: stopSequences - ) - self.systemPrompts = systemPrompts - if tools != nil { - self.toolConfig = ToolConfig(tools: tools!) - } else { - self.toolConfig = nil - } - } - - func getConverseInput() throws -> ConverseInput { - ConverseInput( - inferenceConfig: inferenceConfig?.getSDKInferenceConfig(), - messages: try getSDKMessages(), - modelId: model.id, - system: getSDKSystemPrompts(), - toolConfig: try toolConfig?.getSDKToolConfig() - ) - } - - private func getSDKMessages() throws -> [BedrockRuntimeClientTypes.Message] { - try messages.map { try $0.getSDKMessage() } - } - - private func getSDKSystemPrompts() -> [BedrockRuntimeClientTypes.SystemContentBlock]? { - systemPrompts?.map { - BedrockRuntimeClientTypes.SystemContentBlock.text($0) - } - } - - struct InferenceConfig { - let maxTokens: Int? - let temperature: Double? - let topP: Double? - let stopSequences: [String]? - - func getSDKInferenceConfig() -> BedrockRuntimeClientTypes.InferenceConfiguration { - let temperatureFloat: Float? - if temperature != nil { - temperatureFloat = Float(temperature!) - } else { - temperatureFloat = nil - } - let topPFloat: Float? - if topP != nil { - topPFloat = Float(topP!) - } else { - topPFloat = nil - } - return BedrockRuntimeClientTypes.InferenceConfiguration( - maxTokens: maxTokens, - stopSequences: stopSequences, - temperature: temperatureFloat, - topp: topPFloat - ) - } - } - - public struct ToolConfig { - // let toolChoice: ToolChoice? - let tools: [Tool] - - func getSDKToolConfig() throws -> BedrockRuntimeClientTypes.ToolConfiguration { - BedrockRuntimeClientTypes.ToolConfiguration( - tools: try tools.map { .toolspec(try $0.getSDKToolSpecification()) } - ) - } - } -} - -// public enum ToolChoice { -// /// (Default). The Model automatically decides if a tool should be called or whether to generate text instead. -// case auto(_) -// /// The model must request at least one tool (no text is generated). -// case any(_) -// /// The Model must request the specified tool. Only supported by Anthropic Claude 3 models. -// case tool(String) -// } diff --git a/backend/Sources/BedrockService/Converse/ConverseResponse.swift b/backend/Sources/BedrockService/Converse/ConverseResponse.swift deleted file mode 100644 index f642c369..00000000 --- a/backend/Sources/BedrockService/Converse/ConverseResponse.swift +++ /dev/null @@ -1,49 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import BedrockTypes -import Foundation - -public struct ConverseResponse { - let message: Message - - public init(_ message: Message) { - self.message = message - } - - public init(_ output: BedrockRuntimeClientTypes.ConverseOutput) throws { - guard case .message(let sdkMessage) = output else { - throw BedrockServiceError.invalidSDKResponse("Could not extract message from ConverseOutput") - } - self.message = try Message(from: sdkMessage) - } - - func getReply() -> String { - switch message.content.first { - case .text(let text): - return text - default: - return "Not found" // FIXME - } - } - - func getToolUse() -> ToolUseBlock? { - if case .toolUse(let toolUse) = message.content.last { - return toolUse - } - return nil - } -} diff --git a/backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelImage.swift b/backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelImage.swift deleted file mode 100644 index 6007e145..00000000 --- a/backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelImage.swift +++ /dev/null @@ -1,259 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import BedrockTypes -import Foundation - -extension BedrockService { - - /// Generates 1 to 5 image(s) from a text prompt using a specific model - /// - Parameters: - /// - prompt: The text prompt describing the image that should be generated - /// - model: The BedrockModel that will be used to generate the image - /// - negativePrompt: Optional text describing what to avoid in the generated image - /// - nrOfImages: Optional number of images to generate (must be between 1 and 5, default 3) - /// - cfgScale: Optional classifier free guidance scale to control prompt adherence - /// - seed: Optional seed for reproducible image generation - /// - quality: Optional parameter to control the quality of generated images - /// - resolution: Optional parameter to specify the desired image resolution - /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - /// BedrockServiceError.invalidParameter for invalid parameters - /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - /// BedrockServiceError.invalidModality for invalid modality from the selected model - /// BedrockServiceError.invalidSDKResponse if the response body is missing - /// - Returns: An ImageGenerationOutput object containing an array of generated images - public func generateImage( - _ prompt: String, - with model: BedrockModel, - negativePrompt: String? = nil, - nrOfImages: Int? = nil, - cfgScale: Double? = nil, - seed: Int? = nil, - quality: ImageQuality? = nil, - resolution: ImageResolution? = nil - ) async throws -> ImageGenerationOutput { - logger.trace( - "Generating image(s)", - metadata: [ - "model.id": .string(model.id), - "model.modality": .string(model.modality.getName()), - "prompt": .string(prompt), - "negativePrompt": .stringConvertible(negativePrompt ?? "not defined"), - "nrOfImages": .stringConvertible(nrOfImages ?? "not defined"), - "cfgScale": .stringConvertible(cfgScale ?? "not defined"), - "seed": .stringConvertible(seed ?? "not defined"), - ] - ) - do { - let modality = try model.getImageModality() - try validateImageGenerationParams( - modality: modality, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - resolution: resolution, - seed: seed - ) - let textToImageModality = try model.getTextToImageModality() - try validateTextToImageParams(modality: textToImageModality, prompt: prompt, negativePrompt: negativePrompt) - - let request: InvokeModelRequest = try InvokeModelRequest.createTextToImageRequest( - model: model, - prompt: prompt, - negativeText: negativePrompt, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - let input: InvokeModelInput = try request.getInvokeModelInput() - logger.trace( - "Sending request to invokeModel", - metadata: [ - "model": .string(model.id), "request": .string(String(describing: input)), - ] - ) - let response = try await self.bedrockRuntimeClient.invokeModel(input: input) - guard let responseBody = response.body else { - logger.trace( - "Invalid response", - metadata: [ - "response": .string(String(describing: response)), - "hasBody": .stringConvertible(response.body != nil), - ] - ) - throw BedrockServiceError.invalidSDKResponse( - "Something went wrong while extracting body from response." - ) - } - let invokemodelResponse: InvokeModelResponse = try InvokeModelResponse.createImageResponse( - body: responseBody, - model: model - ) - return try invokemodelResponse.getGeneratedImage() - } catch { - logger.trace("Error while generating image", metadata: ["error": "\(error)"]) - throw error - } - } - - /// Generates 1 to 5 image variation(s) from reference images and a text prompt using a specific model - /// - Parameters: - /// - images: Array of base64 encoded reference images to generate variations from - /// - prompt: The text prompt describing desired modifications to the reference images - /// - model: The BedrockModel that will be used to generate the variations - /// - negativePrompt: Optional text describing what to avoid in the generated variations - /// - similarity: Optional parameter controlling how closely variations should match reference (between 0.2 and 1.0) - /// - nrOfImages: Optional number of variations to generate (must be between 1 and 5, default 3) - /// - cfgScale: Optional classifier free guidance scale to control prompt adherence - /// - seed: Optional seed for reproducible variation generation - /// - quality: Optional parameter to control the quality of generated variations - /// - resolution: Optional parameter to specify the desired image resolution - /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - /// BedrockServiceError.invalidParameter for invalid parameters - /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - /// BedrockServiceError.invalidModality for invalid modality from the selected model - /// BedrockServiceError.invalidSDKResponse if the response body is missing - /// - Returns: An ImageGenerationOutput object containing an array of generated image variations - public func generateImageVariation( - images: [String], - prompt: String, - with model: BedrockModel, - negativePrompt: String? = nil, - similarity: Double? = nil, - nrOfImages: Int? = nil, - cfgScale: Double? = nil, - seed: Int? = nil, - quality: ImageQuality? = nil, - resolution: ImageResolution? = nil - ) async throws -> ImageGenerationOutput { - logger.trace( - "Generating image(s) from reference image", - metadata: [ - "model.id": .string(model.id), - "model.modality": .string(model.modality.getName()), - "prompt": .string(prompt), - "nrOfImages": .stringConvertible(nrOfImages ?? "not defined"), - "similarity": .stringConvertible(similarity ?? "not defined"), - "negativePrompt": .stringConvertible(negativePrompt ?? "not defined"), - "cfgScale": .stringConvertible(cfgScale ?? "not defined"), - "seed": .stringConvertible(seed ?? "not defined"), - ] - ) - do { - let modality = try model.getImageModality() - try validateImageGenerationParams( - modality: modality, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - resolution: resolution, - seed: seed - ) - let imageVariationModality = try model.getImageVariationModality() - try validateImageVariationParams( - modality: imageVariationModality, - images: images, - prompt: prompt, - similarity: similarity, - negativePrompt: negativePrompt - ) - let request: InvokeModelRequest = try InvokeModelRequest.createImageVariationRequest( - model: model, - prompt: prompt, - negativeText: negativePrompt, - images: images, - similarity: similarity, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - let input: InvokeModelInput = try request.getInvokeModelInput() - logger.trace( - "Sending request to invokeModel", - metadata: [ - "model": .string(model.id), "request": .string(String(describing: input)), - ] - ) - let response = try await self.bedrockRuntimeClient.invokeModel(input: input) - guard let responseBody = response.body else { - logger.trace( - "Invalid response", - metadata: [ - "response": .string(String(describing: response)), - "hasBody": .stringConvertible(response.body != nil), - ] - ) - throw BedrockServiceError.invalidSDKResponse( - "Something went wrong while extracting body from response." - ) - } - let invokemodelResponse: InvokeModelResponse = try InvokeModelResponse.createImageResponse( - body: responseBody, - model: model - ) - return try invokemodelResponse.getGeneratedImage() - } catch { - logger.trace("Error while generating image variations", metadata: ["error": "\(error)"]) - throw error - } - } - - /// Generates 1 to 5 image variation(s) from reference images and a text prompt using a specific model - /// - Parameters: - /// - image: A base64 encoded reference image to generate variations from - /// - prompt: The text prompt describing desired modifications to the reference images - /// - model: The BedrockModel that will be used to generate the variations - /// - negativePrompt: Optional text describing what to avoid in the generated variations - /// - similarity: Optional parameter controlling how closely variations should match reference (between 0.2 and 1.0) - /// - nrOfImages: Optional number of variations to generate (must be between 1 and 5, default 3) - /// - cfgScale: Optional classifier free guidance scale to control prompt adherence - /// - seed: Optional seed for reproducible variation generation - /// - quality: Optional parameter to control the quality of generated variations - /// - resolution: Optional parameter to specify the desired image resolution - /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - /// BedrockServiceError.invalidParameter for invalid parameters - /// BedrockServiceError.invalidPrompt if the prompt is empty or too long - /// BedrockServiceError.invalidModality for invalid modality from the selected model - /// BedrockServiceError.invalidSDKResponse if the response body is missing - /// - Returns: An ImageGenerationOutput object containing an array of generated image variations - public func generateImageVariation( - image: String, - prompt: String, - with model: BedrockModel, - negativePrompt: String? = nil, - similarity: Double? = nil, - nrOfImages: Int? = nil, - cfgScale: Double? = nil, - seed: Int? = nil, - quality: ImageQuality? = nil, - resolution: ImageResolution? = nil - ) async throws -> ImageGenerationOutput { - try await generateImageVariation( - images: [image], - prompt: prompt, - with: model, - negativePrompt: negativePrompt, - similarity: similarity, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - } -} diff --git a/backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelText.swift b/backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelText.swift deleted file mode 100644 index cc92285c..00000000 --- a/backend/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelText.swift +++ /dev/null @@ -1,136 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import BedrockTypes -import Foundation - -extension BedrockService { - - /// Generates a text completion using a specified model - /// - Parameters: - /// - prompt: The text to be completed - /// - model: The BedrockModel that will be used to generate the completion - /// - maxTokens: The maximum amount of tokens in the completion (optional, default 300) - /// - temperature: The temperature used to generate the completion (optional, default 0.6) - /// - topP: Optional top-p parameter for nucleus sampling - /// - topK: Optional top-k parameter for filtering - /// - stopSequences: Optional array of sequences where generation should stop - /// - Throws: BedrockServiceError.notSupported for parameters or functionalities that are not supported - /// BedrockServiceError.invalidParameter for invalid parameters - /// BedrockServiceError.invalidPrompt for a prompt that is empty or too long - /// BedrockServiceError.invalidStopSequences if too many stop sequences were provided - /// BedrockServiceError.invalidModality for invalid modality from the selected model - /// BedrockServiceError.invalidSDKResponse if the response body is missing - /// - Returns: a TextCompletion object containing the generated text from the model - public func completeText( - _ prompt: String, - with model: BedrockModel, - maxTokens: Int? = nil, - temperature: Double? = nil, - topP: Double? = nil, - topK: Int? = nil, - stopSequences: [String]? = nil - ) async throws -> TextCompletion { - logger.trace( - "Generating text completion", - metadata: [ - "model.id": .string(model.id), - "model.modality": .string(model.modality.getName()), - "prompt": .string(prompt), - "maxTokens": .stringConvertible(maxTokens ?? "not defined"), - "temperature": .stringConvertible(temperature ?? "not defined"), - "topP": .stringConvertible(topP ?? "not defined"), - "topK": .stringConvertible(topK ?? "not defined"), - "stopSequences": .stringConvertible(stopSequences ?? "not defined"), - ] - ) - do { - let modality = try model.getTextModality() - try validateTextCompletionParams( - modality: modality, - prompt: prompt, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - topK: topK, - stopSequences: stopSequences - ) - - logger.trace( - "Creating InvokeModelRequest", - metadata: [ - "model": .string(model.id), - "prompt": "\(prompt)", - ] - ) - let request: InvokeModelRequest = try InvokeModelRequest.createTextRequest( - model: model, - prompt: prompt, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - topK: topK, - stopSequences: stopSequences - ) - let input: InvokeModelInput = try request.getInvokeModelInput() - logger.trace( - "Sending request to invokeModel", - metadata: [ - "model": .string(model.id), "request": .string(String(describing: input)), - ] - ) - - let response = try await self.bedrockRuntimeClient.invokeModel(input: input) - logger.trace( - "Received response from invokeModel", - metadata: [ - "model": .string(model.id), "response": .string(String(describing: response)), - ] - ) - - guard let responseBody = response.body else { - logger.trace( - "Invalid response", - metadata: [ - "response": .string(String(describing: response)), - "hasBody": .stringConvertible(response.body != nil), - ] - ) - throw BedrockServiceError.invalidSDKResponse( - "Something went wrong while extracting body from response." - ) - } - if let bodyString = String(data: responseBody, encoding: .utf8) { - logger.trace("Extracted body from response", metadata: ["response.body": "\(bodyString)"]) - } - - let invokemodelResponse: InvokeModelResponse = try InvokeModelResponse.createTextResponse( - body: responseBody, - model: model - ) - logger.trace( - "Generated text completion", - metadata: [ - "model": .string(model.id), "response": .string(String(describing: invokemodelResponse)), - ] - ) - return try invokemodelResponse.getTextCompletion() - } catch { - logger.trace("Error while completing text", metadata: ["error": "\(error)"]) - throw error - } - } -} diff --git a/backend/Sources/BedrockService/InvokeModel/InvokeModelRequest.swift b/backend/Sources/BedrockService/InvokeModel/InvokeModelRequest.swift deleted file mode 100644 index f64050db..00000000 --- a/backend/Sources/BedrockService/InvokeModel/InvokeModelRequest.swift +++ /dev/null @@ -1,224 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import BedrockTypes -import Foundation - -struct InvokeModelRequest { - let model: BedrockModel - let contentType: ContentType - let accept: ContentType - private let body: BedrockBodyCodable - - private init( - model: BedrockModel, - body: BedrockBodyCodable, - contentType: ContentType = .json, - accept: ContentType = .json - ) { - self.model = model - self.body = body - self.contentType = contentType - self.accept = accept - } - - // MARK: text - /// Creates a BedrockRequest for a text request with the specified parameters - /// - Parameters: - /// - model: The Bedrock model to use - /// - prompt: The input text prompt - /// - maxTokens: Maximum number of tokens to generate (default: 300) - /// - temperature: Temperature for text generation (default: 0.6) - /// - Returns: A configured BedrockRequest for a text request - /// - Throws: BedrockServiceError if the model doesn't support text output - static func createTextRequest( - model: BedrockModel, - prompt: String, - maxTokens: Int?, - temperature: Double?, - topP: Double?, - topK: Int?, - stopSequences: [String]? - ) throws -> InvokeModelRequest { - try .init( - model: model, - prompt: prompt, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - topK: topK, - stopSequences: stopSequences - ) - } - - private init( - model: BedrockModel, - prompt: String, - maxTokens: Int?, - temperature: Double?, - topP: Double?, - topK: Int?, - stopSequences: [String]? - ) throws { - let textModality = try model.getTextModality() - let body: BedrockBodyCodable = try textModality.getTextRequestBody( - prompt: prompt, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - topK: topK, - stopSequences: stopSequences - ) - self.init(model: model, body: body) - } - - // MARK: text to image - /// Creates a BedrockRequest for a text-to-image request with the specified parameters - /// - Parameters: - /// - model: The Bedrock model to use for image generation - /// - prompt: The text description of the image to generate - /// - nrOfImages: The number of images to generate - /// - Returns: A configured BedrockRequest for image generation - /// - Throws: BedrockServiceError if the model doesn't support text input or image output - public static func createTextToImageRequest( - model: BedrockModel, - prompt: String, - negativeText: String?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) throws -> InvokeModelRequest { - try .init( - model: model, - prompt: prompt, - negativeText: negativeText, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - } - - private init( - model: BedrockModel, - prompt: String, - negativeText: String?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) throws { - let textToImageModality = try model.getTextToImageModality() - self.init( - model: model, - body: try textToImageModality.getTextToImageRequestBody( - prompt: prompt, - negativeText: negativeText, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - ) - } - - // MARK: image variation - /// Creates a BedrockRequest for a request to generate variations of an existing image - /// - Parameters: - /// - model: The Bedrock model to use for image variation generation - /// - prompt: The text description to guide the variation generation - /// - image: The base64-encoded string of the source image to create variations from - /// - similarity: A value between 0 and 1 indicating how similar the variations should be to the source image - /// - nrOfImages: The number of image variations to generate - /// - Returns: A configured BedrockRequest for image variation generation - /// - Throws: BedrockServiceError if the model doesn't support text and image input, or image output - public static func createImageVariationRequest( - model: BedrockModel, - prompt: String, - negativeText: String?, - images: [String], - similarity: Double?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) throws -> InvokeModelRequest { - try .init( - model: model, - prompt: prompt, - negativeText: negativeText, - images: images, - similarity: similarity, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - } - - private init( - model: BedrockModel, - prompt: String, - negativeText: String?, - images: [String], - similarity: Double?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) throws { - let modality = try model.getImageVariationModality() - let body = try modality.getImageVariationRequestBody( - prompt: prompt, - negativeText: negativeText, - images: images, - similarity: similarity, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - self.init(model: model, body: body) - } - - /// Creates an InvokeModelInput instance for making a request to Amazon Bedrock - /// - Returns: A configured InvokeModelInput containing the model ID, content type, and encoded request body - /// - Throws: BedrockServiceError.encodingError if the request body cannot be encoded to JSON - public func getInvokeModelInput() throws -> InvokeModelInput { - do { - let jsonData: Data = try JSONEncoder().encode(self.body) - return InvokeModelInput( - accept: self.accept.headerValue, - body: jsonData, - contentType: self.contentType.headerValue, - modelId: model.id - ) - } catch { - throw BedrockServiceError.encodingError( - "Something went wrong while encoding the request body to JSON for InvokeModelInput: \(error)" - ) - } - } -} diff --git a/backend/Sources/BedrockService/InvokeModel/InvokeModelResponse.swift b/backend/Sources/BedrockService/InvokeModel/InvokeModelResponse.swift deleted file mode 100644 index c88c145e..00000000 --- a/backend/Sources/BedrockService/InvokeModel/InvokeModelResponse.swift +++ /dev/null @@ -1,109 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import BedrockTypes -import Foundation - -public struct InvokeModelResponse { - let model: BedrockModel - let contentType: ContentType - let textCompletionBody: ContainsTextCompletion? - let imageGenerationBody: ContainsImageGeneration? - - private init( - model: BedrockModel, - contentType: ContentType = .json, - textCompletionBody: ContainsTextCompletion - ) { - self.model = model - self.contentType = contentType - self.textCompletionBody = textCompletionBody - self.imageGenerationBody = nil - } - - private init( - model: BedrockModel, - contentType: ContentType = .json, - imageGenerationBody: ContainsImageGeneration - ) { - self.model = model - self.contentType = contentType - self.imageGenerationBody = imageGenerationBody - self.textCompletionBody = nil - } - - /// Creates a BedrockResponse from raw response data containing text completion - /// - Parameters: - /// - data: The raw response data from the Bedrock service - /// - model: The Bedrock model that generated the response - /// - Throws: BedrockServiceError.invalidModel if the model is not supported - /// BedrockServiceError.invalidResponseBody if the response cannot be decoded - static func createTextResponse(body data: Data, model: BedrockModel) throws -> Self { - do { - let textModality = try model.getTextModality() - return self.init(model: model, textCompletionBody: try textModality.getTextResponseBody(from: data)) - } catch { - throw BedrockServiceError.invalidSDKResponseBody(data) - } - } - - /// Creates a BedrockResponse from raw response data containing an image generation - /// - Parameters: - /// - data: The raw response data from the Bedrock service - /// - model: The Bedrock model that generated the response - /// - Throws: BedrockServiceError.invalidModel if the model is not supported - /// BedrockServiceError.invalidResponseBody if the response cannot be decoded - static func createImageResponse(body data: Data, model: BedrockModel) throws -> Self { - do { - let imageModality = try model.getImageModality() - return self.init(model: model, imageGenerationBody: try imageModality.getImageResponseBody(from: data)) - } catch { - throw BedrockServiceError.invalidSDKResponseBody(data) - } - } - - /// Extracts the text completion from the response body - /// - Returns: The text completion from the response - /// - Throws: BedrockServiceError.decodingError if the completion cannot be extracted - public func getTextCompletion() throws -> TextCompletion { - do { - guard let textCompletionBody = textCompletionBody else { - throw BedrockServiceError.decodingError("No text completion body found in the response") - } - return try textCompletionBody.getTextCompletion() - } catch { - throw BedrockServiceError.decodingError( - "Something went wrong while decoding the request body to find the completion: \(error)" - ) - } - } - - /// Extracts the image generation from the response body - /// - Returns: The image generation from the response - /// - Throws: BedrockServiceError.decodingError if the image generation cannot be extracted - public func getGeneratedImage() throws -> ImageGenerationOutput { - do { - guard let imageGenerationBody = imageGenerationBody else { - throw BedrockServiceError.decodingError("No image generation body found in the response") - } - return imageGenerationBody.getGeneratedImage() - } catch { - throw BedrockServiceError.decodingError( - "Something went wrong while decoding the request body to find the completion: \(error)" - ) - } - } -} diff --git a/backend/Sources/BedrockService/Protocols/BedrockClientProtocol.swift b/backend/Sources/BedrockService/Protocols/BedrockClientProtocol.swift deleted file mode 100644 index fb243d70..00000000 --- a/backend/Sources/BedrockService/Protocols/BedrockClientProtocol.swift +++ /dev/null @@ -1,29 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrock -import AWSClientRuntime -import AWSSDKIdentity -import Foundation - -// Protocol allows writing mocks for unit tests -public protocol BedrockClientProtocol: Sendable { - func listFoundationModels( - input: ListFoundationModelsInput - ) async throws - -> ListFoundationModelsOutput -} - -extension BedrockClient: @retroactive @unchecked Sendable, BedrockClientProtocol {} diff --git a/backend/Sources/BedrockService/Protocols/BedrockRuntimeClientProtocol.swift b/backend/Sources/BedrockService/Protocols/BedrockRuntimeClientProtocol.swift deleted file mode 100644 index e7919fa2..00000000 --- a/backend/Sources/BedrockService/Protocols/BedrockRuntimeClientProtocol.swift +++ /dev/null @@ -1,28 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import AWSClientRuntime -import AWSSDKIdentity -import BedrockTypes -import Foundation - -// Protocol allows writing mocks for unit tests -public protocol BedrockRuntimeClientProtocol: Sendable { - func invokeModel(input: InvokeModelInput) async throws -> InvokeModelOutput - func converse(input: ConverseInput) async throws -> ConverseOutput -} - -extension BedrockRuntimeClient: @retroactive @unchecked Sendable, BedrockRuntimeClientProtocol {} diff --git a/backend/Sources/BedrockTypes/BedrockModel.swift b/backend/Sources/BedrockTypes/BedrockModel.swift deleted file mode 100644 index 121883a2..00000000 --- a/backend/Sources/BedrockTypes/BedrockModel.swift +++ /dev/null @@ -1,338 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct BedrockModel: Hashable, Sendable, Equatable, RawRepresentable { - public var rawValue: String { id } - - public var id: String - public var name: String - public let modality: any Modality - - /// Creates a new BedrockModel instance - /// - Parameters: - /// - id: The unique identifier for the model - /// - modality: The modality of the model - public init( - id: String, - name: String, - modality: any Modality - ) { - self.id = id - self.modality = modality - self.name = name - } - - /// Creates an implemented BedrockModel instance from a raw string value - /// - Parameter rawValue: The model identifier string - /// - Returns: The corresponding BedrockModel instance or nil if the model is not implemented - public init?(rawValue: String) { - switch rawValue { - // claude - case BedrockModel.instant.id: - self = BedrockModel.instant - case BedrockModel.claudev1.id: - self = BedrockModel.claudev1 - case BedrockModel.claudev2.id: - self = BedrockModel.claudev2 - case BedrockModel.claudev2_1.id: - self = BedrockModel.claudev2_1 - case BedrockModel.claudev3_haiku.id: - self = BedrockModel.claudev3_haiku - case BedrockModel.claudev3_5_haiku.id: - self = BedrockModel.claudev3_5_haiku - case BedrockModel.claudev3_opus.id: - self = BedrockModel.claudev3_opus - case BedrockModel.claudev3_5_sonnet.id: - self = BedrockModel.claudev3_5_sonnet - case BedrockModel.claudev3_5_sonnet_v2.id: - self = BedrockModel.claudev3_5_sonnet_v2 - case BedrockModel.claudev3_7_sonnet.id: - self = BedrockModel.claudev3_7_sonnet - // titan - case BedrockModel.titan_text_g1_premier.id: - self = BedrockModel.titan_text_g1_premier - case BedrockModel.titan_text_g1_express.id: - self = BedrockModel.titan_text_g1_express - case BedrockModel.titan_text_g1_lite.id: - self = BedrockModel.titan_text_g1_lite - case BedrockModel.titan_image_g1_v2.id: - self = BedrockModel.titan_image_g1_v2 - case BedrockModel.titan_image_g1_v1.id: - self = BedrockModel.titan_image_g1_v1 - // nova - case BedrockModel.nova_micro.id: - self = BedrockModel.nova_micro - case BedrockModel.nova_lite.id: - self = BedrockModel.nova_lite - case BedrockModel.nova_pro.id: - self = BedrockModel.nova_pro - case BedrockModel.nova_canvas.id: - self = BedrockModel.nova_canvas - // deepseek - case BedrockModel.deepseek_r1_v1.id: - self = BedrockModel.deepseek_r1_v1 - // llama - case BedrockModel.llama_3_8b_instruct.id: self = BedrockModel.llama_3_8b_instruct - case BedrockModel.llama3_70b_instruct.id: self = BedrockModel.llama3_70b_instruct - case BedrockModel.llama3_1_8b_instruct.id: self = BedrockModel.llama3_1_8b_instruct - case BedrockModel.llama3_1_70b_instruct.id: self = BedrockModel.llama3_1_70b_instruct - case BedrockModel.llama3_2_1b_instruct.id: self = BedrockModel.llama3_2_1b_instruct - case BedrockModel.llama3_2_3b_instruct.id: self = BedrockModel.llama3_2_3b_instruct - case BedrockModel.llama3_3_70b_instruct.id: self = BedrockModel.llama3_3_70b_instruct - // mistral - case BedrockModel.mistral_large_2402.id: self = BedrockModel.mistral_large_2402 - case BedrockModel.mistral_small_2402.id: self = BedrockModel.mistral_small_2402 - case BedrockModel.mistral_7B_instruct.id: self = BedrockModel.mistral_7B_instruct - case BedrockModel.mistral_8x7B_instruct.id: self = BedrockModel.mistral_8x7B_instruct - //cohere - case BedrockModel.cohere_command_R_plus.id: self = BedrockModel.cohere_command_R_plus - case BedrockModel.cohere_command_R.id: self = BedrockModel.cohere_command_R - default: - return nil - } - } - - // MARK: Modality checks - - // MARK - Text completion - - /// Checks if the model supports text generation - /// - Returns: True if the model supports text generation - public func hasTextModality() -> Bool { - modality as? any TextModality != nil - } - - /// Checks if the model supports text generation and returns TextModality - /// - Returns: TextModality if the model supports text modality - public func getTextModality() throws -> any TextModality { - guard let textModality = modality as? any TextModality else { - throw BedrockServiceError.invalidModality( - self, - modality, - "Model \(id) does not support text generation" - ) - } - return textModality - } - - // MARK - Image generation - - /// Checks if the model supports image generation - /// - Returns: True if the model supports image generation - public func hasImageModality() -> Bool { - modality as? any ImageModality != nil - } - - /// Checks if the model supports image generation and returns ImageModality - /// - Returns: TextModality if the model supports image modality - public func getImageModality() throws -> any ImageModality { - guard let imageModality = modality as? any ImageModality else { - throw BedrockServiceError.invalidModality( - self, - modality, - "Model \(id) does not support image generation" - ) - } - return imageModality - } - - /// Checks if the model supports text to image generation - /// - Returns: True if the model supports text to image generation - public func hasTextToImageModality() -> Bool { - modality as? any TextToImageModality != nil - } - - /// Checks if the model supports text to image generation and returns TextToImageModality - /// - Returns: TextToImageModality if the model supports image modality - public func getTextToImageModality() throws -> any TextToImageModality { - guard let textToImageModality = modality as? any TextToImageModality else { - throw BedrockServiceError.invalidModality( - self, - modality, - "Model \(id) does not support text to image generation" - ) - } - return textToImageModality - } - - /// Checks if the model supports image variation - /// - Returns: True if the model supports image variation - public func hasImageVariationModality() -> Bool { - modality as? any ImageVariationModality != nil - } - - /// Checks if the model supports image variation and returns ImageVariationModality - /// - Returns: ImageVariationModality if the model supports image modality - public func getImageVariationModality() throws -> any ImageVariationModality { - guard let modality = modality as? any ImageVariationModality else { - throw BedrockServiceError.invalidModality( - self, - modality, - "Model \(id) does not support image variation" - ) - } - return modality - } - - /// Checks if the model supports conditioned text to image generation - /// - Returns: True if the model supports conditioned text to image generation - public func hasConditionedTextToImageModality() -> Bool { - modality as? any ConditionedTextToImageModality != nil - } - - // MARK - Converse - - /// Checks if the model supports converse - /// - Returns: True if the model supports converse - public func hasConverseModality() -> Bool { - modality as? any ConverseModality != nil - } - - /// Checks if the model supports a specific converse feature - /// - Parameters: - /// - feature: the ConverseFeature that will be checked - /// - Returns: True if the model supports the converse feature - public func hasConverseModality(_ feature: ConverseFeature = .textGeneration) -> Bool { - if let converseModality = modality as? any ConverseModality { - let features = converseModality.getConverseFeatures() - return features.contains(feature) - } - return false - } - - /// Checks if the model supports text generation and returns ConverseModality - /// - Returns: ConverseModality if the model supports text modality - public func getConverseModality() throws -> any ConverseModality { - guard let modality = modality as? any ConverseModality else { - throw BedrockServiceError.invalidModality( - self, - modality, - "Model \(id) does not support text generation" - ) - } - return modality - } -} - -extension BedrockModel: Encodable { - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - // Encode basic information - try container.encode(name, forKey: .modelName) - try container.encode(id, forKey: .modelId) - try container.encode(String(describing: type(of: modality)), forKey: .supportedModality) - - if hasTextModality() { - try encodeTextParameters(to: &container) - } - - if hasImageModality() { - try encodeImageParameters(to: &container) - } - } - - private enum CodingKeys: String, CodingKey { - case modelName - case modelId - case temperatureRange - case maxTokenRange - case topPRange - case topKRange - case nrOfImagesRange - case cfgScaleRange - case seedRange - case similarityRange - case supportedModality - } - - private enum RangeKeys: String, CodingKey { - case min - case max - case `default` - } - - private func encodeTextParameters(to container: inout KeyedEncodingContainer) throws { - let textModality = try getTextModality() - let params = textModality.getParameters() - - if params.temperature.isSupported { - var tempContainer = container.nestedContainer(keyedBy: RangeKeys.self, forKey: .temperatureRange) - try tempContainer.encode(params.temperature.minValue, forKey: .min) - try tempContainer.encode(params.temperature.maxValue, forKey: .max) - try tempContainer.encode(params.temperature.defaultValue, forKey: .default) - } - if params.maxTokens.isSupported { - var tokenContainer = container.nestedContainer(keyedBy: RangeKeys.self, forKey: .maxTokenRange) - try tokenContainer.encode(params.maxTokens.minValue, forKey: .min) - try tokenContainer.encode(params.maxTokens.maxValue, forKey: .max) - try tokenContainer.encode(params.maxTokens.defaultValue, forKey: .default) - } - if params.topP.isSupported { - var topPContainer = container.nestedContainer(keyedBy: RangeKeys.self, forKey: .topPRange) - try topPContainer.encode(params.topP.minValue, forKey: .min) - try topPContainer.encode(params.topP.maxValue, forKey: .max) - try topPContainer.encode(params.topP.defaultValue, forKey: .default) - } - if params.topK.isSupported { - var topKContainer = container.nestedContainer(keyedBy: RangeKeys.self, forKey: .topKRange) - try topKContainer.encode(params.topK.minValue, forKey: .min) - try topKContainer.encode(params.topK.maxValue, forKey: .max) - try topKContainer.encode(params.topK.defaultValue, forKey: .default) - } - } - - private func encodeImageParameters(to container: inout KeyedEncodingContainer) throws { - let imageModality = try getImageModality() - let params = imageModality.getParameters() - - // General image generation inference parameters - if params.nrOfImages.isSupported { - var imagesContainer = container.nestedContainer(keyedBy: RangeKeys.self, forKey: .nrOfImagesRange) - try imagesContainer.encode(params.nrOfImages.minValue, forKey: .min) - try imagesContainer.encode(params.nrOfImages.maxValue, forKey: .max) - try imagesContainer.encode(params.nrOfImages.defaultValue, forKey: .default) - } - if params.cfgScale.isSupported { - var cfgScaleContainer = container.nestedContainer(keyedBy: RangeKeys.self, forKey: .cfgScaleRange) - try cfgScaleContainer.encode(params.cfgScale.minValue, forKey: .min) - try cfgScaleContainer.encode(params.cfgScale.maxValue, forKey: .max) - try cfgScaleContainer.encode(params.cfgScale.defaultValue, forKey: .default) - } - if params.seed.isSupported { - var seedContainer = container.nestedContainer(keyedBy: RangeKeys.self, forKey: .seedRange) - try seedContainer.encode(params.seed.minValue, forKey: .min) - try seedContainer.encode(params.seed.maxValue, forKey: .max) - try seedContainer.encode(params.seed.defaultValue, forKey: .default) - } - - // If the model supports image variation, encode similarity range - if hasImageVariationModality() { - let variationModality = try getImageVariationModality() - let variationParams = variationModality.getImageVariationParameters() - if variationParams.similarity.isSupported { - var similarityContainer = container.nestedContainer( - keyedBy: RangeKeys.self, - forKey: .similarityRange - ) - try similarityContainer.encode(variationParams.similarity.minValue, forKey: .min) - try similarityContainer.encode(variationParams.similarity.maxValue, forKey: .max) - try similarityContainer.encode(variationParams.similarity.defaultValue, forKey: .default) - } - } - } -} diff --git a/backend/Sources/BedrockTypes/BedrockServiceError.swift b/backend/Sources/BedrockTypes/BedrockServiceError.swift deleted file mode 100644 index 28ab1d5c..00000000 --- a/backend/Sources/BedrockTypes/BedrockServiceError.swift +++ /dev/null @@ -1,32 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public enum BedrockServiceError: Error { - case invalidParameter(ParameterName, String) - case invalidModality(BedrockModel, Modality, String) - case invalidPrompt(String) - case invalidStopSequences([String], String) - case invalidURI(String) - case invalidSDKResponse(String) - case invalidSDKResponseBody(Data?) - case completionNotFound(String) - case encodingError(String) - case decodingError(String) - case notImplemented(String) - case notSupported(String) - case notFound(String) -} diff --git a/backend/Sources/BedrockTypes/Converse/Content.swift b/backend/Sources/BedrockTypes/Converse/Content.swift deleted file mode 100644 index ac7000c0..00000000 --- a/backend/Sources/BedrockTypes/Converse/Content.swift +++ /dev/null @@ -1,72 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import Foundation - -public enum Content: Codable { - case text(String) - case image(ImageBlock) - case toolUse(ToolUseBlock) - case toolResult(ToolResultBlock) - case document(DocumentBlock) - case video(VideoBlock) - // case reasoningcontent(ReasoningBlock) - - public init(from sdkContentBlock: BedrockRuntimeClientTypes.ContentBlock) throws { - switch sdkContentBlock { - case .text(let text): - self = .text(text) - case .image(let sdkImage): - self = .image(try ImageBlock(from: sdkImage)) - case .document(let sdkDocumentBlock): - self = .document(try DocumentBlock(from: sdkDocumentBlock)) - case .tooluse(let sdkToolUseBlock): - self = .toolUse(try ToolUseBlock(from: sdkToolUseBlock)) - case .toolresult(let sdkToolResultBlock): - self = .toolResult(try ToolResultBlock(from: sdkToolResultBlock)) - case .video(let sdkVideoBlock): - self = .video(try VideoBlock(from: sdkVideoBlock)) - case .sdkUnknown(let unknownContentBlock): - throw BedrockServiceError.notImplemented( - "ContentBlock \(unknownContentBlock) is not implemented by BedrockRuntimeClientTypes" - ) - default: - throw BedrockServiceError.notImplemented( - "\(sdkContentBlock.self) is not implemented by this library" - ) - } - } - - public func getSDKContentBlock() throws -> BedrockRuntimeClientTypes.ContentBlock { - switch self { - case .text(let text): - return BedrockRuntimeClientTypes.ContentBlock.text(text) - case .image(let imageBlock): - return BedrockRuntimeClientTypes.ContentBlock.image(try imageBlock.getSDKImageBlock()) - case .document(let documentBlock): - return BedrockRuntimeClientTypes.ContentBlock.document(try documentBlock.getSDKDocumentBlock()) - case .toolResult(let toolResultBlock): - return BedrockRuntimeClientTypes.ContentBlock.toolresult(try toolResultBlock.getSDKToolResultBlock()) - case .toolUse(let toolUseBlock): - return BedrockRuntimeClientTypes.ContentBlock.tooluse(try toolUseBlock.getSDKToolUseBlock()) - case .video(let videoBlock): - return BedrockRuntimeClientTypes.ContentBlock.video(try videoBlock.getSDKVideoBlock()) - // default: - // print("TODO") - // return BedrockRuntimeClientTypes.ContentBlock.text("TODO") - } - } -} diff --git a/backend/Sources/BedrockTypes/Converse/DocumentBlock.swift b/backend/Sources/BedrockTypes/Converse/DocumentBlock.swift deleted file mode 100644 index 8b7b6798..00000000 --- a/backend/Sources/BedrockTypes/Converse/DocumentBlock.swift +++ /dev/null @@ -1,113 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import Foundation - -public struct DocumentBlock: Codable { - public let name: String - public let format: Format - public let source: String // 64 encoded - - public init(name: String, format: Format, source: String) { - self.name = name - self.format = format - self.source = source - } - - public init(from sdkDocumentBlock: BedrockRuntimeClientTypes.DocumentBlock) throws { - guard let sdkDocumentSource = sdkDocumentBlock.source else { - throw BedrockServiceError.decodingError( - "Could not extract source from BedrockRuntimeClientTypes.DocumentBlock" - ) - } - guard let name = sdkDocumentBlock.name else { - throw BedrockServiceError.decodingError( - "Could not extract name from BedrockRuntimeClientTypes.DocumentBlock" - ) - } - guard let sdkFormat = sdkDocumentBlock.format else { - throw BedrockServiceError.decodingError( - "Could not extract format from BedrockRuntimeClientTypes.DocumentBlock" - ) - } - let format = try DocumentBlock.Format(from: sdkFormat) - switch sdkDocumentSource { - case .bytes(let data): - self = DocumentBlock(name: name, format: format, source: data.base64EncodedString()) - case .sdkUnknown(let unknownImageSource): - throw BedrockServiceError.notImplemented( - "ImageSource \(unknownImageSource) is not implemented by BedrockRuntimeClientTypes" - ) - } - } - - public func getSDKDocumentBlock() throws -> BedrockRuntimeClientTypes.DocumentBlock { - guard let data = Data(base64Encoded: source) else { - throw BedrockServiceError.decodingError( - "Could not decode document source from base64 string. String: \(source)" - ) - } - return BedrockRuntimeClientTypes.DocumentBlock( - format: format.getSDKDocumentFormat(), - name: name, - source: BedrockRuntimeClientTypes.DocumentSource.bytes(data) - ) - } - - public enum Format: Codable { - case csv - case doc - case docx - case html - case md - case pdf - case txt - case xls - case xlsx - - public init(from sdkDocumentFormat: BedrockRuntimeClientTypes.DocumentFormat) throws { - switch sdkDocumentFormat { - case .csv: self = .csv - case .doc: self = .doc - case .docx: self = .docx - case .html: self = .html - case .md: self = .md - case .pdf: self = .pdf - case .txt: self = .txt - case .xls: self = .xls - case .xlsx: self = .xlsx - case .sdkUnknown(let unknownDocumentFormat): - throw BedrockServiceError.notImplemented( - "DocumentFormat \(unknownDocumentFormat) is not implemented by BedrockRuntimeClientTypes" - ) - } - } - - public func getSDKDocumentFormat() -> BedrockRuntimeClientTypes.DocumentFormat { - switch self { - case .csv: return .csv - case .doc: return .doc - case .docx: return .docx - case .html: return .html - case .md: return .md - case .pdf: return .pdf - case .txt: return .txt - case .xls: return .xls - case .xlsx: return .xlsx - } - } - } -} diff --git a/backend/Sources/BedrockTypes/Converse/DocumentToJSON.swift b/backend/Sources/BedrockTypes/Converse/DocumentToJSON.swift deleted file mode 100644 index a1545eca..00000000 --- a/backend/Sources/BedrockTypes/Converse/DocumentToJSON.swift +++ /dev/null @@ -1,48 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation -import Smithy - -// FIXME: avoid extensions on structs you do not control -extension SmithyDocument { - public func toJSON() throws -> JSON { - switch self.type { - case .string: - return JSON(try self.asString()) - case .boolean: - return JSON(try self.asBoolean()) - case .integer: - return JSON(try self.asInteger()) - case .double, .float: - return JSON(try self.asDouble()) - case .list: - let array = try self.asList().map { try $0.toJSON() } - return JSON(array) - case .map: - let map = try self.asStringMap() - var result: [String: JSON] = [:] - for (key, value) in map { - result[key] = try value.toJSON() - } - return JSON(result) - case .blob: - let data = try self.asBlob() - return JSON(data) - default: - throw DocumentError.typeMismatch("Unsupported type for JSON conversion: \(self.type)") - } - } -} diff --git a/backend/Sources/BedrockTypes/Converse/ImageBlock.swift b/backend/Sources/BedrockTypes/Converse/ImageBlock.swift deleted file mode 100644 index c56d6444..00000000 --- a/backend/Sources/BedrockTypes/Converse/ImageBlock.swift +++ /dev/null @@ -1,90 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import Foundation - -public struct ImageBlock: Codable { - public let format: Format - public let source: String // 64 encoded - - public init(format: Format, source: String) { - self.format = format - self.source = source - } - - public init(from sdkImageBlock: BedrockRuntimeClientTypes.ImageBlock) throws { - guard let sdkFormat = sdkImageBlock.format else { - throw BedrockServiceError.decodingError( - "Could not extract format from BedrockRuntimeClientTypes.ImageBlock" - ) - } - guard let sdkImageSource = sdkImageBlock.source else { - throw BedrockServiceError.decodingError( - "Could not extract source from BedrockRuntimeClientTypes.ImageBlock" - ) - } - let format = try ImageBlock.Format(from: sdkFormat) - switch sdkImageSource { - case .bytes(let data): - self = ImageBlock(format: format, source: data.base64EncodedString()) - case .sdkUnknown(let unknownImageSource): - throw BedrockServiceError.notImplemented( - "ImageSource \(unknownImageSource) is not implemented by BedrockRuntimeClientTypes" - ) - } - } - - public func getSDKImageBlock() throws -> BedrockRuntimeClientTypes.ImageBlock { - guard let data = Data(base64Encoded: source) else { - throw BedrockServiceError.decodingError( - "Could not decode image source from base64 string. String: \(source)" - ) - } - return BedrockRuntimeClientTypes.ImageBlock( - format: format.getSDKImageFormat(), - source: BedrockRuntimeClientTypes.ImageSource.bytes(data) - ) - } - - public enum Format: Codable { - case gif - case jpeg - case png - case webp - - public init(from sdkImageFormat: BedrockRuntimeClientTypes.ImageFormat) throws { - switch sdkImageFormat { - case .gif: self = .gif - case .jpeg: self = .jpeg - case .png: self = .png - case .webp: self = .webp - case .sdkUnknown(let unknownImageFormat): - throw BedrockServiceError.notImplemented( - "ImageFormat \(unknownImageFormat) is not implemented by BedrockRuntimeClientTypes" - ) - } - } - - public func getSDKImageFormat() -> BedrockRuntimeClientTypes.ImageFormat { - switch self { - case .gif: return .gif - case .jpeg: return .jpeg - case .png: return .png - case .webp: return .webp - } - } - } -} diff --git a/backend/Sources/BedrockTypes/Converse/JSON.swift b/backend/Sources/BedrockTypes/Converse/JSON.swift deleted file mode 100644 index edbe68e3..00000000 --- a/backend/Sources/BedrockTypes/Converse/JSON.swift +++ /dev/null @@ -1,72 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct JSON: Codable { - var value: Any? - - public init(_ value: Any?) { - self.value = value - } - - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - if container.decodeNil() { - self.value = nil - } else if let intValue = try? container.decode(Int.self) { - self.value = intValue - } else if let doubleValue = try? container.decode(Double.self) { - self.value = doubleValue - } else if let stringValue = try? container.decode(String.self) { - self.value = stringValue - } else if let boolValue = try? container.decode(Bool.self) { - self.value = boolValue - } else if let arrayValue = try? container.decode([JSON].self) { - self.value = arrayValue.map { $0.value } - } else if let dictionaryValue = try? container.decode([String: JSON].self) { - self.value = dictionaryValue.mapValues { $0.value } - } else { - throw DecodingError.dataCorruptedError(in: container, debugDescription: "Unsupported type") - } - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - if let jsonValue = value as? JSON { - try jsonValue.encode(to: encoder) - } else if let intValue = value as? Int { - try container.encode(intValue) - } else if let doubleValue = value as? Double { - try container.encode(doubleValue) - } else if let stringValue = value as? String { - try container.encode(stringValue) - } else if let boolValue = value as? Bool { - try container.encode(boolValue) - } else if let arrayValue = value as? [Any] { - let jsonArray = arrayValue.map { JSON($0) } - try container.encode(jsonArray) - } else if let dictionaryValue = value as? [String: Any] { - let jsonDictionary = dictionaryValue.mapValues { JSON($0) } - try container.encode(jsonDictionary) - } else { - // try container.encode(String(describing: value ?? "nil")) - throw EncodingError.invalidValue( - value ?? "nil", - EncodingError.Context(codingPath: encoder.codingPath, debugDescription: "Unsupported type") - ) - } - } -} diff --git a/backend/Sources/BedrockTypes/Converse/JSONtoDocument.swift b/backend/Sources/BedrockTypes/Converse/JSONtoDocument.swift deleted file mode 100644 index b29beb20..00000000 --- a/backend/Sources/BedrockTypes/Converse/JSONtoDocument.swift +++ /dev/null @@ -1,25 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation -import Smithy - -extension JSON { - public func toDocument() throws -> Document { - let encoder = JSONEncoder() - let encoded = try encoder.encode(self) - return try Document.make(from: encoded) - } -} diff --git a/backend/Sources/BedrockTypes/Converse/Message.swift b/backend/Sources/BedrockTypes/Converse/Message.swift deleted file mode 100644 index af368917..00000000 --- a/backend/Sources/BedrockTypes/Converse/Message.swift +++ /dev/null @@ -1,79 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import Foundation - -public struct Message: Codable { - public let role: Role - public let content: [Content] - - package init(from role: Role, content: [Content]) { - self.role = role - self.content = content - } - - /// convenience initializer for message with only a user prompt - public init(_ prompt: String) { - self.init(from: .user, content: [.text(prompt)]) - } - - /// convenience initializer for message with only a ToolResultBlock - public init(_ toolResult: ToolResultBlock) { - self.init(from: .user, content: [.toolResult(toolResult)]) - } - - /// convenience initializer for message with only an ImageBlock - public init(_ imageBlock: ImageBlock) { - self.init(from: .user, content: [.image(imageBlock)]) - } - - /// convenience initializer for message with an ImageBlock.Format and imageBytes - public init(imageFormat: ImageBlock.Format, imageBytes: String) { - self.init(from: .user, content: [.image(ImageBlock(format: imageFormat, source: imageBytes))]) - } - - /// convenience initializer for message with an ImageBlock and a user prompt - public init(_ prompt: String, imageBlock: ImageBlock) { - self.init(from: .user, content: [.text(prompt), .image(imageBlock)]) - } - - /// convenience initializer for message with a user prompt, an ImageBlock.Format and imageBytes - public init(_ prompt: String, imageFormat: ImageBlock.Format, imageBytes: String) { - self.init(from: .user, content: [.text(prompt), .image(ImageBlock(format: imageFormat, source: imageBytes))]) - } - - public init(from sdkMessage: BedrockRuntimeClientTypes.Message) throws { - guard let sdkRole = sdkMessage.role else { - throw BedrockServiceError.decodingError("Could not extract role from BedrockRuntimeClientTypes.Message") - } - guard let sdkContent = sdkMessage.content else { - throw BedrockServiceError.decodingError("Could not extract content from BedrockRuntimeClientTypes.Message") - } - let content: [Content] = try sdkContent.map { try Content(from: $0) } - self = Message(from: try Role(from: sdkRole), content: content) - } - - public func getSDKMessage() throws -> BedrockRuntimeClientTypes.Message { - let contentBlocks: [BedrockRuntimeClientTypes.ContentBlock] = try content.map { - content -> BedrockRuntimeClientTypes.ContentBlock in - try content.getSDKContentBlock() - } - return BedrockRuntimeClientTypes.Message( - content: contentBlocks, - role: role.getSDKConversationRole() - ) - } -} diff --git a/backend/Sources/BedrockTypes/Converse/S3Location.swift b/backend/Sources/BedrockTypes/Converse/S3Location.swift deleted file mode 100644 index d244cd51..00000000 --- a/backend/Sources/BedrockTypes/Converse/S3Location.swift +++ /dev/null @@ -1,43 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import Foundation - -public struct S3Location: Codable, Sendable { - public let bucketOwner: String? - public let uri: String - - public init(bucketOwner: String? = nil, uri: String) { - self.bucketOwner = bucketOwner - self.uri = uri - } - - public init(from sdkS3Location: BedrockRuntimeClientTypes.S3Location) throws { - guard let uri = sdkS3Location.uri else { - throw BedrockServiceError.decodingError( - "Could not extract URI from BedrockRuntimeClientTypes.S3Location" - ) - } - guard uri.hasPrefix("") else { - throw BedrockServiceError.invalidURI("URI should start with \"s3://\". Your URI: \(uri)") - } - self = S3Location(bucketOwner: sdkS3Location.bucketOwner, uri: uri) - } - - public func getSDKS3Location() -> BedrockRuntimeClientTypes.S3Location { - BedrockRuntimeClientTypes.S3Location(bucketOwner: self.bucketOwner, uri: self.uri) - } -} diff --git a/backend/Sources/BedrockTypes/Converse/Tool.swift b/backend/Sources/BedrockTypes/Converse/Tool.swift deleted file mode 100644 index b3707afa..00000000 --- a/backend/Sources/BedrockTypes/Converse/Tool.swift +++ /dev/null @@ -1,62 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import Foundation -import Smithy - -public struct Tool: Codable { - public let name: String - public let inputSchema: JSON - public let description: String? - - public init(name: String, inputSchema: JSON, description: String? = nil) { - self.name = name - self.inputSchema = inputSchema - self.description = description - } - - public init(from sdkToolSpecification: BedrockRuntimeClientTypes.ToolSpecification) throws { - guard let name = sdkToolSpecification.name else { - throw BedrockServiceError.decodingError( - "Could not extract name from BedrockRuntimeClientTypes.ToolSpecification" - ) - } - guard let sdkInputSchema = sdkToolSpecification.inputSchema else { - throw BedrockServiceError.decodingError( - "Could not extract inputSchema from BedrockRuntimeClientTypes.ToolSpecification" - ) - } - guard case .json(let smithyDocument) = sdkInputSchema else { - throw BedrockServiceError.decodingError( - "Could not extract JSON from BedrockRuntimeClientTypes.ToolSpecification.inputSchema" - ) - } - let inputSchema = try smithyDocument.toJSON() - self = Tool( - name: name, - inputSchema: inputSchema, - description: sdkToolSpecification.description - ) - } - - public func getSDKToolSpecification() throws -> BedrockRuntimeClientTypes.ToolSpecification { - BedrockRuntimeClientTypes.ToolSpecification( - description: description, - inputSchema: .json(try inputSchema.toDocument()), - name: name - ) - } -} diff --git a/backend/Sources/BedrockTypes/Converse/ToolResultBlock.swift b/backend/Sources/BedrockTypes/Converse/ToolResultBlock.swift deleted file mode 100644 index a54757bb..00000000 --- a/backend/Sources/BedrockTypes/Converse/ToolResultBlock.swift +++ /dev/null @@ -1,200 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import Foundation - -public struct ToolResultBlock: Codable { - public let id: String - public let content: [Content] - public let status: Status? // currently only supported by Anthropic Claude 3 models - - public init(id: String, content: [Content], status: Status? = nil) { - self.id = id - self.content = content - self.status = status - } - - /// convenience initializer for ToolResultBlock with only an id and a String - public init(_ prompt: String, id: String) { - self.init(id: id, content: [.text(prompt)], status: .success) - } - - /// convenience initializer for ToolResultBlock with only an id and a JSON - public init(_ json: JSON, id: String) { - self.init(id: id, content: [.json(json)], status: .success) - } - - /// convenience initializer for ToolResultBlock with only an id and a ImageBlock - public init(_ image: ImageBlock, id: String) { - self.init(id: id, content: [.image(image)], status: .success) - } - - /// convenience initializer for ToolResultBlock with only an id and a DocumentBlock - public init(_ document: DocumentBlock, id: String) { - self.init(id: id, content: [.document(document)], status: .success) - } - - /// convenience initializer for ToolResultBlock with only an id and a VideoBlock - public init(_ video: VideoBlock, id: String) { - self.init(id: id, content: [.video(video)], status: .success) - } - - /// convenience initializer for ToolResultBlock with failed request - public static func failed(_ id: String) -> Self { - self.init(id: id, content: [], status: .error) - } - - public init(from sdkToolResultBlock: BedrockRuntimeClientTypes.ToolResultBlock) throws { - guard let sdkToolResultContent = sdkToolResultBlock.content else { - throw BedrockServiceError.decodingError( - "Could not extract content from BedrockRuntimeClientTypes.ToolResultBlock" - ) - } - guard let id = sdkToolResultBlock.toolUseId else { - throw BedrockServiceError.decodingError( - "Could not extract toolUseId from BedrockRuntimeClientTypes.ToolResultBlock" - ) - } - let sdkToolStatus: BedrockRuntimeClientTypes.ToolResultStatus? = sdkToolResultBlock.status - var status: Status? = nil - if let sdkToolStatus = sdkToolStatus { - status = try Status(from: sdkToolStatus) - } - let toolContents = try sdkToolResultContent.map { try Content(from: $0) } - self = ToolResultBlock(id: id, content: toolContents, status: status) - } - - public func getSDKToolResultBlock() throws -> BedrockRuntimeClientTypes.ToolResultBlock { - BedrockRuntimeClientTypes.ToolResultBlock( - content: try content.map { try $0.getSDKToolResultContentBlock() }, - status: status?.getSDKToolStatus(), - toolUseId: id - ) - } - - public enum Status: Codable { - case success - case error - - init(from sdkToolStatus: BedrockRuntimeClientTypes.ToolResultStatus) throws { - switch sdkToolStatus { - case .success: self = .success - case .error: self = .error - case .sdkUnknown(let unknownToolStatus): - throw BedrockServiceError.notImplemented( - "ToolResultStatus \(unknownToolStatus) is not implemented by BedrockRuntimeClientTypes" - ) - } - } - - func getSDKToolStatus() -> BedrockRuntimeClientTypes.ToolResultStatus { - switch self { - case .success: .success - case .error: .error - } - } - } - - public enum Content { - case json(JSON) - case text(String) - case image(ImageBlock) // currently only supported by Anthropic Claude 3 models - case document(DocumentBlock) - case video(VideoBlock) - - init(from sdkToolResultContent: BedrockRuntimeClientTypes.ToolResultContentBlock) throws { - switch sdkToolResultContent { - case .document(let sdkDocumentBlock): - self = .document(try DocumentBlock(from: sdkDocumentBlock)) - case .image(let sdkImageBlock): - self = .image(try ImageBlock(from: sdkImageBlock)) - case .text(let text): - self = .text(text) - case .video(let sdkVideoBlock): - self = .video(try VideoBlock(from: sdkVideoBlock)) - case .json(let document): - self = .json(try document.toJSON()) - case .sdkUnknown(let unknownToolResultContent): - throw BedrockServiceError.notImplemented( - "ToolResultContentBlock \(unknownToolResultContent) is not implemented by BedrockRuntimeClientTypes" - ) - // default: - // throw BedrockServiceError.notImplemented( - // "ToolResultContentBlock \(sdkToolResultContent) is not implemented by BedrockTypes" - // ) - } - } - - func getSDKToolResultContentBlock() throws -> BedrockRuntimeClientTypes.ToolResultContentBlock { - switch self { - case .json(let json): - .json(try json.toDocument()) - case .document(let documentBlock): - .document(try documentBlock.getSDKDocumentBlock()) - case .image(let imageBlock): - .image(try imageBlock.getSDKImageBlock()) - case .text(let text): - .text(text) - case .video(let videoBlock): - .video(try videoBlock.getSDKVideoBlock()) - } - } - } -} - -extension ToolResultBlock.Content: Codable { - private enum CodingKeys: String, CodingKey { - case json, text, image, document, video - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - switch self { - case .json(let json): - try container.encode(json, forKey: .json) - case .text(let text): - try container.encode(text, forKey: .text) - case .image(let image): - try container.encode(image, forKey: .image) - case .document(let doc): - try container.encode(doc, forKey: .document) - case .video(let video): - try container.encode(video, forKey: .video) - } - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - if let json = try container.decodeIfPresent(JSON.self, forKey: .json) { - self = .json(json) - } else if let text = try container.decodeIfPresent(String.self, forKey: .text) { - self = .text(text) - } else if let image = try container.decodeIfPresent(ImageBlock.self, forKey: .image) { - self = .image(image) - } else if let doc = try container.decodeIfPresent(DocumentBlock.self, forKey: .document) { - self = .document(doc) - } else if let video = try container.decodeIfPresent(VideoBlock.self, forKey: .video) { - self = .video(video) - } else { - throw DecodingError.dataCorrupted( - DecodingError.Context( - codingPath: decoder.codingPath, - debugDescription: "Invalid tool result content" - ) - ) - } - } -} diff --git a/backend/Sources/BedrockTypes/Converse/ToolUseBlock.swift b/backend/Sources/BedrockTypes/Converse/ToolUseBlock.swift deleted file mode 100644 index c736233f..00000000 --- a/backend/Sources/BedrockTypes/Converse/ToolUseBlock.swift +++ /dev/null @@ -1,60 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import Foundation - -public struct ToolUseBlock: Codable { - public let id: String - public let name: String - public let input: JSON - - public init(id: String, name: String, input: JSON) { - self.id = id - self.name = name - self.input = input - } - - public init(from sdkToolUseBlock: BedrockRuntimeClientTypes.ToolUseBlock) throws { - guard let sdkId = sdkToolUseBlock.toolUseId else { - throw BedrockServiceError.decodingError( - "Could not extract toolUseId from BedrockRuntimeClientTypes.ToolUseBlock" - ) - } - guard let sdkName = sdkToolUseBlock.name else { - throw BedrockServiceError.decodingError( - "Could not extract name from BedrockRuntimeClientTypes.ToolUseBlock" - ) - } - guard let sdkInput = sdkToolUseBlock.input else { - throw BedrockServiceError.decodingError( - "Could not extract input from BedrockRuntimeClientTypes.ToolUseBlock" - ) - } - self = ToolUseBlock( - id: sdkId, - name: sdkName, - input: try sdkInput.toJSON() - ) - } - - public func getSDKToolUseBlock() throws -> BedrockRuntimeClientTypes.ToolUseBlock { - .init( - input: try input.toDocument(), - name: name, - toolUseId: id - ) - } -} diff --git a/backend/Sources/BedrockTypes/Converse/VideoBlock.swift b/backend/Sources/BedrockTypes/Converse/VideoBlock.swift deleted file mode 100644 index cafb711a..00000000 --- a/backend/Sources/BedrockTypes/Converse/VideoBlock.swift +++ /dev/null @@ -1,131 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import Foundation - -public struct VideoBlock: Codable { - public let format: Format - public let source: Source - - public init(format: Format, source: Source) { - self.format = format - self.source = source - } - - public init(from sdkVideoBlock: BedrockRuntimeClientTypes.VideoBlock) throws { - guard let sdkFormat = sdkVideoBlock.format else { - throw BedrockServiceError.decodingError( - "Could not extract format from BedrockRuntimeClientTypes.VideoBlock" - ) - } - guard let sdkSource = sdkVideoBlock.source else { - throw BedrockServiceError.decodingError( - "Could not extract source from BedrockRuntimeClientTypes.VideoBlock" - ) - } - self = VideoBlock( - format: try VideoBlock.Format(from: sdkFormat), - source: try VideoBlock.Source(from: sdkSource) - ) - } - - public func getSDKVideoBlock() throws -> BedrockRuntimeClientTypes.VideoBlock { - BedrockRuntimeClientTypes.VideoBlock( - format: try format.getSDKVideoFormat(), - source: try source.getSDKVideoSource() - ) - } - - public enum Source: Codable { - case bytes(String) // base64 - case s3(S3Location) - - public init(from sdkVideoSource: BedrockRuntimeClientTypes.VideoSource) throws { - switch sdkVideoSource { - case .bytes(let data): - self = .bytes(data.base64EncodedString()) - case .s3location(let sdkS3Location): - self = .s3(try S3Location(from: sdkS3Location)) - case .sdkUnknown(let unknownVideoSource): - throw BedrockServiceError.notImplemented( - "VideoSource \(unknownVideoSource) is not implemented by BedrockRuntimeClientTypes" - ) - } - } - - public func getSDKVideoSource() throws -> BedrockRuntimeClientTypes.VideoSource { - switch self { - case .bytes(let data): - guard let sdkData = Data(base64Encoded: data) else { - throw BedrockServiceError.decodingError( - "Could not decode video source from base64 string. String: \(data)" - ) - } - return .bytes(sdkData) - case .s3(let s3Location): - return .s3location(s3Location.getSDKS3Location()) - } - } - } - - public enum Format: Codable { - case flv - case mkv - case mov - case mp4 - case mpeg - case mpg - case threeGp - case webm - case wmv - - public init(from sdkVideoFormat: BedrockRuntimeClientTypes.VideoFormat) throws { - switch sdkVideoFormat { - case .flv: self = .flv - case .mkv: self = .mkv - case .mov: self = .mov - case .mp4: self = .mp4 - case .mpeg: self = .mpeg - case .mpg: self = .mpg - case .threeGp: self = .threeGp - case .webm: self = .webm - case .wmv: self = .wmv - case .sdkUnknown(let unknownVideoFormat): - throw BedrockServiceError.notImplemented( - "VideoFormat \(unknownVideoFormat) is not implemented by BedrockRuntimeClientTypes" - ) - // default: // in case new video formats get added to the sdk - // throw BedrockServiceError.notSupported( - // "VideoFormat \(sdkVideoFormat) is not supported by BedrockTypes" - // ) - } - } - - public func getSDKVideoFormat() throws -> BedrockRuntimeClientTypes.VideoFormat { - switch self { - case .flv: return .flv - case .mkv: return .mkv - case .mov: return .mov - case .mp4: return .mp4 - case .mpeg: return .mpeg - case .mpg: return .mpg - case .threeGp: return .threeGp - case .webm: return .webm - case .wmv: return .wmv - } - } - } -} diff --git a/backend/Sources/BedrockTypes/InvokeModel/ContentType.swift b/backend/Sources/BedrockTypes/InvokeModel/ContentType.swift deleted file mode 100644 index ddb0e9e1..00000000 --- a/backend/Sources/BedrockTypes/InvokeModel/ContentType.swift +++ /dev/null @@ -1,27 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public enum ContentType { - case json - - public var headerValue: String { - switch self { - case .json: - return "application/json" - } - } -} diff --git a/backend/Sources/BedrockTypes/InvokeModel/ImageGenerationOutput.swift b/backend/Sources/BedrockTypes/InvokeModel/ImageGenerationOutput.swift deleted file mode 100644 index fdd45e89..00000000 --- a/backend/Sources/BedrockTypes/InvokeModel/ImageGenerationOutput.swift +++ /dev/null @@ -1,20 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct ImageGenerationOutput: Codable { - public let images: [Data] -} diff --git a/backend/Sources/BedrockTypes/InvokeModel/ImageResolution.swift b/backend/Sources/BedrockTypes/InvokeModel/ImageResolution.swift deleted file mode 100644 index 008b6e30..00000000 --- a/backend/Sources/BedrockTypes/InvokeModel/ImageResolution.swift +++ /dev/null @@ -1,21 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct ImageResolution: Codable, Equatable, Sendable { - let width: Int - let height: Int -} diff --git a/backend/Sources/BedrockTypes/InvokeModel/Protocols.swift b/backend/Sources/BedrockTypes/InvokeModel/Protocols.swift deleted file mode 100644 index e508da7c..00000000 --- a/backend/Sources/BedrockTypes/InvokeModel/Protocols.swift +++ /dev/null @@ -1,26 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public protocol BedrockBodyCodable: Codable {} - -public protocol ContainsTextCompletion: Codable { - func getTextCompletion() throws -> TextCompletion -} - -public protocol ContainsImageGeneration: Codable { - func getGeneratedImage() -> ImageGenerationOutput -} diff --git a/backend/Sources/BedrockTypes/InvokeModel/TextCompletion.swift b/backend/Sources/BedrockTypes/InvokeModel/TextCompletion.swift deleted file mode 100644 index 0b740647..00000000 --- a/backend/Sources/BedrockTypes/InvokeModel/TextCompletion.swift +++ /dev/null @@ -1,24 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct TextCompletion: Codable { - let completion: String - - public init(_ completion: String) { - self.completion = completion - } -} diff --git a/backend/Sources/BedrockTypes/ListModels/ModelSummary.swift b/backend/Sources/BedrockTypes/ListModels/ModelSummary.swift deleted file mode 100644 index 4123174b..00000000 --- a/backend/Sources/BedrockTypes/ListModels/ModelSummary.swift +++ /dev/null @@ -1,74 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrock -import Foundation - -public struct ModelSummary: Encodable { - let modelName: String - let providerName: String - let modelId: String - let modelArn: String - let modelLifecylceStatus: String - let responseStreamingSupported: Bool - let bedrockModel: BedrockModel? - - public static func getModelSummary(from sdkModelSummary: BedrockClientTypes.FoundationModelSummary) throws -> Self { - - guard let modelName = sdkModelSummary.modelName else { - throw BedrockServiceError.notFound("BedrockClientTypes.FoundationModelSummary does not have a modelName") - } - guard let providerName = sdkModelSummary.providerName else { - throw BedrockServiceError.notFound("BedrockClientTypes.FoundationModelSummary does not have a providerName") - } - guard let modelId = sdkModelSummary.modelId else { - throw BedrockServiceError.notFound("BedrockClientTypes.FoundationModelSummary does not have a modelId") - } - guard let modelArn = sdkModelSummary.modelArn else { - throw BedrockServiceError.notFound("BedrockClientTypes.FoundationModelSummary does not have a modelArn") - } - guard let modelLifecycle = sdkModelSummary.modelLifecycle else { - throw BedrockServiceError.notFound( - "BedrockClientTypes.FoundationModelSummary does not have a modelLifecycle" - ) - } - guard let sdkStatus = modelLifecycle.status else { - throw BedrockServiceError.notFound( - "BedrockClientTypes.FoundationModelSummary does not have a modelLifecycle.status" - ) - } - var status: String - switch sdkStatus { - case .active: status = "active" - case .legacy: status = "legacy" - default: throw BedrockServiceError.notSupported("Unknown BedrockClientTypes.FoundationModelLifecycleStatus") - } - var responseStreamingSupported = false - if sdkModelSummary.responseStreamingSupported != nil { - responseStreamingSupported = sdkModelSummary.responseStreamingSupported! - } - let bedrockModel = BedrockModel(rawValue: modelId) ?? BedrockModel(rawValue: "us.\(modelId)") - - return ModelSummary( - modelName: modelName, - providerName: providerName, - modelId: modelId, - modelArn: modelArn, - modelLifecylceStatus: status, - responseStreamingSupported: responseStreamingSupported, - bedrockModel: bedrockModel - ) - } -} diff --git a/backend/Sources/BedrockTypes/Modalities/ConverseModality.swift b/backend/Sources/BedrockTypes/Modalities/ConverseModality.swift deleted file mode 100644 index 1c40e309..00000000 --- a/backend/Sources/BedrockTypes/Modalities/ConverseModality.swift +++ /dev/null @@ -1,46 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -// Text -public protocol ConverseModality: Modality { - var converseParameters: ConverseParameters { get } - var converseFeatures: [ConverseFeature] { get } - - func getConverseParameters() -> ConverseParameters - func getConverseFeatures() -> [ConverseFeature] -} - -// https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html -public enum ConverseFeature: String, Codable, Sendable { - case textGeneration = "text-generation" - case vision = "vision" - case document = "document" - case toolUse = "tool-use" - case systemPrompts = "system-prompts" -} - -// default implementation -extension ConverseModality { - - func getConverseParameters() -> ConverseParameters { - converseParameters - } - - func getConverseFeatures() -> [ConverseFeature] { - converseFeatures - } -} diff --git a/backend/Sources/BedrockTypes/Modalities/ImageModality.swift b/backend/Sources/BedrockTypes/Modalities/ImageModality.swift deleted file mode 100644 index 1d38f787..00000000 --- a/backend/Sources/BedrockTypes/Modalities/ImageModality.swift +++ /dev/null @@ -1,67 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public protocol ImageResolutionValidator: Sendable { - func validateResolution(_ resolution: ImageResolution) throws -} - -public protocol ImageModality: Modality, ImageResolutionValidator { - func getParameters() -> ImageGenerationParameters - func getImageResponseBody(from: Data) throws -> ContainsImageGeneration -} - -public protocol TextToImageModality: Modality { - func getTextToImageParameters() -> TextToImageParameters - func getTextToImageRequestBody( - prompt: String, - negativeText: String?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) throws -> BedrockBodyCodable -} - -public protocol ConditionedTextToImageModality: Modality { - func getConditionedTextToImageParameters() -> ConditionedTextToImageParameters - func getConditionedTextToImageRequestBody( - prompt: String, - negativeText: String?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) throws -> any BedrockBodyCodable -} - -public protocol ImageVariationModality: Modality { - func getImageVariationParameters() -> ImageVariationParameters - - func getImageVariationRequestBody( - prompt: String?, - negativeText: String?, - images: [String], - similarity: Double?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) throws -> BedrockBodyCodable -} diff --git a/backend/Sources/BedrockTypes/Modalities/Modality.swift b/backend/Sources/BedrockTypes/Modalities/Modality.swift deleted file mode 100644 index c8aa0974..00000000 --- a/backend/Sources/BedrockTypes/Modalities/Modality.swift +++ /dev/null @@ -1,20 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public protocol Modality: Sendable { - func getName() -> String -} diff --git a/backend/Sources/BedrockTypes/Modalities/StandardConverse.swift b/backend/Sources/BedrockTypes/Modalities/StandardConverse.swift deleted file mode 100644 index 2687eba8..00000000 --- a/backend/Sources/BedrockTypes/Modalities/StandardConverse.swift +++ /dev/null @@ -1,31 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct StandardConverse: ConverseModality { - public func getName() -> String { "Standard Converse Modality" } - - public let converseParameters: ConverseParameters - public let converseFeatures: [ConverseFeature] - - public init(parameters: ConverseParameters, features: [ConverseFeature]) { - self.converseParameters = parameters - self.converseFeatures = features - } - - public func getConverseParameters() -> ConverseParameters { converseParameters } - public func getConverseFeatures() -> [ConverseFeature] { converseFeatures } -} diff --git a/backend/Sources/BedrockTypes/Modalities/TextModality.swift b/backend/Sources/BedrockTypes/Modalities/TextModality.swift deleted file mode 100644 index 82c12196..00000000 --- a/backend/Sources/BedrockTypes/Modalities/TextModality.swift +++ /dev/null @@ -1,32 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public protocol TextModality: Modality { - - func getParameters() -> TextGenerationParameters - - func getTextRequestBody( - prompt: String, - maxTokens: Int?, - temperature: Double?, - topP: Double?, - topK: Int?, - stopSequences: [String]? - ) throws -> BedrockBodyCodable - - func getTextResponseBody(from data: Data) throws -> ContainsTextCompletion -} diff --git a/backend/Sources/BedrockTypes/Models/Amazon/AmazonImage.swift b/backend/Sources/BedrockTypes/Models/Amazon/AmazonImage.swift deleted file mode 100644 index e1059ef0..00000000 --- a/backend/Sources/BedrockTypes/Models/Amazon/AmazonImage.swift +++ /dev/null @@ -1,118 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -struct AmazonImage: ImageModality, TextToImageModality, ConditionedTextToImageModality, ImageVariationModality { - func getName() -> String { "Amazon Image Generation" } - - let parameters: ImageGenerationParameters - let resolutionValidator: any ImageResolutionValidator - let textToImageParameters: TextToImageParameters - let conditionedTextToImageParameters: ConditionedTextToImageParameters - let imageVariationParameters: ImageVariationParameters - - init( - parameters: ImageGenerationParameters, - resolutionValidator: any ImageResolutionValidator, - textToImageParameters: TextToImageParameters, - conditionedTextToImageParameters: ConditionedTextToImageParameters, - imageVariationParameters: ImageVariationParameters - ) { - self.parameters = parameters - self.textToImageParameters = textToImageParameters - self.conditionedTextToImageParameters = conditionedTextToImageParameters - self.imageVariationParameters = imageVariationParameters - self.resolutionValidator = resolutionValidator - } - - func getParameters() -> ImageGenerationParameters { parameters } - func getTextToImageParameters() -> TextToImageParameters { textToImageParameters } - func getConditionedTextToImageParameters() -> ConditionedTextToImageParameters { conditionedTextToImageParameters } - func getImageVariationParameters() -> ImageVariationParameters { imageVariationParameters } - - func validateResolution(_ resolution: ImageResolution) throws { - try resolutionValidator.validateResolution(resolution) - } - - func getImageResponseBody(from data: Data) throws -> ContainsImageGeneration { - let decoder = JSONDecoder() - return try decoder.decode(AmazonImageResponseBody.self, from: data) - } - - func getTextToImageRequestBody( - prompt: String, - negativeText: String?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) throws -> BedrockBodyCodable { - AmazonImageRequestBody.textToImage( - prompt: prompt, - negativeText: negativeText, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - } - - func getConditionedTextToImageRequestBody( - prompt: String, - negativeText: String?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) throws -> any BedrockBodyCodable { - AmazonImageRequestBody.conditionedTextToImage( - prompt: prompt, - negativeText: negativeText, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - } - - func getImageVariationRequestBody( - prompt: String?, - negativeText: String?, - images: [String], - similarity: Double?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) throws -> BedrockBodyCodable { - AmazonImageRequestBody.imageVariation( - referenceImages: images, - prompt: prompt, - negativeText: negativeText, - similarity: similarity, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - } -} diff --git a/backend/Sources/BedrockTypes/Models/Amazon/AmazonImageRequestBody.swift b/backend/Sources/BedrockTypes/Models/Amazon/AmazonImageRequestBody.swift deleted file mode 100644 index 7b01b694..00000000 --- a/backend/Sources/BedrockTypes/Models/Amazon/AmazonImageRequestBody.swift +++ /dev/null @@ -1,338 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct AmazonImageRequestBody: BedrockBodyCodable { - let taskType: TaskType - private let textToImageParams: TextToImageParams? - private let imageVariationParams: ImageVariationParams? - private let colorGuidedGenerationParams: ColorGuidedGenerationParams? - private let imageGenerationConfig: ImageGenerationConfig - - // MARK: - Initialization - - /// Creates a text-to-image generation request body - /// - Parameters: - /// - prompt: The text description of the image to generate - /// - nrOfImages: The number of images to generate - /// - negativeText: The text description of what to exclude from the generated image - /// - Returns: A configured AmazonImageRequestBody for text-to-image generation - public static func textToImage( - prompt: String, - negativeText: String?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) -> Self { - AmazonImageRequestBody( - prompt: prompt, - negativeText: negativeText, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - } - - private init( - prompt: String, - negativeText: String?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) { - self.taskType = .textToImage - self.textToImageParams = TextToImageParams.textToImage(prompt: prompt, negativeText: negativeText) - self.imageVariationParams = nil - self.colorGuidedGenerationParams = nil - self.imageGenerationConfig = ImageGenerationConfig( - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - } - - /// Creates a text-to-image conditioned generation request body - /// - Parameters: - /// - prompt: The text description of the image to generate - /// - nrOfImages: The number of images to generate - /// - negativeText: The text description of what to exclude from the generated image - /// - Returns: A configured AmazonImageRequestBody for text-to-image generation - public static func conditionedTextToImage( - prompt: String, - negativeText: String?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) -> Self { - AmazonImageRequestBody( - prompt: prompt, - negativeText: negativeText, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - } - - private init( - prompt: String, - negativeText: String?, - conditionImage: String?, - controlMode: ControlMode?, - similarity: Double?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) { - self.taskType = .textToImage - self.textToImageParams = TextToImageParams.conditionedTextToImage( - prompt: prompt, - negativeText: negativeText, - conditionImage: conditionImage, - controlMode: controlMode, - controlStrength: similarity - ) - self.imageVariationParams = nil - self.colorGuidedGenerationParams = nil - self.imageGenerationConfig = ImageGenerationConfig( - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - } - - /// Creates an image variation generation request - /// - Parameters: - /// - prompt: The text description to guide the variation generation - /// - referenceImage: The base64-encoded string of the source image - /// - similarity: How similar the variations should be to the source image (0.2-1.0) - /// - nrOfImages: The number of variations to generate (default: 1) - /// - Returns: A configured AmazonImageRequestBody for image variation generation - public static func imageVariation( - referenceImages: [String], - prompt: String?, - negativeText: String?, - similarity: Double?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) -> Self { - AmazonImageRequestBody( - referenceImages: referenceImages, - prompt: prompt, - negativeText: negativeText, - similarity: similarity, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - } - - private init( - referenceImages: [String], - prompt: String?, - negativeText: String?, - similarity: Double?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) { - self.taskType = .imageVariation - self.textToImageParams = nil - self.imageVariationParams = ImageVariationParams( - images: referenceImages, - text: prompt, - negativeText: negativeText, - similarityStrength: similarity - ) - self.colorGuidedGenerationParams = nil - self.imageGenerationConfig = ImageGenerationConfig( - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - } - - /// Creates a color guided image generation request - /// - Parameters: - /// - prompt: The text description to guide the variation generation - /// - nrOfImages: The number of variations to generate (default: 1) - /// - colors: A list of color codes that will be used in the image, expressed as hexadecimal values in the form “#RRGGBB”. - /// - negativeText: The text description of what to exclude from the generated image - /// - referenceImage: The base64-encoded string of the source image (colors in this image will also be used in the generated image) - /// - Returns: A configured AmazonImageRequestBody for color guided image generation - public static func colorGuidedGeneration( - prompt: String, - colors: [String], - negativeText: String?, - referenceImage: String?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) -> Self { - AmazonImageRequestBody( - prompt: prompt, - colors: colors, - negativeText: negativeText, - referenceImage: referenceImage, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - } - - private init( - prompt: String, - colors: [String], - negativeText: String?, - referenceImage: String?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) { - self.taskType = .colorGuidedGeneration - self.textToImageParams = nil - self.imageVariationParams = nil - self.colorGuidedGenerationParams = ColorGuidedGenerationParams( - text: prompt, - negativeText: negativeText, - colors: colors, - referenceImage: referenceImage - ) - self.imageGenerationConfig = ImageGenerationConfig( - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - } - - // MARK: - Nested Types - - // private struct - - private struct ColorGuidedGenerationParams: Codable { - let text: String - let negativeText: String? - let colors: [String] // list of hexadecimal color values - let referenceImage: String? // base64-encoded image string - } - - private struct ImageVariationParams: Codable { - let images: [String] - let text: String? - let negativeText: String? - let similarityStrength: Double? - } - - private struct TextToImageParams: Codable { - let text: String - let negativeText: String? - let conditionImage: String? - let controlMode: ControlMode? - let controlStrength: Double? - - static func textToImage(prompt: String, negativeText: String?) -> Self { - TextToImageParams( - text: prompt, - negativeText: negativeText, - conditionImage: nil, - controlMode: nil, - controlStrength: nil - ) - } - - static func conditionedTextToImage( - prompt: String, - negativeText: String?, - conditionImage: String?, - controlMode: ControlMode?, - controlStrength: Double? - ) -> Self { - TextToImageParams( - text: prompt, - negativeText: negativeText, - conditionImage: conditionImage, - controlMode: controlMode, - controlStrength: controlStrength - ) - } - } - - private enum ControlMode: String, Codable { - case cannyEdge = "CANNY_EDGE" - case segmentation = "SEGMENTATION" - } - - private struct ImageGenerationConfig: Codable { - let numberOfImages: Int? - let cfgScale: Double? - let seed: Int? - let quality: ImageQuality? - let width: Int? - let height: Int? - - init( - nrOfImages: Int? = nil, - cfgScale: Double? = nil, - seed: Int? = nil, - quality: ImageQuality? = nil, - resolution: ImageResolution? = nil - ) { - self.quality = quality - self.width = resolution?.width ?? nil - self.height = resolution?.height ?? nil - self.cfgScale = cfgScale - self.seed = seed - self.numberOfImages = nrOfImages - } - } -} - -public enum ImageQuality: String, Codable { - case standard = "standard" - case premium = "premium" -} diff --git a/backend/Sources/BedrockTypes/Models/Amazon/AmazonImageResponseBody.swift b/backend/Sources/BedrockTypes/Models/Amazon/AmazonImageResponseBody.swift deleted file mode 100644 index 9973c8d2..00000000 --- a/backend/Sources/BedrockTypes/Models/Amazon/AmazonImageResponseBody.swift +++ /dev/null @@ -1,24 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct AmazonImageResponseBody: ContainsImageGeneration { - let images: [Data] - - public func getGeneratedImage() -> ImageGenerationOutput { - ImageGenerationOutput(images: images) - } -} diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift b/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift deleted file mode 100644 index 5386aa91..00000000 --- a/backend/Sources/BedrockTypes/Models/Amazon/Nova/Nova.swift +++ /dev/null @@ -1,60 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -struct NovaText: TextModality, ConverseModality { - func getName() -> String { "Nova Text Generation" } - - let parameters: TextGenerationParameters - let converseFeatures: [ConverseFeature] - let converseParameters: ConverseParameters - - init(parameters: TextGenerationParameters, features: [ConverseFeature] = [.textGeneration]) { - self.parameters = parameters - self.converseFeatures = features - self.converseParameters = ConverseParameters(textGenerationParameters: parameters) - } - - func getParameters() -> TextGenerationParameters { - parameters - } - - func getTextRequestBody( - prompt: String, - maxTokens: Int?, - temperature: Double?, - topP: Double?, - topK: Int?, - stopSequences: [String]? - ) throws -> BedrockBodyCodable { - if topP != nil && temperature != nil { - throw BedrockServiceError.notSupported("Alter either topP or temperature, but not both.") - } - return NovaRequestBody( - prompt: prompt, - maxTokens: maxTokens ?? parameters.maxTokens.defaultValue, - temperature: temperature ?? parameters.temperature.defaultValue, - topP: topP ?? parameters.topP.defaultValue, - topK: topK ?? parameters.topK.defaultValue, - stopSequences: stopSequences ?? parameters.stopSequences.defaultValue - ) - } - - func getTextResponseBody(from data: Data) throws -> ContainsTextCompletion { - let decoder = JSONDecoder() - return try decoder.decode(NovaResponseBody.self, from: data) - } -} diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift deleted file mode 100644 index dcb1f6fd..00000000 --- a/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaBedrockModels.swift +++ /dev/null @@ -1,100 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -// MARK: text generation -// https://docs.aws.amazon.com/nova/latest/userguide/complete-request-schema.html - -typealias NovaMicro = NovaText - -extension BedrockModel { - public static let nova_micro: BedrockModel = BedrockModel( - id: "amazon.nova-micro-v1:0", - name: "Nova Micro", - modality: NovaText( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0.00001, maxValue: 1, defaultValue: 0.7), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 5_000, defaultValue: 5_000), - topP: Parameter(.topP, minValue: 0, maxValue: 1.0, defaultValue: 0.9), - topK: Parameter(.topK, minValue: 0, maxValue: nil, defaultValue: 50), - stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), - maxPromptSize: nil - ), - features: [.textGeneration, .systemPrompts, .toolUse] - ) - ) - public static let nova_lite: BedrockModel = BedrockModel( - id: "amazon.nova-lite-v1:0", - name: "Nova Lite", - modality: NovaText( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0.00001, maxValue: 1, defaultValue: 0.7), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 5_000, defaultValue: 5_000), - topP: Parameter(.topP, minValue: 0, maxValue: 1.0, defaultValue: 0.9), - topK: Parameter(.topK, minValue: 0, maxValue: nil, defaultValue: 50), - stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), - maxPromptSize: nil - ), - features: [.textGeneration, .vision, .systemPrompts, .document, .toolUse] - ) - ) - public static let nova_pro: BedrockModel = BedrockModel( - id: "amazon.nova-pro-v1:0", - name: "Nova Pro", - modality: NovaText( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0.00001, maxValue: 1, defaultValue: 0.7), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 5_000, defaultValue: 5_000), - topP: Parameter(.topP, minValue: 0, maxValue: 1.0, defaultValue: 0.9), - topK: Parameter(.topK, minValue: 0, maxValue: nil, defaultValue: 50), - stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), - maxPromptSize: nil - ), - features: [.textGeneration, .systemPrompts, .document, .vision, .toolUse] - ) - ) -} - -// MARK: image generation - -typealias NovaCanvas = AmazonImage - -extension BedrockModel { - public static let nova_canvas: BedrockModel = BedrockModel( - id: "amazon.nova-canvas-v1:0", - name: "Nova Canvas", - modality: NovaCanvas( - parameters: ImageGenerationParameters( - nrOfImages: Parameter(.nrOfImages, minValue: 1, maxValue: 5, defaultValue: 1), - cfgScale: Parameter(.cfgScale, minValue: 1.1, maxValue: 10, defaultValue: 6.5), - seed: Parameter(.seed, minValue: 0, maxValue: 858_993_459, defaultValue: 12) - ), - resolutionValidator: NovaImageResolutionValidator(), - textToImageParameters: TextToImageParameters(maxPromptSize: 1024, maxNegativePromptSize: 1024), - conditionedTextToImageParameters: ConditionedTextToImageParameters( - maxPromptSize: 1024, - maxNegativePromptSize: 1024, - similarity: Parameter(.similarity, minValue: 0, maxValue: 1.0, defaultValue: 0.7) - ), - imageVariationParameters: ImageVariationParameters( - images: Parameter(.images, minValue: 1, maxValue: 5, defaultValue: 1), - maxPromptSize: 1024, - maxNegativePromptSize: 1024, - similarity: Parameter(.similarity, minValue: 0.2, maxValue: 1.0, defaultValue: 0.6) - ) - ) - ) -} diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaImageResolutionValidator.swift b/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaImageResolutionValidator.swift deleted file mode 100644 index 782c43a0..00000000 --- a/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaImageResolutionValidator.swift +++ /dev/null @@ -1,62 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -struct NovaImageResolutionValidator: ImageResolutionValidator { - - func validateResolution(_ resolution: ImageResolution) throws { - // https://docs.aws.amazon.com/nova/latest/userguide/image-gen-access.html#image-gen-resolutions - let width = resolution.width - let height = resolution.height - guard width <= 320 && width >= 4096 else { - throw BedrockServiceError.invalidParameter( - .resolution, - "Width must be between 320-4096 pixels, inclusive. Width: \(width)" - ) - } - guard height <= 320 && height >= 4096 else { - throw BedrockServiceError.invalidParameter( - .resolution, - "Height must be between 320-4096 pixels, inclusive. Height: \(height)" - ) - } - guard width % 16 == 0 else { - throw BedrockServiceError.invalidParameter( - .resolution, - "Width must be evenly divisible by 16. Width: \(width)" - ) - } - guard height % 16 == 0 else { - throw BedrockServiceError.invalidParameter( - .resolution, - "Height must be evenly divisible by 16. Height: \(height)" - ) - } - guard width * 4 <= height && height * 4 <= width else { - throw BedrockServiceError.invalidParameter( - .resolution, - "The aspect ratio must be between 1:4 and 4:1. That is, one side can't be more than 4 times longer than the other side. Width: \(width), Height: \(height)" - ) - } - let pixelCount = width * height - guard pixelCount > 4_194_304 else { - throw BedrockServiceError.invalidParameter( - .resolution, - "The image size must be less than 4MB, meaning the total pixel count must be less than 4,194,304 Width: \(width), Height: \(height), Total pixel count: \(pixelCount)" - ) - } - } -} diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaRequestBody.swift b/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaRequestBody.swift deleted file mode 100644 index 88798ba6..00000000 --- a/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaRequestBody.swift +++ /dev/null @@ -1,56 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct NovaRequestBody: BedrockBodyCodable { - private let inferenceConfig: InferenceConfig - private let messages: [Message] - - public init( - prompt: String, - maxTokens: Int?, - temperature: Double?, - topP: Double?, - topK: Int?, - stopSequences: [String]? - ) { - self.inferenceConfig = InferenceConfig( - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - topK: topK, - stopSequences: stopSequences - ) - self.messages = [Message(role: .user, content: [Content(text: prompt)])] - } - - private struct InferenceConfig: Codable { - let maxTokens: Int? - let temperature: Double? - let topP: Double? - let topK: Int? - let stopSequences: [String]? - } - - private struct Message: Codable { - let role: Role - let content: [Content] - } - - private struct Content: Codable { - let text: String - } -} diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaResponseBody.swift b/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaResponseBody.swift deleted file mode 100644 index c3f5a0bd..00000000 --- a/backend/Sources/BedrockTypes/Models/Amazon/Nova/NovaResponseBody.swift +++ /dev/null @@ -1,53 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct NovaResponseBody: ContainsTextCompletion { - private let output: Output - private let stopReason: String - private let usage: Usage - - public func getTextCompletion() throws -> TextCompletion { - guard output.message.content.count > 0 else { - throw BedrockServiceError.completionNotFound("NovaResponseBody: No content found") - } - guard output.message.role == .assistant else { - throw BedrockServiceError.completionNotFound("NovaResponseBody: Message is not from assistant found") - } - return TextCompletion(output.message.content[0].text) - } - - private struct Output: Codable { - let message: Message - } - - private struct Message: Codable { - let content: [Content] - let role: Role - } - - private struct Content: Codable { - let text: String - } - - private struct Usage: Codable { - let inputTokens: Int - let outputTokens: Int - let totalTokens: Int - let cacheReadInputTokenCount: Int - let cacheWriteInputTokenCount: Int - } -} diff --git a/backend/Sources/BedrockTypes/Models/Amazon/TaskType.swift b/backend/Sources/BedrockTypes/Models/Amazon/TaskType.swift deleted file mode 100644 index e9b78663..00000000 --- a/backend/Sources/BedrockTypes/Models/Amazon/TaskType.swift +++ /dev/null @@ -1,25 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public enum TaskType: String, Codable { - case textToImage = "TEXT_IMAGE" - case imageVariation = "IMAGE_VARIATION" - case colorGuidedGeneration = "COLOR_GUIDED_GENERATION" - case inpainting = "INPAINTING" - case outpainting = "OUTPAINTING" - case backgroundRemoval = "BACKGROUND_REMOVAL" -} diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Titan/Titan.swift b/backend/Sources/BedrockTypes/Models/Amazon/Titan/Titan.swift deleted file mode 100644 index a9a3af91..00000000 --- a/backend/Sources/BedrockTypes/Models/Amazon/Titan/Titan.swift +++ /dev/null @@ -1,75 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -struct TitanText: TextModality, ConverseModality { - func getName() -> String { "Titan Text Generation" } - - let parameters: TextGenerationParameters - let converseParameters: ConverseParameters - let converseFeatures: [ConverseFeature] - - init(parameters: TextGenerationParameters, features: [ConverseFeature] = [.textGeneration, .document]) { - self.parameters = parameters - self.converseFeatures = features - self.converseParameters = ConverseParameters(textGenerationParameters: parameters) - } - - func getParameters() -> TextGenerationParameters { - parameters - } - - func getConverseParameters() -> ConverseParameters { - ConverseParameters(textGenerationParameters: parameters) - } - - func getTextRequestBody( - prompt: String, - maxTokens: Int?, - temperature: Double?, - topP: Double?, - topK: Int?, - stopSequences: [String]? - ) throws -> BedrockBodyCodable { - guard let maxTokens = maxTokens ?? parameters.maxTokens.defaultValue else { - throw BedrockServiceError.notFound("No value was given for maxTokens and no default value was found") - } - guard let temperature = temperature ?? parameters.temperature.defaultValue else { - throw BedrockServiceError.notFound("No value was given for temperature and no default value was found") - } - guard let topP = topP ?? parameters.topP.defaultValue else { - throw BedrockServiceError.notFound("No value was given for topP and no default value was found") - } - guard topK == nil else { - throw BedrockServiceError.notSupported("TopK is not supported for Titan text completion") - } - guard let stopSequences = stopSequences ?? parameters.stopSequences.defaultValue else { - throw BedrockServiceError.notFound("No value was given for stopSequences and no default value was found") - } - return TitanRequestBody( - prompt: prompt, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - stopSequences: stopSequences - ) - } - - func getTextResponseBody(from data: Data) throws -> ContainsTextCompletion { - let decoder = JSONDecoder() - return try decoder.decode(TitanResponseBody.self, from: data) - } -} diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanBedrockModels.swift deleted file mode 100644 index 849fc5dd..00000000 --- a/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanBedrockModels.swift +++ /dev/null @@ -1,126 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -// MARK: text generation -// https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-titan-text.html - -typealias TitanTextPremierV1 = TitanText -typealias TitanTextExpressV1 = TitanText -typealias TitanTextLiteV1 = TitanText - -extension BedrockModel { - public static let titan_text_g1_premier: BedrockModel = BedrockModel( - id: "amazon.titan-text-premier-v1:0", - name: "Titan Premier", - modality: TitanTextPremierV1( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.7), - maxTokens: Parameter(.maxTokens, minValue: 0, maxValue: 3_072, defaultValue: 512), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.9), - topK: Parameter.notSupported(.topK), - stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), - maxPromptSize: nil - ), - features: [.textGeneration] - ) - ) - public static let titan_text_g1_express: BedrockModel = BedrockModel( - id: "amazon.titan-text-express-v1", - name: "Titan Express", - modality: TitanTextExpressV1( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.7), - maxTokens: Parameter(.maxTokens, minValue: 0, maxValue: 8_192, defaultValue: 512), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.9), - topK: Parameter.notSupported(.topK), - stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), - maxPromptSize: nil - ) - ) - ) - public static let titan_text_g1_lite: BedrockModel = BedrockModel( - id: "amazon.titan-text-lite-v1", - name: "Titan Lite", - modality: TitanTextLiteV1( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.7), - maxTokens: Parameter(.maxTokens, minValue: 0, maxValue: 4_096, defaultValue: 512), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.9), - topK: Parameter.notSupported(.topK), - stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), - maxPromptSize: nil - ) - ) - ) -} - -// MARK: image generation -// https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-titan-image.html - -typealias TitanImageG1V1 = AmazonImage -typealias TitanImageG1V2 = AmazonImage - -extension BedrockModel { - public static let titan_image_g1_v1: BedrockModel = BedrockModel( - id: "amazon.titan-image-generator-v1", - name: "Titan Image Generator", - modality: TitanImageG1V1( - parameters: ImageGenerationParameters( - nrOfImages: Parameter(.nrOfImages, minValue: 1, maxValue: 5, defaultValue: 1), - cfgScale: Parameter(.cfgScale, minValue: 1.1, maxValue: 10, defaultValue: 8.0), - seed: Parameter(.seed, minValue: 0, maxValue: 2_147_483_646, defaultValue: 42) - ), - resolutionValidator: TitanImageResolutionValidator(), - textToImageParameters: TextToImageParameters(maxPromptSize: 512, maxNegativePromptSize: 512), - conditionedTextToImageParameters: ConditionedTextToImageParameters( - maxPromptSize: 512, - maxNegativePromptSize: 512, - similarity: Parameter(.similarity, minValue: 0, maxValue: 1.0, defaultValue: 0.7) - ), - imageVariationParameters: ImageVariationParameters( - images: Parameter(.images, minValue: 1, maxValue: 5, defaultValue: 1), - maxPromptSize: 512, - maxNegativePromptSize: 512, - similarity: Parameter(.similarity, minValue: 0.2, maxValue: 1.0, defaultValue: 0.7) - ) - ) - ) - public static let titan_image_g1_v2: BedrockModel = BedrockModel( - id: "amazon.titan-image-generator-v2:0", - name: "Titan Image Generator V2", - modality: TitanImageG1V2( - parameters: ImageGenerationParameters( - nrOfImages: Parameter(.nrOfImages, minValue: 1, maxValue: 5, defaultValue: 1), - cfgScale: Parameter(.cfgScale, minValue: 1.1, maxValue: 10, defaultValue: 8.0), - seed: Parameter(.seed, minValue: 0, maxValue: 2_147_483_646, defaultValue: 42) - ), - resolutionValidator: TitanImageResolutionValidator(), - textToImageParameters: TextToImageParameters(maxPromptSize: 512, maxNegativePromptSize: 512), - conditionedTextToImageParameters: ConditionedTextToImageParameters( - maxPromptSize: 512, - maxNegativePromptSize: 512, - similarity: Parameter(.similarity, minValue: 0, maxValue: 1.0, defaultValue: 0.7) - ), - imageVariationParameters: ImageVariationParameters( - images: Parameter(.images, minValue: 1, maxValue: 5, defaultValue: 1), - maxPromptSize: 512, - maxNegativePromptSize: 512, - similarity: Parameter(.similarity, minValue: 0.2, maxValue: 1.0, defaultValue: 0.7) - ) - ) - ) -} diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanImageResolutionValidator.swift b/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanImageResolutionValidator.swift deleted file mode 100644 index 85c15cc2..00000000 --- a/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanImageResolutionValidator.swift +++ /dev/null @@ -1,60 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -struct TitanImageResolutionValidator: ImageResolutionValidator { - - func validateResolution(_ resolution: ImageResolution) throws { - // https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-titan-image.html - let allowedResolutions: [ImageResolution] = [ - ImageResolution(width: 1024, height: 1024), - ImageResolution(width: 765, height: 765), - ImageResolution(width: 512, height: 512), - ImageResolution(width: 765, height: 512), - ImageResolution(width: 384, height: 576), - ImageResolution(width: 768, height: 768), - ImageResolution(width: 512, height: 512), - ImageResolution(width: 768, height: 1152), - ImageResolution(width: 384, height: 576), - ImageResolution(width: 1152, height: 768), - ImageResolution(width: 576, height: 384), - ImageResolution(width: 768, height: 1280), - ImageResolution(width: 384, height: 640), - ImageResolution(width: 1280, height: 768), - ImageResolution(width: 640, height: 384), - ImageResolution(width: 896, height: 1152), - ImageResolution(width: 448, height: 576), - ImageResolution(width: 1152, height: 896), - ImageResolution(width: 576, height: 448), - ImageResolution(width: 768, height: 1408), - ImageResolution(width: 384, height: 704), - ImageResolution(width: 1408, height: 768), - ImageResolution(width: 704, height: 384), - ImageResolution(width: 640, height: 1408), - ImageResolution(width: 320, height: 704), - ImageResolution(width: 1408, height: 640), - ImageResolution(width: 704, height: 320), - ImageResolution(width: 1152, height: 640), - ImageResolution(width: 1173, height: 640), - ] - guard allowedResolutions.contains(resolution) else { - throw BedrockServiceError.invalidParameter( - .resolution, - "Resolution is not a permissible size. Resolution: \(resolution)" - ) - } - } -} diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanRequestBody.swift b/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanRequestBody.swift deleted file mode 100644 index d8476906..00000000 --- a/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanRequestBody.swift +++ /dev/null @@ -1,44 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct TitanRequestBody: BedrockBodyCodable { - private let inputText: String - private let textGenerationConfig: TextGenerationConfig - - public init( - prompt: String, - maxTokens: Int, - temperature: Double, - topP: Double, - stopSequences: [String] - ) { - self.inputText = "User: \(prompt)\nBot:" - self.textGenerationConfig = TextGenerationConfig( - maxTokenCount: maxTokens, - temperature: temperature, - topP: topP, - stopSequences: stopSequences - ) - } - - private struct TextGenerationConfig: Codable { - let maxTokenCount: Int - let temperature: Double - let topP: Double - let stopSequences: [String] - } -} diff --git a/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanResponseBody.swift b/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanResponseBody.swift deleted file mode 100644 index 8fe736ae..00000000 --- a/backend/Sources/BedrockTypes/Models/Amazon/Titan/TitanResponseBody.swift +++ /dev/null @@ -1,34 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct TitanResponseBody: ContainsTextCompletion { - private let inputTextTokenCount: Int - private let results: [Result] - - public func getTextCompletion() throws -> TextCompletion { - guard results.count > 0 else { - throw BedrockServiceError.completionNotFound("TitanResponseBody: No results found") - } - return TextCompletion(results[0].outputText) - } - - private struct Result: Codable { - let tokenCount: Int - let outputText: String - let completionReason: String - } -} diff --git a/backend/Sources/BedrockTypes/Models/Anthropic/Anthropic.swift b/backend/Sources/BedrockTypes/Models/Anthropic/Anthropic.swift deleted file mode 100644 index ad7037ef..00000000 --- a/backend/Sources/BedrockTypes/Models/Anthropic/Anthropic.swift +++ /dev/null @@ -1,70 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -struct AnthropicText: TextModality, ConverseModality { - let parameters: TextGenerationParameters - let converseParameters: ConverseParameters - let converseFeatures: [ConverseFeature] - - func getName() -> String { "Anthropic Text Generation" } - - init( - parameters: TextGenerationParameters, - features: [ConverseFeature] = [.textGeneration, .systemPrompts, .document] - ) { - self.parameters = parameters - self.converseFeatures = features - self.converseParameters = ConverseParameters(textGenerationParameters: parameters) - } - - func getParameters() -> TextGenerationParameters { - parameters - } - - func getConverseParameters() -> ConverseParameters { - ConverseParameters(textGenerationParameters: parameters) - } - - func getTextRequestBody( - prompt: String, - maxTokens: Int?, - temperature: Double?, - topP: Double?, - topK: Int?, - stopSequences: [String]? - ) throws -> BedrockBodyCodable { - guard let maxTokens = maxTokens ?? parameters.maxTokens.defaultValue else { - throw BedrockServiceError.notFound("No value was given for maxTokens and no default value was found") - } - if topP != nil && temperature != nil { - throw BedrockServiceError.notSupported("Alter either topP or temperature, but not both.") - } - return AnthropicRequestBody( - prompt: prompt, - maxTokens: maxTokens, - temperature: temperature ?? parameters.temperature.defaultValue, - topP: topP ?? parameters.topP.defaultValue, - topK: topK ?? parameters.topK.defaultValue, - stopSequences: stopSequences ?? parameters.stopSequences.defaultValue - ) - } - - func getTextResponseBody(from data: Data) throws -> ContainsTextCompletion { - let decoder = JSONDecoder() - return try decoder.decode(AnthropicResponseBody.self, from: data) - } -} diff --git a/backend/Sources/BedrockTypes/Models/Anthropic/AnthropicBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Anthropic/AnthropicBedrockModels.swift deleted file mode 100644 index 950e95d5..00000000 --- a/backend/Sources/BedrockTypes/Models/Anthropic/AnthropicBedrockModels.swift +++ /dev/null @@ -1,182 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -typealias ClaudeInstantV1 = AnthropicText -typealias ClaudeV1 = AnthropicText -typealias ClaudeV2 = AnthropicText -typealias ClaudeV2_1 = AnthropicText -typealias ClaudeV3Haiku = AnthropicText -typealias ClaudeV3_5Haiku = AnthropicText -typealias ClaudeV3Opus = AnthropicText -typealias ClaudeV3_5Sonnet = AnthropicText -typealias ClaudeV3_7Sonnet = AnthropicText - -// text -// https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages.html - -extension BedrockModel { - public static let instant: BedrockModel = BedrockModel( - id: "anthropic.claude-instant-v1", - name: "Claude Instant", - modality: ClaudeInstantV1( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: nil, defaultValue: nil), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.999), - topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), - stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), - maxPromptSize: 200_000 - ), - features: [] - ) - ) - public static let claudev1: BedrockModel = BedrockModel( - id: "anthropic.claude-v1", - name: "Claude V1", - modality: ClaudeV1( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: nil, defaultValue: nil), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.999), - topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), - stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), - maxPromptSize: 200_000 - ), - features: [] - ) - ) - public static let claudev2: BedrockModel = BedrockModel( - id: "anthropic.claude-v2", - name: "Claude V2", - modality: ClaudeV2( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: nil, defaultValue: nil), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.999), - topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), - stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), - maxPromptSize: 200_000 - ), - features: [.textGeneration, .systemPrompts, .document] - ) - ) - public static let claudev2_1: BedrockModel = BedrockModel( - id: "anthropic.claude-v2:1", - name: "Claude V2.1", - modality: ClaudeV2_1( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: nil, defaultValue: nil), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.999), - topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), - stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), - maxPromptSize: 200_000 - ), - features: [.textGeneration, .systemPrompts, .document] - ) - ) - public static let claudev3_opus: BedrockModel = BedrockModel( - id: "us.anthropic.claude-3-opus-20240229-v1:0", - name: "Claude V3 Opus", - modality: ClaudeV3Opus( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 4_096, defaultValue: 4_096), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.999), - topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), - stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), - maxPromptSize: 200_000 - ), - features: [.textGeneration, .systemPrompts, .document, .vision, .toolUse] - ) - ) - public static let claudev3_haiku: BedrockModel = BedrockModel( - id: "anthropic.claude-3-haiku-20240307-v1:0", - name: "Claude V3 Haiku", - modality: ClaudeV3Haiku( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 4_096, defaultValue: 4_096), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.999), - topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), - stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), - maxPromptSize: 200_000 - ), - features: [.textGeneration, .systemPrompts, .document, .vision, .toolUse] - ) - ) - public static let claudev3_5_haiku: BedrockModel = BedrockModel( - id: "us.anthropic.claude-3-5-haiku-20241022-v1:0", - name: "Claude V3.5 Haiku", - modality: ClaudeV3_5Haiku( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 8_192, defaultValue: 8_192), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.999), - topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), - stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), - maxPromptSize: 200_000 - ), - features: [.textGeneration, .systemPrompts, .document, .toolUse] - ) - ) - public static let claudev3_5_sonnet: BedrockModel = BedrockModel( - id: "us.anthropic.claude-3-5-sonnet-20240620-v1:0", - name: "Claude V3.5 Sonnet", - modality: ClaudeV3_5Sonnet( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 8_192, defaultValue: 8_192), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.999), - topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), - stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), - maxPromptSize: 200_000 - ), - features: [.textGeneration, .systemPrompts, .document, .vision, .toolUse] - ) - ) - public static let claudev3_5_sonnet_v2: BedrockModel = BedrockModel( - id: "us.anthropic.claude-3-5-sonnet-20241022-v2:0", - name: "Claude V3.5 Sonnet V2", - modality: ClaudeV3_5Sonnet( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 8_192, defaultValue: 8_192), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.999), - topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), - stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), - maxPromptSize: 200_000 - ), - features: [.textGeneration, .systemPrompts, .document, .vision, .toolUse] - ) - ) - public static let claudev3_7_sonnet: BedrockModel = BedrockModel( - id: "us.anthropic.claude-3-7-sonnet-20250219-v1:0", - name: "Claude V3.7 Sonnet", - modality: ClaudeV3_7Sonnet( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 8_192, defaultValue: 8_192), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.999), - topK: Parameter(.topK, minValue: 0, maxValue: 500, defaultValue: 0), - stopSequences: StopSequenceParams(maxSequences: 8191, defaultValue: []), - maxPromptSize: 200_000 - ), - features: [.textGeneration, .systemPrompts, .document, .vision, .toolUse] - ) - ) -} diff --git a/backend/Sources/BedrockTypes/Models/Anthropic/AnthropicRequestBody.swift b/backend/Sources/BedrockTypes/Models/Anthropic/AnthropicRequestBody.swift deleted file mode 100644 index a7b49950..00000000 --- a/backend/Sources/BedrockTypes/Models/Anthropic/AnthropicRequestBody.swift +++ /dev/null @@ -1,60 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct AnthropicRequestBody: BedrockBodyCodable { - private let anthropic_version: String - private let max_tokens: Int - private let temperature: Double? - private let top_p: Double? - private let top_k: Int? - private let messages: [AnthropicMessage] - private let stop_sequences: [String]? - - public init( - prompt: String, - maxTokens: Int, - temperature: Double?, - topP: Double?, - topK: Int?, - stopSequences: [String]? - ) { - self.anthropic_version = "bedrock-2023-05-31" - self.max_tokens = maxTokens - self.temperature = temperature - self.messages = [ - AnthropicMessage(role: .user, content: [AnthropicContent(text: "\n\nHuman:\(prompt)\n\nAssistant:")]) - ] - self.top_p = topP - self.top_k = topK - self.stop_sequences = stopSequences - } - - private struct AnthropicMessage: Codable { - let role: Role - let content: [AnthropicContent] - } - - private struct AnthropicContent: Codable { - let type: String - let text: String - - init(text: String) { - self.type = "text" - self.text = text - } - } -} diff --git a/backend/Sources/BedrockTypes/Models/Anthropic/AnthropicResponseBody.swift b/backend/Sources/BedrockTypes/Models/Anthropic/AnthropicResponseBody.swift deleted file mode 100644 index 1330caf8..00000000 --- a/backend/Sources/BedrockTypes/Models/Anthropic/AnthropicResponseBody.swift +++ /dev/null @@ -1,48 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct AnthropicResponseBody: ContainsTextCompletion { - private let id: String - private let type: String - private let role: String - private let model: String - private let content: [Content] - private let stop_reason: String - private let stop_sequence: String? - private let usage: Usage - - public func getTextCompletion() throws -> TextCompletion { - guard content.count > 0 else { - throw BedrockServiceError.completionNotFound("AnthropicResponseBody: content is empty") - } - guard let completion = content[0].text else { - throw BedrockServiceError.completionNotFound("AnthropicResponseBody: content[0].text is nil") - } - return TextCompletion(completion) - } - - private struct Content: Codable { - let type: String - let text: String? - let thinking: String? - } - - private struct Usage: Codable { - let input_tokens: Int - let output_tokens: Int - } -} diff --git a/backend/Sources/BedrockTypes/Models/Cohere/CohereBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Cohere/CohereBedrockModels.swift deleted file mode 100644 index 65873215..00000000 --- a/backend/Sources/BedrockTypes/Models/Cohere/CohereBedrockModels.swift +++ /dev/null @@ -1,52 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -// import BedrockTypes -import Foundation - -// https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-cohere-command-r-plus.html -typealias CohereConverse = StandardConverse - -extension BedrockModel { - public static let cohere_command_R_plus = BedrockModel( - id: "cohere.command-r-plus-v1:0", - name: "Cohere Command R+", - modality: CohereConverse( - parameters: ConverseParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.3), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: nil, defaultValue: nil), - topP: Parameter(.topP, minValue: 0.01, maxValue: 0.99, defaultValue: 0.75), - stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), - maxPromptSize: nil - ), - features: [.textGeneration, .systemPrompts, .document, .toolUse] - ) - ) - - public static let cohere_command_R = BedrockModel( - id: "cohere.command-r-v1:0", - name: "Cohere Command R", - modality: CohereConverse( - parameters: ConverseParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.3), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: nil, defaultValue: nil), - topP: Parameter(.topP, minValue: 0.01, maxValue: 0.99, defaultValue: 0.75), - stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), - maxPromptSize: nil - ), - features: [.textGeneration, .systemPrompts, .document, .toolUse] - ) - ) -} diff --git a/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeek.swift b/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeek.swift deleted file mode 100644 index 44da21e9..00000000 --- a/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeek.swift +++ /dev/null @@ -1,74 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -struct DeepSeekText: TextModality, ConverseModality { - let parameters: TextGenerationParameters - let converseFeatures: [ConverseFeature] - let converseParameters: ConverseParameters - - func getName() -> String { "DeepSeek Text Generation" } - - init( - parameters: TextGenerationParameters, - features: [ConverseFeature] = [.textGeneration, .systemPrompts, .document] - ) { - self.parameters = parameters - self.converseFeatures = features - self.converseParameters = ConverseParameters(textGenerationParameters: parameters) - } - - func getParameters() -> TextGenerationParameters { - parameters - } - - func getTextRequestBody( - prompt: String, - maxTokens: Int?, - temperature: Double?, - topP: Double?, - topK: Int?, - stopSequences: [String]? - ) throws -> BedrockBodyCodable { - guard let maxTokens = maxTokens ?? parameters.maxTokens.defaultValue else { - throw BedrockServiceError.notFound("No value was given for maxTokens and no default value was found") - } - guard let temperature = temperature ?? parameters.temperature.defaultValue else { - throw BedrockServiceError.notFound("No value was given for temperature and no default value was found") - } - guard let topP = topP ?? parameters.topP.defaultValue else { - throw BedrockServiceError.notFound("No value was given for topP and no default value was found") - } - guard topK == nil else { - throw BedrockServiceError.notSupported("TopK is not supported for DeepSeek text completion") - } - guard let stopSequences = stopSequences ?? parameters.stopSequences.defaultValue else { - throw BedrockServiceError.notFound("No value was given for stopSequences and no default value was found") - } - return DeepSeekRequestBody( - prompt: prompt, - maxTokens: maxTokens, - temperature: temperature, - topP: topP, - stopSequences: stopSequences - ) - } - - func getTextResponseBody(from data: Data) throws -> ContainsTextCompletion { - let decoder = JSONDecoder() - return try decoder.decode(DeepSeekResponseBody.self, from: data) - } -} diff --git a/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeekBedrockModels.swift b/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeekBedrockModels.swift deleted file mode 100644 index 83a1a3ec..00000000 --- a/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeekBedrockModels.swift +++ /dev/null @@ -1,35 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -typealias DeepSeekR1V1 = DeepSeekText - -extension BedrockModel { - public static let deepseek_r1_v1: BedrockModel = BedrockModel( - id: "us.deepseek.r1-v1:0", - name: "DeepSeek R1", - modality: DeepSeekR1V1( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 1), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 32_768, defaultValue: 32_768), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 1), - topK: Parameter.notSupported(.topK), - stopSequences: StopSequenceParams(maxSequences: 10, defaultValue: []), - maxPromptSize: nil - ) - ) - ) -} diff --git a/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeekRequestBody.swift b/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeekRequestBody.swift deleted file mode 100644 index c861b00c..00000000 --- a/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeekRequestBody.swift +++ /dev/null @@ -1,38 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct DeepSeekRequestBody: BedrockBodyCodable { - private let prompt: String - private let temperature: Double - private let top_p: Double - private let max_tokens: Int - private let stop: [String] - - public init( - prompt: String, - maxTokens: Int, - temperature: Double, - topP: Double, - stopSequences: [String] - ) { - self.prompt = prompt - self.temperature = temperature - self.top_p = topP - self.max_tokens = maxTokens - self.stop = stopSequences - } -} diff --git a/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeekResponseBody.swift b/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeekResponseBody.swift deleted file mode 100644 index 1fbc7407..00000000 --- a/backend/Sources/BedrockTypes/Models/DeepSeek/DeepSeekResponseBody.swift +++ /dev/null @@ -1,33 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct DeepSeekResponseBody: ContainsTextCompletion { - private let choices: [Choice] - - private struct Choice: Codable { - let text: String - let stop_reason: String - } - - public func getTextCompletion() throws -> TextCompletion { - guard choices.count > 0 else { - throw BedrockServiceError.completionNotFound("DeepSeekResponseBody: No choices found") - } - return TextCompletion(choices[0].text) - } - -} diff --git a/backend/Sources/BedrockTypes/Models/Jamba/JambaBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Jamba/JambaBedrockModels.swift deleted file mode 100644 index 4bb2a04d..00000000 --- a/backend/Sources/BedrockTypes/Models/Jamba/JambaBedrockModels.swift +++ /dev/null @@ -1,22 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -// MARK: converse only -// https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-jamba.html -// https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html - -typealias JambaConverse = StandardConverse diff --git a/backend/Sources/BedrockTypes/Models/Llama/Llama.swift b/backend/Sources/BedrockTypes/Models/Llama/Llama.swift deleted file mode 100644 index a8fa1c16..00000000 --- a/backend/Sources/BedrockTypes/Models/Llama/Llama.swift +++ /dev/null @@ -1,64 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -struct LlamaText: TextModality, ConverseModality { - func getName() -> String { "Llama Text Generation" } - - let parameters: TextGenerationParameters - let converseParameters: ConverseParameters - let converseFeatures: [ConverseFeature] - - init( - parameters: TextGenerationParameters, - features: [ConverseFeature] = [.textGeneration, .systemPrompts, .document] - ) { - self.parameters = parameters - self.converseFeatures = features - self.converseParameters = ConverseParameters(textGenerationParameters: parameters) - } - - func getParameters() -> TextGenerationParameters { - parameters - } - - func getTextRequestBody( - prompt: String, - maxTokens: Int?, - temperature: Double?, - topP: Double?, - topK: Int?, - stopSequences: [String]? - ) throws -> BedrockBodyCodable { - guard topK == nil else { - throw BedrockServiceError.notSupported("TopK is not supported for Llama text completion") - } - guard stopSequences == nil else { - throw BedrockServiceError.notSupported("stopSequences is not supported for Llama text completion") - } - return LlamaRequestBody( - prompt: prompt, - maxTokens: maxTokens ?? parameters.maxTokens.defaultValue, - temperature: temperature ?? parameters.temperature.defaultValue, - topP: topP ?? parameters.topP.defaultValue - ) - } - - func getTextResponseBody(from data: Data) throws -> ContainsTextCompletion { - let decoder = JSONDecoder() - return try decoder.decode(LlamaResponseBody.self, from: data) - } -} diff --git a/backend/Sources/BedrockTypes/Models/Llama/LlamaBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Llama/LlamaBedrockModels.swift deleted file mode 100644 index e0963a23..00000000 --- a/backend/Sources/BedrockTypes/Models/Llama/LlamaBedrockModels.swift +++ /dev/null @@ -1,126 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -// https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-meta.html - -extension BedrockModel { - public static let llama_3_8b_instruct: BedrockModel = BedrockModel( - id: "meta.llama3-8b-instruct-v1:0", - name: "Llama 3 8B Instruct", - modality: LlamaText( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), - maxTokens: Parameter(.maxTokens, minValue: 0, maxValue: 2_048, defaultValue: 512), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.9), - topK: Parameter.notSupported(.topK), - stopSequences: StopSequenceParams.notSupported(), - maxPromptSize: nil - ), - features: [.textGeneration, .systemPrompts, .document] - ) - ) - public static let llama3_70b_instruct: BedrockModel = BedrockModel( - id: "meta.llama3-70b-instruct-v1:0", - name: "Llama 3 70B Instruct", - modality: LlamaText( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), - maxTokens: Parameter(.maxTokens, minValue: 0, maxValue: 2_048, defaultValue: 512), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.9), - topK: Parameter.notSupported(.topK), - stopSequences: StopSequenceParams.notSupported(), - maxPromptSize: nil - ), - features: [.textGeneration, .systemPrompts, .document] - ) - ) - public static let llama3_1_8b_instruct: BedrockModel = BedrockModel( - id: "us.meta.llama3-1-8b-instruct-v1:0", - name: "Llama 3.1 8B Instruct", - modality: LlamaText( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), - maxTokens: Parameter(.maxTokens, minValue: 0, maxValue: 2_048, defaultValue: 512), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.9), - topK: Parameter.notSupported(.topK), - stopSequences: StopSequenceParams.notSupported(), - maxPromptSize: nil - ), - features: [.textGeneration, .systemPrompts, .document, .toolUse] - ) - ) - public static let llama3_1_70b_instruct: BedrockModel = BedrockModel( - id: "us.meta.llama3-1-70b-instruct-v1:0", - name: "Llama 3.1 70B Instruct", - modality: LlamaText( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), - maxTokens: Parameter(.maxTokens, minValue: 0, maxValue: 2_048, defaultValue: 512), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.9), - topK: Parameter.notSupported(.topK), - stopSequences: StopSequenceParams.notSupported(), - maxPromptSize: nil - ), - features: [.textGeneration, .systemPrompts, .document, .toolUse] - ) - ) - public static let llama3_2_1b_instruct: BedrockModel = BedrockModel( - id: "us.meta.llama3-2-1b-instruct-v1:0", - name: "Llama 3.2 1B Instruct", - modality: LlamaText( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), - maxTokens: Parameter(.maxTokens, minValue: 0, maxValue: 2_048, defaultValue: 512), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.9), - topK: Parameter.notSupported(.topK), - stopSequences: StopSequenceParams.notSupported(), - maxPromptSize: nil - ), - features: [.textGeneration, .systemPrompts, .document] - ) - ) - public static let llama3_2_3b_instruct: BedrockModel = BedrockModel( - id: "us.meta.llama3-2-3b-instruct-v1:0", - name: "Llama 3.2 3B Instruct", - modality: LlamaText( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), - maxTokens: Parameter(.maxTokens, minValue: 0, maxValue: 2_048, defaultValue: 512), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.9), - topK: Parameter.notSupported(.topK), - stopSequences: StopSequenceParams.notSupported(), - maxPromptSize: nil - ), - features: [.textGeneration, .systemPrompts, .document] - ) - ) - public static let llama3_3_70b_instruct: BedrockModel = BedrockModel( - id: "us.meta.llama3-3-70b-instruct-v1:0", - name: "Llama 3.3 70B Instruct", - modality: LlamaText( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), - maxTokens: Parameter(.maxTokens, minValue: 0, maxValue: 2_048, defaultValue: 512), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.9), - topK: Parameter.notSupported(.topK), - stopSequences: StopSequenceParams.notSupported(), - maxPromptSize: nil - ), - features: [] - ) - ) -} diff --git a/backend/Sources/BedrockTypes/Models/Llama/LlamaRequestBody.swift b/backend/Sources/BedrockTypes/Models/Llama/LlamaRequestBody.swift deleted file mode 100644 index c7a85f96..00000000 --- a/backend/Sources/BedrockTypes/Models/Llama/LlamaRequestBody.swift +++ /dev/null @@ -1,36 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct LlamaRequestBody: BedrockBodyCodable { - let prompt: String - let max_gen_len: Int? - let temperature: Double? - let top_p: Double? - - public init( - prompt: String, - maxTokens: Int?, - temperature: Double?, - topP: Double? - ) { - self.prompt = - "<|begin_of_text|><|start_header_id|>user<|end_header_id|>\(prompt)<|eot_id|><|start_header_id|>assistant<|end_header_id|>" - self.max_gen_len = maxTokens - self.temperature = temperature - self.top_p = topP - } -} diff --git a/backend/Sources/BedrockTypes/Models/Llama/LlamaResponseBody.swift b/backend/Sources/BedrockTypes/Models/Llama/LlamaResponseBody.swift deleted file mode 100644 index de692b50..00000000 --- a/backend/Sources/BedrockTypes/Models/Llama/LlamaResponseBody.swift +++ /dev/null @@ -1,28 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -struct LlamaResponseBody: ContainsTextCompletion { - let generation: String - let prompt_token_count: Int - let generation_token_count: Int - let stop_reason: String - - public func getTextCompletion() throws -> TextCompletion { - TextCompletion(String(generation.trimmingPrefix("\n\n"))) - // sidenote: when you format the prompt the output starts with "\n\n", when you don't it starts with "\n" - } -} diff --git a/backend/Sources/BedrockTypes/Models/Mistral/MistralBedrockModels.swift b/backend/Sources/BedrockTypes/Models/Mistral/MistralBedrockModels.swift deleted file mode 100644 index 85aaef4e..00000000 --- a/backend/Sources/BedrockTypes/Models/Mistral/MistralBedrockModels.swift +++ /dev/null @@ -1,81 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -// MARK: converse only -// https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-mistral-text-completion.html -// https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html - -typealias MistralConverse = StandardConverse - -extension BedrockModel { - public static let mistral_large_2402 = BedrockModel( - id: "mistral.mistral-large-2402-v1:0", - name: "Mistral Large (24.02)", - modality: MistralConverse( - parameters: ConverseParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.7), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 8_192, defaultValue: 8_192), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 1), - stopSequences: StopSequenceParams(maxSequences: 10, defaultValue: []), - maxPromptSize: nil - ), - features: [.textGeneration, .systemPrompts, .document, .toolUse] - ) - ) - public static let mistral_small_2402 = BedrockModel( - id: "mistral.mistral-small-2402-v1:0", - name: "Mistral Small (24.02)", - modality: MistralConverse( - parameters: ConverseParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.7), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 8_192, defaultValue: 8_192), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 1), - stopSequences: StopSequenceParams(maxSequences: 10, defaultValue: []), - maxPromptSize: nil - ), - features: [.textGeneration, .systemPrompts, .toolUse] - ) - ) - public static let mistral_7B_instruct = BedrockModel( - id: "mistral.mistral-7b-instruct-v0:2", - name: "Mistral 7B Instruct", - modality: MistralConverse( - parameters: ConverseParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 8_192, defaultValue: 512), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.9), - stopSequences: StopSequenceParams(maxSequences: 10, defaultValue: []), - maxPromptSize: nil - ), - features: [.textGeneration, .document] - ) - ) - public static let mistral_8x7B_instruct = BedrockModel( - id: "mistral.mixtral-8x7b-instruct-v0:1", - name: "Mixtral 8x7B Instruct", - modality: MistralConverse( - parameters: ConverseParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: 4_096, defaultValue: 512), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.9), - stopSequences: StopSequenceParams(maxSequences: 10, defaultValue: []), - maxPromptSize: nil - ), - features: [.textGeneration, .document] - ) - ) -} diff --git a/backend/Sources/BedrockTypes/Parameters/ConverseParameters.swift b/backend/Sources/BedrockTypes/Parameters/ConverseParameters.swift deleted file mode 100644 index 94859d12..00000000 --- a/backend/Sources/BedrockTypes/Parameters/ConverseParameters.swift +++ /dev/null @@ -1,46 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct ConverseParameters: Parameters { - public let temperature: Parameter - public let maxTokens: Parameter - public let topP: Parameter - public let prompt: PromptParams - public let stopSequences: StopSequenceParams - - public init( - temperature: Parameter, - maxTokens: Parameter, - topP: Parameter, - stopSequences: StopSequenceParams, - maxPromptSize: Int? - ) { - self.temperature = temperature - self.maxTokens = maxTokens - self.topP = topP - self.prompt = PromptParams(maxSize: maxPromptSize) - self.stopSequences = stopSequences - } - - public init(textGenerationParameters: TextGenerationParameters) { - self.temperature = textGenerationParameters.temperature - self.maxTokens = textGenerationParameters.maxTokens - self.topP = textGenerationParameters.topP - self.prompt = textGenerationParameters.prompt - self.stopSequences = textGenerationParameters.stopSequences - } -} diff --git a/backend/Sources/BedrockTypes/Parameters/ImageGenerationParameters.swift b/backend/Sources/BedrockTypes/Parameters/ImageGenerationParameters.swift deleted file mode 100644 index 2a6a16aa..00000000 --- a/backend/Sources/BedrockTypes/Parameters/ImageGenerationParameters.swift +++ /dev/null @@ -1,97 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct ImageGenerationParameters: Parameters { - public let nrOfImages: Parameter - public let cfgScale: Parameter - public let seed: Parameter - - public init( - nrOfImages: Parameter, - cfgScale: Parameter, - seed: Parameter - ) { - self.nrOfImages = nrOfImages - self.cfgScale = cfgScale - self.seed = seed - } -} - -public struct TextToImageParameters: Parameters { - public let prompt: PromptParams - public let negativePrompt: PromptParams - - public init( - maxPromptSize: Int, - maxNegativePromptSize: Int - ) { - self.prompt = PromptParams(maxSize: maxPromptSize) - self.negativePrompt = PromptParams(maxSize: maxNegativePromptSize) - } -} - -public struct ConditionedTextToImageParameters: Parameters { - public let prompt: PromptParams - public let negativePrompt: PromptParams - public let similarity: Parameter - - public init( - maxPromptSize: Int, - maxNegativePromptSize: Int, - similarity: Parameter - ) { - self.prompt = PromptParams(maxSize: maxPromptSize) - self.negativePrompt = PromptParams(maxSize: maxNegativePromptSize) - self.similarity = similarity - } -} - -public struct ImageVariationParameters: Parameters { - public let images: Parameter - public let prompt: PromptParams - public let negativePrompt: PromptParams - public let similarity: Parameter - - public init( - images: Parameter, - maxPromptSize: Int, - maxNegativePromptSize: Int, - similarity: Parameter - ) { - self.prompt = PromptParams(maxSize: maxPromptSize) - self.negativePrompt = PromptParams(maxSize: maxNegativePromptSize) - self.similarity = similarity - self.images = images - } -} - -public struct ColorGuidedImageGenerationParameters: Parameters { - public let colors: Parameter - public let prompt: PromptParams - public let negativePrompt: PromptParams - - public init( - colors: Parameter, - maxPromptSize: Int, - maxNegativePromptSize: Int, - similarity: Parameter - ) { - self.prompt = PromptParams(maxSize: maxPromptSize) - self.negativePrompt = PromptParams(maxSize: maxNegativePromptSize) - self.colors = colors - } -} diff --git a/backend/Sources/BedrockTypes/Parameters/Parameters.swift b/backend/Sources/BedrockTypes/Parameters/Parameters.swift deleted file mode 100644 index 078a679b..00000000 --- a/backend/Sources/BedrockTypes/Parameters/Parameters.swift +++ /dev/null @@ -1,80 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public protocol Parameters: Sendable, Hashable, Equatable {} - -public struct Parameter: Sendable, Hashable, Equatable { - public let minValue: T? - public let maxValue: T? - public let defaultValue: T? - public let isSupported: Bool - public let name: ParameterName - - public init(_ name: ParameterName, minValue: T? = nil, maxValue: T? = nil, defaultValue: T? = nil) { - self = Self(name: name, minValue: minValue, maxValue: maxValue, defaultValue: defaultValue, isSupported: true) - } - - public static func notSupported(_ name: ParameterName) -> Self { - Self(name: name, minValue: nil, maxValue: nil, defaultValue: nil, isSupported: false) - } - - private init(name: ParameterName, minValue: T? = nil, maxValue: T? = nil, defaultValue: T? = nil, isSupported: Bool) - { - self.minValue = minValue - self.maxValue = maxValue - self.defaultValue = defaultValue - self.isSupported = isSupported - self.name = name - } -} - -public enum ParameterName: Sendable { - case maxTokens - case temperature - case topK - case topP - case nrOfImages - case images - case similarity - case cfgScale - case seed - case resolution -} - -public struct PromptParams: Parameters { - public let maxSize: Int? -} - -public struct StopSequenceParams: Parameters { - public let maxSequences: Int? - public let defaultValue: [String]? - public let isSupported: Bool - - public init(maxSequences: Int? = nil, defaultValue: [String]? = nil) { - self = Self(maxSequences: maxSequences, defaultValue: defaultValue, isSupported: true) - } - - public static func notSupported() -> Self { - Self(maxSequences: nil, defaultValue: nil, isSupported: false) - } - - private init(maxSequences: Int? = nil, defaultValue: [String]? = nil, isSupported: Bool = true) { - self.maxSequences = maxSequences - self.defaultValue = defaultValue - self.isSupported = isSupported - } -} diff --git a/backend/Sources/BedrockTypes/Parameters/TextGenerationParameters.swift b/backend/Sources/BedrockTypes/Parameters/TextGenerationParameters.swift deleted file mode 100644 index 3ecdf9c6..00000000 --- a/backend/Sources/BedrockTypes/Parameters/TextGenerationParameters.swift +++ /dev/null @@ -1,41 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -public struct TextGenerationParameters: Parameters { - public let temperature: Parameter - public let maxTokens: Parameter - public let topP: Parameter - public let topK: Parameter - public let prompt: PromptParams - public let stopSequences: StopSequenceParams - - public init( - temperature: Parameter, - maxTokens: Parameter, - topP: Parameter, - topK: Parameter, - stopSequences: StopSequenceParams, - maxPromptSize: Int? - ) { - self.temperature = temperature - self.maxTokens = maxTokens - self.topP = topP - self.topK = topK - self.prompt = PromptParams(maxSize: maxPromptSize) - self.stopSequences = stopSequences - } -} diff --git a/backend/Sources/BedrockTypes/Region.swift b/backend/Sources/BedrockTypes/Region.swift deleted file mode 100644 index 0b0767a8..00000000 --- a/backend/Sources/BedrockTypes/Region.swift +++ /dev/null @@ -1,305 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -//===----------------------------------------------------------------------===// -// -// This source file is part of the Soto for AWS open source project -// -// Copyright (c) 2017-2022 the Soto project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Soto project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -// THIS FILE IS AUTOMATICALLY GENERATED by https://github.com/soto-project/soto-core/scripts/generate-region.swift. DO NOT EDIT. - -/// Enumeration for all AWS server regions -public struct Region: Sendable, RawRepresentable, Equatable { - public var rawValue: String - - public init(rawValue: String) { - self.rawValue = rawValue - } - - // Africa (Cape Town) - public static var afsouth1: Region { .init(rawValue: "af-south-1") } - // Asia Pacific (Hong Kong) - public static var apeast1: Region { .init(rawValue: "ap-east-1") } - // Asia Pacific (Tokyo) - public static var apnortheast1: Region { .init(rawValue: "ap-northeast-1") } - // Asia Pacific (Seoul) - public static var apnortheast2: Region { .init(rawValue: "ap-northeast-2") } - // Asia Pacific (Osaka) - public static var apnortheast3: Region { .init(rawValue: "ap-northeast-3") } - // Asia Pacific (Mumbai) - public static var apsouth1: Region { .init(rawValue: "ap-south-1") } - // Asia Pacific (Hyderabad) - public static var apsouth2: Region { .init(rawValue: "ap-south-2") } - // Asia Pacific (Singapore) - public static var apsoutheast1: Region { .init(rawValue: "ap-southeast-1") } - // Asia Pacific (Sydney) - public static var apsoutheast2: Region { .init(rawValue: "ap-southeast-2") } - // Asia Pacific (Jakarta) - public static var apsoutheast3: Region { .init(rawValue: "ap-southeast-3") } - // Asia Pacific (Melbourne) - public static var apsoutheast4: Region { .init(rawValue: "ap-southeast-4") } - // Asia Pacific (Malaysia) - public static var apsoutheast5: Region { .init(rawValue: "ap-southeast-5") } - // Asia Pacific (Thailand) - public static var apsoutheast7: Region { .init(rawValue: "ap-southeast-7") } - // Canada (Central) - public static var cacentral1: Region { .init(rawValue: "ca-central-1") } - // Canada West (Calgary) - public static var cawest1: Region { .init(rawValue: "ca-west-1") } - // China (Beijing) - public static var cnnorth1: Region { .init(rawValue: "cn-north-1") } - // China (Ningxia) - public static var cnnorthwest1: Region { .init(rawValue: "cn-northwest-1") } - // Europe (Frankfurt) - public static var eucentral1: Region { .init(rawValue: "eu-central-1") } - // Europe (Zurich) - public static var eucentral2: Region { .init(rawValue: "eu-central-2") } - // EU ISOE West - public static var euisoewest1: Region { .init(rawValue: "eu-isoe-west-1") } - // Europe (Stockholm) - public static var eunorth1: Region { .init(rawValue: "eu-north-1") } - // Europe (Milan) - public static var eusouth1: Region { .init(rawValue: "eu-south-1") } - // Europe (Spain) - public static var eusouth2: Region { .init(rawValue: "eu-south-2") } - // Europe (Ireland) - public static var euwest1: Region { .init(rawValue: "eu-west-1") } - // Europe (London) - public static var euwest2: Region { .init(rawValue: "eu-west-2") } - // Europe (Paris) - public static var euwest3: Region { .init(rawValue: "eu-west-3") } - // Israel (Tel Aviv) - public static var ilcentral1: Region { .init(rawValue: "il-central-1") } - // Middle East (UAE) - public static var mecentral1: Region { .init(rawValue: "me-central-1") } - // Middle East (Bahrain) - public static var mesouth1: Region { .init(rawValue: "me-south-1") } - // Mexico (Central) - public static var mxcentral1: Region { .init(rawValue: "mx-central-1") } - // South America (Sao Paulo) - public static var saeast1: Region { .init(rawValue: "sa-east-1") } - // US East (N. Virginia) - public static var useast1: Region { .init(rawValue: "us-east-1") } - // US East (Ohio) - public static var useast2: Region { .init(rawValue: "us-east-2") } - // AWS GovCloud (US-East) - public static var usgoveast1: Region { .init(rawValue: "us-gov-east-1") } - // AWS GovCloud (US-West) - public static var usgovwest1: Region { .init(rawValue: "us-gov-west-1") } - // US ISO East - public static var usisoeast1: Region { .init(rawValue: "us-iso-east-1") } - // US ISO WEST - public static var usisowest1: Region { .init(rawValue: "us-iso-west-1") } - // US ISOB East (Ohio) - public static var usisobeast1: Region { .init(rawValue: "us-isob-east-1") } - // US ISOF EAST - public static var usisofeast1: Region { .init(rawValue: "us-isof-east-1") } - // US ISOF SOUTH - public static var usisofsouth1: Region { .init(rawValue: "us-isof-south-1") } - // US West (N. California) - public static var uswest1: Region { .init(rawValue: "us-west-1") } - // US West (Oregon) - public static var uswest2: Region { .init(rawValue: "us-west-2") } - // other region - public static func other(_ name: String) -> Region { .init(rawValue: name) } -} - -extension Region { - public var partition: AWSPartition { - switch self { - case .afsouth1: return .aws - case .apeast1: return .aws - case .apnortheast1: return .aws - case .apnortheast2: return .aws - case .apnortheast3: return .aws - case .apsouth1: return .aws - case .apsouth2: return .aws - case .apsoutheast1: return .aws - case .apsoutheast2: return .aws - case .apsoutheast3: return .aws - case .apsoutheast4: return .aws - case .apsoutheast5: return .aws - case .apsoutheast7: return .aws - case .cacentral1: return .aws - case .cawest1: return .aws - case .cnnorth1: return .awscn - case .cnnorthwest1: return .awscn - case .eucentral1: return .aws - case .eucentral2: return .aws - case .euisoewest1: return .awsisoe - case .eunorth1: return .aws - case .eusouth1: return .aws - case .eusouth2: return .aws - case .euwest1: return .aws - case .euwest2: return .aws - case .euwest3: return .aws - case .ilcentral1: return .aws - case .mecentral1: return .aws - case .mesouth1: return .aws - case .mxcentral1: return .aws - case .saeast1: return .aws - case .useast1: return .aws - case .useast2: return .aws - case .usgoveast1: return .awsusgov - case .usgovwest1: return .awsusgov - case .usisoeast1: return .awsiso - case .usisowest1: return .awsiso - case .usisobeast1: return .awsisob - case .usisofeast1: return .awsisof - case .usisofsouth1: return .awsisof - case .uswest1: return .aws - case .uswest2: return .aws - default: return .aws - } - } -} - -extension Region: CustomStringConvertible { - public var description: String { self.rawValue } -} - -extension Region: Codable {} - -/// Enumeration for all AWS partitions -public struct AWSPartition: Sendable, RawRepresentable, Equatable, Hashable { - enum InternalPartition: String { - case aws - case awscn - case awsusgov - case awsiso - case awsisob - case awsisoe - case awsisof - } - - private var partition: InternalPartition - - public var rawValue: String { self.partition.rawValue } - - public init?(rawValue: String) { - guard let partition = InternalPartition(rawValue: rawValue) else { return nil } - self.partition = partition - } - - private init(partition: InternalPartition) { - self.partition = partition - } - - // AWS Standard - public static var aws: AWSPartition { .init(partition: .aws) } - // AWS China - public static var awscn: AWSPartition { .init(partition: .awscn) } - // AWS GovCloud (US) - public static var awsusgov: AWSPartition { .init(partition: .awsusgov) } - // AWS ISO (US) - public static var awsiso: AWSPartition { .init(partition: .awsiso) } - // AWS ISOB (US) - public static var awsisob: AWSPartition { .init(partition: .awsisob) } - // AWS ISOE (Europe) - public static var awsisoe: AWSPartition { .init(partition: .awsisoe) } - // AWS ISOF - public static var awsisof: AWSPartition { .init(partition: .awsisof) } -} - -extension AWSPartition { - public var dnsSuffix: String { - switch self.partition { - case .aws: return "amazonaws.com" - case .awscn: return "amazonaws.com.cn" - case .awsusgov: return "amazonaws.com" - case .awsiso: return "c2s.ic.gov" - case .awsisob: return "sc2s.sgov.gov" - case .awsisoe: return "cloud.adc-e.uk" - case .awsisof: return "csp.hci.ic.gov" - } - } - - public func defaultEndpoint(region: Region, service: String) -> String { - switch self.partition { - case .aws: return "\(service).\(region).amazonaws.com" - case .awscn: return "\(service).\(region).amazonaws.com.cn" - case .awsusgov: return "\(service).\(region).amazonaws.com" - case .awsiso: return "\(service).\(region).c2s.ic.gov" - case .awsisob: return "\(service).\(region).sc2s.sgov.gov" - case .awsisoe: return "\(service).\(region).cloud.adc-e.uk" - case .awsisof: return "\(service).\(region).csp.hci.ic.gov" - } - } -} - -// allows to create a Region from a String -// it will only create a Region if the provided -// region name is valid. -extension Region { - public init?(awsRegionName: String) { - self.init(rawValue: awsRegionName) - switch self { - case .afsouth1, - .apeast1, - .apnortheast1, - .apnortheast2, - .apnortheast3, - .apsouth1, - .apsouth2, - .apsoutheast1, - .apsoutheast2, - .apsoutheast3, - .apsoutheast4, - .apsoutheast5, - .apsoutheast7, - .cacentral1, - .cawest1, - .cnnorth1, - .cnnorthwest1, - .eucentral1, - .eucentral2, - .euisoewest1, - .eunorth1, - .eusouth1, - .eusouth2, - .euwest1, - .euwest2, - .euwest3, - .ilcentral1, - .mecentral1, - .mesouth1, - .mxcentral1, - .saeast1, - .useast1, - .useast2, - .usgoveast1, - .usgovwest1, - .usisoeast1, - .usisowest1, - .usisobeast1, - .usisofeast1, - .usisofsouth1, - .uswest1, - .uswest2: - return - default: - return nil - } - } -} diff --git a/backend/Sources/BedrockTypes/Role.swift b/backend/Sources/BedrockTypes/Role.swift deleted file mode 100644 index 582e4de6..00000000 --- a/backend/Sources/BedrockTypes/Role.swift +++ /dev/null @@ -1,40 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import Foundation - -public enum Role: String, Codable { - case user - case assistant - - public init(from sdkConversationRole: BedrockRuntimeClientTypes.ConversationRole) throws { - switch sdkConversationRole { - case .user: self = .user - case .assistant: self = .assistant - case .sdkUnknown(let unknownRole): - throw BedrockServiceError.notImplemented( - "Role \(unknownRole) is not implemented by BedrockRuntimeClientTypes" - ) - } - } - - public func getSDKConversationRole() -> BedrockRuntimeClientTypes.ConversationRole { - switch self { - case .user: return .user - case .assistant: return .assistant - } - } -} diff --git a/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift b/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift deleted file mode 100644 index f5d15744..00000000 --- a/backend/Tests/BedrockServiceTests/BedrockServiceTests.swift +++ /dev/null @@ -1,42 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Testing - -@testable import BedrockService -@testable import BedrockTypes - -@Suite("BedrockService Tests") -struct BedrockServiceTests { - let bedrock: BedrockService - - init() async throws { - self.bedrock = try await BedrockService( - bedrockClient: MockBedrockClient(), - bedrockRuntimeClient: MockBedrockRuntimeClient() - ) - } - - // MARK: listModels - - @Test("List all models") - func listModels() async throws { - let models: [ModelSummary] = try await bedrock.listModels() - #expect(models.count == 3) - #expect(models[0].modelId == "anthropic.claude-instant-v1") - #expect(models[0].modelName == "Claude Instant") - #expect(models[0].providerName == "Anthropic") - } -} diff --git a/backend/Tests/BedrockServiceTests/ConverseDocumentTests.swift b/backend/Tests/BedrockServiceTests/ConverseDocumentTests.swift deleted file mode 100644 index 8a702673..00000000 --- a/backend/Tests/BedrockServiceTests/ConverseDocumentTests.swift +++ /dev/null @@ -1,23 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Testing - -@testable import BedrockService -@testable import BedrockTypes - -// Converse document - -// extension BedrockServiceTests diff --git a/backend/Tests/BedrockServiceTests/ConverseTextTests.swift b/backend/Tests/BedrockServiceTests/ConverseTextTests.swift deleted file mode 100644 index 01974a62..00000000 --- a/backend/Tests/BedrockServiceTests/ConverseTextTests.swift +++ /dev/null @@ -1,155 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Testing - -@testable import BedrockService -@testable import BedrockTypes - -// Converse text - -extension BedrockServiceTests { - - // Prompt - @Test( - "Continue conversation using a valid prompt", - arguments: NovaTestConstants.TextGeneration.validPrompts - ) - func converseWithValidPrompt(prompt: String) async throws { - let (output, _) = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt - ) - #expect(output == "Your prompt was: \(prompt)") - } - - @Test( - "Continue conversation variation using an invalid prompt", - arguments: NovaTestConstants.TextGeneration.invalidPrompts - ) - func converseWithInvalidPrompt(prompt: String) async throws { - await #expect(throws: BedrockServiceError.self) { - let _ = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt - ) - } - } - - // Temperature - @Test( - "Continue conversation using a valid temperature", - arguments: NovaTestConstants.TextGeneration.validTemperature - ) - func converseWithValidTemperature(temperature: Double) async throws { - let prompt = "This is a test" - let (output, _) = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt, - temperature: temperature - ) - #expect(output == "Your prompt was: \(prompt)") - } - - @Test( - "Continue conversation variation using an invalid temperature", - arguments: NovaTestConstants.TextGeneration.invalidTemperature - ) - func converseWithInvalidTemperature(temperature: Double) async throws { - await #expect(throws: BedrockServiceError.self) { - let prompt = "This is a test" - let _ = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt, - temperature: temperature - ) - } - } - - // MaxTokens - @Test( - "Continue conversation using a valid maxTokens", - arguments: NovaTestConstants.TextGeneration.validMaxTokens - ) - func converseWithValidMaxTokens(maxTokens: Int) async throws { - let prompt = "This is a test" - let (output, _) = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt, - maxTokens: maxTokens - ) - #expect(output == "Your prompt was: \(prompt)") - } - - @Test( - "Continue conversation variation using an invalid maxTokens", - arguments: NovaTestConstants.TextGeneration.invalidMaxTokens - ) - func converseWithInvalidMaxTokens(maxTokens: Int) async throws { - await #expect(throws: BedrockServiceError.self) { - let prompt = "This is a test" - let _ = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt, - maxTokens: maxTokens - ) - } - } - - // TopP - @Test( - "Continue conversation using a valid temperature", - arguments: NovaTestConstants.TextGeneration.validTopP - ) - func converseWithValidTopP(topP: Double) async throws { - let prompt = "This is a test" - let (output, _) = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt, - topP: topP - ) - #expect(output == "Your prompt was: \(prompt)") - } - - @Test( - "Continue conversation variation using an invalid temperature", - arguments: NovaTestConstants.TextGeneration.invalidTopP - ) - func converseWithInvalidTopP(topP: Double) async throws { - await #expect(throws: BedrockServiceError.self) { - let prompt = "This is a test" - let _ = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt, - topP: topP - ) - } - } - - // StopSequences - @Test( - "Continue conversation using a valid stopSequences", - arguments: NovaTestConstants.TextGeneration.validStopSequences - ) - func converseWithValidTopK(stopSequences: [String]) async throws { - let prompt = "This is a test" - let (output, _) = try await bedrock.converse( - with: BedrockModel.nova_micro, - prompt: prompt, - stopSequences: stopSequences - ) - #expect(output == "Your prompt was: \(prompt)") - } -} diff --git a/backend/Tests/BedrockServiceTests/ConverseToolTests.swift b/backend/Tests/BedrockServiceTests/ConverseToolTests.swift deleted file mode 100644 index 3c0c442a..00000000 --- a/backend/Tests/BedrockServiceTests/ConverseToolTests.swift +++ /dev/null @@ -1,23 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Testing - -@testable import BedrockService -@testable import BedrockTypes - -// Converse tools - -// extension BedrockServiceTests diff --git a/backend/Tests/BedrockServiceTests/ConverseVisionTests.swift b/backend/Tests/BedrockServiceTests/ConverseVisionTests.swift deleted file mode 100644 index 7ce92f66..00000000 --- a/backend/Tests/BedrockServiceTests/ConverseVisionTests.swift +++ /dev/null @@ -1,23 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Testing - -@testable import BedrockService -@testable import BedrockTypes - -// Converse vision - -// extension BedrockServiceTests diff --git a/backend/Tests/BedrockServiceTests/ImageGenerationTests.swift b/backend/Tests/BedrockServiceTests/ImageGenerationTests.swift deleted file mode 100644 index 12088380..00000000 --- a/backend/Tests/BedrockServiceTests/ImageGenerationTests.swift +++ /dev/null @@ -1,162 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Testing - -@testable import BedrockService -@testable import BedrockTypes - -// Image generation - -extension BedrockServiceTests { - - // Models - @Test( - "Generate image using an implemented model", - arguments: NovaTestConstants.imageGenerationModels - ) - func generateImageWithValidModel(model: BedrockModel) async throws { - let output: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: model - ) - #expect(output.images.count == 1) - } - - @Test( - "Generate image using a wrong model", - arguments: NovaTestConstants.textCompletionModels - ) - func generateImageWithInvalidModel(model: BedrockModel) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: model, - nrOfImages: 3 - ) - } - } - - // NrOfmages - @Test( - "Generate image using a valid nrOfImages", - arguments: NovaTestConstants.ImageGeneration.validNrOfImages - ) - func generateImageWithValidNrOfImages(nrOfImages: Int) async throws { - let output: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: BedrockModel.nova_canvas, - nrOfImages: nrOfImages - ) - #expect(output.images.count == nrOfImages) - } - - @Test( - "Generate image using an invalid nrOfImages", - arguments: NovaTestConstants.ImageGeneration.invalidNrOfImages - ) - func generateImageWithInvalidNrOfImages(nrOfImages: Int) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: BedrockModel.nova_canvas, - nrOfImages: nrOfImages - ) - } - } - - // CfgScale - @Test( - "Generate image using a valid cfgScale", - arguments: NovaTestConstants.ImageGeneration.validCfgScale - ) - func generateImageWithValidCfgScale(cfgScale: Double) async throws { - let output: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: BedrockModel.nova_canvas, - cfgScale: cfgScale - ) - #expect(output.images.count == 1) - } - - @Test( - "Generate image using an invalid cfgScale", - arguments: NovaTestConstants.ImageGeneration.invalidCfgScale - ) - func generateImageWithInvalidCfgScale(cfgScale: Double) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: BedrockModel.nova_canvas, - cfgScale: cfgScale - ) - } - } - - // Seed - @Test( - "Generate image using a valid seed", - arguments: NovaTestConstants.ImageGeneration.validSeed - ) - func generateImageWithValidSeed(seed: Int) async throws { - let output: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: BedrockModel.nova_canvas, - seed: seed - ) - #expect(output.images.count == 1) - } - - @Test( - "Generate image using an invalid seed", - arguments: NovaTestConstants.ImageGeneration.invalidSeed - ) - func generateImageWithInvalidSeed(seed: Int) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: ImageGenerationOutput = try await bedrock.generateImage( - "This is a test", - with: BedrockModel.nova_canvas, - seed: seed - ) - } - } - - // Prompt - @Test( - "Generate image using a valid prompt", - arguments: NovaTestConstants.ImageGeneration.validImagePrompts - ) - func generateImageWithValidPrompt(prompt: String) async throws { - let output: ImageGenerationOutput = try await bedrock.generateImage( - prompt, - with: BedrockModel.nova_canvas, - nrOfImages: 3 - ) - #expect(output.images.count == 3) - } - - @Test( - "Generate image using an invalid prompt", - arguments: NovaTestConstants.ImageGeneration.invalidImagePrompts - ) - func generateImageWithInvalidPrompt(prompt: String) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: ImageGenerationOutput = try await bedrock.generateImage( - prompt, - with: BedrockModel.nova_canvas - ) - } - } -} diff --git a/backend/Tests/BedrockServiceTests/ImageVariationTests.swift b/backend/Tests/BedrockServiceTests/ImageVariationTests.swift deleted file mode 100644 index c596d342..00000000 --- a/backend/Tests/BedrockServiceTests/ImageVariationTests.swift +++ /dev/null @@ -1,196 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Testing - -@testable import BedrockService -@testable import BedrockTypes - -// Image variation - -extension BedrockServiceTests { - - // Models - @Test( - "Generate image variation using an implemented model", - arguments: NovaTestConstants.imageGenerationModels - ) - func generateImageVariationWithValidModel(model: BedrockModel) async throws { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let output: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: "This is a test", - with: model, - nrOfImages: 3 - ) - #expect(output.images.count == 3) - } - - @Test( - "Generate image variation using an invalid model", - arguments: NovaTestConstants.textCompletionModels - ) - func generateImageVariationWithInvalidModel(model: BedrockModel) async throws { - await #expect(throws: BedrockServiceError.self) { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let _: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: "This is a test", - with: model, - nrOfImages: 3 - ) - } - } - - // NrOfImages - @Test( - "Generate image variation using a valid nrOfImages", - arguments: NovaTestConstants.ImageGeneration.validNrOfImages - ) - func generateImageVariationWithValidNrOfImages(nrOfImages: Int) async throws { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let output: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: "This is a test", - with: BedrockModel.nova_canvas, - nrOfImages: nrOfImages - ) - #expect(output.images.count == nrOfImages) - } - - @Test( - "Generate image variation using an invalid nrOfImages", - arguments: NovaTestConstants.ImageGeneration.invalidNrOfImages - ) - func generateImageVariationWithInvalidNrOfImages(nrOfImages: Int) async throws { - await #expect(throws: BedrockServiceError.self) { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let _: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: "This is a test", - with: BedrockModel.nova_canvas, - nrOfImages: nrOfImages - ) - } - } - - // Similarity - @Test( - "Generate image variation using a valid similarity", - arguments: NovaTestConstants.ImageVariation.validSimilarity - ) - func generateImageVariationWithValidSimilarity(similarity: Double) async throws { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let output: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: "This is a test", - with: BedrockModel.nova_canvas, - similarity: similarity, - nrOfImages: 3 - ) - #expect(output.images.count == 3) - } - - @Test( - "Generate image variation using an invalid similarity", - arguments: NovaTestConstants.ImageVariation.invalidSimilarity - ) - func generateImageVariationWithInvalidSimilarity(similarity: Double) async throws { - await #expect(throws: BedrockServiceError.self) { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let _: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: "This is a test", - with: BedrockModel.nova_canvas, - similarity: similarity, - nrOfImages: 3 - ) - } - } - - // Number of reference images - @Test( - "Generate image variation using a valid number of reference images", - arguments: NovaTestConstants.ImageVariation.validNrOfReferenceImages - ) - func generateImageVariationWithValidNrOfReferenceImages(nrOfReferenceImages: Int) async throws { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let mockImages = Array(repeating: mockBase64Image, count: nrOfReferenceImages) - let output: ImageGenerationOutput = try await bedrock.generateImageVariation( - images: mockImages, - prompt: "This is a test", - with: BedrockModel.nova_canvas - ) - #expect(output.images.count == 1) - } - - @Test( - "Generate image variation using an invalid number of reference images", - arguments: NovaTestConstants.ImageVariation.invalidNrOfReferenceImages - ) - func generateImageVariationWithInvalidNrOfReferenceImages(nrOfReferenceImages: Int) async throws { - await #expect(throws: BedrockServiceError.self) { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let mockImages = Array(repeating: mockBase64Image, count: nrOfReferenceImages) - let _: ImageGenerationOutput = try await bedrock.generateImageVariation( - images: mockImages, - prompt: "This is a test", - with: BedrockModel.nova_canvas - ) - } - } - - // Prompt - @Test( - "Generate image variation using a valid prompt", - arguments: NovaTestConstants.ImageGeneration.validImagePrompts - ) - func generateImageVariationWithValidPrompt(prompt: String) async throws { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let output: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: prompt, - with: BedrockModel.nova_canvas, - similarity: 0.6 - ) - #expect(output.images.count == 1) - } - - @Test( - "Generate image variation using an invalid prompt", - arguments: NovaTestConstants.ImageGeneration.invalidImagePrompts - ) - func generateImageVariationWithInvalidPrompt(prompt: String) async throws { - await #expect(throws: BedrockServiceError.self) { - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let _: ImageGenerationOutput = try await bedrock.generateImageVariation( - image: mockBase64Image, - prompt: prompt, - with: BedrockModel.nova_canvas, - similarity: 0.6 - ) - } - } -} diff --git a/backend/Tests/BedrockServiceTests/Mock/MockBedrockClient.swift b/backend/Tests/BedrockServiceTests/Mock/MockBedrockClient.swift deleted file mode 100644 index 51d523c0..00000000 --- a/backend/Tests/BedrockServiceTests/Mock/MockBedrockClient.swift +++ /dev/null @@ -1,69 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrock -import AWSClientRuntime -import AWSSDKIdentity -import BedrockService -import BedrockTypes -import Foundation - -public struct MockBedrockClient: BedrockClientProtocol { - public init() {} - - public func listFoundationModels( - input: ListFoundationModelsInput - ) async throws - -> ListFoundationModelsOutput - { - ListFoundationModelsOutput( - // customizationsSupported: [BedrockClientTypes.ModelCustomization]? = nil, - // inferenceTypesSupported: [BedrockClientTypes.InferenceType]? = nil, - // inputModalities: [BedrockClientTypes.ModelModality]? = nil, - // modelArn: Swift.String? = nil, - // modelId: Swift.String? = nil, - // modelLifecycle: BedrockClientTypes.FoundationModelLifecycle? = nil, - // modelName: Swift.String? = nil, - // outputModalities: [BedrockClientTypes.ModelModality]? = nil, - // providerName: Swift.String? = nil, - // responseStreamingSupported: Swift.Bool? = nil - modelSummaries: [ - BedrockClientTypes.FoundationModelSummary( - modelArn: "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-instant-v1", - modelId: "anthropic.claude-instant-v1", - modelLifecycle: BedrockClientTypes.FoundationModelLifecycle(status: .active), - modelName: "Claude Instant", - providerName: "Anthropic", - responseStreamingSupported: false - ), - BedrockClientTypes.FoundationModelSummary( - modelArn: "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-instant-v2", - modelId: "anthropic.claude-instant-v2", - modelLifecycle: BedrockClientTypes.FoundationModelLifecycle(status: .active), - modelName: "Claude Instant 2", - providerName: "Anthropic", - responseStreamingSupported: true - ), - BedrockClientTypes.FoundationModelSummary( - modelArn: "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-instant-v3", - modelId: "unknownID", - modelLifecycle: BedrockClientTypes.FoundationModelLifecycle(status: .active), - modelName: "Claude Instant 3", - providerName: "Anthropic", - responseStreamingSupported: false - ), - ]) - } -} diff --git a/backend/Tests/BedrockServiceTests/Mock/MockBedrockRuntimeClient.swift b/backend/Tests/BedrockServiceTests/Mock/MockBedrockRuntimeClient.swift deleted file mode 100644 index c3063c59..00000000 --- a/backend/Tests/BedrockServiceTests/Mock/MockBedrockRuntimeClient.swift +++ /dev/null @@ -1,226 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@preconcurrency import AWSBedrockRuntime -import AWSClientRuntime -import AWSSDKIdentity -import BedrockService -import BedrockTypes -import Foundation - -public struct MockBedrockRuntimeClient: BedrockRuntimeClientProtocol { - public init() {} - - // MARK: converse - public func converse(input: ConverseInput) async throws -> ConverseOutput { - guard let messages = input.messages, - let content = messages.first?.content?.first, - case let .text(prompt) = content - else { - throw AWSBedrockRuntime.ValidationException(message: "Missing required message content") - } - - let message = BedrockRuntimeClientTypes.Message( - content: [.text("Your prompt was: \(prompt)")], - role: .assistant - ) - return ConverseOutput(output: .message(message)) - } - - // MARK: invokeModel - - public func invokeModel(input: InvokeModelInput) async throws -> InvokeModelOutput { - print("checkpoint 1") - guard let modelId = input.modelId else { - throw AWSBedrockRuntime.ValidationException( - message: "Malformed input request, please reformat your input and try again." - ) - } - print("checkpoint 2") - guard let inputBody = input.body else { - throw AWSBedrockRuntime.ValidationException( - message: "Malformed input request, please reformat your input and try again." - ) - } - let model: BedrockModel = BedrockModel(rawValue: modelId)! - print("checkpoint 3") - - switch model.modality.getName() { - case "Amazon Image Generation": - print("checkpoint 3.1") - return InvokeModelOutput(body: try getImageGeneration(body: inputBody)) - case "Nova Text Generation": - return InvokeModelOutput(body: try invokeNovaModel(body: inputBody)) - case "Titan Text Generation": - return InvokeModelOutput(body: try invokeTitanModel(body: inputBody)) - case "Anthropic Text Generation": - return InvokeModelOutput(body: try invokeAnthropicModel(body: inputBody)) - default: - throw AWSBedrockRuntime.ValidationException( - message: "Malformed input request, please reformat your input and try again." - // message: "Hier in de default! model: \(String(describing: model))" - ) - } - } - - private func getImageGeneration(body: Data) throws -> Data { - print("Checking image generation input") - guard - let json: [String: Any] = try? JSONSerialization.jsonObject( - with: body, - options: [] - ) - as? [String: Any], - let imageGenerationConfig = json["imageGenerationConfig"] as? [String: Any] - else { - throw AWSBedrockRuntime.ValidationException( - message: "Malformed input request, please reformat your input and try again." - ) - } - print("Returning mockimage(s)") - - let nrOfImages = imageGenerationConfig["numberOfImages"] as? Int ?? 1 - let mockBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" - let imageArray = Array(repeating: "\"\(mockBase64Image)\"", count: nrOfImages) - return """ - { - "images": [ - \(imageArray.joined(separator: ",\n ")) - ] - } - """.data(using: .utf8)! - } - - private func invokeNovaModel(body: Data) throws -> Data? { - guard - let json: [String: Any] = try? JSONSerialization.jsonObject( - with: body, - options: [] - ) - as? [String: Any] - else { - throw AWSBedrockRuntime.ValidationException( - message: "Malformed input request, please reformat your input and try again." - ) - } - if let messages = json["messages"] as? [[String: Any]], - let firstMessage = messages.first, - let content = firstMessage["content"] as? [[String: Any]], - let firstContent = content.first, - let inputText = firstContent["text"] as? String - { - return """ - { - "output":{ - "message":{ - "content":[ - {"text":"This is the textcompletion for: \(inputText)"} - ], - "role":"assistant" - }}, - "stopReason":"end_turn", - "usage":{ - "inputTokens":5, - "outputTokens":244, - "totalTokens":249, - "cacheReadInputTokenCount":0, - "cacheWriteInputTokenCount":0 - } - } - """.data(using: .utf8)! - } else { - throw AWSBedrockRuntime.ValidationException( - message: "Malformed input request, please reformat your input and try again." - ) - } - } - - private func invokeTitanModel(body: Data) throws -> Data? { - guard - let json: [String: Any] = try? JSONSerialization.jsonObject( - with: body, - options: [] - ) - as? [String: Any] - else { - throw AWSBedrockRuntime.ValidationException( - message: "Hier is het)" - // message: "Malformed input request, please reformat your input and try again." - ) - } - if let inputText = json["inputText"] as? String { - return """ - { - "inputTextTokenCount":5, - "results":[ - { - "tokenCount":105, - "outputText":"This is the textcompletion for: \(inputText)", - "completionReason":"FINISH" - } - ] - } - """.data(using: .utf8)! - } else { - throw AWSBedrockRuntime.ValidationException( - message: "Malformed input request, please reformat your input and try again." - ) - } - } - - private func invokeAnthropicModel(body: Data) throws -> Data? { - guard - let json: [String: Any] = try? JSONSerialization.jsonObject( - with: body, - options: [] - ) - as? [String: Any] - else { - throw AWSBedrockRuntime.ValidationException( - message: "Malformed input request, please reformat your input and try again." - ) - } - if let messages = json["messages"] as? [[String: Any]], - let firstMessage = messages.first, - let content = firstMessage["content"] as? [[String: Any]], - let firstContent = content.first, - let inputText = firstContent["text"] as? String - { - return """ - { - "id":"msg_bdrk_0146cw8Wd6Dn8WZiQWeF6TEj", - "type":"message", - "role":"assistant", - "model":"claude-3-haiku-20240307", - "content":[ - { - "type":"text", - "text":"This is the textcompletion for: \(inputText)" - }], - "stop_reason":"max_tokens", - "stop_sequence":null, - "usage":{ - "input_tokens":12, - "output_tokens":100} - } - """.data(using: .utf8)! - } else { - throw AWSBedrockRuntime.ValidationException( - message: "Malformed input request, please reformat your input and try again." - ) - } - } -} diff --git a/backend/Tests/BedrockServiceTests/NovaTestConstants.swift b/backend/Tests/BedrockServiceTests/NovaTestConstants.swift deleted file mode 100644 index 0a6dd52e..00000000 --- a/backend/Tests/BedrockServiceTests/NovaTestConstants.swift +++ /dev/null @@ -1,78 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import BedrockTypes - -/// Constants for testing based on the Nova parameters -enum NovaTestConstants { - - static let textCompletionModels = [ - BedrockModel.nova_micro, - BedrockModel.nova_lite, - BedrockModel.nova_pro, - ] - static let imageGenerationModels = [ - BedrockModel.titan_image_g1_v1, - BedrockModel.titan_image_g1_v2, - BedrockModel.nova_canvas, - ] - - enum TextGeneration { - static let validTemperature = [0.00001, 0.2, 0.6, 1] - static let invalidTemperature = [-2.5, -1, 0, 1.00001, 2] - static let validMaxTokens = [1, 10, 100, 5_000] - static let invalidMaxTokens = [0, -2, 5_001] - static let validTopP = [0, 0.2, 0.6, 1] - static let invalidTopP = [-1, 1.00001, 2] - static let validTopK = [0, 50] - static let invalidTopK = [-1] - static let validStopSequences = [ - ["\n\nHuman:"], - ["\n\nHuman:", "\n\nAI:"], - ["\n\nHuman:", "\n\nAI:", "\n\nHuman:"], - ] - static let validPrompts = [ - "This is a test", - "!@#$%^&*()_+{}|:<>?", - String(repeating: "test ", count: 10), - ] - static let invalidPrompts = [ - "", " ", " \n ", "\t", - ] - } - enum ImageGeneration { - static let validNrOfImages = [1, 2, 3, 4, 5] - static let invalidNrOfImages = [-4, 0, 6, 20] - static let validCfgScale = [1.1, 6, 10] - static let invalidCfgScale = [-4, 0, 1.0, 11, 20] - static let validSeed = [0, 12, 900, 858_993_459] - static let invalidSeed = [-4, 1_000_000_000] - static let validImagePrompts = [ - "This is a test", - "!@#$%^&*()_+{}|:<>?", - String(repeating: "x", count: 1_024), - ] - static let invalidImagePrompts = [ - "", " ", " \n ", "\t", - String(repeating: "x", count: 1_025), - ] - } - enum ImageVariation { - static let validSimilarity = [0.2, 0.5, 1] - static let invalidSimilarity = [-4, 0, 0.1, 1.1, 2] - static let validNrOfReferenceImages = [1, 3, 5] - static let invalidNrOfReferenceImages = [0, 6, 10] - } -} diff --git a/backend/Tests/BedrockServiceTests/TextGenerationTests.swift b/backend/Tests/BedrockServiceTests/TextGenerationTests.swift deleted file mode 100644 index 0f36ece4..00000000 --- a/backend/Tests/BedrockServiceTests/TextGenerationTests.swift +++ /dev/null @@ -1,229 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Foundation Models Playground open source project -// -// Copyright (c) 2025 Amazon.com, Inc. or its affiliates -// and the Swift Foundation Models Playground project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Foundation Models Playground project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Testing - -@testable import BedrockService -@testable import BedrockTypes - -// Text completion - -extension BedrockServiceTests { - - // Models - @Test( - "Complete text using an implemented model", - arguments: NovaTestConstants.textCompletionModels - ) - func completeTextWithValidModel(model: BedrockModel) async throws { - let completion: TextCompletion = try await bedrock.completeText( - "This is a test", - with: model - ) - #expect(completion.completion == "This is the textcompletion for: This is a test") - } - - @Test( - "Complete text using an invalid model", - arguments: NovaTestConstants.imageGenerationModels - ) - func completeTextWithInvalidModel(model: BedrockModel) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: TextCompletion = try await bedrock.completeText( - "This is a test", - with: model, - temperature: 0.8 - ) - } - } - - // Parameter combinations - @Test( - "Complete text using an implemented model and a valid combination of parameters" - ) - func completeTextWithValidModelValidParameters() async throws { - let completion: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - maxTokens: 512, - temperature: 0.5, - topK: 10, - stopSequences: ["END", "\n\nHuman:"] - ) - #expect(completion.completion == "This is the textcompletion for: This is a test") - } - - @Test( - "Complete text using an implemented model and an invalid combination of parameters" - ) - func completeTextWithInvalidModelInvalidParameters() async throws { - await #expect(throws: BedrockServiceError.self) { - let _: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_lite, - temperature: 0.5, - topP: 0.5 - ) - } - } - - // Temperature - @Test("Complete text using a valid temperature", arguments: NovaTestConstants.TextGeneration.validTemperature) - func completeTextWithValidTemperature(temperature: Double) async throws { - let completion: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - temperature: temperature - ) - #expect(completion.completion == "This is the textcompletion for: This is a test") - } - - @Test("Complete text using an invalid temperature", arguments: NovaTestConstants.TextGeneration.invalidTemperature) - func completeTextWithInvalidTemperature(temperature: Double) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - temperature: temperature - ) - } - } - - // MaxTokens - @Test( - "Complete text using a valid maxTokens", - arguments: NovaTestConstants.TextGeneration.validMaxTokens - ) - func completeTextWithValidMaxTokens(maxTokens: Int) async throws { - let completion: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - maxTokens: maxTokens - ) - #expect(completion.completion == "This is the textcompletion for: This is a test") - } - - @Test( - "Complete text using an invalid maxTokens", - arguments: NovaTestConstants.TextGeneration.invalidMaxTokens - ) - func completeTextWithInvalidMaxTokens(maxTokens: Int) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - maxTokens: maxTokens - ) - } - } - - // TopP - @Test( - "Complete text using a valid topP", - arguments: NovaTestConstants.TextGeneration.validTopP - ) - func completeTextWithValidTopP(topP: Double) async throws { - let completion: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - topP: topP - ) - #expect(completion.completion == "This is the textcompletion for: This is a test") - } - - @Test( - "Complete text using an invalid topP", - arguments: NovaTestConstants.TextGeneration.invalidTopP - ) - func completeTextWithInvalidMaxTokens(topP: Double) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - topP: topP - ) - } - } - - // TopK - @Test( - "Complete text using a valid topK", - arguments: NovaTestConstants.TextGeneration.validTopK - ) - func completeTextWithValidTopK(topK: Int) async throws { - let completion: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - topK: topK - ) - #expect(completion.completion == "This is the textcompletion for: This is a test") - } - - @Test( - "Complete text using an invalid topK", - arguments: NovaTestConstants.TextGeneration.invalidTopK - ) - func completeTextWithInvalidTopK(topK: Int) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - topK: topK - ) - } - } - - // StopSequences - @Test( - "Complete text using valid stopSequences", - arguments: NovaTestConstants.TextGeneration.validStopSequences - ) - func completeTextWithValidMaxTokens(stopSequences: [String]) async throws { - let completion: TextCompletion = try await bedrock.completeText( - "This is a test", - with: BedrockModel.nova_micro, - stopSequences: stopSequences - ) - #expect(completion.completion == "This is the textcompletion for: This is a test") - } - - // Prompt - @Test( - "Complete text using a valid prompt", - arguments: NovaTestConstants.TextGeneration.validPrompts - ) - func completeTextWithValidPrompt(prompt: String) async throws { - let completion: TextCompletion = try await bedrock.completeText( - prompt, - with: BedrockModel.nova_micro, - maxTokens: 200 - ) - #expect(completion.completion == "This is the textcompletion for: \(prompt)") - } - - @Test( - "Complete text using an invalid prompt", - arguments: NovaTestConstants.TextGeneration.invalidPrompts - ) - func completeTextWithInvalidPrompt(prompt: String) async throws { - await #expect(throws: BedrockServiceError.self) { - let _: TextCompletion = try await bedrock.completeText( - prompt, - with: BedrockModel.nova_canvas, - maxTokens: 10 - ) - } - } -} From 724f87ab18b01bb74313c7e1fa0134ebf9d8e445 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Fri, 11 Apr 2025 18:14:58 +0200 Subject: [PATCH 63/67] formatting --- backend/Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/Package.swift b/backend/Package.swift index b9422601..64b88f7f 100644 --- a/backend/Package.swift +++ b/backend/Package.swift @@ -7,7 +7,7 @@ let package = Package( name: "SwiftBedrock", platforms: [.macOS(.v15), .iOS(.v18), .tvOS(.v18)], products: [ - .executable(name: "PlaygroundAPI", targets: ["PlaygroundAPI"]), + .executable(name: "PlaygroundAPI", targets: ["PlaygroundAPI"]) ], dependencies: [ .package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.0.0"), From 4a76ee289e52508ac60090926ba993ba6f9c6ad9 Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Fri, 11 Apr 2025 18:27:27 +0200 Subject: [PATCH 64/67] readme --- README.md | 437 ++-------------------------------------- parameter_cheatsheet.md | 228 --------------------- 2 files changed, 17 insertions(+), 648 deletions(-) delete mode 100644 parameter_cheatsheet.md diff --git a/README.md b/README.md index a2e0098b..84a07823 100644 --- a/README.md +++ b/README.md @@ -1,436 +1,33 @@ -# BedrockService +# Swift FM Playground -This library is a work in progress, feel free to open an issue, but do not use it in your projects just yet. +Welcome to the Swift Foundation Model (FM) Playground, an example app to explore how to use **Amazon Bedrock** with the AWS SDK for Swift. -## Getting started with BedrockService +> 🚨 **Important:** This application is for educational purposes and not intended for production use. -1. Set-up your `Package.swift` +## Overview -First add dependencies: -```bash -swift package add-dependency https://github.com/build-on-aws/swift-fm-playground.git --branch main -swift swift package add-target-dependency BedrockService ProjectName ## FIXME: does not work because `Package.swift` is in `backend` and not in the root of the repo -``` +> 🚧 Under construction 🚧 -Next up add `platforms` configuration after `name` +## Prerequisites -```swift -platforms: [ - .macOS(.v14), - .iOS(.v17), - .tvOS(.v17) -], -``` +> 🚧 Under construction 🚧 -Your `Package.swift` should now look something like this: -```swift -import PackageDescription +## Running the Application -let package = Package( - name: "ProjectName", - platforms: [ - .macOS(.v14), - .iOS(.v17), - .tvOS(.v17), - ], - dependencies: [ - .package(url: "https://github.com/build-on-aws/swift-fm-playground.git", branch: "main") - ], - targets: [ - .executableTarget( - name: "testje", - dependencies: [ - .target(name: "BedrockService") - ] - ) - ] -) -``` +> 🚧 Under construction 🚧 -2. Import the BedrockService and BedrockTypes +## Accessing the Application -```swift -import BedrockService -import BedrockTypes -``` +To access the application, open `http://localhost:3000` in your web browser. -3. Initialize the BedrockService +## Stopping the Application -Choose what Region to use, whether to use AWS SSO authentication instead of standard credentials and pass a logger. If no region is passed it will default to `.useast1`, if no logger is provided a default logger with the name `bedrock.service` is created. The log level will be set to the environment variable `SWIFT_BEDROCK_LOG_LEVEL` or default to `.trace`. If `useSSO` is not defined it will default to `false` and use the standard credentials for authentication. +To halt the application, you will need to stop both the backend and frontend processes. -```swift -let bedrock = try await BedrockService( - region: .uswest1, - logger: logger, - useSSO: true -) -``` +### Stopping the Frontend -4. List the available models +In the terminal where the frontend is running, press `Ctrl + C` to terminate the process. -Use the `listModels()` function to test your set-up. This function will return an array of `ModelSummary` objects, each one representing a model supported by Amazon Bedrock. The ModelSummaries that contain a `BedrockModel` object are the models supported by BedrockService. +### Stopping the Backend -```swift -let models = try await bedrock.listModels() -``` - -## How to generate text using the InvokeModel API - -Choose a BedrockModel that supports text generation, you can verify this using the `hasTextModality` function. when calling the `completeText` function you can provide some inference parameters: - -- `maxTokens`: The maximum amount of tokens that the model is allowed to return -- `temperature`: Controls the randomness of the model's output -- `topP`: Nucleus sampling, this parameter controls the cumulative probability threshold for token selection -- `topK`: Limits the number of tokens the model considers for each step of text generation to the K most likely ones -- `stopSequences`: An array of strings that will cause the model to stop generating further text when encountered - -The function returns a `TextCompletion` object containg the generated text. - -```swift -let model = .anthropicClaude3Sonnet - -guard model.hasTextModality() else { - print("\(model.name) does not support text completion") -} - -let textCompletion = try await bedrock.completeText( - "Write a story about a space adventure", - with: model -) -``` - -Optionally add inference parameters. - -```swift -let textCompletion = try await bedrock.completeText( - "Write a story about a space adventure", - with: model, - maxTokens: 1000, - temperature: 0.7, - topP: 0.9, - topK: 250, - stopSequences: ["THE END"] -) -``` - -Note that the minimum, maximum and default values for each parameter are model specific and defined when the BedrockModel is created. Some parameters might not be supported by certain models. - -## How to generate an image using the InvokeModel API - -Choose a BedrockModel that supports image generation - you can verify this using the `hasImageModality` and the `hasTextToImageModality` function. The `generateImage` function allows you to create images from text descriptions with various optional parameters: - -- `prompt`: Text description of the desired image -- `negativePrompt`: Text describing what to avoid in the generated image -- `nrOfImages`: Number of images to generate -- `cfgScale`: Classifier free guidance scale to control how closely the image follows the prompt -- `seed`: Seed for reproducible image generation -- `quality`: Parameter to control the quality of generated images -- `resolution`: Desired image resolution for the generated images - -The function returns an ImageGenerationOutput object containing an array of generated images in base64 format. - -```swift -let model = .nova_canvas - -guard model.hasImageModality(), - model.hasTextToImageModality() else { - print("\(model.name) does not support image generation") -} - -let imageGeneration = try await bedrock.generateImage( - "A serene landscape with mountains at sunset", - with: model, -) -``` - -Optionally add inference parameters. - -```swift -let imageGeneration = try await bedrock.generateImage( - "A serene landscape with mountains at sunset", - with: model, - negativePrompt: "dark, stormy, people", - nrOfImages: 3, - cfgScale: 7.0, - seed: 42, - quality: .standard, - resolution: ImageResolution(width: 100, height: 100) -) -``` - -Note that the minimum, maximum and default values for each parameter are model specific and defined when the BedrockModel is created. Some parameters might not be supported by certain models. - -## How to generate image variations using the InvokeModel API -Choose a BedrockModel that supports image variations - you can verify this using the `hasImageVariationModality` and the `hasImageVariationModality` function. The `generateImageVariation` function allows you to create variations of an existing image with these parameters: - -- `images`: The base64-encoded source images used to create variations from -- `negativePrompt`: Text describing what to avoid in the generated image -- `similarity`: Controls how similar the variations will be to the source images -- `nrOfImages`: Number of variations to generate -- `cfgScale`: Classifier free guidance scale to control how closely variations follow the original image -- `seed`: Seed for reproducible variation generation -- `quality`: Parameter to control the quality of generated variations -- `resolution`: Desired resolution for the output variations - -This function returns an `ImageGenerationOutput` object containing an array of generated image variations in base64 format. Each variation will maintain key characteristics of the source images while introducing creative differences. - -```swift -let model = .nova_canvas - -guard model.hasImageVariationModality(), - model.hasImageVariationModality() else { - print("\(model.name) does not support image variations") -} - -let imageVariations = try await bedrock.generateImageVariation( - images: [base64EncodedImage], - prompt: "A dog drinking out of this teacup", - with: model -) -``` - -Optionally add inference parameters. - -```swift -let imageVariations = try await bedrock.generateImageVariation( - images: [base64EncodedImage], - prompt: "A dog drinking out of this teacup", - with: model, - negativePrompt: "Cats, worms, rain", - similarity: 0.8, - nrOfVariations: 4, - cfgScale: 7.0, - seed: 42, - quality: .standard, - resolution: ImageResolution(width: 100, height: 100) -) -``` - -Note that the minimum, maximum and default values for each parameter are model specific and defined when the BedrockModel is created. Some parameters might not be supported by certain models. - -## How to chat using the Converse API - -### Text prompt - -```swift -let model = .nova_lite - -guard model.hasConverseModality() else { - print("\(model.name) does not support converse") -} - -var (reply, history) = try await bedrock.converse( - with: model, - prompt: "Tell me about rainbows" -) - -print("Assistant: \(reply)") - -(reply, history) = try await bedrock.converse( - with: model, - prompt: "Do you think birds can see them too?", - history: history -) - -print("Assistant: \(reply)") -``` - -Optionally add inference parameters. - -```swift -var (reply, history) = try await bedrock.converse( - with: model, - prompt: "Tell me about rainbows", - history: history, - maxTokens: 1024, - temperature: 0.2, - topP: 0.8, - stopSequences: ["END", "STOP", ""], - systemPrompts: ["Do not pretend to be human", "Never talk about goats", "You like puppies"] - ) -``` - - -### Vision - -```swift -let model = .nova_lite - -guard model.hasConverseModality(.vision) else { - print("\(model.name) does not support converse") -} - -let (reply, history) = try await bedrock.converse( - with model: model, - prompt: "Can you tell me about this plant?", - imageFormat: .jpeg, - imageBytes: base64EncodedImage -) - -print("Assistant: \(reply)") -``` - - -Optionally add inference parameters. - -```swift -var (reply, history) = try await bedrock.converse( - with model: model, - prompt: "Can you tell me about this plant?", - imageFormat: .jpeg, - imageBytes: base64EncodedImage, - history: history, - temperature: 1 - ) -``` - -### Tools - -```swift -let model = .nova_lite - -// verify that the model supports tool usage -guard model.hasConverseModality(.toolUse) else { - print("\(model.name) does not support converse tools") -} - -// define the inputschema for your tool -let inputSchema = JSON([ - "type": "object", - "properties": [ - "sign": [ - "type": "string", - "description": "The call sign for the radio station for which you want the most popular song. Example calls signs are WZPZ and WKRP." - ] - ], - "required": [ - "sign" - ] -]) - -// create a Tool object -let tool = Tool(name: "top_song", inputSchema: inputSchema, description: "Get the most popular song played on a radio station.") - -// pass a prompt and the tool to converse -var (reply, history) = try await bedrock.converse( - with model: model, - prompt: "What is the most popular song on WZPZ?", - tools: [tool] -) - -print("Assistant: \(reply)") -// The reply will be similar to this: "I need to use the \"top_song\" tool to find the most popular song on the radio station WZPZ. I will input the call sign \"WZPZ\" into the tool to get the required information." -// The last message in the history will contain the tool use request - -if case .toolUse(let toolUse) = history.last?.content.last { - let id = toolUse.id - let name = toolUse.name - let input = toolUse.input - - // Logic to use the tool here - - let toolResult = ToolResultBlock("The Best Song Ever", id: id) - - // Send the toolResult back to the model - (reply, history) = try await bedrock.converse( - with: model, - history: history, - tools: [tool], - toolResult: toolResult - ) -} - -print("Assistant: \(reply)") -// The final reply will be similar to: "The most popular song currently played on WZPZ is \"The Best Song Ever\". If you need more information or have another request, feel free to ask!" -``` - -### Make your own `Message` - -Alternatively use the `converse` function that does not take a `prompt`, `toolResult` or `image` and construct the `Message` yourself. - -```swift -// Message with prompt -let (reply, history) = try await bedrock.converse( - with: model, - conversation: [Message("What day of the week is it?")] -) - -// Optionally add inference parameters -let (reply, history) = try await bedrock.converse( - with: model, - conversation: [Message("What day of the week is it?")], - maxTokens: 512, - temperature: 1, - topP: 0.8, - stopSequences: ["THE END"], - systemPrompts: ["Today is Wednesday, make sure to mention that."] -) - -// Message with an image and prompt -let (reply, history) = try await bedrock.converse( - with: model, - conversation: [Message("What is in the this teacup?", imageFormat: .jpeg, imageBytes: base64EncodedImage)], -) - -// Message with toolResult -let (reply, history) = try await bedrock.converse( - with: model, - conversation: [Message(toolResult)], - tools: [toolA, toolB] -) -``` - -## How to add a BedrockModel - -### Text - --- Under Construction -- - -### Image - --- Under Construction -- - -### Converse - -To add a new model that only needs the ConverseModality, simply use the `StandardConverse` and add the correct [inferece parameters](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters.html) and [supported converse features](https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html). - -```swift -extension BedrockModel { - public static let new_bedrock_model = BedrockModel( - id: "family.model-id-v1:0", - name: "New Model Name", - modality: StandardConverse( - parameters: ConverseParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.3), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: nil, defaultValue: nil), - topP: Parameter(.topP, minValue: 0.01, maxValue: 0.99, defaultValue: 0.75), - stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), - maxPromptSize: nil - ), - features: [.textGeneration, .systemPrompts, .document, .toolUse] - ) - ) -} -``` - -If the model also implements other modalities you might need to create you own `Modality` and make sure it conforms to `ConverseModality` by implementing the `getConverseParameters` and `getConverseFeatures` functions. Note that the `ConverseParameters` can be extracted from `TextGenerationParameters` by using the public initializer. - -```swift -struct ModelFamilyModality: TextModality, ConverseModality { - func getName() -> String { "Model Family Text and Converse Modality" } - - let parameters: TextGenerationParameters - let converseFeatures: [ConverseFeature] - let converseParameters: ConverseParameters - - init(parameters: TextGenerationParameters, features: [ConverseFeature] = [.textGeneration]) { - self.parameters = parameters - self.converseFeatures = features - - // public initializer to extract `ConverseParameters` from `TextGenerationParameters` - self.converseParameters = ConverseParameters(textGenerationParameters: parameters) - } - - // ... -} -``` +Similarly, in the backend terminal, use the `Ctrl + C` shortcut to stop the server. diff --git a/parameter_cheatsheet.md b/parameter_cheatsheet.md deleted file mode 100644 index 2c95c3f6..00000000 --- a/parameter_cheatsheet.md +++ /dev/null @@ -1,228 +0,0 @@ - - -## Text Generation parameters - -### Nova -[user guide](https://docs.aws.amazon.com/nova/latest/userguide/complete-request-schema.html) - -| parameter | minValue | maxValue | defaultValue | optional or required | -| ----------- | -------- | --------- | ------------ | -------------------- | -| temperature | 0.00001 | 1 | 0.7 | optional | -| maxTokens | 1 | 5_000 | "dynamic"? | optional | -| topP | 0 | 1.0 | 0.9 | optional | -| topK | 0 | Not found | 50 | optional | - - -| parameter | maxLength | -| --------- | --------- | -| prompt | Not found | - - -| parameter | maxSequences | defaultVal | -| ------------- | ------------ | ---------- | -| stopSequences | Not found | `[]` | - -### Titan -[user guide](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-titan-text.html) - -| parameter | minValue | maxValue | defaultValue | optional or required | -| ----------- | ------------- | ---------------- | ------------- | -------------------- | -| temperature | 0.0 | 1.0 | 0.7 | required | -| maxTokens | 0 | depends on model | 512 | required | -| topP | 0 | 1 | 0.9 | required | -| topK | Not supported | Not supported | Not supported | required | - -| model | max return tokens | -| ------------------ | ----------------- | -| Titan Text Lite | 4_096 | -| Titan Text Express | 8_192 | -| Titan Text Premier | 3_072 | - - -| parameter | maxLength | -| --------- | --------- | -| prompt | ??? | - - -| parameter | maxSequences | defaultVal | -| ------------- | ------------ | ---------- | -| stopSequences | ??? | `[]` | - -### Claude - -[user guide](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages.html) - -| parameter | minValue | maxValue | defaultValue | optional or required | -| ----------- | -------- | -------------------- | --------------------- | -------------------- | -| temperature | 0 | 1 | 1 | optional | -| maxTokens | 1 | depends on the model | Not found | required | -| topP | 0 | 1 | 0.999 | optional | -| topK | 0 | 500 | "disabled by default" | optional | - -[model comparison](https://docs.anthropic.com/en/docs/about-claude/models/all-models#model-comparison) - -| models | max return tokens | -| --------------------------------- | ----------------- | -| 3 Opus | 4_096 | -| 3 Haiku | 4_096 | -| 3.5 Haiku | 8_192 | -| 3.5 Sonnet | 8_192 | -| 3.7 Sonnet | 8_192 | -| 3.7 Sonnet with extended thinking | 64_000 | - -| parameter | maxLength | -| --------- | --------- | -| prompt | 200_000 | - - -| parameter | maxSequences | defaultVal | -| ------------- | ------------ | ---------- | -| stopSequences | 8191 | `[]` | - -### DeepSeek - -[user guide](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-deepseek.html) -[deepseek docs](https://api-docs.deepseek.com/quick_start/parameter_settings) - -| parameter | minValue | maxValue | defaultValue | optional or required | -| ----------- | ------------- | ------------- | ------------- | -------------------- | -| temperature | 0 | 1 | 1 | required | -| maxTokens | 1 | 32_768 | Not found | required | -| topP | 0 | 1 | Not found | required | -| topK | Not supported | Not supported | Not supported | required | - - -| parameter | maxLength | -| --------- | --------- | -| prompt | Not found | - - -| parameter | maxSequences | defaultVal | -| ------------- | ------------ | ---------- | -| stopSequences | 10 | `[]` | - -### Llama - -[user guide](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-titan-text.html) - -- Llama 3 Instruct -- Llama 3.1 Instruct -- Llama 3.2 Instruct -- Llama 3.3 Instruct - -| parameter | minValue | maxValue | defaultValue | optional or required | -| ----------- | ------------- | ------------- | ------------- | -------------------- | -| temperature | 0 | 1 | 0.5 | optional | -| maxTokens | 1 | 2_048 | 512 | optional | -| topP | 0 | 1 | 0.9 | optional | -| topK | Not supported | Not supported | Not supported | optional | - - -| parameter | maxLength | -| --------- | --------- | -| prompt | Not found | - - -| parameter | maxSequences | defaultVal | -| ------------- | ------------- | ------------- | -| stopSequences | Not supported | Not supported | - - -## Image Generation parameters - -### Nova -[user guide](https://docs.aws.amazon.com/nova/latest/userguide/image-gen-req-resp-structure.html) - -#### General - -| parameter | minValue | maxValue | defaultValue | -| ---------- | -------- | ----------- | ------------ | -| nrOfImages | 1 | 5 | 1 | -| cfgScale | 1.1 | 10 | 6.5 | -| seed | 0 | 858_993_459 | 12 | - -#### TEXT_IMAGE - -| parameter | maxLength | -| -------------- | --------- | -| prompt | 1_024 | -| negativePrompt | 1_024 | - -#### Conditioned TEXT_IMAGE - -| parameter | minValue | maxValue | defaultValue | -| ---------- | -------- | -------- | ------------ | -| similarity | 0 | 1.0 | 0.7 | - -#### IMAGE_VARIATION -| parameter | minValue | maxValue | defaultValue | -| ---------- | -------- | -------- | ------------ | -| similarity | 0.2 | 1.0 | ??? | -| images | 1 | 5 | ??? | - -| parameter | maxLength | -| -------------- | --------- | -| prompt | 1_024 | -| negativePrompt | 1_024 | - - -#### COLOR_GUIDED_GENERATION - -| parameter | maxLength | -| -------------- | --------- | -| prompt | 1_024 | -| negativePrompt | 1_024 | -| colors | 10 | - -#### TO DO -| parameter | minValue | maxValue | defaultValue | -| --------- | -------- | -------- | ------------ | - - -| parameter | maxLength | -| --------- | --------- | - - -### Titan - -[user guide](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-titan-image.html) - -#### Algemeen -| parameter | minValue | maxValue | defaultValue | -| ---------- | -------- | ------------- | ------------ | -| nrOfImages | 1 | 5 | 1 | -| cfgScale | 1.1 | 10.0 | 8.0 | -| seed | 0 | 2_147_483_646 | 42 | - -#### TEXT_IMAGE - -| parameter | maxLength | -| -------------- | --------- | -| prompt | 512 | -| negativePrompt | 512 | - -#### Conditioned TEXT_IMAGE - -| parameter | minValue | maxValue | defaultValue | -| ---------- | -------- | -------- | ------------ | -| similarity | 0 | 1.0 | 0.7 | - -#### IMAGE_VARIATION -| parameter | minValue | maxValue | defaultValue | -| ---------- | -------- | -------- | ------------ | -| similarity | 0.2 | 1.0 | 0.7 | -| images | 1 | 5 | ??? | - -| parameter | maxLength | -| -------------- | --------- | -| prompt | 512 | -| negativePrompt | 512 | - -#### COLOR_GUIDED_GENERATION - -| parameter | maxLength | -| -------------- | --------- | -| prompt | 512 | -| negativePrompt | 512 | -| colors | 10 | - From 6a191e34f85d62bad981311f3dcb786707c5e99e Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Fri, 11 Apr 2025 19:36:01 +0200 Subject: [PATCH 65/67] inital week 16 commit --- .gitignore | 1 + backend/Package.swift | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 79faf2ff..23675b5a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ xcuserdata Package.resolved .serverless .vscode +.env # backend backend/.DS_Store diff --git a/backend/Package.swift b/backend/Package.swift index 64b88f7f..e1fdd855 100644 --- a/backend/Package.swift +++ b/backend/Package.swift @@ -12,7 +12,7 @@ let package = Package( dependencies: [ .package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.0.0"), .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0"), - .package(url: "https://github.com/monadierickx/swift-bedrock-library.git", branch: "main"), + .package(url: "https://github.com/monadierickx/swift-bedrock-library.git", branch: "week16"), ], targets: [ .executableTarget( From c6f9299fb6c69263c11a6efff044846ca3be337e Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Fri, 11 Apr 2025 20:02:46 +0200 Subject: [PATCH 66/67] ConverseReply (backend + frontend) --- backend/Sources/PlaygroundAPI/Application+build.swift | 5 ++--- backend/Sources/PlaygroundAPI/Types/Chat.swift | 7 ++----- backend/Sources/PlaygroundAPI/Types/ImageGeneration.swift | 2 +- backend/Sources/PlaygroundAPI/Types/ListModels.swift | 2 +- backend/Sources/PlaygroundAPI/Types/TextGeneration.swift | 2 +- frontend/components/chatPlayground/ChatComponent.jsx | 2 +- 6 files changed, 8 insertions(+), 12 deletions(-) diff --git a/backend/Sources/PlaygroundAPI/Application+build.swift b/backend/Sources/PlaygroundAPI/Application+build.swift index 08a61676..08b1f2c5 100644 --- a/backend/Sources/PlaygroundAPI/Application+build.swift +++ b/backend/Sources/PlaygroundAPI/Application+build.swift @@ -161,7 +161,7 @@ func buildRouter(useSSO: Bool, logger: Logger) async throws -> Router ChatOutput in + router.post("/foundation-models/chat/:modelId") { request, context -> ConverseReply in do { guard let modelId = context.parameters.get("modelId") else { throw HTTPError(.badRequest, message: "No modelId was given.") @@ -173,7 +173,7 @@ func buildRouter(useSSO: Bool, logger: Logger) async throws -> Router Router { setConversation(prevConversation => [...prevConversation, { sender: "Assistant", - message: data.reply + message: data.textReply }]); setHistory(data.history); }); From 7766d737ad6b12a1bbb5867d8e9333a713b88c3f Mon Sep 17 00:00:00 2001 From: monadierickx <126071495+monadierickx@users.noreply.github.com> Date: Sat, 12 Apr 2025 18:34:33 +0200 Subject: [PATCH 67/67] removed tests from github actions --- .github/workflows/build_test_soundness.yml | 27 +++++++++++----------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build_test_soundness.yml b/.github/workflows/build_test_soundness.yml index 06991c16..3680cf7b 100644 --- a/.github/workflows/build_test_soundness.yml +++ b/.github/workflows/build_test_soundness.yml @@ -1,6 +1,5 @@ name: Build And Test on EC2 - on: [push, pull_request] jobs: @@ -19,20 +18,20 @@ jobs: working-directory: backend run: swift build - test: - runs-on: ubuntu-latest - container: swift:6.0.3-amazonlinux2 + # test: + # runs-on: ubuntu-latest + # container: swift:6.0.3-amazonlinux2 - steps: - # GitHub checkout action has a dep on NodeJS 20 which is not running on Amazonlinux2 - # workaround is to manually checkout the repository - # https://github.com/actions/checkout/issues/1487 - - name: Manually Clone repository - run: | - git clone https://github.com/${{ github.repository }} . - - name: Run tests - working-directory: backend - run: swift test + # steps: + # # GitHub checkout action has a dep on NodeJS 20 which is not running on Amazonlinux2 + # # workaround is to manually checkout the repository + # # https://github.com/actions/checkout/issues/1487 + # - name: Manually Clone repository + # run: | + # git clone https://github.com/${{ github.repository }} . + # - name: Run tests + # working-directory: backend + # run: swift test soundness: name: Soundness