Initial commit
This commit is contained in:
313
oAI/Providers/OpenRouterModels.swift
Normal file
313
oAI/Providers/OpenRouterModels.swift
Normal file
@@ -0,0 +1,313 @@
|
||||
//
|
||||
// OpenRouterModels.swift
|
||||
// oAI
|
||||
//
|
||||
// OpenRouter API request and response models
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// MARK: - API Request
|
||||
|
||||
struct OpenRouterChatRequest: Codable {
|
||||
let model: String
|
||||
let messages: [APIMessage]
|
||||
var stream: Bool
|
||||
let maxTokens: Int?
|
||||
let temperature: Double?
|
||||
let topP: Double?
|
||||
let tools: [Tool]?
|
||||
let toolChoice: String?
|
||||
let modalities: [String]?
|
||||
|
||||
struct APIMessage: Codable {
|
||||
let role: String
|
||||
let content: MessageContent
|
||||
|
||||
enum MessageContent: Codable {
|
||||
case string(String)
|
||||
case array([ContentItem])
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
if let str = try? container.decode(String.self) {
|
||||
self = .string(str)
|
||||
} else if let arr = try? container.decode([ContentItem].self) {
|
||||
self = .array(arr)
|
||||
} else {
|
||||
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid content")
|
||||
}
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
switch self {
|
||||
case .string(let str):
|
||||
try container.encode(str)
|
||||
case .array(let arr):
|
||||
try container.encode(arr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ContentItem: Codable {
|
||||
case text(String)
|
||||
case image(ImageContent)
|
||||
|
||||
struct TextContent: Codable {
|
||||
let type: String // "text"
|
||||
let text: String
|
||||
}
|
||||
|
||||
struct ImageContent: Codable {
|
||||
let type: String // "image_url"
|
||||
let imageUrl: ImageURL
|
||||
|
||||
struct ImageURL: Codable {
|
||||
let url: String
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case type
|
||||
case imageUrl = "image_url"
|
||||
}
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
if let textContent = try? container.decode(TextContent.self), textContent.type == "text" {
|
||||
self = .text(textContent.text)
|
||||
} else if let image = try? container.decode(ImageContent.self) {
|
||||
self = .image(image)
|
||||
} else if let str = try? container.decode(String.self) {
|
||||
self = .text(str)
|
||||
} else {
|
||||
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid content item")
|
||||
}
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
switch self {
|
||||
case .text(let text):
|
||||
try container.encode(TextContent(type: "text", text: text))
|
||||
case .image(let image):
|
||||
try container.encode(image)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case model
|
||||
case messages
|
||||
case stream
|
||||
case maxTokens = "max_tokens"
|
||||
case temperature
|
||||
case topP = "top_p"
|
||||
case tools
|
||||
case toolChoice = "tool_choice"
|
||||
case modalities
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - API Response
|
||||
|
||||
struct OpenRouterChatResponse: Codable {
|
||||
let id: String
|
||||
let model: String
|
||||
let choices: [Choice]
|
||||
let usage: Usage?
|
||||
let created: Int
|
||||
|
||||
struct Choice: Codable {
|
||||
let index: Int
|
||||
let message: MessageContent
|
||||
let finishReason: String?
|
||||
|
||||
struct MessageContent: Codable {
|
||||
let role: String
|
||||
let content: String?
|
||||
let toolCalls: [APIToolCall]?
|
||||
let images: [ImageOutput]?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case role
|
||||
case content
|
||||
case toolCalls = "tool_calls"
|
||||
case images
|
||||
}
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case index
|
||||
case message
|
||||
case finishReason = "finish_reason"
|
||||
}
|
||||
}
|
||||
|
||||
struct ImageOutput: Codable {
|
||||
let imageUrl: ImageURL
|
||||
|
||||
struct ImageURL: Codable {
|
||||
let url: String
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case imageUrl = "image_url"
|
||||
}
|
||||
}
|
||||
|
||||
struct Usage: Codable {
|
||||
let promptTokens: Int
|
||||
let completionTokens: Int
|
||||
let totalTokens: Int
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case promptTokens = "prompt_tokens"
|
||||
case completionTokens = "completion_tokens"
|
||||
case totalTokens = "total_tokens"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Streaming Response
|
||||
|
||||
struct OpenRouterStreamChunk: Codable {
|
||||
let id: String
|
||||
let model: String
|
||||
let choices: [StreamChoice]
|
||||
let usage: OpenRouterChatResponse.Usage?
|
||||
|
||||
struct StreamChoice: Codable {
|
||||
let index: Int
|
||||
let delta: Delta
|
||||
let finishReason: String?
|
||||
|
||||
struct Delta: Codable {
|
||||
let role: String?
|
||||
let content: String?
|
||||
let images: [OpenRouterChatResponse.ImageOutput]?
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case index
|
||||
case delta
|
||||
case finishReason = "finish_reason"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Models List
|
||||
|
||||
struct OpenRouterModelsResponse: Codable {
|
||||
let data: [ModelData]
|
||||
|
||||
struct ModelData: Codable {
|
||||
let id: String
|
||||
let name: String
|
||||
let description: String?
|
||||
let contextLength: Int
|
||||
let pricing: PricingData
|
||||
let architecture: Architecture?
|
||||
let supportedParameters: [String]?
|
||||
let outputModalities: [String]?
|
||||
|
||||
struct PricingData: Codable {
|
||||
let prompt: String
|
||||
let completion: String
|
||||
}
|
||||
|
||||
struct Architecture: Codable {
|
||||
let modality: String?
|
||||
let tokenizer: String?
|
||||
let instructType: String?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case modality
|
||||
case tokenizer
|
||||
case instructType = "instruct_type"
|
||||
}
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case name
|
||||
case description
|
||||
case contextLength = "context_length"
|
||||
case pricing
|
||||
case architecture
|
||||
case supportedParameters = "supported_parameters"
|
||||
case outputModalities = "output_modalities"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Credits Response
|
||||
|
||||
struct OpenRouterCreditsResponse: Codable {
|
||||
let data: CreditsData
|
||||
|
||||
struct CreditsData: Codable {
|
||||
let totalCredits: Double?
|
||||
let totalUsage: Double?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case totalCredits = "total_credits"
|
||||
case totalUsage = "total_usage"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Tool Call Models
|
||||
|
||||
struct APIToolCall: Codable {
|
||||
let id: String
|
||||
let type: String
|
||||
let function: FunctionCall
|
||||
|
||||
struct FunctionCall: Codable {
|
||||
let name: String
|
||||
let arguments: String
|
||||
}
|
||||
}
|
||||
|
||||
/// Message shape for encoding assistant messages that contain tool calls
|
||||
struct AssistantToolCallMessage: Encodable {
|
||||
let role: String
|
||||
let content: String?
|
||||
let toolCalls: [APIToolCall]
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case role
|
||||
case content
|
||||
case toolCalls = "tool_calls"
|
||||
}
|
||||
}
|
||||
|
||||
/// Message shape for encoding tool result messages back to the API
|
||||
struct ToolResultMessage: Encodable {
|
||||
let role: String // "tool"
|
||||
let toolCallId: String
|
||||
let name: String
|
||||
let content: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case role
|
||||
case toolCallId = "tool_call_id"
|
||||
case name
|
||||
case content
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Error Response
|
||||
|
||||
struct OpenRouterErrorResponse: Codable {
|
||||
let error: ErrorDetail
|
||||
|
||||
struct ErrorDetail: Codable {
|
||||
let message: String
|
||||
let type: String?
|
||||
let code: String?
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user