An agnostic AI assistant into Tiro Notes
One of the most interesting features that I wanted to implement in Tiro is the idea of having an AI assistant available on mobile and desktop.
Final result, you can test it using the command line npx tiro-notes@0.30.99.12
User experience #
The main problem I saw with current solutions was the friction between the writing process and the AI assistance at the user experience level. I don't use AI assistants because of that friction required to use them (Opening a new tab, copy/pasting text 2 times etc.). It creates a certain level of steps under which I'd just rather use search engines or do it myself.
My first attempt was to add an AI assistant throught command line using the Tiro command line manager custom tag called Commander, but I quickly realized that the integration still required too much steps to be usable in real-life scenarios, especially the copy/pasting part which is really tedious on mobile. So I gave up in using AI assistance inside Tiro that way.
Initial AI integration attempt using the commander custom tag
My second attempt, which I will describe here, was to get as close as possible to the user experience offered by Microsoft's Copilot or Notion's AI system.
The goal was to reduce the number of steps needed to get an answer:
- Write the question in Tiro Notes
- Select the text of the question
- Immediately obtain an AI-generated response in the note
The objective is also to respect a certain open source philosophy by offering the choice to users to use different AI engines, including open source ones in the future. The solution is designed to be easily interchangeable from the user settings.
New settings options for AI assistant. The AI system used can be modified
Technical implementation #
On the coding side, it happened to be quite easy to implement it. The main logic was :
- User select a part of the text
- A small popup appears suggesting AI assistance
- If clicked, the selected text is sent to Tiro Notes backend that executes a configurable command line with it as a parameter
- The answer is then sent back to the active note, below the question
- As for ChatGPT, the answer is not sent once but streamed (almost word by word), I had to create a new api function
api.command.stream
The code is visible at that link, I will probably refactor it latest to its own manager file
I am using two api functions:
-
the newly created
api.command.stream(cmd, streamChunk => { })
to execute the command on Tiro Notes backend. That function sends from the react client frontend a command line to the node.js backend, which executes it using the library execa. Details of the backend function used can be checked at that link -
api.file.saveContent(p.file.path, nText) to save the note regularly
getApi(api => {
let cmd = api.userSettings.get("ui_editor_ai_command")
cmd = cmd.replace("", selectionTxt)
genTextAt(genParams())
api.command.stream(cmd, streamChunk => {
if (streamChunk.isError) isError = true
// if it is an error, display it in a popup
if (isError) {
api.ui.notification.emit({
content: `[AI] Error from CLI <br/> "${cmd}" <br/>=> <br/>${streamChunk.text}`,
options: {hideAfter: -1 }
})
genTextAt({...genParams(), textUpdate:"", isLast: true})
} else {
// else insert it
genTextAt({...genParams(), textUpdate:streamChunk.textTot, isLast: streamChunk.isLast})
}
})
})
You can test that AI assistant by downloading Tiro Notes from the command line npx tiro-notes@0.30.99.12