Files
PhoneClaw/LLM/MLX/Installation/ModelInstallState.swift
KellyvvandClaude Opus 4.6 779be78843 refactor: split MLX service, introduce skill type & runtime budgets
Major restructuring of the agent stack to support content-type skills,
data-driven runtime budgets, and the new translate skill.

LLM/MLX/
- Split MLXLocalLLMService.swift (1500 → 700 lines) into Core/, Gemma4/,
  Installation/ subdirectories
- New BundledModel: LinearBudgetFormula replaces tier tables, model
  parameters fully data-driven via ModelRuntimeProfile
- New RuntimeBudgets: pure functions for text/thinking/multimodal budgets,
  zero hardcoded model IDs
- Drop adjustedTextOutputTokens prepared-subtraction (chunked prefill made
  it obsolete and triggered false-positive truncation on long prompts)

Skills/
- Add SkillType (.device / .content) metadata, parsed from SKILL.md
  frontmatter
- New translate skill (type: content, no tools) with anaphora handling
- Trim translate (4563→1804), device (2357→1680), calendar (2169→1790)
- contacts SKILL.md adds multi-turn disambiguation + cancel guidance,
  semantics owned by body not framework
- SkillRegistry validates allowed-tools against ToolRegistry at load time

Agent/
- ToolCallParser becomes registry-aware: drops tool_call names not in
  ToolRegistry or protocol set (load_skill / list_skills); ToolRegistry
  is now the single white list, validation moves to parser boundary
- Router sticky routing uses semantic user-turn boundary instead of
  fixed suffix(4); correctly handles long agent loops
- AgentEngine system prompt template differentiates device vs content
  skills with explicit invocation rules
- SYSPROMPT.md auto-migration: old single-placeholder template backed up
  to .bak when new dual-placeholder is detected
- PromptBuilder.buildLoadedSkillPrompt receives availableTools list,
  explicitly tells model which tools (or none) are callable

Tools/
- ToolRegistry split into Helpers/Stores/AppPermissions/Handlers/* for
  separation of concerns
- Mark Tools/Handlers/* with explicit allowed argument signatures

UI/
- Add long-press copy contextMenu on user and assistant bubbles
- Enable .textSelection on user bubble

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 09:22:20 +08:00

68 lines
2.2 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Foundation
// MARK: - Install State
public enum ModelInstallState: Equatable, Sendable {
case notInstalled
case checkingSource
case downloading(completedFiles: Int, totalFiles: Int, currentFile: String)
case downloaded
case bundled
case failed(String)
}
public struct ModelDownloadMetrics: Equatable, Sendable {
public let bytesReceived: Int64
public let totalBytes: Int64?
public let bytesPerSecond: Double?
public let sourceLabel: String?
}
// MARK: - Errors
enum MLXError: LocalizedError {
case modelNotLoaded
case modelDirectoryMissing(String)
case gpuExecutionRequiresForeground
case multimodalMemoryRisk(model: String, headroomMB: Int, recommendation: String)
var errorDescription: String? {
switch self {
case .modelNotLoaded:
return "MLX model not loaded. Call load() first."
case .modelDirectoryMissing(let modelName):
return "\(modelName) 模型文件不存在,请先在配置页下载或重新安装。"
case .gpuExecutionRequiresForeground:
return "应用进入后台时,iPhone 不允许继续提交 GPU 推理任务。"
case .multimodalMemoryRisk(let model, let headroomMB, let recommendation):
return "\(model) 当前剩余内存仅约 \(headroomMB) MB,继续处理图片/音频很可能被系统直接杀掉。\(recommendation)"
}
}
}
enum DownloadError: LocalizedError {
case invalidURL(String)
case invalidResponse
case httpStatus(Int)
var errorDescription: String? {
switch self {
case .invalidURL(let file):
return "无法构造下载链接:\(file)"
case .invalidResponse:
return "下载源响应无效"
case .httpStatus(let statusCode):
switch statusCode {
case 401, 403:
return "下载源拒绝访问(\(statusCode))"
case 404:
return "模型文件不存在(404)"
case 429:
return "下载过于频繁,请稍后重试(429)"
default:
return "下载失败,HTTP \(statusCode)"
}
}
}
}