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>
58 lines
1.9 KiB
Swift
58 lines
1.9 KiB
Swift
import Foundation
|
|
|
|
// MARK: - Skill 数据模型
|
|
//
|
|
// 文件驱动架构:
|
|
// - SKILL.md 定义 Skill 元数据 + 指令体(热更新)
|
|
// - ToolRegistry.swift 注册原生工具实现(编译时)
|
|
// - SkillLoader.swift 解析 SKILL.md (无状态) + SkillRegistry 注册/查询 (有状态)
|
|
//
|
|
// 以下仅为给 UI 和 PromptBuilder 使用的精简数据结构。
|
|
|
|
// MARK: - Skill 条目(给 UI 管理用)
|
|
|
|
struct ToolInfo: Equatable {
|
|
let name: String
|
|
let description: String
|
|
let parameters: String
|
|
}
|
|
|
|
struct SkillEntry: Identifiable {
|
|
let id: String // skill directory name, e.g. "clipboard"
|
|
var name: String // display name, e.g. "Clipboard"
|
|
var description: String
|
|
var icon: String
|
|
var type: SkillType
|
|
var samplePrompt: String
|
|
var tools: [ToolInfo] = []
|
|
var isEnabled: Bool = true
|
|
var filePath: URL? // SKILL.md 路径(用于编辑)
|
|
|
|
/// 从 SkillDefinition 转换
|
|
init(from def: SkillDefinition, registry: ToolRegistry) {
|
|
self.id = def.id
|
|
self.name = def.metadata.displayName
|
|
self.description = def.metadata.description
|
|
self.icon = def.metadata.icon
|
|
self.type = def.metadata.type
|
|
self.samplePrompt = def.metadata.examples.first?.query ?? ""
|
|
self.isEnabled = def.isEnabled
|
|
self.filePath = def.filePath
|
|
self.tools = def.metadata.allowedTools.compactMap { toolName in
|
|
guard let tool = registry.find(name: toolName) else { return nil }
|
|
return ToolInfo(name: tool.name, description: tool.description, parameters: tool.parameters)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - SkillInfo(给 PromptBuilder 用的精简描述)
|
|
|
|
struct SkillInfo {
|
|
let name: String // skill id, e.g. "clipboard"
|
|
let description: String
|
|
var displayName: String = ""
|
|
var icon: String = "wrench"
|
|
var type: SkillType = .device
|
|
var samplePrompt: String = ""
|
|
}
|