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
Next Next commit
Change initializer apiKey parameter to be a token provider closure
  • Loading branch information
mattt committed Oct 28, 2025
commit 936405f9b85a1a8c0ed354d84bc4aac29dc785ea
39 changes: 22 additions & 17 deletions Sources/AnyLanguageModel/Models/AnthropicLanguageModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ public struct AnthropicLanguageModel: LanguageModel {
/// The base URL for the API endpoint.
public let baseURL: URL

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

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

private let urlSession: URLSession
private let headers: [String: String]

/// Creates an Anthropic language model.
///
/// - Parameters:
/// - baseURL: The base URL for the API endpoint. Defaults to Anthropic's official API.
/// - apiKey: Your Anthropic API key.
/// - apiKey: Your Anthropic API key or a closure that returns it.
/// - apiVersion: The API version to use for requests. Defaults to `2023-06-01`.
/// - betas: Optional beta version(s) of the API to use.
/// - model: The model identifier (for example, "claude-3-5-sonnet-20241022").
/// - session: The URL session to use for network requests.
public init(
baseURL: URL = defaultBaseURL,
apiKey: String,
apiKey tokenProvider: @escaping @autoclosure @Sendable () -> String,
apiVersion: String = defaultAPIVersion,
betas: [String]? = nil,
model: String,
Expand All @@ -79,22 +78,11 @@ public struct AnthropicLanguageModel: LanguageModel {
}

self.baseURL = baseURL
self.apiKey = apiKey
self.tokenProvider = tokenProvider
self.apiVersion = apiVersion
self.betas = betas
self.model = model
self.urlSession = session

var headers: [String: String] = [
"x-api-key": apiKey,
"anthropic-version": apiVersion,
]

if let betas = betas, !betas.isEmpty {
headers["anthropic-beta"] = betas.joined(separator: ",")
}

self.headers = headers
}

public func respond<Content>(
Expand Down Expand Up @@ -128,6 +116,15 @@ public struct AnthropicLanguageModel: LanguageModel {

let url = baseURL.appendingPathComponent("v1/messages")
let body = try JSONEncoder().encode(params)

var headers: [String: String] = [
"x-api-key": tokenProvider(),
"anthropic-version": apiVersion,
]
if let betas = betas, !betas.isEmpty {
headers["anthropic-beta"] = betas.joined(separator: ",")
}

let message: AnthropicMessageResponse = try await urlSession.fetch(
.post,
url: url,
Expand Down Expand Up @@ -205,6 +202,14 @@ public struct AnthropicLanguageModel: LanguageModel {

let body = try JSONEncoder().encode(params)

var headers: [String: String] = [
"x-api-key": tokenProvider(),
"anthropic-version": apiVersion,
]
if let betas = betas, !betas.isEmpty {
headers["anthropic-beta"] = betas.joined(separator: ",")
}

// Stream server-sent events from Anthropic API
let events: AsyncThrowingStream<AnthropicStreamEvent, any Error> =
urlSession
Expand Down
18 changes: 9 additions & 9 deletions Sources/AnyLanguageModel/Models/OpenAILanguageModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ public struct OpenAILanguageModel: LanguageModel {
/// The base URL for the API endpoint.
public let baseURL: URL

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

/// The model identifier to use for generation.
public let model: String
Expand All @@ -52,13 +52,13 @@ public struct OpenAILanguageModel: LanguageModel {
///
/// - Parameters:
/// - baseURL: The base URL for the API endpoint. Defaults to OpenAI's official API.
/// - apiKey: Your OpenAI API key.
/// - apiKey: Your OpenAI API key or a closure that returns it.
/// - model: The model identifier (for example, "gpt-4" or "gpt-3.5-turbo").
/// - apiVariant: The API variant to use. Defaults to `.chatCompletions`.
/// - session: The URL session to use for network requests.
public init(
baseURL: URL = defaultBaseURL,
apiKey: String,
apiKey tokenProvider: @escaping @autoclosure @Sendable () -> String,
model: String,
apiVariant: APIVariant = .chatCompletions,
session: URLSession = URLSession(configuration: .default)
Expand All @@ -69,7 +69,7 @@ public struct OpenAILanguageModel: LanguageModel {
}

self.baseURL = baseURL
self.apiKey = apiKey
self.tokenProvider = tokenProvider
self.model = model
self.apiVariant = apiVariant
self.urlSession = session
Expand Down Expand Up @@ -140,7 +140,7 @@ public struct OpenAILanguageModel: LanguageModel {
.post,
url: url,
headers: [
"Authorization": "Bearer \(apiKey)"
"Authorization": "Bearer \(tokenProvider())"
],
body: body
)
Expand Down Expand Up @@ -193,7 +193,7 @@ public struct OpenAILanguageModel: LanguageModel {
.post,
url: url,
headers: [
"Authorization": "Bearer \(apiKey)"
"Authorization": "Bearer \(tokenProvider())"
],
body: body
)
Expand Down Expand Up @@ -269,7 +269,7 @@ public struct OpenAILanguageModel: LanguageModel {
.post,
url: url,
headers: [
"Authorization": "Bearer \(apiKey)"
"Authorization": "Bearer \(tokenProvider())"
],
body: body
)
Expand Down Expand Up @@ -332,7 +332,7 @@ public struct OpenAILanguageModel: LanguageModel {
.post,
url: url,
headers: [
"Authorization": "Bearer \(apiKey)"
"Authorization": "Bearer \(tokenProvider())"
],
body: body
)
Expand Down
1 change: 0 additions & 1 deletion Tests/AnyLanguageModelTests/OpenAILanguageModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ struct OpenAILanguageModelTests {
for apiVariant in [OpenAILanguageModel.APIVariant.chatCompletions, .responses] {
let model = OpenAILanguageModel(apiKey: "test-key", model: "test-model", apiVariant: apiVariant)
#expect(model.apiVariant == apiVariant)
#expect(model.apiKey == "test-key")
#expect(model.model == "test-model")
}
}
Expand Down