-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
System Prompts #70
base: main
Are you sure you want to change the base?
System Prompts #70
Conversation
db3f201
to
0f14648
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
This PR implements a comprehensive system prompts feature with suggestion capabilities and keyboard shortcut integration.
- Added
SystemPromptSuggestionService
in/macos/Onit/Data/Services/SystemPromptSuggestionService.swift
that scores prompts based on app context and tags - Introduced
SwiftDataContainer
in/macos/Onit/Data/SwiftDataContainer.swift
for managing persistent storage of Chat and SystemPrompt models - Added new UI components in
/macos/Onit/UI/Prompt/SystemPrompt/
for managing and selecting system prompts - Centralized keyboard shortcut handling in
/macos/Onit/KeyboardShortcuts/KeyboardShortcutsManager.swift
- Improved cleanup script with safer file deletion logic in
/macos/cleanup_app.sh
💡 (2/5) Greptile learns from your feedback when you react with 👍/👎!
39 file(s) reviewed, 41 comment(s)
Edit PR Review Bot Settings | Greptile
macos/Onit/Data/Model/Chat.swift
Outdated
@@ -23,3 +23,7 @@ final class Chat { | |||
prompts.map { $0.fullText }.joined(separator: "\n") | |||
} | |||
} | |||
|
|||
extension Chat { | |||
@MainActor static let sample = Chat(prompts: [Prompt.sample]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Consider making sample private to prevent accidental usage in production code
@MainActor static let sample = Chat(prompts: [Prompt.sample]) | |
@MainActor private static let sample = Chat(prompts: [Prompt.sample]) |
"properties" : { | ||
"preserves-vector-representation" : true | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Missing 'template-rendering-intent': 'template' property that's present in pencil.imageset. This icon won't adapt to UI color schemes like dark/light mode.
"properties" : { | |
"preserves-vector-representation" : true | |
} | |
"properties" : { | |
"preserves-vector-representation" : true, | |
"template-rendering-intent" : "template" | |
} |
init() { | ||
KeyboardShortcuts.onKeyUp(for: .launch) { [weak model] in | ||
let eventProperties: [String: Any] = [ | ||
"app_hidden": model?.panel == nil, | ||
"highlight_hint_visible": HighlightHintWindowController.shared.isVisible(), | ||
"highlight_hint_mode": FeatureFlagManager.shared.highlightHintMode, | ||
] | ||
PostHogSDK.shared.capture("shortcut_launch", properties: eventProperties) | ||
|
||
model?.launchShortcutAction() | ||
} | ||
KeyboardShortcuts.onKeyUp(for: .toggleLocalMode) { [weak model] in | ||
model?.toggleLocalVsRemoteShortcutAction() | ||
} | ||
KeyboardShortcuts.onKeyUp(for: .newChat) { [weak model] in | ||
model?.newChat() | ||
} | ||
KeyboardShortcuts.onKeyUp(for: .resizeWindow) { [weak model] in | ||
model?.resizeWindow() | ||
} | ||
KeyboardShortcuts.onKeyUp(for: .escape) { [weak model] in | ||
model?.escapeAction() | ||
} | ||
KeyboardShortcuts.onKeyUp(for: .launchWithAutoContext) { [weak model] in | ||
let eventProperties: [String: Any] = [ | ||
"app_hidden": model?.panel == nil | ||
] | ||
PostHogSDK.shared.capture( | ||
"shortcut_launch_with_auto_context", properties: eventProperties) | ||
model?.addAutoContext() | ||
model?.launchPanel() | ||
} | ||
KeyboardShortcutsManager.configure(model: model) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: KeyboardShortcutsManager.configure() is called before model.showPanel(), but model is a weak reference in the manager. Consider reordering to ensure model is fully initialized
systemPrompt = try container.mainContext.fetch(FetchDescriptor<SystemPrompt>()) | ||
.first(where: { $0.id == Defaults[.systemPromptId] }) ?? SystemPrompt.outputOnly | ||
} catch { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: No error handling for database fetch failure - could silently use outputOnly without logging the error
|
||
let systemPrompt = prompt.systemPrompt ?? SystemPrompt.outputOnly |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Redundant fallback to SystemPrompt.outputOnly since it's already handled in prompt creation
let systemPrompt = prompt.systemPrompt ?? SystemPrompt.outputOnly | |
let systemPrompt = prompt.systemPrompt |
if !savedPrompt.applications.contains(url) { | ||
savedPrompt.applications.append(url) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: redundant check - the allApps list is already filtered to exclude existing applications
if !savedPrompt.applications.contains(url) { | |
savedPrompt.applications.append(url) | |
} | |
savedPrompt.applications.append(url) |
Image(nsImage: NSWorkspace.shared.icon(forFile: url.path)) | ||
.resizable() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Loading app icons synchronously could cause UI stutter. Consider async loading with placeholder.
KeyboardShortcuts.Recorder( | ||
"Hotkey", name: KeyboardShortcuts.Name(savedPrompt.id) | ||
) { shortcut in | ||
if isSaved { | ||
shortcutChanged.toggle() | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: keyboard shortcut changes are only tracked when prompt is saved, but not during initial creation - could lead to shortcut conflicts
prompts.filter { | ||
$0.name.lowercased().contains(clearFilter) || | ||
$0.prompt.lowercased().contains(clearFilter) || | ||
$0.tags.joined(separator: ",").lowercased().contains(clearFilter) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: joined(separator: ",") creates a single string for tag search - could miss partial matches across tag boundaries
@@ -50,7 +50,7 @@ if [ -d "$DATABASE_PATH" ]; then | |||
find "$DATABASE_PATH" -type f -name "default.store*" -exec cp {} "$BACKUP_DIR" \; > /dev/null 2>&1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Consider adding -maxdepth option to find command to limit directory traversal depth
Hi @Niduank - overall this looks great! I have one big behavioral change to request: Since the system prompts are a special case that are inserted only at the beginning of the chat messages, we shouldn't be able to change the system prompt once after the first message. The prompt should show up only once at the top of the chat, and not be editable in the "Follow-up" field: ![]() To use a different system prompt, users will have to start a new chat. This might create a confusing experience, so I asked Elise to add a message explaining why the system prompt can't be edited (and a 'start new chat' button). Design is here |
96a1178
to
82449c3
Compare
* List, add, edit, remove * Search filters on name, prompt, tags * Persist with SwiftData
* Added a non-editable and non-deletable default SystemPrompt called "outputResponse" * Added the views SystemPromptView, SystemPromptSelectionView, and SystemPromptDetailView * Selection persists in UserDefaults * Used the selected prompt during a chat * Changed the management of EnvironmentValues * Changed the management of ModelContainer * Updated the script that was not deleting default.store* files
* No need of SwiftData migration, we fallback on 'outputOnly' * Save SystemPrompt in Prompt * Display it in GeneratedView
* Added timestamp & lastUsed dates to SystemPrompt * Sort SystemPrompts by timestamp in Settings & Selection * When we remove the current system prompt, fallback to last used or outputOnly * Fix random crash in Settings/SystemPrompt
* Display applications & tags correctly in a FlowLayout * Set max width 400 to new system prompt form
* Frontmost application (apps) * Input application (apps) * Input selected text (tags) * Auto context & file context (tags)
1e0f069
to
9fcd050
Compare
@timlenardo I made the changes, everything works as expected. |
Hi @Niduank - Yes, the system prompt gets sent with each prompt, but it's static and always at the top of the message stack. For example, if a user is sending a follow-up message, the stack will be: System Prompt > 1st User Message > 1st LLM Reply > 2nd User message |
Hey Kevin - this looks great! There are a few changes to the header that should go along with this, as shown in this design. ![]() Basically:
Also, when the first time I ran this, I got a crash. It's worked on subsequent runs, but here's the crash I got: ![]() |
Important to know
Old system message
We re-use the old system message as a default non-editable and non-removable
SystemPrompt
calledoutputOnly
.SystemPrompt.outputOnly
is inserted in SwiftData at app launch if there is no entry.Settings
List is sorted by timestamp desc
Search bar will filter on
SystemPrompt[.name, .prompt, .tags]
containing textWhen deleting the selected SystemPrompt, we will try to select the most recently used one or
outputOnly
ContentView
We always display/use a system prompt
Suggested System Prompt
The suggestions are displayed asynchronously.
Each SystemPrompt's suggestion is calculated based on a score.
For each application:
+5 for a match with the active (frontmost) application
+5 for a match with the input application (selected text)
+5 for a match with the applications from auto-context
For each tag:
+3 for a match in the input (selected text)
+3 for a match in auto-context and context files
Misc
KeyboardShortcutManager
which observe, enable/disable keyboard shortcutsModelContainer
&EnvironmentValues
clean_app.sh
updated - was not deleting default.store* files after backup