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>
66 lines
2.1 KiB
Swift
66 lines
2.1 KiB
Swift
import Foundation
|
|
|
|
// MARK: - ModelPaths
|
|
//
|
|
// 静态路径解析: bundle 路径优先, 否则落到 Documents/models/<directoryName>。
|
|
// 从 MLXLocalLLMService.swift L278-332 原样迁移。
|
|
|
|
enum ModelPaths {
|
|
|
|
static func resolve(for model: BundledModelOption) -> URL {
|
|
if let bundled = bundled(for: model) {
|
|
return bundled
|
|
}
|
|
return downloaded(for: model)
|
|
}
|
|
|
|
static func documentsRoot() -> URL {
|
|
let documentsPath = FileManager.default.urls(
|
|
for: .documentDirectory,
|
|
in: .userDomainMask
|
|
).first!
|
|
return documentsPath.appendingPathComponent("models", isDirectory: true)
|
|
}
|
|
|
|
static func downloaded(for model: BundledModelOption) -> URL {
|
|
documentsRoot().appendingPathComponent(model.directoryName, isDirectory: true)
|
|
}
|
|
|
|
static func partial(for model: BundledModelOption) -> URL {
|
|
documentsRoot().appendingPathComponent("\(model.directoryName).partial", isDirectory: true)
|
|
}
|
|
|
|
static func bundled(for model: BundledModelOption) -> URL? {
|
|
guard let resourceURL = Bundle.main.resourceURL else { return nil }
|
|
|
|
let directBundleDir = resourceURL.appendingPathComponent(
|
|
model.directoryName,
|
|
isDirectory: true
|
|
)
|
|
if hasRequiredFiles(model, at: directBundleDir) {
|
|
return directBundleDir
|
|
}
|
|
|
|
let nestedBundleDir = resourceURL
|
|
.appendingPathComponent("Models", isDirectory: true)
|
|
.appendingPathComponent(model.directoryName, isDirectory: true)
|
|
if hasRequiredFiles(model, at: nestedBundleDir) {
|
|
return nestedBundleDir
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
static func hasRequiredFiles(_ model: BundledModelOption, at directory: URL) -> Bool {
|
|
let fm = FileManager.default
|
|
var isDirectory: ObjCBool = false
|
|
guard fm.fileExists(atPath: directory.path, isDirectory: &isDirectory),
|
|
isDirectory.boolValue else {
|
|
return false
|
|
}
|
|
return model.requiredFiles.allSatisfy { file in
|
|
fm.fileExists(atPath: directory.appendingPathComponent(file).path)
|
|
}
|
|
}
|
|
}
|