spb/zyquo-agent Public MIT
The autonomous agent that actually operates your Mac — plans, runs real commands, verifies its own work.
Swift 94.7%
Shell 4.1%
Python 0.7%
Makefile 0.5%
1//2// AIModel.swift3// Zyquo Agent4//5// Author: Simon-Pierre Boucher6// Mail: contact@spboucher.ai7//8// Ported verbatim from Zyquo Cloud. Zyquo Agent adds the `agentCapable`9// marking (see AgentModelSupport.swift) on top of the identical catalog.10//1112import Foundation1314/// A chat-capable model offered by a provider. Instances come exclusively from15/// `ModelCatalog` (built-in data generated from docs/PROVIDERS.md, dynamic16/// `/models` refreshes, and user-defined custom models) — never hardcode these17/// in views or clients.18struct AIModel: Codable, Identifiable, Hashable {19 /// Exact model ID as sent in API requests (e.g. "gpt-5.6-terra").20 let id: String21 let provider: ProviderID22 /// Human-friendly name shown in the UI (e.g. "GPT-5.6 Terra").23 let displayName: String24 /// Context window in tokens.25 let contextWindow: Int26 /// Maximum output tokens, when documented.27 let maxOutputTokens: Int?28 let capabilities: ModelCapabilities29 let pricing: ModelPricing?30 let parameterSupport: ParameterSupport31 /// Deprecated or superseded models stay selectable but are ranked last and badged.32 var isLegacy: Bool = false33 /// Featured/flagship models surface at the top of pickers.34 var isRecommended: Bool = false35 /// Base URL override for user-defined custom models; nil for built-ins.36 var customBaseURL: URL? = nil3738 /// Short badge text for the model chip (e.g. "1M ctx").39 var contextBadge: String {40 switch contextWindow {41 case 1_000_000...: return "\(contextWindow / 1_000_000)M ctx"42 case 1_000...: return "\(contextWindow / 1_000)K ctx"43 default: return "\(contextWindow) ctx"44 }45 }46}4748/// What a model can do. Drives UI affordances (attach button, thinking section…)49/// and request construction.50struct ModelCapabilities: Codable, Hashable {51 /// Accepts image input.52 var vision: Bool = false53 /// Supports function calling / tools.54 var tools: Bool = false55 /// Produces reasoning/thinking output (shown in the collapsible section).56 var reasoning: Bool = false57 /// Supports SSE streaming (true for every catalog model; custom endpoints may vary).58 var streaming: Bool = true59 /// Supports JSON mode / structured output.60 var jsonMode: Bool = false61 /// Returns web-search citations (Perplexity sonar family).62 var citations: Bool = false63}6465/// USD per 1M tokens. Cached/tiered pricing is intentionally simplified to the66/// base rate — cost figures in the UI are labeled as estimates.67struct ModelPricing: Codable, Hashable {68 var inputPerMTok: Double69 var outputPerMTok: Double7071 /// Estimated cost in USD for a usage record.72 func cost(inputTokens: Int, outputTokens: Int) -> Double {73 (Double(inputTokens) * inputPerMTok + Double(outputTokens) * outputPerMTok) / 1_000_00074 }75}7677/// Which sampling/control parameters a model accepts. Providers reject requests78/// carrying unsupported parameters, so requests only include what's supported —79/// and Settings only shows sliders that apply.80struct ParameterSupport: Codable, Hashable {81 var temperature: Bool = true82 var topP: Bool = true83 var frequencyPenalty: Bool = false84 var presencePenalty: Bool = false85 /// Send "max_completion_tokens" instead of "max_tokens" (OpenAI reasoning models, Cerebras).86 var usesMaxCompletionTokens: Bool = false87 /// Accepts `reasoning_effort` (OpenAI, xAI, Mistral, DeepSeek, Kimi K-series, Cerebras…).88 var reasoningEffort: Bool = false89 /// Anthropic `thinking` / Qwen `enable_thinking` style explicit thinking toggle.90 var thinkingToggle: Bool = false91 /// Model rejects non-streaming calls (Qwen qwq/qvq, DashScope-hosted models92 /// on Together…) — `complete` aggregates a stream instead.93 var requiresStreaming: Bool = false9495 static let openAIDefault = ParameterSupport(frequencyPenalty: true, presencePenalty: true)96}9798/// Token usage reported by a provider for one exchange.99struct TokenUsage: Codable, Hashable {100 var inputTokens: Int = 0101 var outputTokens: Int = 0102 var reasoningTokens: Int? = nil103104 var totalTokens: Int { inputTokens + outputTokens }105106 static func + (lhs: TokenUsage, rhs: TokenUsage) -> TokenUsage {107 TokenUsage(108 inputTokens: lhs.inputTokens + rhs.inputTokens,109 outputTokens: lhs.outputTokens + rhs.outputTokens,110 reasoningTokens: (lhs.reasoningTokens ?? 0) + (rhs.reasoningTokens ?? 0) == 0111 ? nil : (lhs.reasoningTokens ?? 0) + (rhs.reasoningTokens ?? 0)112 )113 }114}115