78 lines
3.0 KiB
Swift
78 lines
3.0 KiB
Swift
// SPDX-FileCopyrightText: 2026 Kaede Akino
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
import AgentKit
|
|
import Foundation
|
|
|
|
/// Form hints are also concrete save defaults, never implicit runtime destinations.
|
|
nonisolated extension ModelAPIType {
|
|
public var defaultProviderName: String {
|
|
switch self {
|
|
case .automatic, .chatCompletions, .responses: "OpenAI"
|
|
case .messages: "Anthropic"
|
|
case .generateContent: "Google"
|
|
}
|
|
}
|
|
|
|
public var defaultURL: String {
|
|
switch self {
|
|
case .automatic: "https://api.openai.com/v1/responses"
|
|
case .chatCompletions, .responses: "https://api.openai.com/v1"
|
|
case .messages: "https://api.anthropic.com/v1"
|
|
case .generateContent: "https://generativelanguage.googleapis.com/v1beta"
|
|
}
|
|
}
|
|
}
|
|
|
|
nonisolated extension ProviderEndpoint {
|
|
public var withDefaultURL: ProviderEndpoint {
|
|
var value = self
|
|
if value.url.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
value.url = apiType.defaultURL
|
|
}
|
|
return value
|
|
}
|
|
}
|
|
|
|
public nonisolated enum ModelProviderForm {
|
|
public static func normalized(_ provider: ModelProvider, endpoint: ProviderEndpoint) -> (
|
|
provider: ModelProvider, endpoint: ProviderEndpoint
|
|
) {
|
|
var provider = provider
|
|
let endpoint = endpoint.withDefaultURL
|
|
provider.name = provider.name.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
provider.name = SavedName.resolve(provider.name, default: endpoint.apiType.defaultProviderName)
|
|
provider.models = provider.models.map(\.savingName)
|
|
provider.inferenceURL = endpoint.requestURL
|
|
provider.baseURL = endpoint.baseURL
|
|
provider.apiFormat = endpoint.format
|
|
provider.usesVertex = provider.usesVertex && endpoint.apiType == .generateContent
|
|
provider.vertex.projectID = provider.vertex.projectID.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
provider.vertex.location = provider.vertex.location.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
if provider.vertex.location.isEmpty { provider.vertex.location = "global" }
|
|
return (provider, endpoint)
|
|
}
|
|
|
|
public static func hasValidAddress(_ provider: ModelProvider) -> Bool {
|
|
if provider.usesVertexEndpoint {
|
|
return isPathIdentifier(provider.vertex.projectID) && isPathIdentifier(provider.vertex.location)
|
|
}
|
|
return isValidURL(provider.inferenceURL)
|
|
}
|
|
|
|
public static func isValidURL(_ value: String) -> Bool {
|
|
guard let url = URL(string: value),
|
|
["http", "https"].contains(url.scheme?.lowercased() ?? ""),
|
|
let host = url.host, !host.isEmpty
|
|
else { return false }
|
|
return true
|
|
}
|
|
|
|
private static func isPathIdentifier(_ value: String) -> Bool {
|
|
!value.isEmpty
|
|
&& value.utf8.allSatisfy {
|
|
(48...57).contains($0) || (65...90).contains($0) || (97...122).contains($0) || $0 == 45
|
|
}
|
|
}
|
|
}
|