Eai.System (eai v1.0.3)

Copy Markdown

Step 4 — whole-runtime snapshot and restore.

Owns the "lock + serialize" logic for the global export/import tools (Eai.Tool.ExportGlobalContext, Eai.Tool.ReplaceGlobalContext). The per-session variants (export_chat_session_context, replace_chat_session_context) are still served directly by Eai.Chat.export_history/2 and Eai.Chat.replace_history/3.

This module does NOT add a force parameter, does NOT install any auto-snapshot trigger, and does NOT add cron / periodic jobs. The caller (an LLM tool call or a human) is the only thing that can invoke it.

Summary

Functions

Block until every chat session and PTY session is idle, then return :ok. If the deadline expires first, return {:error, :timeout}.

Restore the entire Eai runtime state from a gzip file written by snapshot_to_gzip/1.

Snapshot the entire Eai runtime state to a gzip file.

Types

await_result()

@type await_result() :: :ok | {:error, :timeout}

restore_info()

@type restore_info() :: %{
  chat_sessions_restored: non_neg_integer(),
  cache_entries_restored: non_neg_integer()
}

snapshot_info()

@type snapshot_info() :: %{
  file: String.t(),
  chat_session_count: non_neg_integer(),
  cache_entry_count: non_neg_integer()
}

Functions

await_idle(timeout \\ 30000)

@spec await_idle(timeout()) :: await_result()

Block until every chat session and PTY session is idle, then return :ok. If the deadline expires first, return {:error, :timeout}.

Polls every 50ms. After both sides report all-idle once, sleeps one more 50ms to absorb any in-flight Task.Supervisor spawn that hasn't appeared in the listing yet, then returns :ok.

Options

  • timeout — Max wait in ms. Default: 30000.

restore_from_gzip(file_path)

@spec restore_from_gzip(Path.t()) :: {:ok, restore_info()} | {:error, term()}

Restore the entire Eai runtime state from a gzip file written by snapshot_to_gzip/1.

Awaits system-idle first. Reads the file, gunzips, binary_to_terms. Validates the version field (must equal 1; an unknown version is an error rather than a silent compat fall-back). For every chat_session in the snapshot, writes its gzip blob to a temp file, calls Eai.Chat.replace_history/3 with format: "converse", then deletes the temp file. For every cache_entry, calls Eai.Naming.cache().put/2. Cache restore is a MERGE — keys that exist in the runtime but not in the snapshot are NOT deleted.

Options

  • file_path — Source .gz path.

Returns

  • {:ok, %{chat_sessions_restored, cache_entries_restored}} on success
  • {:error, :timeout} if await_idle/1 times out
  • {:error, term} for any other failure

snapshot_to_gzip(file_path)

@spec snapshot_to_gzip(Path.t()) :: {:ok, snapshot_info()} | {:error, term()}

Snapshot the entire Eai runtime state to a gzip file.

Awaits system-idle first. Then, for every chat session, calls Eai.Chat.snapshot_messages_bytes/1 to get the gzip blob of its messages in the exact same format as the per-session export_chat_session_context tool. Iterates the Nebulex cache (skipping chat_session:* and chat_history:* keys as a defensive measure — those names are reserved for future per-session cache storage) and captures every other entry. Writes the top-level %{version, exported_at, chat_sessions, cache_entries} map as a gzipped :erlang.term_to_binary/1 to file_path.

Options

  • file_path — Destination .gz path.

Returns

  • {:ok, %{file, chat_session_count, cache_entry_count}} on success
  • {:error, :timeout} if await_idle/1 times out
  • {:error, term} for any other failure