diff --git a/Cotabby/Models/SuggestionSettingsModel.swift b/Cotabby/Models/SuggestionSettingsModel.swift index 20c1009..46e3f97 100644 --- a/Cotabby/Models/SuggestionSettingsModel.swift +++ b/Cotabby/Models/SuggestionSettingsModel.swift @@ -138,12 +138,15 @@ final class SuggestionSettingsModel: ObservableObject { static let defaultGhostTextOpacity: Double = 1.0 static let ghostTextOpacityStep: Double = 0.1 - /// Hard upper bound on the persisted Extended Context blob, in characters. Sized so the user - /// can paste a meaningful glossary or style guide without crowding the model's shared context: - /// roughly ~1000 tokens of English, which still leaves headroom for instructions, prefix text, - /// clipboard, and visual context inside Apple's 4096-token window. Larger pastes are truncated - /// at write time so the cost is bounded on every subsequent request. - static let maximumExtendedContextCharacters: Int = 4_000 + /// Hard upper bound on the persisted Extended Context blob, in characters. Sized to match what the + /// engines actually consume rather than what they can store: the OSS base path renders this as a + /// budgeted "notes" section (`BaseCompletionPromptRenderer`, `maxChars` 1300) inside a 2400-char + /// prompt, so a larger cap would just be clipped on-device instead of used. ~1200 chars (~300 + /// tokens) is a meaningful glossary or style guide that still leaves room for the prefix and other + /// context, and stays well inside Apple's 4096-token window on the Foundation Models path. Keep this + /// at or below the notes section's `maxChars` minus its label so the full blob survives on the OSS + /// path. Larger pastes are truncated at write time so the cost is bounded on every request. + static let maximumExtendedContextCharacters: Int = 1_200 init( configuration: SuggestionConfiguration, diff --git a/Cotabby/Support/BaseCompletionPromptRenderer.swift b/Cotabby/Support/BaseCompletionPromptRenderer.swift index f92ac62..dd1f5dc 100644 --- a/Cotabby/Support/BaseCompletionPromptRenderer.swift +++ b/Cotabby/Support/BaseCompletionPromptRenderer.swift @@ -43,7 +43,12 @@ enum BaseCompletionPromptRenderer { sections.append(Self.contextSection("language", language, priority: 50, maxChars: 300)) } if let notes = Self.nonEmpty(extendedContext) { - sections.append(Self.contextSection("notes", "Notes the writer keeps in mind: \(notes)", priority: 40, maxChars: 600)) + // `maxChars` must stay at or above `SuggestionSettingsModel.maximumExtendedContextCharacters` + // plus this label (~32 chars) so the full user-entered Extended Context survives here instead + // of being silently clipped far under the advertised cap. It still competes for the 2400-char + // total budget below (priority 40), so an unusually long prefix can trim it, but in normal use + // the whole blob lands. + sections.append(Self.contextSection("notes", "Notes the writer keeps in mind: \(notes)", priority: 40, maxChars: 1300)) } if let clip = Self.nonEmpty(clipboardContext) { sections.append(Self.contextSection("clipboard", "On the clipboard: \(clip)", priority: 35, maxChars: 400))