Skip to content

Commit 520d777

Browse files
committed
add a text chat example
1 parent cad9083 commit 520d777

File tree

4 files changed

+140
-1
lines changed

4 files changed

+140
-1
lines changed

.github/workflows/pull_request.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
# We pass the list of examples here, but we can't pass an array as argument
3636
# Instead, we pass a String with a valid JSON array.
3737
# The workaround is mentioned here https://github.com/orgs/community/discussions/11692
38-
examples: "[ 'converse', 'converse-stream' ]"
38+
examples: "[ 'converse', 'converse-stream', 'text_chat' ]"
3939

4040
swift-6-language-mode:
4141
name: Swift 6 Language Mode

Examples/text_chat/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
xcuserdata/
5+
DerivedData/
6+
.swiftpm/configuration/registries.json
7+
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8+
.netrc

Examples/text_chat/Package.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// swift-tools-version: 6.0
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "TextChat",
8+
platforms: [.macOS(.v15), .iOS(.v18), .tvOS(.v18)],
9+
products: [
10+
.executable(name: "TextChat", targets: ["TextChat"]),
11+
],
12+
dependencies: [
13+
// for production use, uncomment the following line
14+
// .package(url: "https://github.com/build-on-aws/swift-bedrock-library.git", branch: "main"),
15+
16+
// for local development, use the following line
17+
.package(name: "swift-bedrock-library", path: "../.."),
18+
19+
.package(url: "https://github.com/apple/swift-log.git", from: "1.5.0"),
20+
],
21+
targets: [
22+
.executableTarget(
23+
name: "TextChat",
24+
dependencies: [
25+
.product(name: "BedrockService", package: "swift-bedrock-library"),
26+
.product(name: "Logging", package: "swift-log"),
27+
]
28+
)
29+
]
30+
)
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
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+
import BedrockService
17+
import Logging
18+
19+
@main
20+
struct TextChat {
21+
static func main() async throws {
22+
do {
23+
try await TextChat.run()
24+
} catch {
25+
print("Error:\n\(error)")
26+
}
27+
}
28+
static func run() async throws {
29+
var logger = Logger(label: "TextChat")
30+
logger.logLevel = .debug
31+
32+
let bedrock = try await BedrockService(
33+
region: .useast1,
34+
logger: logger
35+
// uncomment if you use SSO with AWS Identity Center
36+
// authentication: .sso
37+
)
38+
39+
// select a model that supports the converse modality
40+
// models must be enabled in your AWS account
41+
let model: BedrockModel = .claudev3_7_sonnet
42+
43+
guard model.hasConverseModality() else {
44+
throw MyError.incorrectModality("\(model.name) does not support converse")
45+
}
46+
47+
// a reusable var to build the requests
48+
var request: ConverseRequestBuilder? = nil
49+
50+
// we keep track of the history of the conversation
51+
var history: [Message] = []
52+
53+
// while the user doesn't type "exit" or "quit"
54+
while true {
55+
56+
print("\nYou: ", terminator: "")
57+
let prompt: String = readLine() ?? ""
58+
guard prompt.isEmpty == false else { continue }
59+
if ["exit", "quit"].contains(prompt.lowercased()) {
60+
break
61+
}
62+
63+
print("\nAssistant: ", terminator: "")
64+
65+
if request == nil {
66+
// create a new request
67+
request = try ConverseRequestBuilder(with: model)
68+
.withPrompt(prompt)
69+
} else {
70+
// append the new prompt to the existing request
71+
// ConverseRequestBuilder is stateles, it doesn't keep track of the history
72+
request = try ConverseRequestBuilder(from: request!)
73+
.withHistory(history)
74+
.withPrompt(prompt)
75+
}
76+
77+
// keep track of the history of the conversation
78+
history.append(Message(prompt))
79+
80+
// send the request
81+
let reply = try await bedrock.converseStream(with: request!)
82+
83+
for try await element in reply.stream {
84+
// process the stream elements
85+
switch element {
86+
case .text(_, let text):
87+
print(text, terminator: "")
88+
case .messageComplete(let message):
89+
print("\n")
90+
history.append(message)
91+
default:
92+
break
93+
}
94+
}
95+
}
96+
}
97+
98+
enum MyError: Error {
99+
case incorrectModality(String)
100+
}
101+
}

0 commit comments

Comments
 (0)