feature/example
swift 154 lines 4.88 KB
Raw
1 import SwiftUI
2 import Onde
3
4 enum ExampleDefaults {
5 static let systemPrompt = "You are a helpful on-device assistant. Be concise, practical, and truthful."
6
7 static let promptSuggestions: [PromptSuggestion] = [
8 PromptSuggestion(
9 title: "Explain Onde",
10 prompt: "In simple terms, what is Onde Inference and when would I use it in an iOS app?"
11 ),
12 PromptSuggestion(
13 title: "SwiftUI idea",
14 prompt: "Give me three ideas for an on-device AI feature in a SwiftUI app."
15 ),
16 PromptSuggestion(
17 title: "Debugging help",
18 prompt: "What are the first things to check when a local iOS ML model fails to load?"
19 ),
20 PromptSuggestion(
21 title: "Prompt writing",
22 prompt: "Write a strong system prompt for an iOS coding assistant focused on SwiftUI."
23 )
24 ]
25 }
26
27 struct PromptSuggestion: Identifiable, Hashable {
28 let title: String
29 let prompt: String
30
31 var id: String { title }
32 }
33
34 enum SamplingPreset: String, CaseIterable, Identifiable {
35 case balanced
36 case deterministic
37 case mobile
38
39 var id: String { rawValue }
40
41 var title: String {
42 switch self {
43 case .balanced:
44 return "Balanced"
45 case .deterministic:
46 return "Deterministic"
47 case .mobile:
48 return "Fast / Mobile"
49 }
50 }
51
52 var subtitle: String {
53 switch self {
54 case .balanced:
55 return "General-purpose responses with a bit of creativity."
56 case .deterministic:
57 return "Best for reproducible, focused coding and analysis tasks."
58 case .mobile:
59 return "Shorter responses with lower latency on constrained devices."
60 }
61 }
62
63 var samplingConfig: SamplingConfig {
64 switch self {
65 case .balanced:
66 return defaultSamplingConfig()
67 case .deterministic:
68 return deterministicSamplingConfig()
69 case .mobile:
70 return mobileSamplingConfig()
71 }
72 }
73 }
74
75 struct ModelSettingsSheet: View {
76 let initialSystemPrompt: String
77 let initialSamplingPreset: SamplingPreset
78 let onSave: (String, SamplingPreset) -> Void
79
80 @Environment(\.dismiss) private var dismiss
81 @State private var systemPrompt: String
82 @State private var samplingPreset: SamplingPreset
83
84 init(
85 systemPrompt: String,
86 samplingPreset: SamplingPreset,
87 onSave: @escaping (String, SamplingPreset) -> Void
88 ) {
89 self.initialSystemPrompt = systemPrompt
90 self.initialSamplingPreset = samplingPreset
91 self.onSave = onSave
92 _systemPrompt = State(initialValue: systemPrompt)
93 _samplingPreset = State(initialValue: samplingPreset)
94 }
95
96 var body: some View {
97 NavigationStack {
98 Form {
99 Section {
100 TextEditor(text: $systemPrompt)
101 .frame(minHeight: 140)
102 } header: {
103 Text("System Prompt")
104 } footer: {
105 Text("This prompt applies to future generations. Leave it blank to clear the runtime system prompt.")
106 }
107
108 Section("Response Style") {
109 Picker("Sampling Preset", selection: $samplingPreset) {
110 ForEach(SamplingPreset.allCases) { preset in
111 VStack(alignment: .leading, spacing: 2) {
112 Text(preset.title)
113 Text(preset.subtitle)
114 .font(.caption)
115 .foregroundStyle(.secondary)
116 }
117 .tag(preset)
118 }
119 }
120 .pickerStyle(.inline)
121 }
122
123 Section("Current Configuration") {
124 LabeledContent("System prompt") {
125 Text(initialSystemPrompt.isEmpty ? "None" : "Custom")
126 .foregroundStyle(.secondary)
127 }
128
129 LabeledContent("Sampling preset") {
130 Text(initialSamplingPreset.title)
131 .foregroundStyle(.secondary)
132 }
133 }
134 }
135 .navigationTitle("Model Settings")
136 .navigationBarTitleDisplayMode(.inline)
137 .toolbar {
138 ToolbarItem(placement: .cancellationAction) {
139 Button("Cancel") {
140 dismiss()
141 }
142 }
143
144 ToolbarItem(placement: .confirmationAction) {
145 Button("Save") {
146 onSave(systemPrompt, samplingPreset)
147 dismiss()
148 }
149 }
150 }
151 }
152 .presentationDetents([.medium, .large])
153 }
154 }