Skip to content

Commit 13e1b29

Browse files
authored
Change initializer apiKey parameter to be a token provider closure (#6)
* Change initializer apiKey parameter to be a token provider closure * Define helper method for constructing request headers
1 parent f9bd55d commit 13e1b29

File tree

3 files changed

+33
-28
lines changed

3 files changed

+33
-28
lines changed

Sources/AnyLanguageModel/Models/AnthropicLanguageModel.swift

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ public struct AnthropicLanguageModel: LanguageModel {
4141
/// The base URL for the API endpoint.
4242
public let baseURL: URL
4343

44-
/// The API key for authentication.
45-
public let apiKey: String
44+
/// The closure providing the API key for authentication.
45+
private let tokenProvider: @Sendable () -> String
4646

4747
/// The API version to use for requests.
4848
public let apiVersion: String
@@ -54,20 +54,19 @@ public struct AnthropicLanguageModel: LanguageModel {
5454
public let model: String
5555

5656
private let urlSession: URLSession
57-
private let headers: [String: String]
5857

5958
/// Creates an Anthropic language model.
6059
///
6160
/// - Parameters:
6261
/// - baseURL: The base URL for the API endpoint. Defaults to Anthropic's official API.
63-
/// - apiKey: Your Anthropic API key.
62+
/// - apiKey: Your Anthropic API key or a closure that returns it.
6463
/// - apiVersion: The API version to use for requests. Defaults to `2023-06-01`.
6564
/// - betas: Optional beta version(s) of the API to use.
6665
/// - model: The model identifier (for example, "claude-3-5-sonnet-20241022").
6766
/// - session: The URL session to use for network requests.
6867
public init(
6968
baseURL: URL = defaultBaseURL,
70-
apiKey: String,
69+
apiKey tokenProvider: @escaping @autoclosure @Sendable () -> String,
7170
apiVersion: String = defaultAPIVersion,
7271
betas: [String]? = nil,
7372
model: String,
@@ -79,22 +78,11 @@ public struct AnthropicLanguageModel: LanguageModel {
7978
}
8079

8180
self.baseURL = baseURL
82-
self.apiKey = apiKey
81+
self.tokenProvider = tokenProvider
8382
self.apiVersion = apiVersion
8483
self.betas = betas
8584
self.model = model
8685
self.urlSession = session
87-
88-
var headers: [String: String] = [
89-
"x-api-key": apiKey,
90-
"anthropic-version": apiVersion,
91-
]
92-
93-
if let betas = betas, !betas.isEmpty {
94-
headers["anthropic-beta"] = betas.joined(separator: ",")
95-
}
96-
97-
self.headers = headers
9886
}
9987

10088
public func respond<Content>(
@@ -109,6 +97,9 @@ public struct AnthropicLanguageModel: LanguageModel {
10997
fatalError("AnthropicLanguageModel only supports generating String content")
11098
}
11199

100+
let url = baseURL.appendingPathComponent("v1/messages")
101+
let headers = buildHeaders()
102+
112103
let messages = [
113104
AnthropicMessage(role: .user, content: [.text(.init(text: prompt.description))])
114105
]
@@ -126,8 +117,8 @@ public struct AnthropicLanguageModel: LanguageModel {
126117
options: options
127118
)
128119

129-
let url = baseURL.appendingPathComponent("v1/messages")
130120
let body = try JSONEncoder().encode(params)
121+
131122
let message: AnthropicMessageResponse = try await urlSession.fetch(
132123
.post,
133124
url: url,
@@ -189,6 +180,8 @@ public struct AnthropicLanguageModel: LanguageModel {
189180
continuation in
190181
let task = Task { @Sendable in
191182
do {
183+
let headers = buildHeaders()
184+
192185
// Convert available tools to Anthropic format
193186
let anthropicTools: [AnthropicTool] = try session.tools.map { tool in
194187
try convertToolToAnthropicFormat(tool)
@@ -247,6 +240,19 @@ public struct AnthropicLanguageModel: LanguageModel {
247240

248241
return LanguageModelSession.ResponseStream(stream: stream)
249242
}
243+
244+
private func buildHeaders() -> [String: String] {
245+
var headers: [String: String] = [
246+
"x-api-key": tokenProvider(),
247+
"anthropic-version": apiVersion,
248+
]
249+
250+
if let betas = betas, !betas.isEmpty {
251+
headers["anthropic-beta"] = betas.joined(separator: ",")
252+
}
253+
254+
return headers
255+
}
250256
}
251257

252258
// MARK: - Conversions

Sources/AnyLanguageModel/Models/OpenAILanguageModel.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ public struct OpenAILanguageModel: LanguageModel {
3737
/// The base URL for the API endpoint.
3838
public let baseURL: URL
3939

40-
/// The API key for authentication.
41-
public let apiKey: String
40+
/// The closure providing the API key for authentication.
41+
private let tokenProvider: @Sendable () -> String
4242

4343
/// The model identifier to use for generation.
4444
public let model: String
@@ -52,13 +52,13 @@ public struct OpenAILanguageModel: LanguageModel {
5252
///
5353
/// - Parameters:
5454
/// - baseURL: The base URL for the API endpoint. Defaults to OpenAI's official API.
55-
/// - apiKey: Your OpenAI API key.
55+
/// - apiKey: Your OpenAI API key or a closure that returns it.
5656
/// - model: The model identifier (for example, "gpt-4" or "gpt-3.5-turbo").
5757
/// - apiVariant: The API variant to use. Defaults to `.chatCompletions`.
5858
/// - session: The URL session to use for network requests.
5959
public init(
6060
baseURL: URL = defaultBaseURL,
61-
apiKey: String,
61+
apiKey tokenProvider: @escaping @autoclosure @Sendable () -> String,
6262
model: String,
6363
apiVariant: APIVariant = .chatCompletions,
6464
session: URLSession = URLSession(configuration: .default)
@@ -69,7 +69,7 @@ public struct OpenAILanguageModel: LanguageModel {
6969
}
7070

7171
self.baseURL = baseURL
72-
self.apiKey = apiKey
72+
self.tokenProvider = tokenProvider
7373
self.model = model
7474
self.apiVariant = apiVariant
7575
self.urlSession = session
@@ -140,7 +140,7 @@ public struct OpenAILanguageModel: LanguageModel {
140140
.post,
141141
url: url,
142142
headers: [
143-
"Authorization": "Bearer \(apiKey)"
143+
"Authorization": "Bearer \(tokenProvider())"
144144
],
145145
body: body
146146
)
@@ -193,7 +193,7 @@ public struct OpenAILanguageModel: LanguageModel {
193193
.post,
194194
url: url,
195195
headers: [
196-
"Authorization": "Bearer \(apiKey)"
196+
"Authorization": "Bearer \(tokenProvider())"
197197
],
198198
body: body
199199
)
@@ -269,7 +269,7 @@ public struct OpenAILanguageModel: LanguageModel {
269269
.post,
270270
url: url,
271271
headers: [
272-
"Authorization": "Bearer \(apiKey)"
272+
"Authorization": "Bearer \(tokenProvider())"
273273
],
274274
body: body
275275
)
@@ -332,7 +332,7 @@ public struct OpenAILanguageModel: LanguageModel {
332332
.post,
333333
url: url,
334334
headers: [
335-
"Authorization": "Bearer \(apiKey)"
335+
"Authorization": "Bearer \(tokenProvider())"
336336
],
337337
body: body
338338
)

Tests/AnyLanguageModelTests/OpenAILanguageModelTests.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ struct OpenAILanguageModelTests {
1818
for apiVariant in [OpenAILanguageModel.APIVariant.chatCompletions, .responses] {
1919
let model = OpenAILanguageModel(apiKey: "test-key", model: "test-model", apiVariant: apiVariant)
2020
#expect(model.apiVariant == apiVariant)
21-
#expect(model.apiKey == "test-key")
2221
#expect(model.model == "test-model")
2322
}
2423
}

0 commit comments

Comments
 (0)