CMDC.Backend behaviour (cmdc v0.5.1)

Copy Markdown View Source

CMDC.Backend behaviour — 文件 / 状态 / 远程存储的统一访问层。

让工具层(read_file / write_file / edit_file / ls / glob / grep / execute 等) 与底层执行环境完全解耦,支持任意 backend 实现(ETS / 本地文件 / Docker / Modal / E2B / Postgres 等)。

设计

  • 10 个核心 callback
    • 文件操作:ls/1 read/3 write/2 edit/4 grep/3 glob/2
    • 二进制传输:upload_files/1 download_files/1
    • 沙盒扩展(可选):execute/2 id/0(由 CMDC.Sandbox 子 behaviour 引入)
  • 统一 Result struct:所有 callback 返回标准 Result struct(详见各 callback 文档), 不再用 {:ok, val} | {:error, reason} 双形态,便于 pattern match 与 序列化(Postgres / JSON)。
  • 标准错误码 atom:file_not_found / :permission_denied / :is_directory / :invalid_path(4 基础 + 允许 backend 扩展 string)。
  • 可路由 (Composite)CMDC.Backend.Composite 支持按路径前缀路由到不同子 backend,让一个会话同时挂载 sandbox + PG memory + ETS history。

CMDC.Sandbox 关系

历史 CMDC.Sandbox 已经定义了类似但更简单的 8 callback(read_file/write_file/...)。 未来版本会将 CMDC.Sandbox 重构为 extends CMDC.Backend,只新增 execute/2id/0,消除两套接口重叠。

CMDC.Memory 关系

现有 CMDC.Memory 是「语义记忆存储」behaviour(store/search/similarity_search), 与本模块用途完全不同,命名上独立,不冲突。

Quick Start

backend = CMDC.Backend.State.new(:my_table)
%CMDC.Backend.Results.WriteResult{path: "/hello.txt"} =
  CMDC.Backend.write(backend, "/hello.txt", "world")
%CMDC.Backend.Results.ReadResult{file_data: %{content: "world"}} =
  CMDC.Backend.read(backend, "/hello.txt")

实现指南

实现 backend 时只需 use CMDC.Backend(引入默认 read/3 等 dispatcher)+ 实现 callback。最少要实现 ls / read / write 三件套,其他可逐步补齐。

defmodule MyBackend do
  use CMDC.Backend

  @impl true
  def read(backend, path, opts), do: ...

  # 其他 callback ...
end

Backend Factory

支持运行期注入:传递工厂 {module, opts}lambda runtime -> backend.t()

Summary

Types

Backend 工厂 — 接受 runtime(context)返回 backend 实例。

callback 通用选项。

路径 — 必须以 / 开头的绝对路径。

t()

Backend 实例 —— 通常是 struct,由 Backend.State.new/1 等构造器返回。

Callbacks

批量下载二进制文件。

glob 文件匹配。

搜索文件内容(literal substring,不是 regex)。

列出指定目录下的直接子项(非递归)。

读取文件内容,支持按行分页。

批量上传二进制文件。

创建新文件。文件已存在时返回 :invalid_path 错误(用 edit/4 修改既有文件)。

Functions

委派到 backend.download_files/2。

委派到 backend.edit/4,opts 可选。

委派到 backend.glob/3,opts 可选(含 :path)。

委派到 backend.grep/3,opts 可选。

委派到 backend.ls/1。

委派到 backend.read/3,opts 可选。

解析 backend factory — 接受 struct 或函数。

委派到 backend.upload_files/2。

委派到 backend.write/2。

Types

factory()

@type factory() :: t() | (term() -> t())

Backend 工厂 — 接受 runtime(context)返回 backend 实例。

opts()

@type opts() :: keyword()

callback 通用选项。

path()

@type path() :: String.t()

路径 — 必须以 / 开头的绝对路径。

t()

@type t() :: struct()

Backend 实例 —— 通常是 struct,由 Backend.State.new/1 等构造器返回。

Callbacks

download_files(t, paths)

@callback download_files(t(), paths :: [path()]) :: [
  CMDC.Backend.Results.FileDownloadResponse.t()
]

批量下载二进制文件。

返回与输入等长的 [%FileDownloadResponse{}],支持 partial success。

edit(t, path, old_string, new_string, opts)

@callback edit(t(), path(), old_string :: String.t(), new_string :: String.t(), opts()) ::
  CMDC.Backend.Results.EditResult.t()

精确字符串替换。

选项

  • :replace_alltrue 替换全部、false 要求唯一匹配(默认 false

glob(t, pattern, opts)

@callback glob(t(), pattern :: String.t(), opts()) :: CMDC.Backend.Results.GlobResult.t()

glob 文件匹配。

选项

  • :path — 基础目录(默认 /

grep(t, pattern, opts)

@callback grep(t(), pattern :: String.t(), opts()) :: CMDC.Backend.Results.GrepResult.t()

搜索文件内容(literal substring,不是 regex)。

选项

  • :path — 搜索目录(默认 cwd)
  • :glob — 文件名过滤

ls(t, path)

@callback ls(t(), path()) :: CMDC.Backend.Results.LsResult.t()

列出指定目录下的直接子项(非递归)。

返回 %LsResult{entries: [%FileInfo{}]},目录条目以 / 结尾、 :is_dirtrue

read(t, path, opts)

@callback read(t(), path(), opts()) :: CMDC.Backend.Results.ReadResult.t()

读取文件内容,支持按行分页。

选项

  • :offset — 起始行(0-indexed,默认 0)
  • :limit — 最大行数(默认 2000)

upload_files(t, files)

@callback upload_files(t(), files :: [{path(), binary()}]) :: [
  CMDC.Backend.Results.FileUploadResponse.t()
]

批量上传二进制文件。

返回与输入等长的 [%FileUploadResponse{}],支持 partial success。

write(t, path, content)

@callback write(t(), path(), content :: String.t()) ::
  CMDC.Backend.Results.WriteResult.t()

创建新文件。文件已存在时返回 :invalid_path 错误(用 edit/4 修改既有文件)。

Functions

download_files(backend, paths)

@spec download_files(t(), [path()]) :: [CMDC.Backend.Results.FileDownloadResponse.t()]

委派到 backend.download_files/2。

edit(backend, path, old, new, opts \\ [])

委派到 backend.edit/4,opts 可选。

glob(backend, pattern, opts \\ [])

委派到 backend.glob/3,opts 可选(含 :path)。

grep(backend, pattern, opts \\ [])

委派到 backend.grep/3,opts 可选。

ls(backend, path)

@spec ls(t(), path()) :: CMDC.Backend.Results.LsResult.t()

委派到 backend.ls/1。

read(backend, path, opts \\ [])

@spec read(t(), path(), opts()) :: CMDC.Backend.Results.ReadResult.t()

委派到 backend.read/3,opts 可选。

resolve(backend, runtime)

@spec resolve(factory(), term()) :: t()

解析 backend factory — 接受 struct 或函数。

  • struct → 直接返回
  • 1-arity function → 用 runtime 调用并返回结果

upload_files(backend, files)

@spec upload_files(t(), [{path(), binary()}]) :: [
  CMDC.Backend.Results.FileUploadResponse.t()
]

委派到 backend.upload_files/2。

write(backend, path, content)

委派到 backend.write/2。