Raw
1 //
2 // SitiDefaults.swift
3 // Siti AI visionOS
4 //
5
6 import Ed
7 import SwiftUI
8
9 /// Mirrors `CHAT_SYSTEM_PROMPT` in `src-tauri/src/chat/mod.rs` so the native
10 /// visionOS client and the Tauri desktop/mobile client present the same
11 /// assistant persona.
12 enum SitiDefaults {
13 static let systemPrompt = """
14 You are Siti, a friendly and capable personal assistant that runs entirely on the user's device. Nothing the user says ever leaves the device.
15
16 You help with everyday things: answering questions, brainstorming, drafting and editing text, summarising, explaining ideas in plain terms, and planning.
17
18 Keep replies clear and concise, and prefer plain language. If a request is ambiguous, ask one short question instead of guessing. Use a short list only when it genuinely helps, and go easy on emoji.
19
20 You run locally, so you may not know about very recent events. When you are not sure, say so instead of inventing an answer.
21 """
22
23 /// Qwen 2.5 3B (GGUF Q4_K_M), the default Onde picks on visionOS.
24 static let defaultModel = EdGGUFModelConfiguration(
25 modelID: "bartowski/Qwen2.5-3B-Instruct-GGUF",
26 files: ["Qwen2.5-3B-Instruct-Q4_K_M.gguf"],
27 displayName: "Qwen 2.5 3B",
28 approximateMemory: "~1.93 GB (GGUF Q4_K_M)"
29 )
30
31 static let promptSuggestions: [PromptSuggestion] = [
32 PromptSuggestion(
33 title: "How can I help?",
34 prompt: "What can you help me with on visionOS?"
35 ),
36 PromptSuggestion(
37 title: "Brainstorm",
38 prompt: "Give me three ideas for staying focused while working in a shared space."
39 ),
40 PromptSuggestion(
41 title: "Summarize",
42 prompt: "Explain the difference between a window and a volume in visionOS, in plain terms."
43 ),
44 PromptSuggestion(
45 title: "Draft something",
46 prompt: "Draft a short, friendly message asking a teammate to review my PR."
47 )
48 ]
49 }
50
51 struct PromptSuggestion: Identifiable, Hashable {
52 let title: String
53 let prompt: String
54
55 var id: String { title }
56 }
57
58 enum SamplingPreset: String, CaseIterable, Identifiable {
59 case balanced
60 case deterministic
61 case mobile
62
63 var id: String { rawValue }
64
65 var title: String {
66 switch self {
67 case .balanced:
68 return "Balanced"
69 case .deterministic:
70 return "Deterministic"
71 case .mobile:
72 return "Fast"
73 }
74 }
75
76 var subtitle: String {
77 switch self {
78 case .balanced:
79 return "General-purpose responses with a bit of creativity."
80 case .deterministic:
81 return "Reproducible, focused answers."
82 case .mobile:
83 return "Shorter responses with lower latency."
84 }
85 }
86
87 /// The same three profiles Onde ships (`SamplingConfig::default`,
88 /// `deterministic` and `mobile`).
89 var samplingConfig: EdSamplingConfiguration {
90 switch self {
91 case .balanced:
92 return EdSamplingConfiguration(temperature: 0.7, topP: 0.95, maxTokens: 512)
93 case .deterministic:
94 return EdSamplingConfiguration(temperature: 0.0, maxTokens: 512)
95 case .mobile:
96 return EdSamplingConfiguration(temperature: 0.7, topP: 0.95, maxTokens: 128)
97 }
98 }
99 }
100
101 /// Makes any `Error` `Identifiable` so it can drive a SwiftUI `.alert`.
102 struct IdentifiableError: Identifiable {
103 let id = UUID()
104 let underlying: Error
105
106 init(_ error: Error) {
107 self.underlying = error
108 }
109
110 var message: String {
111 underlying.localizedDescription
112 }
113 }