|
| 1 | +// |
| 2 | +// MiniMaxPromptPreferences.swift |
| 3 | +// Dayflow |
| 4 | +// |
| 5 | +// User-overridable prompt blocks for the MiniMax provider. Defaults are |
| 6 | +// intentionally aligned with the Ollama prompt defaults so behaviour stays |
| 7 | +// consistent across providers. Users can customise either block independently. |
| 8 | +// |
| 9 | + |
| 10 | +import Foundation |
| 11 | + |
| 12 | +struct MiniMaxPromptOverrides: Codable, Equatable { |
| 13 | + var summaryBlock: String? |
| 14 | + var titleBlock: String? |
| 15 | + |
| 16 | + var isEmpty: Bool { |
| 17 | + [summaryBlock, titleBlock].allSatisfy { value in |
| 18 | + let trimmed = value?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" |
| 19 | + return trimmed.isEmpty |
| 20 | + } |
| 21 | + } |
| 22 | +} |
| 23 | + |
| 24 | +enum MiniMaxPromptPreferences { |
| 25 | + private static let overridesKey = "minimaxPromptOverrides" |
| 26 | + private static let store = UserDefaults.standard |
| 27 | + |
| 28 | + static func load() -> MiniMaxPromptOverrides { |
| 29 | + guard let data = store.data(forKey: overridesKey) else { |
| 30 | + return MiniMaxPromptOverrides() |
| 31 | + } |
| 32 | + guard let overrides = try? JSONDecoder().decode(MiniMaxPromptOverrides.self, from: data) |
| 33 | + else { |
| 34 | + return MiniMaxPromptOverrides() |
| 35 | + } |
| 36 | + return overrides |
| 37 | + } |
| 38 | + |
| 39 | + static func save(_ overrides: MiniMaxPromptOverrides) { |
| 40 | + guard let data = try? JSONEncoder().encode(overrides) else { return } |
| 41 | + store.set(data, forKey: overridesKey) |
| 42 | + } |
| 43 | + |
| 44 | + static func reset() { |
| 45 | + store.removeObject(forKey: overridesKey) |
| 46 | + } |
| 47 | +} |
| 48 | + |
| 49 | +enum MiniMaxPromptDefaults { |
| 50 | + static let summaryBlock = """ |
| 51 | + SUMMARY GUIDELINES: |
| 52 | + - Write in first person without using "I" (like a personal journal entry) |
| 53 | + - 2-3 sentences maximum |
| 54 | + - Include specific details (app names, search topics, etc.) |
| 55 | + - Natural, conversational tone |
| 56 | +
|
| 57 | + GOOD EXAMPLES: |
| 58 | + "Managed Mac system preferences focusing on software updates and accessibility settings. Browsed Chrome searching for iPhone wireless charging info while |
| 59 | + checking Twitter and Slack messages." |
| 60 | +
|
| 61 | + "Configured GitHub Actions pipeline for automated testing. Quick Slack check interrupted focus, then back to debugging deployment issues." |
| 62 | +
|
| 63 | + "Researched React performance optimization techniques in Chrome, reading articles about useMemo patterns. Switched between documentation tabs and took notes in |
| 64 | + Notion about component re-rendering." |
| 65 | +
|
| 66 | + "Updated Xcode project dependencies and resolved build errors in SwiftUI views. Tested app on simulator while responding to client messages about timeline |
| 67 | + changes." |
| 68 | +
|
| 69 | + "Browsed Instagram and TikTok while listening to Spotify playlist. Responded to personal messages on WhatsApp about weekend plans." |
| 70 | +
|
| 71 | + BAD EXAMPLES: |
| 72 | + - "The user did various computer activities" (too vague, wrong perspective, never say the user) |
| 73 | + - "I was working on my computer doing different tasks" (uses "I", not specific) |
| 74 | + - "Spent time on multiple applications and websites" (generic, no details) |
| 75 | + """ |
| 76 | + |
| 77 | + static let titleBlock = """ |
| 78 | + Write one activity title for a 15-minute window using ONLY the observations. |
| 79 | + Rules: |
| 80 | + - 5-10 words, natural and specific, single line |
| 81 | + - Choose the dominant activity (most time), not necessarily the first |
| 82 | + - Ignore brief interruptions (<3 minutes) |
| 83 | + - Include a second activity only if both take ~5+ minutes |
| 84 | + - If 3+ unrelated activities appear, output exactly: "Scattered apps and sites" |
| 85 | + - Prefer proper nouns/topics (Bookface, Claude, League of Legends, Paul Graham, etc.) |
| 86 | + - Never use: worked on, looked at, handled, various, some, multiple, browsing, browse, multitasking, tabs, brief, quick, short |
| 87 | + - Do NOT use the word "browsing"; use "scrolling" or "reading" instead |
| 88 | + - Avoid long lists; no more than one conjunction |
| 89 | + - Return only the title text (no quotes, no JSON) |
| 90 | + """ |
| 91 | +} |
| 92 | + |
| 93 | +struct MiniMaxPromptSections { |
| 94 | + let summary: String |
| 95 | + let title: String |
| 96 | + |
| 97 | + init(overrides: MiniMaxPromptOverrides) { |
| 98 | + self.summary = MiniMaxPromptSections.compose( |
| 99 | + defaultBlock: MiniMaxPromptDefaults.summaryBlock, custom: overrides.summaryBlock) |
| 100 | + self.title = MiniMaxPromptSections.compose( |
| 101 | + defaultBlock: MiniMaxPromptDefaults.titleBlock, custom: overrides.titleBlock) |
| 102 | + } |
| 103 | + |
| 104 | + private static func compose(defaultBlock: String, custom: String?) -> String { |
| 105 | + let trimmed = custom?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" |
| 106 | + return trimmed.isEmpty ? defaultBlock : trimmed |
| 107 | + } |
| 108 | +} |
0 commit comments