Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
# We pass the list of examples here, but we can't pass an array as argument
# Instead, we pass a String with a valid JSON array.
# The workaround is mentioned here https://github.com/orgs/community/discussions/11692
examples: "[ 'converse', 'converse-stream' ]"
examples: "[ 'converse', 'converse-stream', 'text_chat' ]"

swift-6-language-mode:
name: Swift 6 Language Mode
Expand Down
8 changes: 8 additions & 0 deletions Examples/text_chat/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
30 changes: 30 additions & 0 deletions Examples/text_chat/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "TextChat",
platforms: [.macOS(.v15), .iOS(.v18), .tvOS(.v18)],
products: [
.executable(name: "TextChat", targets: ["TextChat"]),
],
dependencies: [
// for production use, uncomment the following line
// .package(url: "https://github.com/build-on-aws/swift-bedrock-library.git", branch: "main"),

// for local development, use the following line
.package(name: "swift-bedrock-library", path: "../.."),

.package(url: "https://github.com/apple/swift-log.git", from: "1.5.0"),
],
targets: [
.executableTarget(
name: "TextChat",
dependencies: [
.product(name: "BedrockService", package: "swift-bedrock-library"),
.product(name: "Logging", package: "swift-log"),
]
)
]
)
101 changes: 101 additions & 0 deletions Examples/text_chat/Sources/TextChat.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Bedrock Library open source project
//
// Copyright (c) 2025 Amazon.com, Inc. or its affiliates
// and the Swift Bedrock Library project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Bedrock Library project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import BedrockService
import Logging

@main
struct TextChat {
static func main() async throws {
do {
try await TextChat.run()
} catch {
print("Error:\n\(error)")
Comment on lines +22 to +25
Copy link

Copilot AI Jun 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Consider using 'logger.error' instead of 'print' for error messages to maintain consistent logging and support structured logs.

Suggested change
do {
try await TextChat.run()
} catch {
print("Error:\n\(error)")
var logger = Logger(label: "TextChat.Main")
do {
try await TextChat.run()
} catch {
logger.error("Error:\n\(error)")

Copilot uses AI. Check for mistakes.
}
}
static func run() async throws {
var logger = Logger(label: "TextChat")
logger.logLevel = .debug

let bedrock = try await BedrockService(
region: .useast1,
logger: logger
// uncomment if you use SSO with AWS Identity Center
// authentication: .sso
)

// select a model that supports the converse modality
// models must be enabled in your AWS account
let model: BedrockModel = .claudev3_7_sonnet

guard model.hasConverseModality() else {
throw MyError.incorrectModality("\(model.name) does not support converse")
}

// a reusable var to build the requests
var request: ConverseRequestBuilder? = nil

// we keep track of the history of the conversation
var history: [Message] = []

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

print("\nYou: ", terminator: "")
let prompt: String = readLine() ?? ""
guard prompt.isEmpty == false else { continue }
if ["exit", "quit"].contains(prompt.lowercased()) {
break
}

print("\nAssistant: ", terminator: "")

if request == nil {
// create a new request
request = try ConverseRequestBuilder(with: model)
.withPrompt(prompt)
} else {
// append the new prompt to the existing request
// ConverseRequestBuilder is stateles, it doesn't keep track of the history
Copy link

Copilot AI Jun 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in comment: 'stateles' should be 'stateless'.

Suggested change
// ConverseRequestBuilder is stateles, it doesn't keep track of the history
// ConverseRequestBuilder is stateless, it doesn't keep track of the history

Copilot uses AI. Check for mistakes.
request = try ConverseRequestBuilder(from: request!)
Copy link

Copilot AI Jun 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Force-unwrapping 'request' may cause a runtime crash if it's nil; consider using a guard let to safely unwrap or refactor the logic to avoid optionals here.

Suggested change
request = try ConverseRequestBuilder(from: request!)
guard let existingRequest = request else {
throw MyError.incorrectModality("Request is unexpectedly nil")
}
request = try ConverseRequestBuilder(from: existingRequest)

Copilot uses AI. Check for mistakes.
.withHistory(history)
.withPrompt(prompt)
}

// keep track of the history of the conversation
history.append(Message(prompt))

// send the request
let reply = try await bedrock.converseStream(with: request!)
Copy link

Copilot AI Jun 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Force-unwrapping 'request' here may crash if nil; ensure it's non-nil with a guard or unwrap earlier.

Suggested change
let reply = try await bedrock.converseStream(with: request!)
guard let unwrappedRequest = request else {
throw MyError.incorrectModality("Request is unexpectedly nil")
}
let reply = try await bedrock.converseStream(with: unwrappedRequest)

Copilot uses AI. Check for mistakes.

for try await element in reply.stream {
// process the stream elements
switch element {
case .text(_, let text):
print(text, terminator: "")
case .messageComplete(let message):
print("\n")
history.append(message)
default:
break
}
}
}
}

enum MyError: Error {
case incorrectModality(String)
}
}
Loading