diff --git a/Examples/openai/Sources/Converse/main.swift b/Examples/openai/Sources/Converse/main.swift index 261add2b..3c17b54c 100644 --- a/Examples/openai/Sources/Converse/main.swift +++ b/Examples/openai/Sources/Converse/main.swift @@ -26,6 +26,7 @@ let bedrock = try await BedrockService( var builder = try ConverseRequestBuilder(with: .openai_gpt_oss_20b) .withPrompt("Who are you?") + .withServiceTier(.priority) var reply = try await bedrock.converse(with: builder) diff --git a/Examples/openai/Sources/Invoke/main.swift b/Examples/openai/Sources/Invoke/main.swift index 0cba5e5c..967fa058 100644 --- a/Examples/openai/Sources/Invoke/main.swift +++ b/Examples/openai/Sources/Invoke/main.swift @@ -26,7 +26,8 @@ let bedrock = try await BedrockService( let textCompletion = try await bedrock.completeText( "Who are you?", - with: .openai_gpt_oss_20b + with: .openai_gpt_oss_20b, + // serviceTier: .default ) if let reasoning = textCompletion.reasoning { diff --git a/Package.swift b/Package.swift index e54c2f02..3743899b 100644 --- a/Package.swift +++ b/Package.swift @@ -10,11 +10,11 @@ let package = Package( .library(name: "BedrockService", targets: ["BedrockService"]) ], dependencies: [ - .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.6.1"), - .package(url: "https://github.com/awslabs/aws-sdk-swift", from: "1.5.51"), - .package(url: "https://github.com/smithy-lang/smithy-swift", from: "0.158.0"), + .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.6.2"), + .package(url: "https://github.com/awslabs/aws-sdk-swift", from: "1.6.3"), + .package(url: "https://github.com/smithy-lang/smithy-swift", from: "0.173.0"), .package(url: "https://github.com/apple/swift-log.git", from: "1.6.4"), - .package(url: "https://github.com/awslabs/aws-crt-swift", from: "0.53.0"), + .package(url: "https://github.com/awslabs/aws-crt-swift", from: "0.54.2"), ], targets: [ .target( diff --git a/Sources/BedrockService/BedrockRuntimeClient/Converse/BedrockService+Converse.swift b/Sources/BedrockService/BedrockRuntimeClient/Converse/BedrockService+Converse.swift index 7563fd96..dba0d043 100644 --- a/Sources/BedrockService/BedrockRuntimeClient/Converse/BedrockService+Converse.swift +++ b/Sources/BedrockService/BedrockRuntimeClient/Converse/BedrockService+Converse.swift @@ -71,7 +71,8 @@ extension BedrockService { systemPrompts: systemPrompts, tools: tools, enableReasoning: enableReasoning, - maxReasoningTokens: maxReasoningTokens + maxReasoningTokens: maxReasoningTokens, + serviceTier: .default ) } @@ -87,6 +88,7 @@ extension BedrockService { /// - tools: Optional array of tools the model can use /// - enableReasoning: Optional flag to enable reasoning output /// - maxReasoningTokens: Optional maximum number of reasoning tokens to generate + /// - serviceTier: Optional. The service tier to serve this request (.default | .priority | .flex) /// - Throws: BedrockLibraryError.notSupported for parameters or functionalities that are not supported /// BedrockLibraryError.invalidParameter for invalid parameters /// BedrockLibraryError.invalidPrompt if the prompt is empty or too long @@ -103,7 +105,8 @@ extension BedrockService { systemPrompts: [String]? = nil, tools: [Tool]? = nil, enableReasoning: Bool? = false, - maxReasoningTokens: Int? = nil + maxReasoningTokens: Int? = nil, + serviceTier: ServiceTier ) async throws -> Message { do { let modality = try model.getConverseModality() @@ -127,6 +130,7 @@ extension BedrockService { "stopSequences": "\(String(describing: stopSequences))", "systemPrompts": "\(String(describing: systemPrompts))", "tools": "\(String(describing: tools))", + "serviceTier": "\(serviceTier.rawValue)", ] ) let converseRequest = ConverseRequest( @@ -138,7 +142,8 @@ extension BedrockService { stopSequences: stopSequences, systemPrompts: systemPrompts, tools: tools, - maxReasoningTokens: maxReasoningTokens + maxReasoningTokens: maxReasoningTokens, + serviceTier: serviceTier ) logger.trace("Creating ConverseInput") @@ -180,7 +185,8 @@ extension BedrockService { stopSequences: builder.stopSequences, systemPrompts: builder.systemPrompts, tools: builder.tools, - maxReasoningTokens: builder.maxReasoningTokens + maxReasoningTokens: builder.maxReasoningTokens, + serviceTier: builder.serviceTier ) history.append(assistantMessage) logger.trace( diff --git a/Sources/BedrockService/BedrockRuntimeClient/Converse/BedrockService+ConverseStreaming.swift b/Sources/BedrockService/BedrockRuntimeClient/Converse/BedrockService+ConverseStreaming.swift index d475eec0..b9137829 100644 --- a/Sources/BedrockService/BedrockRuntimeClient/Converse/BedrockService+ConverseStreaming.swift +++ b/Sources/BedrockService/BedrockRuntimeClient/Converse/BedrockService+ConverseStreaming.swift @@ -74,7 +74,8 @@ extension BedrockService { systemPrompts: systemPrompts, tools: tools, enableReasoning: enableReasoning, - maxReasoningTokens: maxReasoningTokens + maxReasoningTokens: maxReasoningTokens, + serviceTier: .default ) } @@ -90,6 +91,7 @@ extension BedrockService { /// - tools: Optional array of tools the model can use /// - enableReasoning: Optional flag to enable reasoning capabilities /// - maxReasoningTokens: Optional maximum number of tokens for reasoning + /// - serviceTier: Optional. The service tier to serve this request (.default | .priority | .flex) /// - Throws: BedrockLibraryError.notSupported for parameters or functionalities that are not supported /// BedrockLibraryError.invalidParameter for invalid parameters /// BedrockLibraryError.invalidPrompt if the prompt is empty or too long @@ -107,7 +109,8 @@ extension BedrockService { systemPrompts: [String]? = nil, tools: [Tool]? = nil, enableReasoning: Bool? = false, - maxReasoningTokens: Int? = nil + maxReasoningTokens: Int? = nil, + serviceTier: ServiceTier ) async throws -> ConverseReplyStream { do { guard model.hasConverseStreamingModality() else { @@ -138,6 +141,7 @@ extension BedrockService { "stopSequences": "\(String(describing: stopSequences))", "systemPrompts": "\(String(describing: systemPrompts))", "tools": "\(String(describing: tools))", + "serviceTier": "\(serviceTier.rawValue)", ] ) let converseRequest = ConverseStreamingRequest( @@ -149,7 +153,8 @@ extension BedrockService { stopSequences: stopSequences, systemPrompts: systemPrompts, tools: tools, - maxReasoningTokens: maxReasoningTokens + maxReasoningTokens: maxReasoningTokens, + serviceTier: serviceTier ) logger.trace("Creating ConverseStreamingInput") @@ -220,7 +225,8 @@ extension BedrockService { stopSequences: builder.stopSequences, systemPrompts: builder.systemPrompts, tools: builder.tools, - maxReasoningTokens: builder.maxReasoningTokens + maxReasoningTokens: builder.maxReasoningTokens, + serviceTier: builder.serviceTier ) return streamingResponse } catch { diff --git a/Sources/BedrockService/BedrockRuntimeClient/Converse/ConverseRequest.swift b/Sources/BedrockService/BedrockRuntimeClient/Converse/ConverseRequest.swift index 40ccab2b..cd427cd7 100644 --- a/Sources/BedrockService/BedrockRuntimeClient/Converse/ConverseRequest.swift +++ b/Sources/BedrockService/BedrockRuntimeClient/Converse/ConverseRequest.swift @@ -29,6 +29,7 @@ public struct ConverseRequest { let toolConfig: ToolConfig? let systemPrompts: [String]? let maxReasoningTokens: Int? + let serviceTier: ServiceTier @available( *, @@ -56,7 +57,8 @@ public struct ConverseRequest { stopSequences: stopSequences, systemPrompts: systemPrompts, tools: tools, - maxReasoningTokens: maxReasoningTokens + maxReasoningTokens: maxReasoningTokens, + serviceTier: .default ) } @@ -69,7 +71,8 @@ public struct ConverseRequest { stopSequences: [String]?, systemPrompts: [String]?, tools: [Tool]?, - maxReasoningTokens: Int? + maxReasoningTokens: Int?, + serviceTier: ServiceTier ) { self.messages = messages self.model = model @@ -86,6 +89,7 @@ public struct ConverseRequest { } else { self.toolConfig = nil } + self.serviceTier = serviceTier } func getConverseInput(forRegion region: Region) throws -> ConverseInput { @@ -94,6 +98,7 @@ public struct ConverseRequest { inferenceConfig: inferenceConfig?.getSDKInferenceConfig(), messages: try getSDKMessages(), modelId: model.getModelIdWithCrossRegionInferencePrefix(region: region), + serviceTier: BedrockRuntimeClientTypes.ServiceTier(type: .init(rawValue: serviceTier.rawValue)), system: getSDKSystemPrompts(), toolConfig: try toolConfig?.getSDKToolConfig() ) diff --git a/Sources/BedrockService/BedrockRuntimeClient/Converse/ConverseRequestBuilder.swift b/Sources/BedrockService/BedrockRuntimeClient/Converse/ConverseRequestBuilder.swift index 91887056..3f70d892 100644 --- a/Sources/BedrockService/BedrockRuntimeClient/Converse/ConverseRequestBuilder.swift +++ b/Sources/BedrockService/BedrockRuntimeClient/Converse/ConverseRequestBuilder.swift @@ -19,29 +19,63 @@ import FoundationEssentials import Foundation #endif +/// A builder for constructing Amazon Bedrock Converse API requests. +/// +/// `ConverseRequestBuilder` provides a fluent interface for building conversational AI requests +/// with support for multi-turn conversations, tool use, vision, documents, and reasoning capabilities. +/// +/// ## Usage +/// +/// ```swift +/// let builder = try ConverseRequestBuilder(with: .claude3Sonnet) +/// .withPrompt("What is the weather?") +/// .withMaxTokens(1000) +/// .withTemperature(0.7) +/// ``` public struct ConverseRequestBuilder: Sendable { + /// The Bedrock model to use for the conversation. public private(set) var model: BedrockModel private var parameters: ConverseParameters + /// The conversation history containing previous messages. public private(set) var history: History + /// The tools available for the model to use, if any. public private(set) var tools: [Tool]? + /// The text prompt for the current user message, if any. public private(set) var prompt: String? + /// The image content for the current user message, if any. public private(set) var image: ImageBlock? + /// The document content for the current user message, if any. public private(set) var document: DocumentBlock? + /// The tool result for the current user message, if any. public private(set) var toolResult: ToolResultBlock? + /// The maximum number of tokens to generate in the response, if specified. public private(set) var maxTokens: Int? + /// The temperature parameter controlling randomness in generation, if specified. public private(set) var temperature: Double? + /// The top-p parameter for nucleus sampling, if specified. public private(set) var topP: Double? + /// The stop sequences that halt generation, if any. public private(set) var stopSequences: [String]? + /// The system prompts that guide model behavior, if any. public private(set) var systemPrompts: [String]? + /// The maximum number of reasoning tokens for extended thinking models, if specified. public private(set) var maxReasoningTokens: Int? + /// Indicates whether reasoning mode is enabled for extended thinking models. public private(set) var enableReasoning: Bool = false + /// The service tier for the inference request. + public private(set) var serviceTier: ServiceTier = .default + // MARK - Initializers + /// Creates a new builder for the specified Bedrock model. + /// + /// - Parameter model: The Bedrock model to use for conversation. + /// - Throws: `BedrockLibraryError` if the model doesn't support the Converse API. public init(with model: BedrockModel) throws { self.model = model let modality = try model.getConverseModality() @@ -49,6 +83,11 @@ public struct ConverseRequestBuilder: Sendable { self.history = [] } + /// Creates a new builder for the specified model ID. + /// + /// - Parameter modelId: The string identifier of the Bedrock model. + /// - Throws: `BedrockLibraryError.notFound` if the model ID is invalid, + /// or other errors if the model doesn't support the Converse API. public init(with modelId: String) throws { guard let model = BedrockModel(rawValue: modelId) else { throw BedrockLibraryError.notFound("No model with model id \(modelId) found.") @@ -56,7 +95,10 @@ public struct ConverseRequestBuilder: Sendable { self = try .init(with: model) } - /// Creates a ConverseRequestBuilder object based of a ConverseRequestBuilder object + /// Creates a new builder by copying configuration from an existing builder. + /// + /// - Parameter builder: The builder to copy configuration from. + /// - Throws: `BedrockLibraryError` if validation fails during configuration copy. public init(from builder: ConverseRequestBuilder) throws { self = try ConverseRequestBuilder(with: builder.model) .withHistory(builder.history) @@ -67,17 +109,32 @@ public struct ConverseRequestBuilder: Sendable { .withSystemPrompts(builder.systemPrompts) .withTools(builder.tools) .withReasoning(enabled: builder.enableReasoning, maxReasoningTokens: builder.maxReasoningTokens) - } - - /// Creates a ConverseRequestBuilder object based of a ConverseRequestBuilder object - /// with an updated history and all the user input (prompt, image, document or toolresult) emptied out. + .withServiceTier(builder.serviceTier) + } + + /// Creates a new builder from an existing builder with updated conversation history from a reply. + /// + /// This initializer clears all user input (prompt, image, document, tool result) and updates + /// the history with the conversation from the reply. + /// + /// - Parameters: + /// - builder: The builder to copy configuration from. + /// - reply: The reply containing the updated conversation history. + /// - Throws: `BedrockLibraryError` if validation fails. public init(from builder: ConverseRequestBuilder, with reply: ConverseReply) throws { self = try .init(from: builder) .withHistory(reply.getHistory()) } - /// Creates a ConverseRequestBuilder object based of a ConverseRequestBuilder object - /// with an updated history and all the user input (prompt, image, document or toolresult) emptied out. + /// Creates a new builder from an existing builder with an assistant message added to history. + /// + /// This initializer clears all user input (prompt, image, document, tool result) and appends + /// both the user message and assistant message to the conversation history. + /// + /// - Parameters: + /// - builder: The builder to copy configuration from. + /// - assistantMessage: The assistant's message to add to history. + /// - Throws: `BedrockLibraryError` if validation fails. public init(from builder: ConverseRequestBuilder, with assistantMessage: Message) throws { let userMessage = try builder.getUserMessage() let history = builder.history + [userMessage, assistantMessage] @@ -88,6 +145,11 @@ public struct ConverseRequestBuilder: Sendable { // MARK - builder methods // MARK - builder methods - history + /// Sets the conversation history from a message array. + /// + /// - Parameter history: The array of messages representing the conversation history. + /// - Returns: A new builder with the updated history. + /// - Throws: `BedrockLibraryError` if validation fails. @available( *, deprecated, @@ -96,6 +158,14 @@ public struct ConverseRequestBuilder: Sendable { public func withHistory(_ history: [Message]) throws -> ConverseRequestBuilder { try withHistory(History(history)) } + /// Sets the conversation history. + /// + /// The history must end with an assistant message if non-empty. If a tool result is set, + /// the last message must contain a tool use. + /// + /// - Parameter history: The conversation history to set. + /// - Returns: A new builder with the updated history. + /// - Throws: `BedrockLibraryError.ConverseRequestBuilder` if validation fails. public func withHistory(_ history: History) throws -> ConverseRequestBuilder { if history.count > 0, @@ -121,6 +191,15 @@ public struct ConverseRequestBuilder: Sendable { // MARK - builder methods - tools + /// Sets the tools available for the model to use. + /// + /// Tool names must be unique. If the last message in history contains a tool use, + /// a matching tool must be present in the tools array. + /// + /// - Parameter tools: The array of tools to make available. Must not be empty. + /// - Returns: A new builder with the updated tools. + /// - Throws: `BedrockLibraryError.ConverseRequestBuilder` if validation fails, + /// or if the model doesn't support tool use. public func withTools(_ tools: [Tool]) throws -> ConverseRequestBuilder { try validateFeature(.toolUse) guard tools.count > 0 else { @@ -150,16 +229,36 @@ public struct ConverseRequestBuilder: Sendable { return copy } + /// Sets a single tool for the model to use. + /// + /// - Parameter tool: The tool to make available. + /// - Returns: A new builder with the tool set. + /// - Throws: `BedrockLibraryError` if validation fails or the model doesn't support tool use. public func withTool(_ tool: Tool) throws -> ConverseRequestBuilder { try self.withTools([tool]) } + /// Creates and sets a single tool from its components. + /// + /// - Parameters: + /// - name: The name of the tool. + /// - inputSchema: The JSON schema defining the tool's input parameters. + /// - description: An optional description of what the tool does. + /// - Returns: A new builder with the tool set. + /// - Throws: `BedrockLibraryError` if validation fails or the model doesn't support tool use. public func withTool(name: String, inputSchema: JSON, description: String?) throws -> ConverseRequestBuilder { try self.withTools([try Tool(name: name, inputSchema: inputSchema, description: description)]) } // MARK - builder methods - user prompt + /// Sets the text prompt for the user message. + /// + /// Cannot be set when a tool result is already set. + /// + /// - Parameter prompt: The text prompt to send. + /// - Returns: A new builder with the prompt set. + /// - Throws: `BedrockLibraryError` if validation fails or a tool result is already set. public func withPrompt(_ prompt: String) throws -> ConverseRequestBuilder { guard toolResult == nil else { throw BedrockLibraryError.ConverseRequestBuilder("Cannot set prompt when tool result is set") @@ -170,6 +269,14 @@ public struct ConverseRequestBuilder: Sendable { return copy } + /// Sets an image for the user message. + /// + /// Cannot be set when a tool result is already set. + /// + /// - Parameter image: The image block to include. + /// - Returns: A new builder with the image set. + /// - Throws: `BedrockLibraryError` if validation fails, the model doesn't support vision, + /// or a tool result is already set. public func withImage(_ image: ImageBlock) throws -> ConverseRequestBuilder { try validateFeature(.vision) guard toolResult == nil else { @@ -180,14 +287,36 @@ public struct ConverseRequestBuilder: Sendable { return copy } + /// Sets an image for the user message from binary data. + /// + /// - Parameters: + /// - format: The image format (e.g., PNG, JPEG). + /// - source: The image data. + /// - Returns: A new builder with the image set. + /// - Throws: `BedrockLibraryError` if validation fails or the model doesn't support vision. public func withImage(format: ImageBlock.Format, source: Data) throws -> ConverseRequestBuilder { try self.withImage(try ImageBlock(format: format, source: source.base64EncodedString())) } + /// Sets an image for the user message from a base64-encoded string. + /// + /// - Parameters: + /// - format: The image format (e.g., PNG, JPEG). + /// - source: The base64-encoded image data. + /// - Returns: A new builder with the image set. + /// - Throws: `BedrockLibraryError` if validation fails or the model doesn't support vision. public func withImage(format: ImageBlock.Format, source: String) throws -> ConverseRequestBuilder { try self.withImage(try ImageBlock(format: format, source: source)) } + /// Sets a document for the user message. + /// + /// Cannot be set when a tool result is already set. + /// + /// - Parameter document: The document block to include. + /// - Returns: A new builder with the document set. + /// - Throws: `BedrockLibraryError` if validation fails, the model doesn't support documents, + /// or a tool result is already set. public func withDocument(_ document: DocumentBlock) throws -> ConverseRequestBuilder { try validateFeature(.document) guard toolResult == nil else { @@ -198,6 +327,14 @@ public struct ConverseRequestBuilder: Sendable { return copy } + /// Sets a document for the user message from its components. + /// + /// - Parameters: + /// - name: The name of the document. + /// - format: The document format (e.g., PDF, TXT). + /// - source: The base64-encoded document data. + /// - Returns: A new builder with the document set. + /// - Throws: `BedrockLibraryError` if validation fails or the model doesn't support documents. public func withDocument( name: String, format: DocumentBlock.Format, @@ -206,6 +343,14 @@ public struct ConverseRequestBuilder: Sendable { try self.withDocument(try DocumentBlock(name: name, format: format, source: source)) } + /// Sets a tool result for the user message. + /// + /// Tool results can only be set when tools are configured, the history is non-empty, + /// and the last message contains a tool use. Cannot be set when prompt, image, or document is set. + /// + /// - Parameter toolResult: The tool result block to include. + /// - Returns: A new builder with the tool result set. + /// - Throws: `BedrockLibraryError` if validation fails or preconditions aren't met. public func withToolResult(_ toolResult: ToolResultBlock) throws -> ConverseRequestBuilder { guard prompt == nil && image == nil && document == nil else { throw BedrockLibraryError.ConverseRequestBuilder( @@ -230,6 +375,14 @@ public struct ConverseRequestBuilder: Sendable { return copy } + /// Sets a tool result with custom content. + /// + /// - Parameters: + /// - id: The tool use ID. If `nil`, uses the ID from the last tool use in history. + /// - content: The result content. + /// - status: The result status. Defaults to success if `nil`. + /// - Returns: A new builder with the tool result set. + /// - Throws: `BedrockLibraryError` if validation fails or preconditions aren't met. public func withToolResult( id: String? = nil, content: [ToolResultBlock.Content], @@ -240,6 +393,14 @@ public struct ConverseRequestBuilder: Sendable { return try self.withToolResult(toolResult) } + /// Sets a tool result with text content. + /// + /// - Parameters: + /// - text: The text result. + /// - id: The tool use ID. If `nil`, uses the ID from the last tool use in history. + /// - status: The result status. Defaults to success if `nil`. + /// - Returns: A new builder with the tool result set. + /// - Throws: `BedrockLibraryError` if validation fails. public func withToolResult( _ text: String, id: String? = nil, @@ -250,6 +411,14 @@ public struct ConverseRequestBuilder: Sendable { return try self.withToolResult(toolResult) } + /// Sets a tool result with image content. + /// + /// - Parameters: + /// - image: The image result. + /// - id: The tool use ID. If `nil`, uses the ID from the last tool use in history. + /// - status: The result status. Defaults to success if `nil`. + /// - Returns: A new builder with the tool result set. + /// - Throws: `BedrockLibraryError` if validation fails. public func withToolResult( _ image: ImageBlock, id: String? = nil, @@ -260,6 +429,14 @@ public struct ConverseRequestBuilder: Sendable { return try self.withToolResult(toolResult) } + /// Sets a tool result with document content. + /// + /// - Parameters: + /// - document: The document result. + /// - id: The tool use ID. If `nil`, uses the ID from the last tool use in history. + /// - status: The result status. Defaults to success if `nil`. + /// - Returns: A new builder with the tool result set. + /// - Throws: `BedrockLibraryError` if validation fails. public func withToolResult( _ document: DocumentBlock, id: String? = nil, @@ -270,6 +447,14 @@ public struct ConverseRequestBuilder: Sendable { return try self.withToolResult(toolResult) } + /// Sets a tool result with JSON content. + /// + /// - Parameters: + /// - json: The JSON result. + /// - id: The tool use ID. If `nil`, uses the ID from the last tool use in history. + /// - status: The result status. Defaults to success if `nil`. + /// - Returns: A new builder with the tool result set. + /// - Throws: `BedrockLibraryError` if validation fails. public func withToolResult( _ json: JSON, id: String? = nil, @@ -280,6 +465,14 @@ public struct ConverseRequestBuilder: Sendable { return try self.withToolResult(toolResult) } + /// Sets a tool result with video content. + /// + /// - Parameters: + /// - video: The video result. + /// - id: The tool use ID. If `nil`, uses the ID from the last tool use in history. + /// - status: The result status. Defaults to success if `nil`. + /// - Returns: A new builder with the tool result set. + /// - Throws: `BedrockLibraryError` if validation fails. public func withToolResult( _ video: VideoBlock, id: String? = nil, @@ -290,6 +483,14 @@ public struct ConverseRequestBuilder: Sendable { return try self.withToolResult(toolResult) } + /// Sets a tool result with raw data content. + /// + /// - Parameters: + /// - data: The raw data result. + /// - id: The tool use ID. If `nil`, uses the ID from the last tool use in history. + /// - status: The result status. Defaults to success if `nil`. + /// - Returns: A new builder with the tool result set. + /// - Throws: `BedrockLibraryError` if validation fails. public func withToolResult( _ data: Data, id: String? = nil, @@ -300,6 +501,14 @@ public struct ConverseRequestBuilder: Sendable { return try self.withToolResult(toolResult) } + /// Sets a tool result with an encodable object. + /// + /// - Parameters: + /// - object: The encodable object to use as the result. + /// - id: The tool use ID. If `nil`, uses the ID from the last tool use in history. + /// - status: The result status. Defaults to success if `nil`. + /// - Returns: A new builder with the tool result set. + /// - Throws: `BedrockLibraryError` if validation or encoding fails. public func withToolResult( _ object: C, id: String? = nil, @@ -310,6 +519,11 @@ public struct ConverseRequestBuilder: Sendable { return try self.withToolResult(toolResult) } + /// Sets a failed tool result with error status. + /// + /// - Parameter id: The tool use ID. If `nil`, uses the ID from the last tool use in history. + /// - Returns: A new builder with the failed tool result set. + /// - Throws: `BedrockLibraryError` if validation fails. public func withFailedToolResult(id: String?) throws -> ConverseRequestBuilder { let id = try id ?? getToolResultId() let toolResult = ToolResultBlock(id: id, content: [], status: .error) @@ -318,6 +532,13 @@ public struct ConverseRequestBuilder: Sendable { // MARK - builder methods - inference parameters + /// Sets the maximum number of tokens to generate. + /// + /// Must be greater than `maxReasoningTokens` if reasoning is enabled. + /// + /// - Parameter maxTokens: The maximum token count, or `nil` to use the model's default. + /// - Returns: A new builder with the max tokens set. + /// - Throws: `BedrockLibraryError` if the value is invalid or conflicts with reasoning tokens. public func withMaxTokens(_ maxTokens: Int?) throws -> ConverseRequestBuilder { var copy = self if let maxTokens { @@ -334,6 +555,13 @@ public struct ConverseRequestBuilder: Sendable { return copy } + /// Sets the temperature parameter controlling randomness in generation. + /// + /// Higher values (e.g., 1.0) make output more random, lower values (e.g., 0.0) make it more deterministic. + /// + /// - Parameter temperature: The temperature value, or `nil` to use the model's default. + /// - Returns: A new builder with the temperature set. + /// - Throws: `BedrockLibraryError` if the value is outside the model's supported range. public func withTemperature(_ temperature: Double?) throws -> ConverseRequestBuilder { var copy = self if let temperature { @@ -343,6 +571,14 @@ public struct ConverseRequestBuilder: Sendable { return copy } + /// Sets the top-p parameter for nucleus sampling. + /// + /// Controls diversity by limiting the cumulative probability of tokens considered. + /// Values closer to 0 make output more focused, closer to 1 more diverse. + /// + /// - Parameter topP: The top-p value, or `nil` to use the model's default. + /// - Returns: A new builder with the top-p set. + /// - Throws: `BedrockLibraryError` if the value is outside the model's supported range. public func withTopP(_ topP: Double?) throws -> ConverseRequestBuilder { var copy = self if let topP { @@ -352,6 +588,11 @@ public struct ConverseRequestBuilder: Sendable { return copy } + /// Sets the stop sequences that halt generation when encountered. + /// + /// - Parameter stopSequences: The array of stop sequences, or `nil` to use defaults. Must not be empty if provided. + /// - Returns: A new builder with the stop sequences set. + /// - Throws: `BedrockLibraryError` if validation fails or the array is empty. public func withStopSequences(_ stopSequences: [String]?) throws -> ConverseRequestBuilder { var copy = self if let stopSequences { @@ -364,6 +605,11 @@ public struct ConverseRequestBuilder: Sendable { return copy } + /// Sets a single stop sequence that halts generation when encountered. + /// + /// - Parameter stopSequence: The stop sequence, or `nil` to clear stop sequences. + /// - Returns: A new builder with the stop sequence set. + /// - Throws: `BedrockLibraryError` if validation fails. public func withStopSequence(_ stopSequence: String?) throws -> ConverseRequestBuilder { var stopSequences: [String]? = nil if let stopSequence { @@ -372,6 +618,13 @@ public struct ConverseRequestBuilder: Sendable { return try self.withStopSequences(stopSequences) } + /// Sets the system prompts that guide model behavior. + /// + /// System prompts provide instructions or context that influence how the model responds. + /// + /// - Parameter systemPrompts: The array of system prompts, or `nil` to clear. Must not be empty if provided. + /// - Returns: A new builder with the system prompts set. + /// - Throws: `BedrockLibraryError.ConverseRequestBuilder` if the array is empty. public func withSystemPrompts(_ systemPrompts: [String]?) throws -> ConverseRequestBuilder { var copy = self if let systemPrompts { @@ -383,6 +636,11 @@ public struct ConverseRequestBuilder: Sendable { return copy } + /// Sets a single system prompt that guides model behavior. + /// + /// - Parameter systemPrompt: The system prompt, or `nil` to clear system prompts. + /// - Returns: A new builder with the system prompt set. + /// - Throws: `BedrockLibraryError` if validation fails. public func withSystemPrompt(_ systemPrompt: String?) throws -> ConverseRequestBuilder { var systemPrompts: [String]? = nil if let systemPrompt { @@ -391,6 +649,13 @@ public struct ConverseRequestBuilder: Sendable { return try self.withSystemPrompts(systemPrompts) } + /// Enables or disables extended thinking mode for reasoning-capable models. + /// + /// When enabled, the model uses additional tokens for internal reasoning before generating output. + /// + /// - Parameter enabled: Whether to enable reasoning mode. Defaults to `true`. + /// - Returns: A new builder with reasoning configured. + /// - Throws: `BedrockLibraryError` if the model doesn't support reasoning. public func withReasoning(_ enabled: Bool = true) throws -> ConverseRequestBuilder { var copy = self if enabled { @@ -406,6 +671,14 @@ public struct ConverseRequestBuilder: Sendable { return copy } + /// Sets the maximum number of tokens for internal reasoning. + /// + /// Must be less than `maxTokens` and can only be set when reasoning is enabled. + /// + /// - Parameter maxReasoningTokens: The maximum reasoning token count, or `nil` to use defaults. + /// - Returns: A new builder with the max reasoning tokens set. + /// - Throws: `BedrockLibraryError` if reasoning is disabled, the value is invalid, + /// or it exceeds `maxTokens`. public func withMaxReasoningTokens(_ maxReasoningTokens: Int?) throws -> ConverseRequestBuilder { var copy = self if let maxReasoningTokens { @@ -428,7 +701,11 @@ public struct ConverseRequestBuilder: Sendable { return copy } - /// convenience method to enable reasoning and set maxReasoningTokens at the same time + /// Enables reasoning mode and sets the maximum reasoning tokens in one call. + /// + /// - Parameter maxReasoningTokens: The maximum number of tokens for internal reasoning. + /// - Returns: A new builder with reasoning enabled and max reasoning tokens set. + /// - Throws: `BedrockLibraryError` if the model doesn't support reasoning or validation fails. public func withReasoning(maxReasoningTokens: Int) throws -> ConverseRequestBuilder { try self.withReasoning(true).withMaxReasoningTokens(maxReasoningTokens) } @@ -443,6 +720,19 @@ public struct ConverseRequestBuilder: Sendable { return try copy.withReasoning(enabled) } + /// Sets the service tier for the inference request. + /// + /// Service tiers control the priority and cost of inference requests. + /// For more information, see [Service Tiers](https://docs.aws.amazon.com/bedrock/latest/userguide/service-tiers-inference.html). + /// + /// - Parameter serviceTier: The service tier to use. + /// - Returns: A new builder with the service tier set. + public func withServiceTier(_ serviceTier: ServiceTier) throws -> ConverseRequestBuilder { + var copy = self + copy.serviceTier = serviceTier + return copy + } + // MARK - public methods /// Returns the user Message made up of the user input in the builder diff --git a/Sources/BedrockService/BedrockRuntimeClient/InvokeModel/BedrockService+InvokeModelText.swift b/Sources/BedrockService/BedrockRuntimeClient/InvokeModel/BedrockService+InvokeModelText.swift index 720bff2e..c6d9ab3a 100644 --- a/Sources/BedrockService/BedrockRuntimeClient/InvokeModel/BedrockService+InvokeModelText.swift +++ b/Sources/BedrockService/BedrockRuntimeClient/InvokeModel/BedrockService+InvokeModelText.swift @@ -33,6 +33,7 @@ extension BedrockService { /// - topP: Optional top-p parameter for nucleus sampling /// - topK: Optional top-k parameter for filtering /// - stopSequences: Optional array of sequences where generation should stop + /// - serviceTier: An optional service tier .default | .priority | .flex /// - Throws: BedrockLibraryError.notSupported for parameters or functionalities that are not supported /// BedrockLibraryError.invalidParameter for invalid parameters /// BedrockLibraryError.invalidPrompt for a prompt that is empty or too long @@ -47,7 +48,8 @@ extension BedrockService { temperature: Double? = nil, topP: Double? = nil, topK: Int? = nil, - stopSequences: [String]? = nil + stopSequences: [String]? = nil, + serviceTier: ServiceTier = .default ) async throws -> TextCompletion { logger.trace( "Generating text completion", @@ -60,6 +62,7 @@ extension BedrockService { "topP": .stringConvertible(topP ?? "not defined"), "topK": .stringConvertible(topK ?? "not defined"), "stopSequences": .stringConvertible(stopSequences ?? "not defined"), + "serviceTier": .string(serviceTier.rawValue), ] ) do { @@ -88,7 +91,8 @@ extension BedrockService { temperature: temperature, topP: topP, topK: topK, - stopSequences: stopSequences + stopSequences: stopSequences, + serviceTier: serviceTier ) let input: InvokeModelInput = try request.getInvokeModelInput(forRegion: self.region) logger.trace( diff --git a/Sources/BedrockService/BedrockRuntimeClient/InvokeModel/InvokeModelRequest.swift b/Sources/BedrockService/BedrockRuntimeClient/InvokeModel/InvokeModelRequest.swift index 3e694ae7..ce8d89b8 100644 --- a/Sources/BedrockService/BedrockRuntimeClient/InvokeModel/InvokeModelRequest.swift +++ b/Sources/BedrockService/BedrockRuntimeClient/InvokeModel/InvokeModelRequest.swift @@ -55,7 +55,8 @@ struct InvokeModelRequest { temperature: Double?, topP: Double?, topK: Int?, - stopSequences: [String]? + stopSequences: [String]?, + serviceTier: ServiceTier ) throws -> InvokeModelRequest { try .init( model: model, @@ -64,7 +65,8 @@ struct InvokeModelRequest { temperature: temperature, topP: topP, topK: topK, - stopSequences: stopSequences + stopSequences: stopSequences, + serviceTier: serviceTier ) } @@ -75,7 +77,8 @@ struct InvokeModelRequest { temperature: Double?, topP: Double?, topK: Int?, - stopSequences: [String]? + stopSequences: [String]?, + serviceTier: ServiceTier ) throws { let textModality = try model.getTextModality() let body: BedrockBodyCodable = try textModality.getTextRequestBody( @@ -84,7 +87,8 @@ struct InvokeModelRequest { temperature: temperature, topP: topP, topK: topK, - stopSequences: stopSequences + stopSequences: stopSequences, + serviceTier: serviceTier ) self.init(model: model, body: body) } diff --git a/Sources/BedrockService/BedrockRuntimeClient/Modalities/TextModality.swift b/Sources/BedrockService/BedrockRuntimeClient/Modalities/TextModality.swift index 648edee0..a5924ebf 100644 --- a/Sources/BedrockService/BedrockRuntimeClient/Modalities/TextModality.swift +++ b/Sources/BedrockService/BedrockRuntimeClient/Modalities/TextModality.swift @@ -29,7 +29,8 @@ public protocol TextModality: Modality { temperature: Double?, topP: Double?, topK: Int?, - stopSequences: [String]? + stopSequences: [String]?, + serviceTier: ServiceTier ) throws -> BedrockBodyCodable func getTextResponseBody(from data: Data) throws -> ContainsTextCompletion diff --git a/Sources/BedrockService/BedrockRuntimeClient/Parameters/ConverseParameters.swift b/Sources/BedrockService/BedrockRuntimeClient/Parameters/ConverseParameters.swift index d91d651a..bf6fc0d0 100644 --- a/Sources/BedrockService/BedrockRuntimeClient/Parameters/ConverseParameters.swift +++ b/Sources/BedrockService/BedrockRuntimeClient/Parameters/ConverseParameters.swift @@ -26,6 +26,7 @@ public struct ConverseParameters: Parameters { public let prompt: PromptParams public let stopSequences: StopSequenceParams public let maxReasoningTokens: Parameter + public let serviceTier: ServiceTier public init( temperature: Parameter, @@ -33,7 +34,8 @@ public struct ConverseParameters: Parameters { topP: Parameter, stopSequences: StopSequenceParams, maxPromptSize: Int?, - maxReasoningTokens: Parameter = .notSupported(.maxReasoningTokens) + maxReasoningTokens: Parameter = .notSupported(.maxReasoningTokens), + serviceTier: ServiceTier = .default ) { self.temperature = temperature self.maxTokens = maxTokens @@ -41,6 +43,7 @@ public struct ConverseParameters: Parameters { self.prompt = PromptParams(maxSize: maxPromptSize) self.stopSequences = stopSequences self.maxReasoningTokens = maxReasoningTokens + self.serviceTier = serviceTier } public init( @@ -53,6 +56,7 @@ public struct ConverseParameters: Parameters { self.prompt = textGenerationParameters.prompt self.stopSequences = textGenerationParameters.stopSequences self.maxReasoningTokens = maxReasoningTokens + self.serviceTier = textGenerationParameters.serviceTier } package func validate( diff --git a/Sources/BedrockService/BedrockRuntimeClient/Parameters/Parameters.swift b/Sources/BedrockService/BedrockRuntimeClient/Parameters/Parameters.swift index ba5f03b5..1c150b84 100644 --- a/Sources/BedrockService/BedrockRuntimeClient/Parameters/Parameters.swift +++ b/Sources/BedrockService/BedrockRuntimeClient/Parameters/Parameters.swift @@ -19,19 +19,48 @@ import FoundationEssentials import Foundation #endif +/// A protocol that defines the base requirements for model parameter configurations. +/// +/// Types conforming to `Parameters` represent configuration parameters for Amazon Bedrock models, +/// ensuring they are thread-safe, hashable, and equatable for use in concurrent environments. public protocol Parameters: Sendable, Hashable, Equatable {} +/// A generic parameter definition for numeric model configuration values. +/// +/// `Parameter` encapsulates validation rules and metadata for numeric parameters used in Amazon Bedrock models, +/// including minimum and maximum bounds, default values, and support status. +/// +/// - Parameters: +/// - T: The numeric type of the parameter value, which must be `Sendable`, `Hashable`, `Equatable`, `Numeric`, and `Comparable`. public struct Parameter: Sendable, Hashable, Equatable { + /// The minimum allowed value for this parameter, if any. public let minValue: T? + /// The maximum allowed value for this parameter, if any. public let maxValue: T? + /// The default value for this parameter, if any. public let defaultValue: T? + /// Indicates whether this parameter is supported by the model. public let isSupported: Bool + /// The name identifier for this parameter. public let name: ParameterName + /// Creates a supported parameter with optional validation bounds and default value. + /// + /// - Parameters: + /// - name: The parameter name identifier. + /// - minValue: The minimum allowed value. Defaults to `nil` (no minimum). + /// - maxValue: The maximum allowed value. Defaults to `nil` (no maximum). + /// - defaultValue: The default value. Defaults to `nil` (no default). 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) } + /// Creates an unsupported parameter marker. + /// + /// Use this factory method to indicate that a parameter is not supported by a particular model. + /// + /// - Parameter name: The parameter name identifier. + /// - Returns: A parameter instance marked as unsupported. public static func notSupported(_ name: ParameterName) -> Self { Self(name: name, minValue: nil, maxValue: nil, defaultValue: nil, isSupported: false) } @@ -45,6 +74,14 @@ public struct Parameter Self { Self(maxSequences: nil, defaultValue: nil, isSupported: false) } @@ -124,6 +190,12 @@ public struct StopSequenceParams: Parameters { self.isSupported = isSupported } + /// Validates that a stop sequence array meets the defined constraints. + /// + /// This method checks whether the number of stop sequences exceeds the maximum allowed. + /// + /// - Parameter value: The array of stop sequences to validate. + /// - Throws: `BedrockLibraryError.invalidStopSequences` if the number of sequences exceeds the maximum. public func validateValue(_ value: [String]) throws { if let maxSequences { guard value.count <= maxSequences else { @@ -135,3 +207,21 @@ public struct StopSequenceParams: Parameters { } } } + +// MARK: - Support for Service Tier +// https://docs.aws.amazon.com/bedrock/latest/userguide/service-tiers-inference.html + +/// Service tier options for Amazon Bedrock inference requests. +/// +/// Service tiers allow you to control the priority and cost of your inference requests. +/// Different tiers provide different latency guarantees and pricing models. +/// +/// For more information, see [Service Tiers](https://docs.aws.amazon.com/bedrock/latest/userguide/service-tiers-inference.html). +public enum ServiceTier: String, Codable, Sendable { + /// Standard service tier with balanced performance and cost. + case `default` + /// Priority service tier for low-latency, high-priority workloads. + case priority + /// Flexible service tier optimized for cost-sensitive, non-time-critical workloads. + case flex +} diff --git a/Sources/BedrockService/BedrockRuntimeClient/Parameters/TextGenerationParameters.swift b/Sources/BedrockService/BedrockRuntimeClient/Parameters/TextGenerationParameters.swift index 97ca962a..9d375b2a 100644 --- a/Sources/BedrockService/BedrockRuntimeClient/Parameters/TextGenerationParameters.swift +++ b/Sources/BedrockService/BedrockRuntimeClient/Parameters/TextGenerationParameters.swift @@ -26,6 +26,7 @@ public struct TextGenerationParameters: Parameters { public let topK: Parameter public let prompt: PromptParams public let stopSequences: StopSequenceParams + public let serviceTier: ServiceTier public init( temperature: Parameter, @@ -33,7 +34,8 @@ public struct TextGenerationParameters: Parameters { topP: Parameter, topK: Parameter, stopSequences: StopSequenceParams, - maxPromptSize: Int? + maxPromptSize: Int?, + serviceTier: ServiceTier = .default ) { self.temperature = temperature self.maxTokens = maxTokens @@ -41,6 +43,7 @@ public struct TextGenerationParameters: Parameters { self.topK = topK self.prompt = PromptParams(maxSize: maxPromptSize) self.stopSequences = stopSequences + self.serviceTier = serviceTier } package func validate( diff --git a/Sources/BedrockService/Models/Amazon/Nova/Nova.swift b/Sources/BedrockService/Models/Amazon/Nova/Nova.swift index 4354a243..608c42f5 100644 --- a/Sources/BedrockService/Models/Amazon/Nova/Nova.swift +++ b/Sources/BedrockService/Models/Amazon/Nova/Nova.swift @@ -42,7 +42,8 @@ struct NovaText: TextModality, ConverseModality, ConverseStreamingModality, Cros temperature: Double?, topP: Double?, topK: Int?, - stopSequences: [String]? + stopSequences: [String]?, + serviceTier: ServiceTier ) throws -> BedrockBodyCodable { if topP != nil && temperature != nil { throw BedrockLibraryError.notSupported("Alter either topP or temperature, but not both.") @@ -53,7 +54,8 @@ struct NovaText: TextModality, ConverseModality, ConverseStreamingModality, Cros temperature: temperature ?? parameters.temperature.defaultValue, topP: topP ?? parameters.topP.defaultValue, topK: topK ?? parameters.topK.defaultValue, - stopSequences: stopSequences ?? parameters.stopSequences.defaultValue + stopSequences: stopSequences ?? parameters.stopSequences.defaultValue, + serviceTier: serviceTier ) } diff --git a/Sources/BedrockService/Models/Amazon/Nova/NovaRequestBody.swift b/Sources/BedrockService/Models/Amazon/Nova/NovaRequestBody.swift index 4f1de994..97610e81 100644 --- a/Sources/BedrockService/Models/Amazon/Nova/NovaRequestBody.swift +++ b/Sources/BedrockService/Models/Amazon/Nova/NovaRequestBody.swift @@ -29,14 +29,16 @@ public struct NovaRequestBody: BedrockBodyCodable { temperature: Double?, topP: Double?, topK: Int?, - stopSequences: [String]? + stopSequences: [String]?, + serviceTier: ServiceTier ) { self.inferenceConfig = InferenceConfig( maxTokens: maxTokens, temperature: temperature, topP: topP, topK: topK, - stopSequences: stopSequences + stopSequences: stopSequences, + serviceTier: serviceTier ) self.messages = [Message(role: .user, content: [Content(text: prompt)])] } @@ -47,6 +49,7 @@ public struct NovaRequestBody: BedrockBodyCodable { let topP: Double? let topK: Int? let stopSequences: [String]? + let serviceTier: ServiceTier } private struct Message: Codable { diff --git a/Sources/BedrockService/Models/Amazon/Titan/TitanText.swift b/Sources/BedrockService/Models/Amazon/Titan/TitanText.swift index 7d0ff0d9..6fbce19b 100644 --- a/Sources/BedrockService/Models/Amazon/Titan/TitanText.swift +++ b/Sources/BedrockService/Models/Amazon/Titan/TitanText.swift @@ -46,7 +46,8 @@ struct TitanText: TextModality, ConverseModality, ConverseStreamingModality { temperature: Double?, topP: Double?, topK: Int?, - stopSequences: [String]? + stopSequences: [String]?, + serviceTier: ServiceTier ) throws -> BedrockBodyCodable { guard let maxTokens = maxTokens ?? parameters.maxTokens.defaultValue else { throw BedrockLibraryError.notFound("No value was given for maxTokens and no default value was found") @@ -70,6 +71,8 @@ struct TitanText: TextModality, ConverseModality, ConverseStreamingModality { topP: topP, stopSequences: stopSequences ) + // service tier is ignored as per + // https://docs.aws.amazon.com/bedrock/latest/userguide/service-tiers-inference.html } func getTextResponseBody(from data: Data) throws -> ContainsTextCompletion { diff --git a/Sources/BedrockService/Models/Anthropic/Anthropic.swift b/Sources/BedrockService/Models/Anthropic/Anthropic.swift index 5a4c4639..493de7c1 100644 --- a/Sources/BedrockService/Models/Anthropic/Anthropic.swift +++ b/Sources/BedrockService/Models/Anthropic/Anthropic.swift @@ -55,7 +55,8 @@ struct AnthropicText: TextModality, ConverseModality, ConverseStreamingModality, temperature: Double?, topP: Double?, topK: Int?, - stopSequences: [String]? + stopSequences: [String]?, + serviceTier: ServiceTier ) throws -> BedrockBodyCodable { guard let maxTokens = maxTokens ?? parameters.maxTokens.defaultValue else { throw BedrockLibraryError.notFound("No value was given for maxTokens and no default value was found") @@ -71,6 +72,8 @@ struct AnthropicText: TextModality, ConverseModality, ConverseStreamingModality, topK: topK ?? parameters.topK.defaultValue, stopSequences: stopSequences ?? parameters.stopSequences.defaultValue ) + // service tier is ignored as per + // https://docs.aws.amazon.com/bedrock/latest/userguide/service-tiers-inference.html } func getTextResponseBody(from data: Data) throws -> ContainsTextCompletion { diff --git a/Sources/BedrockService/Models/DeepSeek/DeepSeek.swift b/Sources/BedrockService/Models/DeepSeek/DeepSeek.swift index 93ca6f32..bd57388f 100644 --- a/Sources/BedrockService/Models/DeepSeek/DeepSeek.swift +++ b/Sources/BedrockService/Models/DeepSeek/DeepSeek.swift @@ -60,7 +60,8 @@ struct DeepSeekText: TextModality, CrossRegionInferenceModality { temperature: Double?, topP: Double?, topK: Int?, - stopSequences: [String]? + stopSequences: [String]?, + serviceTier: ServiceTier ) throws -> BedrockBodyCodable { guard let maxTokens = maxTokens ?? parameters.maxTokens.defaultValue else { throw BedrockLibraryError.notFound("No value was given for maxTokens and no default value was found") @@ -82,7 +83,8 @@ struct DeepSeekText: TextModality, CrossRegionInferenceModality { maxTokens: maxTokens, temperature: temperature, topP: topP, - stopSequences: stopSequences + stopSequences: stopSequences, + serviceTier: serviceTier.rawValue ) } diff --git a/Sources/BedrockService/Models/DeepSeek/DeepSeekRequestBody.swift b/Sources/BedrockService/Models/DeepSeek/DeepSeekRequestBody.swift index 547dbeb2..d5313228 100644 --- a/Sources/BedrockService/Models/DeepSeek/DeepSeekRequestBody.swift +++ b/Sources/BedrockService/Models/DeepSeek/DeepSeekRequestBody.swift @@ -25,18 +25,21 @@ public struct DeepSeekRequestBody: BedrockBodyCodable { private let top_p: Double private let max_tokens: Int private let stop: [String] + private let service_tier: String public init( prompt: String, maxTokens: Int, temperature: Double, topP: Double, - stopSequences: [String] + stopSequences: [String], + serviceTier: String ) { self.prompt = prompt self.temperature = temperature self.top_p = topP self.max_tokens = maxTokens self.stop = stopSequences + self.service_tier = serviceTier } } diff --git a/Sources/BedrockService/Models/Llama/Llama.swift b/Sources/BedrockService/Models/Llama/Llama.swift index 815067a9..f7e5affc 100644 --- a/Sources/BedrockService/Models/Llama/Llama.swift +++ b/Sources/BedrockService/Models/Llama/Llama.swift @@ -45,7 +45,8 @@ struct LlamaText: TextModality, ConverseModality, ConverseStreamingModality, Cro temperature: Double?, topP: Double?, topK: Int?, - stopSequences: [String]? + stopSequences: [String]?, + serviceTier: ServiceTier ) throws -> BedrockBodyCodable { guard topK == nil else { throw BedrockLibraryError.notSupported("TopK is not supported for Llama text completion") @@ -59,6 +60,9 @@ struct LlamaText: TextModality, ConverseModality, ConverseStreamingModality, Cro temperature: temperature ?? parameters.temperature.defaultValue, topP: topP ?? parameters.topP.defaultValue ) + + // service tier is ignored as per + // https://docs.aws.amazon.com/bedrock/latest/userguide/service-tiers-inference.html } func getTextResponseBody(from data: Data) throws -> ContainsTextCompletion { diff --git a/Sources/BedrockService/Models/OpenAI/OpenAI.swift b/Sources/BedrockService/Models/OpenAI/OpenAI.swift index 06be1da6..49d176b1 100644 --- a/Sources/BedrockService/Models/OpenAI/OpenAI.swift +++ b/Sources/BedrockService/Models/OpenAI/OpenAI.swift @@ -55,7 +55,8 @@ struct OpenAIText: TextModality, ConverseModality { temperature: Double?, topP: Double?, topK: Int?, - stopSequences: [String]? + stopSequences: [String]?, + serviceTier: ServiceTier ) throws -> BedrockBodyCodable { guard let maxTokens = maxTokens ?? parameters.maxTokens.defaultValue else { throw BedrockLibraryError.notFound("No value was given for maxTokens and no default value was found") @@ -68,6 +69,7 @@ struct OpenAIText: TextModality, ConverseModality { maxTokens: maxTokens, temperature: temperature ?? parameters.temperature.defaultValue, topP: topP ?? parameters.topP.defaultValue, + serviceTier: serviceTier ) } diff --git a/Sources/BedrockService/Models/OpenAI/OpenAIRequestBody.swift b/Sources/BedrockService/Models/OpenAI/OpenAIRequestBody.swift index c28c4f72..644c7913 100644 --- a/Sources/BedrockService/Models/OpenAI/OpenAIRequestBody.swift +++ b/Sources/BedrockService/Models/OpenAI/OpenAIRequestBody.swift @@ -24,12 +24,14 @@ public struct OpenAIRequestBody: BedrockBodyCodable { private let temperature: Double? private let top_p: Double? private let messages: [OpenAIMessage] + private let service_tier: ServiceTier public init( prompt: String, maxTokens: Int, temperature: Double?, - topP: Double? + topP: Double?, + serviceTier: ServiceTier ) { self.max_completion_tokens = maxTokens self.temperature = temperature @@ -37,6 +39,7 @@ public struct OpenAIRequestBody: BedrockBodyCodable { OpenAIMessage(role: .user, content: prompt) ] self.top_p = topP + self.service_tier = serviceTier } private struct OpenAIMessage: Codable { diff --git a/Tests/Converse/ConverseToolTests.swift b/Tests/Converse/ConverseToolTests.swift index b72c0ff7..091f13b1 100644 --- a/Tests/Converse/ConverseToolTests.swift +++ b/Tests/Converse/ConverseToolTests.swift @@ -124,7 +124,7 @@ extension BedrockServiceTests { ) let id = "toolId" let toolUse = ToolUseBlock(id: id, name: "toolName", input: JSON(with: .object(["code": .string("string")]))) - let history = [Message("Use tool"), Message(toolUse)] + let history: History = [Message("Use tool"), Message(toolUse)] let builder = try ConverseRequestBuilder(with: .nova_lite) .withHistory(history) @@ -144,7 +144,7 @@ extension BedrockServiceTests { description: "toolDescription" ) let id = "toolId" - let history = [Message("Use tool"), Message(from: .assistant, content: [.text("No need for a tool")])] + let history: History = [Message("Use tool"), Message(from: .assistant, content: [.text("No need for a tool")])] #expect(throws: BedrockLibraryError.self) { let _ = try ConverseRequestBuilder(with: .nova_lite) .withHistory(history) @@ -157,7 +157,7 @@ extension BedrockServiceTests { func converseToolResultWithoutTools() async throws { let id = "toolId" let toolUse = ToolUseBlock(id: id, name: "toolName", input: JSON(with: .object(["code": .string("string")]))) - let history = [Message("Use tool"), Message(toolUse)] + let history: History = [Message("Use tool"), Message(toolUse)] #expect(throws: BedrockLibraryError.self) { let _ = try ConverseRequestBuilder(with: .nova_lite) .withHistory(history) @@ -174,7 +174,7 @@ extension BedrockServiceTests { ) let id = "toolId" let toolUse = ToolUseBlock(id: id, name: "toolName", input: JSON(with: .object(["code": .string("string")]))) - let history = [Message("Use tool"), Message(toolUse)] + let history: History = [Message("Use tool"), Message(toolUse)] #expect(throws: BedrockLibraryError.self) { let _ = try ConverseRequestBuilder(with: .titan_text_g1_express) .withHistory(history) @@ -187,7 +187,7 @@ extension BedrockServiceTests { func converseToolResultInvalidModelWithoutTools() async throws { let id = "toolId" let toolUse = ToolUseBlock(id: id, name: "toolName", input: JSON(with: .object(["code": .string("abc")]))) - let history = [Message("Use tool"), Message(toolUse)] + let history: History = [Message("Use tool"), Message(toolUse)] #expect(throws: BedrockLibraryError.self) { let _ = try ConverseRequestBuilder(with: .titan_text_g1_express) @@ -203,7 +203,7 @@ extension BedrockServiceTests { inputSchema: JSON(with: .object(["code": .string("string")])), description: "toolDescription" ) - let history = [Message("Use tool"), Message(from: .assistant, content: [.text("No need for a tool")])] + let history: History = [Message("Use tool"), Message(from: .assistant, content: [.text("No need for a tool")])] #expect(throws: BedrockLibraryError.self) { let _ = try ConverseRequestBuilder(with: .titan_text_g1_express) @@ -215,7 +215,7 @@ extension BedrockServiceTests { @Test("Tool result with invalid model without toolUse and without tools") func converseToolResultInvalidModelWithoutToolUseAndTools() async throws { - let history = [Message("Use tool"), Message(from: .assistant, content: [.text("No need for a tool")])] + let history: History = [Message("Use tool"), Message(from: .assistant, content: [.text("No need for a tool")])] #expect(throws: BedrockLibraryError.self) { let _ = try ConverseRequestBuilder(with: .titan_text_g1_express) .withHistory(history)