Writing the news with AI, with the key of the app: a draft from what shipped, a translation into another language, and changes to a passage (rewrite, shorter, warmer, fix the spelling).
The admin chooses the model and gives the key in its Settings: any model
that ReqLLM knows, such as
anthropic:claude-sonnet-5-5, or a model of OpenAI or Google. The key is encrypted
by the app's Cloak vault (:vault), else with the :secret_key_base of
Tayori's config, kept in the app's database, and never shown again. The app needs the dependency:
{:req_llm, "~> 1.6"}The text streams into the editor as a suggestion, which the admin accepts or rejects (Kotoba's suggestions): AI never publishes, and never changes an entry by itself.
The admin can also say what the product is and how it speaks ("About your product"), which every request carries.
Outside the editor, complete/2 waits for the whole text: a digest
writes its entry from the inbox's changes with it (Tayori.Digest),
a draft unless the :digest config says to publish it.
Summary
Callbacks
The adapter that calls the model: stream(model, key, system, prompt).
Functions
The actions on a passage, [{id, label}], for the editor's Assist menu.
Whether the app can use AI: it has ReqLLM, and Tayori can keep a key.
Checks the model and the key with a request of a few words. Returns
:ok or {:error, reason}.
The whole text of a task (see stream/1), for a request that no one
watches as it streams. Options: :timeout, in milliseconds (two
minutes). Returns {:ok, text}, or {:error, reason}: :timeout when
the model takes longer.
Whether the admin has given a model and a key.
Reads the head of a draft or a translation, TITLE: and SUMMARY:
lines and a --- line, from the text streamed so far.
Saves the model and what the product is; the key is a string to keep,
:keep, or nil to remove it.
The settings, without the key: %{model: ..., about: ..., key?: bool}.
Streams the text of a task, as a stream of strings
Callbacks
Functions
The actions on a passage, [{id, label}], for the editor's Assist menu.
@spec available?() :: boolean()
Whether the app can use AI: it has ReqLLM, and Tayori can keep a key.
@spec check() :: :ok | {:error, term()}
Checks the model and the key with a request of a few words. Returns
:ok or {:error, reason}.
The whole text of a task (see stream/1), for a request that no one
watches as it streams. Options: :timeout, in milliseconds (two
minutes). Returns {:ok, text}, or {:error, reason}: :timeout when
the model takes longer.
@spec configured?() :: boolean()
Whether the admin has given a model and a key.
Reads the head of a draft or a translation, TITLE: and SUMMARY:
lines and a --- line, from the text streamed so far.
Examples
iex> Tayori.AI.parse_header("TITLE: Exports\nSUMMARY: In two clicks.\n---\nEvery project")
{:ok, %{title: "Exports", summary: "In two clicks."}, "Every project"}
iex> Tayori.AI.parse_header("TITLE: Exp")
:more
Saves the model and what the product is; the key is a string to keep,
:keep, or nil to remove it.
The settings, without the key: %{model: ..., about: ..., key?: bool}.
@spec stream(tuple()) :: {:ok, Enumerable.t()} | {:error, term()}
Streams the text of a task, as a stream of strings:
{:draft, notes, locale}- an entry from notes of what shipped (a list of changes, commits, pull requests), with a title and a summary first (seeparse_header/1);{:digest, changes, locale}- an entry from changes of the inbox (Tayori.Change), in the same form;{:translate, %{title:, summary:, markdown:}, from, to}- an entry in another language, in the same form;{:assist, action, text, locale}- a passage changed by an action ofactions/0.