Tayori.AI behaviour (Tayori v0.1.0)

Copy Markdown View Source

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

stream(model, key, system, prompt)

@callback stream(
  model :: String.t(),
  key :: String.t(),
  system :: String.t(),
  prompt :: String.t()
) ::
  {:ok, Enumerable.t()} | {:error, term()}

The adapter that calls the model: stream(model, key, system, prompt).

Functions

actions()

@spec actions() :: [{String.t(), String.t()}]

The actions on a passage, [{id, label}], for the editor's Assist menu.

available?()

@spec available?() :: boolean()

Whether the app can use AI: it has ReqLLM, and Tayori can keep a key.

check()

@spec check() :: :ok | {:error, term()}

Checks the model and the key with a request of a few words. Returns :ok or {:error, reason}.

complete(task, opts \\ [])

@spec complete(tuple(), keyword()) :: {:ok, String.t()} | {:error, term()}

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.

configured?()

@spec configured?() :: boolean()

Whether the admin has given a model and a key.

parse_header(text)

@spec parse_header(String.t()) :: {:ok, map(), String.t()} | :more

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

save_settings(params, key \\ :keep)

@spec save_settings(map(), String.t() | :keep | nil) :: :ok

Saves the model and what the product is; the key is a string to keep, :keep, or nil to remove it.

settings()

@spec settings() :: %{
  model: String.t() | nil,
  about: String.t() | nil,
  key?: boolean()
}

The settings, without the key: %{model: ..., about: ..., key?: bool}.

stream(task)

@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 (see parse_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 of actions/0.