The ability to point TabType at a document (notes, a style guide, a glossary, past writing) and have it seed personalization immediately, instead of waiting for typing history to accumulate.
To be clear on scope: this is not fine-tuning. The MLX path here is inference-only, so this is corpus seeding, not training.
The pipeline already exists. TypingHistoryStore feeds three consumers:
PhraseMemory.ingest() for verbatim phrase recall
contextSamples(budget:) for the <recently_written_by_author> block
topWords(limit:) for the persona line
Importing a document would mean parsing it to text and feeding that same pipeline, so most of the work is already done.
How does Cotypist or another tool handle it? (optional)
Not aware of an equivalent, this would be new ground.
Anything else
Parsing needs no new dependencies: NSAttributedString reads .docx/.rtf/.html, PDFKit reads .pdf, .txt/.md are free, and String.enumerateSubstrings(options: .bySentences) chunks it.
Two design questions I'd want your ruling on before writing anything:
-
How the document reaches the prompt. A stable <reference> block costs prefill once and fits the volatility ordering in PromptBuilder. Retrieving relevant chunks per keystroke would give better quality but would invalidate the KV prefix cache constantly, which works against the design in Predictor.swift. I'd default to the stable block unless you'd rather have retrieval.
-
Storage. TypingHistoryStore caps at maxEntries = 500 with FIFO eviction, so writing document chunks into entries would flush real typing history out. I'd add docChunks: [String]? to the existing Snapshot so it stays inside the same AES-GCM encrypted file rather than a plaintext sidecar. There's already legacy-decode handling at TypingHistoryStore.swift:189, so an optional field should stay backward compatible.
One trap worth flagging: PhraseMemory.minCount = 2 means a continuation has to recur twice before it's suggested. A document ingested once gives every trigram a count of 1, so continuation() returns nothing and the feature would look like it does nothing at all. Would need an ingest(_:weight:) parameter or similar.
This would pair well with the custom dictionary idea (other issue), since document import could harvest rare capitalised terms straight into that word list. Happy to do them as two separate PRs in that order.
The ability to point TabType at a document (notes, a style guide, a glossary, past writing) and have it seed personalization immediately, instead of waiting for typing history to accumulate.
To be clear on scope: this is not fine-tuning. The MLX path here is inference-only, so this is corpus seeding, not training.
The pipeline already exists.
TypingHistoryStorefeeds three consumers:PhraseMemory.ingest()for verbatim phrase recallcontextSamples(budget:)for the<recently_written_by_author>blocktopWords(limit:)for the persona lineImporting a document would mean parsing it to text and feeding that same pipeline, so most of the work is already done.
How does Cotypist or another tool handle it? (optional)
Not aware of an equivalent, this would be new ground.
Anything else
Parsing needs no new dependencies:
NSAttributedStringreads .docx/.rtf/.html, PDFKit reads .pdf, .txt/.md are free, andString.enumerateSubstrings(options: .bySentences)chunks it.Two design questions I'd want your ruling on before writing anything:
How the document reaches the prompt. A stable
<reference>block costs prefill once and fits the volatility ordering inPromptBuilder. Retrieving relevant chunks per keystroke would give better quality but would invalidate the KV prefix cache constantly, which works against the design inPredictor.swift. I'd default to the stable block unless you'd rather have retrieval.Storage.
TypingHistoryStorecaps atmaxEntries = 500with FIFO eviction, so writing document chunks intoentrieswould flush real typing history out. I'd adddocChunks: [String]?to the existingSnapshotso it stays inside the same AES-GCM encrypted file rather than a plaintext sidecar. There's already legacy-decode handling atTypingHistoryStore.swift:189, so an optional field should stay backward compatible.One trap worth flagging:
PhraseMemory.minCount = 2means a continuation has to recur twice before it's suggested. A document ingested once gives every trigram a count of 1, socontinuation()returns nothing and the feature would look like it does nothing at all. Would need aningest(_:weight:)parameter or similar.This would pair well with the custom dictionary idea (other issue), since document import could harvest rare capitalised terms straight into that word list. Happy to do them as two separate PRs in that order.