// // 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? } }