From 9780e2662333801a202d9fe7a55f5822a737ccee Mon Sep 17 00:00:00 2001 From: lk340 Date: Thu, 17 Jul 2025 15:11:20 -0400 Subject: [PATCH] Add inputs history, chat messages builder, and OnitPanelState updates --- .../ChatEndpointMessagesBuilder.swift | 17 ++++++++++------ macos/Onit/Data/Fetching/FetchingClient.swift | 4 ++-- .../Fetching/Streaming/StreamingClient.swift | 4 ++-- macos/Onit/Data/Persistence/Prompt.swift | 6 +++--- .../UI/Panels/State/OnitPanelState+Chat.swift | 16 +++++++++++---- .../Panels/State/OnitPanelState+Input.swift | 2 +- .../Onit/UI/Panels/State/OnitPanelState.swift | 20 +++++++++++++++---- 7 files changed, 47 insertions(+), 22 deletions(-) diff --git a/macos/Onit/Data/Fetching/ChatEndpointMessagesBuilder.swift b/macos/Onit/Data/Fetching/ChatEndpointMessagesBuilder.swift index 5f1ee13e..bad2da1b 100644 --- a/macos/Onit/Data/Fetching/ChatEndpointMessagesBuilder.swift +++ b/macos/Onit/Data/Fetching/ChatEndpointMessagesBuilder.swift @@ -12,7 +12,7 @@ import Foundation */ struct ChatEndpointMessagesBuilder { - static func user(instructions: [String], inputs: [Input?], files: [[URL]], autoContexts: [[String: String]], webSearchContexts: [[(title: String, content: String, source: String, url: URL?)]]) -> [String] { + static func user(instructions: [String], inputs: [[Input]], files: [[URL]], autoContexts: [[String: String]], webSearchContexts: [[(title: String, content: String, source: String, url: URL?)]]) -> [String] { var userMessages: [String] = [] for (index, instruction) in instructions.enumerated() { var message = "" @@ -46,13 +46,18 @@ struct ChatEndpointMessagesBuilder { message += "\n\(webSearchContext.content)" } } + + if !inputs[index].isEmpty { + let pendingInputs = inputs[index] - if let input = inputs[index], !input.selectedText.isEmpty { message += "\n\nUse the following selected text as context. When present, selected text should take priority over other context." - if let application = input.application { - message += "\n\nSelected Text from \(application): \(input.selectedText)" - } else { - message += "\n\nSelected Text: \(input.selectedText)" + + for input in pendingInputs { + if let application = input.application { + message += "\n\nSelected Text from \(application): \(input.selectedText)" + } else { + message += "\n\nSelected Text: \(input.selectedText)" + } } } diff --git a/macos/Onit/Data/Fetching/FetchingClient.swift b/macos/Onit/Data/Fetching/FetchingClient.swift index 4fd59edf..8cd13bad 100644 --- a/macos/Onit/Data/Fetching/FetchingClient.swift +++ b/macos/Onit/Data/Fetching/FetchingClient.swift @@ -28,7 +28,7 @@ actor FetchingClient { func chat( systemMessage: String, instructions: [String], - inputs: [Input?], + inputs: [[Input]], files: [[URL]], images: [[URL]], autoContexts: [[String: String]], @@ -75,7 +75,7 @@ actor FetchingClient { func localChat( systemMessage: String, instructions: [String], - inputs: [Input?], + inputs: [[Input]], files: [[URL]], images: [[URL]], autoContexts: [[String: String]], diff --git a/macos/Onit/Data/Fetching/Streaming/StreamingClient.swift b/macos/Onit/Data/Fetching/Streaming/StreamingClient.swift index 8950c4d9..305e6747 100644 --- a/macos/Onit/Data/Fetching/Streaming/StreamingClient.swift +++ b/macos/Onit/Data/Fetching/Streaming/StreamingClient.swift @@ -13,7 +13,7 @@ actor StreamingClient { func chat(systemMessage: String, instructions: [String], - inputs: [Input?], + inputs: [[Input]], files: [[URL]], images: [[URL]], autoContexts: [[String: String]], @@ -56,7 +56,7 @@ actor StreamingClient { func localChat(systemMessage: String, instructions: [String], - inputs: [Input?], + inputs: [[Input]], files: [[URL]], images: [[URL]], autoContexts: [[String: String]], diff --git a/macos/Onit/Data/Persistence/Prompt.swift b/macos/Onit/Data/Persistence/Prompt.swift index 7ca0ab13..e3250ff0 100644 --- a/macos/Onit/Data/Persistence/Prompt.swift +++ b/macos/Onit/Data/Persistence/Prompt.swift @@ -12,7 +12,7 @@ import SwiftData var instruction: String var timestamp: Date - var input: Input? + var inputs: [Input] var contextList: [Context] = [] // @Relationship(deleteRule: .cascade, inverse: \Response.prompt) @@ -30,12 +30,12 @@ import SwiftData var generationIndex = -1 init( - instruction: String, timestamp: Date, input: Input? = nil, + instruction: String, timestamp: Date, inputs: [Input] = [], contextList: [Context] = [], responses: [Response] = [] ) { self.instruction = instruction self.timestamp = timestamp - self.input = input + self.inputs = inputs self.contextList = contextList self.responses = responses self.generationState = GenerationState.done diff --git a/macos/Onit/UI/Panels/State/OnitPanelState+Chat.swift b/macos/Onit/UI/Panels/State/OnitPanelState+Chat.swift index 0615b813..14ec9aff 100644 --- a/macos/Onit/UI/Panels/State/OnitPanelState+Chat.swift +++ b/macos/Onit/UI/Panels/State/OnitPanelState+Chat.swift @@ -14,10 +14,18 @@ import SwiftData extension OnitPanelState { func createAndSavePrompt(accountId: Int?) { + var inputs = pinnedPendingInputs + + if let unpinnedPendingInput = unpinnedPendingInput, + !inputs.contains(unpinnedPendingInput) + { + inputs.append(unpinnedPendingInput) + } + let prompt = Prompt( instruction: pendingInstruction, timestamp: .now, - input: pendingInput, + inputs: inputs, contextList: pendingContextList ) @@ -62,7 +70,7 @@ extension OnitPanelState { currentChat?.prompts.append(prompt) currentPrompts?.append(prompt) pendingInstruction = "" - pendingInput = nil + clearHighlightedTextStates() do { try container.mainContext.save() @@ -91,7 +99,7 @@ extension OnitPanelState { let curInstruction = prompt.instruction var filesHistory: [[URL]] = [prompt.contextList.files] - var inputsHistory: [Input?] = [prompt.input] + var inputsHistory: [[Input]] = [prompt.inputs] var imagesHistory: [[URL]] = [prompt.contextList.images] var instructionsHistory: [String] = [curInstruction] var autoContextsHistory: [[String: String]] = [prompt.contextList.autoContexts] @@ -110,7 +118,7 @@ extension OnitPanelState { if response.type != .error { instructionsHistory.insert(currentPrompt!.instruction, at: 0) - inputsHistory.insert(currentPrompt!.input, at: 0) + inputsHistory.insert(currentPrompt!.inputs, at: 0) filesHistory.insert(currentPrompt!.contextList.files, at: 0) imagesHistory.insert(currentPrompt!.contextList.images, at: 0) autoContextsHistory.insert(currentPrompt!.contextList.autoContexts, at: 0) diff --git a/macos/Onit/UI/Panels/State/OnitPanelState+Input.swift b/macos/Onit/UI/Panels/State/OnitPanelState+Input.swift index 7aee9670..b003eccf 100644 --- a/macos/Onit/UI/Panels/State/OnitPanelState+Input.swift +++ b/macos/Onit/UI/Panels/State/OnitPanelState+Input.swift @@ -215,7 +215,7 @@ extension OnitPanelState { pendingInstruction = "" if (clearContext) { pendingContextList.removeAll() - pendingInput = nil + clearHighlightedTextStates() } focusText() diff --git a/macos/Onit/UI/Panels/State/OnitPanelState.swift b/macos/Onit/UI/Panels/State/OnitPanelState.swift index ece64a2f..cba07a14 100644 --- a/macos/Onit/UI/Panels/State/OnitPanelState.swift +++ b/macos/Onit/UI/Panels/State/OnitPanelState.swift @@ -124,13 +124,25 @@ class OnitPanelState: NSObject { notifyDelegateInputsChange() } } - var pendingInput: Input? = nil { + + var selectedPendingInput: Input? = nil + + var trackedPendingInput: Input? = nil + + var unpinnedPendingInput: Input? = nil { didSet { notifyDelegateInputsChange() } } - - var trackedPendingInput: Input? = nil + + var pinnedPendingInputs: [Input] = [] + + func clearHighlightedTextStates() { + selectedPendingInput = nil + trackedPendingInput = nil + unpinnedPendingInput = nil + pinnedPendingInputs = [] + } var systemPromptId: String = SystemPrompt.outputOnly.id @@ -219,7 +231,7 @@ class OnitPanelState: NSObject { delegate.userInputsDidChange( instruction: pendingInstruction, contexts: pendingContextList, - input: pendingInput + input: unpinnedPendingInput ) } }