Skip to content

Commit f2f1583

Browse files
authored
API Changes to create AgentKit (#46)
* make converserequestbuilder sendable * Transform [Message] to History struct * swift format * enable API breakage CI check * [ci] run soundness check on amazon linux (because ssl-dev is not installed on swift-6.2:noble" by default) * add our own api breakage check * disable API breakage check until further notice * Ensure changes are compatibles * add more deprecation messages * swift format
1 parent 6837a83 commit f2f1583

File tree

12 files changed

+258
-37
lines changed

12 files changed

+258
-37
lines changed

.github/workflows/pull_request.yml

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,43 @@ jobs:
2929
license_header_check_project_name: "Swift Bedrock Library"
3030
shell_check_enabled: false
3131
python_lint_check_enabled: false
32+
# do not use this sub-action because the build requires
33+
# libssl-dev which is not installed on swift:6.2-noble
34+
# Using swift:6.2-amazonlinux2 is not a solution
35+
# because the @checkout action doesn't works on ALI2 (requires Node.js 20)
3236
api_breakage_check_enabled: false
33-
# api_breakage_check_container_image: "swift:6.1-noble"
34-
docs_check_container_image: "swift:6.1-noble"
35-
format_check_container_image: "swift:6.1-noble"
37+
api_breakage_check_container_image: "swift:6.2-noble"
38+
docs_check_container_image: "swift:6.2-noble"
39+
format_check_container_image: "swift:6.2-noble"
3640
yamllint_check_enabled: true
3741

42+
# api-breakage-check:
43+
# name: API breakage check
44+
# runs-on: ubuntu-latest
45+
# timeout-minutes: 20
46+
# steps:
47+
# - name: Checkout repository
48+
# uses: actions/checkout@v4
49+
# with:
50+
# # This is set to true since swift package diagnose-api-breaking-changes is
51+
# # cloning the repo again and without it being set to true this job won't work for
52+
# # private repos.
53+
# persist-credentials: true
54+
# submodules: true
55+
# fetch-tags: true
56+
# fetch-depth: 0 # Fetching tags requires fetch-depth: 0 (https://github.com/actions/checkout/issues/1471)
57+
# - name: Mark the workspace as safe
58+
# # https://github.com/actions/checkout/issues/766
59+
# run: git config --global --add safe.directory ${GITHUB_WORKSPACE}
60+
# - name: Run API breakage check
61+
# shell: bash
62+
# run: |
63+
# git fetch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} ${GITHUB_BASE_REF}:pull-base-ref
64+
# BASELINE_REF='pull-base-ref'
65+
# echo "Using baseline: $BASELINE_REF"
66+
# swift package diagnose-api-breaking-changes "$BASELINE_REF"
67+
68+
3869
integration-tests:
3970
name: Integration Tests
4071
uses: ./.github/workflows/integration_tests.yml

Examples/ios-math-solver/Sources/MathSolverViewModel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ final class MathSolverViewModel: ObservableObject, @unchecked Sendable {
113113

114114
// Make the streaming request
115115
Task {
116-
var messages: [Message] = []
116+
var messages: History = []
117117
do {
118118
// Process the stream
119119
let response = try await bedrockService.converseStream(with: promptBuilder)

Examples/text_chat/Sources/TextChat.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ struct TextChat {
4848
var request: ConverseRequestBuilder? = nil
4949

5050
// we keep track of the history of the conversation
51-
var history: [Message] = []
51+
var history: History = []
5252

5353
// while the user doesn't type "exit" or "quit"
5454
while true {

Examples/web-playground/backend/Sources/PlaygroundAPI/Types/Chat.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ extension Message: @retroactive ResponseCodable {}
2121

2222
struct ChatInput: Codable {
2323
let prompt: String?
24-
let history: [Message]?
24+
let history: History?
2525
let imageFormat: ImageBlock.Format?
2626
let imageBytes: String?
2727
let documentName: String?

Sources/Converse/BedrockService+Converse.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ extension BedrockService {
4040
/// BedrockLibraryError.invalidModality for invalid modality from the selected model
4141
/// BedrockLibraryError.invalidSDKResponse if the response body is missing
4242
/// - Returns: A Message containing the model's response
43+
@available(
44+
*,
45+
deprecated,
46+
message:
47+
"Use converse(with:conversation:...) that takes History instead of [Message]. This func will be removed in the next major version."
48+
)
4349
public func converse(
4450
with model: BedrockModel,
4551
conversation: [Message],
@@ -51,6 +57,49 @@ extension BedrockService {
5157
tools: [Tool]? = nil,
5258
enableReasoning: Bool? = false,
5359
maxReasoningTokens: Int? = nil
60+
) async throws -> Message {
61+
let history = History(conversation)
62+
return try await converse(
63+
with: model,
64+
conversation: history,
65+
maxTokens: maxTokens,
66+
temperature: temperature,
67+
topP: topP,
68+
stopSequences: stopSequences,
69+
systemPrompts: systemPrompts,
70+
tools: tools,
71+
enableReasoning: enableReasoning,
72+
maxReasoningTokens: maxReasoningTokens
73+
)
74+
}
75+
76+
/// Converse with a model using the Bedrock Converse API
77+
/// - Parameters:
78+
/// - model: The BedrockModel to converse with
79+
/// - conversation: Array of previous messages in the conversation
80+
/// - maxTokens: Optional maximum number of tokens to generate
81+
/// - temperature: Optional temperature parameter for controlling randomness
82+
/// - topP: Optional top-p parameter for nucleus sampling
83+
/// - stopSequences: Optional array of sequences where generation should stop
84+
/// - systemPrompts: Optional array of system prompts to guide the conversation
85+
/// - tools: Optional array of tools the model can use
86+
/// - Throws: BedrockLibraryError.notSupported for parameters or functionalities that are not supported
87+
/// BedrockLibraryError.invalidParameter for invalid parameters
88+
/// BedrockLibraryError.invalidPrompt if the prompt is empty or too long
89+
/// BedrockLibraryError.invalidModality for invalid modality from the selected model
90+
/// BedrockLibraryError.invalidSDKResponse if the response body is missing
91+
/// - Returns: A Message containing the model's response
92+
public func converse(
93+
with model: BedrockModel,
94+
conversation: History,
95+
maxTokens: Int? = nil,
96+
temperature: Double? = nil,
97+
topP: Double? = nil,
98+
stopSequences: [String]? = nil,
99+
systemPrompts: [String]? = nil,
100+
tools: [Tool]? = nil,
101+
enableReasoning: Bool? = false,
102+
maxReasoningTokens: Int? = nil
54103
) async throws -> Message {
55104
do {
56105
let modality = try model.getConverseModality()

Sources/Converse/BedrockService+ConverseStreaming.swift

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ extension BedrockService {
4141
/// BedrockLibraryError.invalidSDKResponse if the response body is missing
4242
/// - Returns: A ConverseReplyStream object that gives access to the high-level stream of ConverseStreamElements objects
4343
/// or the low-level stream provided by the AWS SDK.
44+
@available(
45+
*,
46+
deprecated,
47+
message:
48+
"Use converseStream(with:conversation:...) that takes History instead of [Message]. This func will be removed in the next major version."
49+
)
4450
public func converseStream(
4551
with model: BedrockModel,
4652
conversation: [Message],
@@ -52,6 +58,52 @@ extension BedrockService {
5258
tools: [Tool]? = nil,
5359
enableReasoning: Bool? = false,
5460
maxReasoningTokens: Int? = nil
61+
) async throws -> ConverseReplyStream {
62+
// Convert [Message] array to History
63+
let history = History(conversation)
64+
65+
return try await converseStream(
66+
with: model,
67+
conversation: history,
68+
maxTokens: maxTokens,
69+
temperature: temperature,
70+
topP: topP,
71+
stopSequences: stopSequences,
72+
systemPrompts: systemPrompts,
73+
tools: tools,
74+
enableReasoning: enableReasoning,
75+
maxReasoningTokens: maxReasoningTokens
76+
)
77+
}
78+
79+
/// Converse with a model using the Bedrock Converse Streaming API
80+
/// - Parameters:
81+
/// - model: The BedrockModel to converse with
82+
/// - conversation: Array of previous messages in the conversation
83+
/// - maxTokens: Optional maximum number of tokens to generate
84+
/// - temperature: Optional temperature parameter for controlling randomness
85+
/// - topP: Optional top-p parameter for nucleus sampling
86+
/// - stopSequences: Optional array of sequences where generation should stop
87+
/// - systemPrompts: Optional array of system prompts to guide the conversation
88+
/// - tools: Optional array of tools the model can use
89+
/// - Throws: BedrockLibraryError.notSupported for parameters or functionalities that are not supported
90+
/// BedrockLibraryError.invalidParameter for invalid parameters
91+
/// BedrockLibraryError.invalidPrompt if the prompt is empty or too long
92+
/// BedrockLibraryError.invalidModality for invalid modality from the selected model
93+
/// BedrockLibraryError.invalidSDKResponse if the response body is missing
94+
/// - Returns: A ConverseReplyStream object that gives access to the high-level stream of ConverseStreamElements objects
95+
/// or the low-level stream provided by the AWS SDK.
96+
public func converseStream(
97+
with model: BedrockModel,
98+
conversation: History,
99+
maxTokens: Int? = nil,
100+
temperature: Double? = nil,
101+
topP: Double? = nil,
102+
stopSequences: [String]? = nil,
103+
systemPrompts: [String]? = nil,
104+
tools: [Tool]? = nil,
105+
enableReasoning: Bool? = false,
106+
maxReasoningTokens: Int? = nil
55107
) async throws -> ConverseReplyStream {
56108
do {
57109
guard model.hasConverseStreamingModality() else {

Sources/Converse/ContentBlocks/History.swift

Lines changed: 0 additions & 26 deletions
This file was deleted.

Sources/Converse/ConverseRequest.swift

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,45 @@ import Foundation
2424

2525
public struct ConverseRequest {
2626
let model: BedrockModel
27-
let messages: [Message]
27+
let messages: History
2828
let inferenceConfig: InferenceConfig?
2929
let toolConfig: ToolConfig?
3030
let systemPrompts: [String]?
3131
let maxReasoningTokens: Int?
3232

33+
@available(
34+
*,
35+
deprecated,
36+
message:
37+
"Use the initializer that accepts a History instead of [Message]. This func will be removed in the next major version."
38+
)
3339
init(
3440
model: BedrockModel,
35-
messages: [Message] = [],
41+
messages: [Message],
42+
maxTokens: Int?,
43+
temperature: Double?,
44+
topP: Double?,
45+
stopSequences: [String]?,
46+
systemPrompts: [String]?,
47+
tools: [Tool]?,
48+
maxReasoningTokens: Int?
49+
) {
50+
self.init(
51+
model: model,
52+
messages: History(messages),
53+
maxTokens: maxTokens,
54+
temperature: temperature,
55+
topP: topP,
56+
stopSequences: stopSequences,
57+
systemPrompts: systemPrompts,
58+
tools: tools,
59+
maxReasoningTokens: maxReasoningTokens
60+
)
61+
}
62+
63+
init(
64+
model: BedrockModel,
65+
messages: History,
3666
maxTokens: Int?,
3767
temperature: Double?,
3868
topP: Double?,

Sources/Converse/ConverseRequestBuilder.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ import FoundationEssentials
1919
import Foundation
2020
#endif
2121

22-
public struct ConverseRequestBuilder {
22+
public struct ConverseRequestBuilder: Sendable {
2323

2424
public private(set) var model: BedrockModel
2525
private var parameters: ConverseParameters
2626

27-
public private(set) var history: [Message]
27+
public private(set) var history: History
2828
public private(set) var tools: [Tool]?
2929

3030
public private(set) var prompt: String?
@@ -88,8 +88,15 @@ public struct ConverseRequestBuilder {
8888
// MARK - builder methods
8989

9090
// MARK - builder methods - history
91-
91+
@available(
92+
*,
93+
deprecated,
94+
message: "Use withHistory(_: [History])instead. This func will be removed in the next major version."
95+
)
9296
public func withHistory(_ history: [Message]) throws -> ConverseRequestBuilder {
97+
try withHistory(History(history))
98+
}
99+
public func withHistory(_ history: History) throws -> ConverseRequestBuilder {
93100
if let lastMessage = history.last {
94101
guard lastMessage.role == .assistant else {
95102
throw BedrockLibraryError.ConverseRequestBuilder("Last message in history must be from assistant.")

Sources/Converse/History.swift

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Bedrock Library open source project
4+
//
5+
// Copyright (c) 2025 Amazon.com, Inc. or its affiliates
6+
// and the Swift Bedrock Library project authors
7+
// Licensed under Apache License v2.0
8+
//
9+
// See LICENSE.txt for license information
10+
// See CONTRIBUTORS.txt for the list of Swift Bedrock Library project authors
11+
//
12+
// SPDX-License-Identifier: Apache-2.0
13+
//
14+
//===----------------------------------------------------------------------===//
15+
16+
public struct History: Codable, Sendable {
17+
private var messages: [Message] = []
18+
19+
public init() {}
20+
21+
public init(_ message: Message) {
22+
messages = [message]
23+
}
24+
25+
public init(_ messages: [Message]) {
26+
self.messages = messages
27+
}
28+
29+
public init(_ messages: Message...) {
30+
self.messages = messages
31+
}
32+
33+
public mutating func append(_ message: Message) {
34+
messages.append(message)
35+
}
36+
37+
/// Essentials functions from Array that History needs
38+
public var count: Int { messages.count }
39+
40+
public subscript(index: Int) -> Message {
41+
messages[index]
42+
}
43+
44+
public var last: Message? {
45+
messages.last
46+
}
47+
48+
public static func + (lhs: History, rhs: [Message]) -> History {
49+
var result = lhs
50+
result.messages.append(contentsOf: rhs)
51+
return result
52+
}
53+
}
54+
55+
/// Collection
56+
extension History: Collection {
57+
public var startIndex: Int { messages.startIndex }
58+
public var endIndex: Int { messages.endIndex }
59+
public func index(after i: Int) -> Int { messages.index(after: i) }
60+
}
61+
62+
/// CustomString Convertible
63+
extension History: CustomStringConvertible {
64+
public var description: String {
65+
var result = "\(self.count) turns:\n"
66+
for message in self {
67+
result += "\(message)\n"
68+
}
69+
return result
70+
}
71+
}
72+
73+
/// ExpressibleByArrayLiteral
74+
extension History: ExpressibleByArrayLiteral {
75+
public init(arrayLiteral elements: Message...) {
76+
self.messages = elements
77+
}
78+
}

0 commit comments

Comments
 (0)