Sagents.FileSystem.Persistence behaviour (Sagents v0.4.0)

Copy Markdown

Behaviour for persisting files to storage.

Custom persistence implementations must implement all required callbacks. The default implementation writes to the local filesystem.

Usage

Create a FileSystemConfig with your persistence module:

alias Sagents.FileSystem.{FileSystemServer, FileSystemConfig}

{:ok, config} = FileSystemConfig.new(%{
  base_directory: "user_files",
  persistence_module: MyApp.DBPersistence,
  storage_opts: [path: "/data/agents"]
})

FileSystemServer.start_link(
  agent_id: "agent-123",
  persistence_configs: [config]
)

Storage Options

The :storage_opts from your FileSystemConfig are passed to your persistence module's callbacks via the opts parameter. Use it to configure storage location, DB connection, credentials, etc.

The FileSystemConfig also automatically adds :agent_id and :base_directory to the opts for convenience.

Callbacks

All callbacks receive opts as their second parameter, which includes:

  • :agent_id - The agent's unique identifier
  • :base_directory - The virtual directory (from FileSystemConfig). Note: this key is not present for default configs (default: true), since the default config catches all paths and has no meaningful base directory.
  • All custom options from FileSystemConfig.storage_opts

write_to_storage/2

Write a file entry to persistent storage. Called after the debounce timer fires.

load_from_storage/2

Load a file's content from persistent storage. Called during lazy loading when a file is read but content is not yet in memory.

delete_from_storage/2

Delete a file from persistent storage. Called immediately when a persisted file is deleted (no debounce).

list_persisted_entries/2

List all persisted file entries for an agent. Called during initialization to index existing files with their metadata (title, type, tags, etc.) without loading content.

Summary

Callbacks

Delete a file from persistent storage.

List all persisted file entries for an agent.

Load a file from persistent storage.

Move a file entry to a new path in persistent storage.

Update only metadata for a persisted file entry (no content write).

Write a file entry to persistent storage.

Callbacks

delete_from_storage(file_entry, opts)

@callback delete_from_storage(
  file_entry :: Sagents.FileSystem.FileEntry.t(),
  opts :: keyword()
) ::
  :ok | {:error, term()}

Delete a file from persistent storage.

Parameters

  • file_entry - The FileEntry to delete (uses path field)
  • opts - Storage configuration options

Returns

  • :ok on success (even if file doesn't exist)
  • {:error, reason} on failure

list_persisted_entries(agent_id, opts)

@callback list_persisted_entries(agent_id :: String.t(), opts :: keyword()) ::
  {:ok, [Sagents.FileSystem.FileEntry.t()]} | {:error, term()}

List all persisted file entries for an agent.

Used during initialization to index existing files with their metadata (title, entry_type, custom metadata, etc.) without loading content.

Returns FileEntry structs with content: nil, loaded: false (for files) or loaded: true (for directories, which have no content to load).

Parameters

  • agent_id - The agent's unique identifier
  • opts - Storage configuration options

Returns

  • {:ok, entries} where entries is a list of FileEntry structs
  • {:error, reason} on failure

load_from_storage(file_entry, opts)

@callback load_from_storage(
  file_entry :: Sagents.FileSystem.FileEntry.t(),
  opts :: keyword()
) ::
  {:ok, Sagents.FileSystem.FileEntry.t()} | {:error, term()}

Load a file from persistent storage.

The persistence backend should return a complete FileEntry with all metadata populated from the storage system (size, MIME type, timestamps, etc.).

Parameters

  • file_entry - The FileEntry with path to load (content may be nil)
  • opts - Storage configuration options

Returns

  • {:ok, file_entry} where file_entry is a complete FileEntry with content and metadata
  • {:error, :enoent} if file doesn't exist
  • {:error, reason} on other failures

move_in_storage(file_entry, new_path, opts)

(optional)
@callback move_in_storage(
  file_entry :: Sagents.FileSystem.FileEntry.t(),
  new_path :: String.t(),
  opts :: keyword()
) :: {:ok, Sagents.FileSystem.FileEntry.t()} | {:error, term()}

Move a file entry to a new path in persistent storage.

This is an optional callback invoked by move_file/3 for each entry being moved. If not implemented, moved entries are marked dirty and persisted via the normal write_to_storage/2 or update_metadata_in_storage/2 cycle.

Parameters

  • file_entry - The FileEntry at its old path (before the move)
  • new_path - The target path
  • opts - Storage configuration options

Returns

  • {:ok, file_entry} with the entry updated to reflect the new path
  • {:error, reason} on failure (entry will be marked dirty as fallback)

update_metadata_in_storage(file_entry, opts)

(optional)
@callback update_metadata_in_storage(
  file_entry :: Sagents.FileSystem.FileEntry.t(),
  opts :: keyword()
) ::
  {:ok, Sagents.FileSystem.FileEntry.t()} | {:error, term()}

Update only metadata for a persisted file entry (no content write).

This is an optional callback used when only non-content fields change (dirty_non_content: true) — e.g., custom metadata, title, or file_type updates. Falls back to write_to_storage/2 if not implemented.

Parameters

  • file_entry - The FileEntry with updated metadata
  • opts - Storage configuration options

Returns

  • {:ok, file_entry} with updated metadata
  • {:error, reason} on failure

write_to_storage(file_entry, opts)

@callback write_to_storage(
  file_entry :: Sagents.FileSystem.FileEntry.t(),
  opts :: keyword()
) ::
  {:ok, Sagents.FileSystem.FileEntry.t()} | {:error, term()}

Write a file entry to persistent storage.

The persistence backend should return the FileEntry with updated metadata after the write completes (actual size on disk, updated timestamps, etc.).

Parameters

  • file_entry - The FileEntry to persist (includes path, content, metadata)
  • opts - Storage configuration options (e.g., [path: "/data/agents", agent_id: "agent-123"])

Returns

  • {:ok, file_entry} where file_entry has updated metadata from the storage system
  • {:error, reason} on failure