s_新手程序员

后端 API

定义带 schema 的插件方法,使用后端模型和 KV 上下文。

更新于 · 13 分钟阅读

本页适用于 麻辣烫 0.4.3 / SDK 0.3.2。后端在宿主 Bun 进程运行,适合业务校验、数据整理和调用宿主模型。纯前端笔记插件不需要后端。

定义与调用

导出签名与行为
definePlugin(plugin: BackendPlugin): BackendPlugin;返回传入对象,默认导出给宿主加载
defineMethod(description: string, schema: z.ZodType<T>, handler: (input: T, context: PluginContext) => unknown 或 Promise<unknown>): PluginMethod
zSDK 使用的 Zod;输入 schema 同时用于 JSON Schema 发现与调用时 parse

BackendPlugin.methods 为必填的方法名到 PluginMethod 的映射;可选 activate(context)、dispose() 返回 void 或 Promise<void>。PluginMethod 包含 description、JSON 格式 inputSchema 和 execute(input, context),优先用 defineMethod 生成。

schema 必须能转换成 JSON Schema;避免 Date、自定义 transform 等不能表示的输入。调用必须传 JSON,方法返回值也必须是 JSON,不能返回 undefined、函数或循环对象。返回 JSON 序列化后最多 256 KiB;校验或处理器抛错会使前端 Promise reject。

PluginContext

成员返回值说明
pluginIdreadonly string当前插件 ID,无需由调用方传入
models.list()ModelInfo[]同步读取宿主模型目录
models.start(request)Promise<ModelRun>启动宿主模型任务,返回运行记录,不等待生成结束
kv.get(key)JSONValue 或 null同步读取;不存在返回 null
kv.set(key, value)Promise<void>每值 JSON UTF-8 最多 64 KiB,等待持久化
kv.delete(key)Promise<void>删除并持久化

上下文没有 runs、invoke 或事件订阅接口。模型请求及限制见前端 API。前端 KV 所有操作均为 Promise,set/delete 返回 null;不要把前端与后端签名混用。

完整示例:保存并统计笔记

在入门的 my-notes 项目添加 src/backend.ts,并在 package.json.malatang 添加 "backend": "dist/backend.js"。源码文件与清单必须同时存在。

import { defineMethod, definePlugin, z } from "@semicoder/malatang-sdk/runtime";
 
export default definePlugin({
  methods: {
    saveNote: defineMethod(
      "保存笔记并返回字符数",
      z.strictObject({ text: z.string().max(8000) }),
      async ({ text }, context) => {
        await context.kv.set("note", text);
        return { text, characters: Array.from(text).length };
      },
    ),
  },
});

用下列代码替换 src/client.tsx。invoke 的方法名是本插件 methods 的键,泛型只提供 TypeScript 提示,不会验证返回数据。

import "./styles.css";
import { useEffect, useState } from "react";
import { createPluginClient } from "@semicoder/malatang-sdk/client";
import { Alert, Button, Field, Page, PageHeader, Textarea } from "@semicoder/malatang-sdk/ui";
 
const client = createPluginClient("my-notes");
type SavedNote = { text: string; characters: number };
 
export default function BackendNotes() {
  const [text, setText] = useState("");
  const [ready, setReady] = useState(false);
  const [busy, setBusy] = useState(false);
  const [message, setMessage] = useState("");
  const [error, setError] = useState("");
  useEffect(() => {
    let live = true;
    void client.kv
      .get("note")
      .then((value) => {
        if (live) {
          setText(typeof value === "string" ? value : "");
          setReady(true);
        }
      })
      .catch((reason: unknown) => {
        if (live) setError(String(reason));
      });
    return () => {
      live = false;
    };
  }, []);
  async function save() {
    setBusy(true);
    setError("");
    setMessage("");
    try {
      const result = await client.invoke<SavedNote>("saveNote", { text });
      setMessage(`已保存 ${result.characters} 个字符`);
    } catch (reason) {
      setError(String(reason));
    } finally {
      setBusy(false);
    }
  }
  return (
    <Page className="p:space-y-4">
      <PageHeader title="带后端的笔记" />
      <Field label="笔记">
        <Textarea
          value={text}
          maxLength={8000}
          disabled={!ready || busy}
          onChange={(event) => setText(event.target.value)}
        />
      </Field>
      <Button disabled={!ready} loading={busy} onClick={() => void save()}>
        保存并统计
      </Button>
      {message && <Alert tone="success">{message}</Alert>}
      {error && <Alert>{error}</Alert>}
    </Page>
  );
}

继续执行 bun run check、bun run build、bun run pack 并安装归档。修改已安装插件时提高包版本后更新;仅修改源码不会改变宿主正在运行的版本。

初始化与释放

activate(context) 用于短时初始化;完成前插件尚未 active,不要在这里调用 context.models.start。激活失败会让插件进入 error 状态。将模型启动放在用户触发的方法里,返回 ModelRun 后由前端订阅进度。

dispose() 应释放插件自己创建的资源;不要依赖它在崩溃时保存数据。没有宿主管理的自建持久任务接口,避免创建无法取消的常驻后台任务。方法调用期间会占用插件调用计数,阻止冲突的停用和更新,处理器应及时结束。

故障定位

症状检查项
插件方法不存在methods 的键、前端 invoke 名称、是否安装了新归档
插件未就绪或已停用应用中心的 active 状态、激活错误、是否正在更新
输入校验失败JSON 形状、strictObject 多余字段、长度约束
返回结果失败是否为 JSON、是否超过 256 KiB、是否遗漏 return
激活失败backend 默认导出、清单入口、模块初始化异常

类型来源:runtime.ts、types.ts。宿主最终执行校验见插件实现。