@chat-adapter/ state-pg (英语)
npm 软件包 :
@chat-adapter/state-pg
生产 PostgreSQL 状态适配器 聊天SDK 与 页:1 (节相. 当 PostgreSQL 是您的主数据存储器时使用此功能, 您想要状态持续, 没有单独的 Redis 依赖性 .
联合国 聊天-sdk.dev/适应者/官员/邮递员 指南: vercel.com/kb/chat-sdk (英语)
安装
pnpm add @chat-adapter/state-pg
带CLI的脚手架
要脚手架上使用 PostgreSQL 状态的新 Slack bot :
npx create-chat-sdk@latest my-bot --adapter slack postgres
访问 适配器目录 见其他官方和供应商官方适配器.
使用量
createPostgresState() 自动检测 POSTGRES_URL (或 DATABASE_URL这样你就可以不争吵地称呼它:
import { Chat } from "chat";
import { createPostgresState } from "@chat-adapter/state-pg";
const bot = new Chat({
userName: "mybot",
adapters: { /* ... */ },
state: createPostgresState(),
});
明确提供URL:
const state = createPostgresState({
url: "postgres://postgres:postgres@localhost:5432/chat",
});
使用已有客户端
import pg from "pg";
const client = new pg.Pool({ connectionString: process.env.POSTGRES_URL! });
const state = createPostgresState({ client });
配置
| 选项 | 需求 | 说明 |
|---|---|---|
url | 无* | Postgres 连接 URL |
client | 没有 | 现有 pg.Pool 实例 |
autoCreateSchema | 没有 | 在连接上创建表格和索引( 默认 ): true); 设置为 false 移民人数 |
keyPrefix | 没有 | 所有状态行的前缀( 默认 ): "chat-sdk"(中文(简体) ) |
logger | 没有 | 锁定实例( 默认为 ConsoleLogger("info").child("postgres")(中文(简体) ) |
*以太过 url, (中文(简体) ), POSTGRES_URL编号 :DATABASE_URL,或 client 需要时间.
环境变量
POSTGRES_URL=postgres://postgres:postgres@localhost:5432/chat
数据模型
默认情况下,适配器会在 connect()数字 :
chat_state_subscriptions
chat_state_locks
chat_state_cache
chat_state_lists
chat_state_queues
所有行名均由 key_prefix。 。 。 .
移徙自有计划a
设定 autoCreateSchema: false 当您的迁移提供了表格和索引时。 默认是 true。 。 。 .
// Explicit URL, or omit url to use POSTGRES_URL / DATABASE_URL.
const state = createPostgresState({
url: process.env.POSTGRES_URL,
autoCreateSchema: false,
});
现有集合支持同一选项。 适配器没有 schemaName 选项:每个查询使用通过连接的 PostgreSQL 解析的不合格表格名称 search_path设置运行时角色的默认值 search_path. 角色默认通过交易模式中的PgBuncers,Neon等连接池来跟踪角色,它们可能拒绝或默默地降低每次连接启动参数.
ALTER ROLE chat_runtime SET search_path TO chat_state;
在直接连接时,您可以设定每条连接, 也可以与 options 在游泳池或与 ?options=-c%20search_path%3Dchat_state 在连接 URL 上.
import pg from "pg";
import { createPostgresState } from "@chat-adapter/state-pg";
const client = new pg.Pool({
connectionString: process.env.POSTGRES_URL,
options: "-c search_path=chat_state",
});
const state = createPostgresState({ client, autoCreateSchema: false });
在启动机器人之前, 将适配器迁移为 schema 拥有者, 使用相同的 search_path 作为运行时间连接。 如果需要, 先创建您选择的策略 。 适配器导出完整迁移为 postgresSchemaStatements,可以使用自己的迁移工具运行的命令语句数组:
import { postgresSchemaStatements } from "@chat-adapter/state-pg";
for (const statement of postgresSchemaStatements) {
await client.query(statement);
}
如果你宁愿保留SQL的迁移,这些是相同的语句. 它们组成了完整的适配器计划; bigserial 还创建列表和队列序列.
CREATE TABLE IF NOT EXISTS chat_state_subscriptions (
key_prefix text NOT NULL,
thread_id text NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (key_prefix, thread_id)
);
CREATE TABLE IF NOT EXISTS chat_state_locks (
key_prefix text NOT NULL,
thread_id text NOT NULL,
token text NOT NULL,
expires_at timestamptz NOT NULL,
updated_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (key_prefix, thread_id)
);
CREATE TABLE IF NOT EXISTS chat_state_cache (
key_prefix text NOT NULL,
cache_key text NOT NULL,
value text NOT NULL,
expires_at timestamptz,
updated_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (key_prefix, cache_key)
);
CREATE INDEX IF NOT EXISTS chat_state_locks_expires_idx
ON chat_state_locks (expires_at);
CREATE INDEX IF NOT EXISTS chat_state_cache_expires_idx
ON chat_state_cache (expires_at);
CREATE TABLE IF NOT EXISTS chat_state_lists (
key_prefix text NOT NULL,
list_key text NOT NULL,
seq bigserial NOT NULL,
value text NOT NULL,
expires_at timestamptz,
PRIMARY KEY (key_prefix, list_key, seq)
);
CREATE INDEX IF NOT EXISTS chat_state_lists_expires_idx
ON chat_state_lists (expires_at);
CREATE TABLE IF NOT EXISTS chat_state_queues (
key_prefix text NOT NULL,
thread_id text NOT NULL,
seq bigserial NOT NULL,
value text NOT NULL,
expires_at timestamptz NOT NULL,
PRIMARY KEY (key_prefix, thread_id, seq)
);
CREATE INDEX IF NOT EXISTS chat_state_queues_expires_idx
ON chat_state_queues (expires_at);
将运行时间角色访问权授予了schema,所有五个表,以及两个序列. 例如,在专门设置中创建对象后 chat_state 单独拥有者的计划 :
GRANT USAGE ON SCHEMA chat_state TO chat_runtime;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA chat_state TO chat_runtime;
GRANT USAGE ON ALL SEQUENCES IN SCHEMA chat_state TO chat_runtime;
运行时间的作用也需要数据库 CONNECT 允许。 它不需要计划 CREATE 允许或表格所有权。 调整您的部署图和角色名称;这些赠款仅适用于现有对象.
由于此选项已禁用, connect() 检查连接 SELECT 1 然后运行一个只读查询,以确认所有五个表格都存在,并且当前角色拥有适配器对每个表格使用的特权: SELECT, (中文(简体) ), INSERT,以及 DELETE 到处都是, UPDATE 锁定、缓存和列表表格,以及 nextval 列表和队列序列通过任意 USAGE 或者说 UPDATE标识 seq 列不需要序列授权 。 它从来没有发行DDL。 如果有什么东西不见了, connect() 以错误名称拒绝问题,所以错误 search_path 或被遗忘的赠款在启动时失败,而不是在第一个消息上失败. 您的应用程序也拥有未来适配器计划变化的迁移; 更改log for @chat-adapter/state-pg 列出当方案改变时要运行的语句。 外部供应的游泳池在之后仍然开放 disconnect()。 。 。 .
列级赠款得到支持 SELECT, (中文(简体) ), INSERT,以及 UPDATE:探测器检查适配器使用的每列,接受一列补助金或全表补助金. DELETE 在表格中检查。 上述较广泛的赠款仍然足够.
特征
| 特性 | 支助 |
|---|---|
| 持久性 | 对 |
| 多方参与 | 对 |
| 订阅 | 对 |
| 分布式锁定 | 对 |
| 键值缓存 | 是(带有TTL) |
| 自动创建表格 | 对 |
| 密钥前缀名称间距 | 对 |
锁定考虑
Redis 状态适配器使用原子 SET NX PX 用于锁定获取, 这是一种单一原子操作。 PostgreSQL 适配器使用 INSERT ... ON CONFLICT DO UPDATE WHERE expires_at <= now(),它依赖于 Postgres 行级锁定。 这对于大多数工作量来说是安全的,但是在极端争论中(许多过程同时争夺同一个锁),其行为可能与Redis略有不同. 对于高意向的分布式锁,更喜欢Redis适配器.
已过期行清理
与Redis(在本土处理TTL过期)不同,PostgreSQL不会自动删除过期行. 适配器执行机会性清理——下个已过期的锁被覆盖 acquireLock() 调用, 下次删除已过期的缓存项 get() 调用此密钥, 而给定线程的已过期队列条目将在下一个上清除 enqueue() 或者说 dequeue() 电话 已过期的列表条目会在读取时被过滤出,但不会被适配器删除.
对于高通量部署,您可能想要进行定期清理工作:
DELETE FROM chat_state_locks WHERE expires_at <= now();
DELETE FROM chat_state_cache WHERE expires_at <= now();
DELETE FROM chat_state_lists WHERE expires_at <= now();
DELETE FROM chat_state_queues WHERE expires_at <= now();
AI 编码代理
如果您使用OpenAI Codex, Claude Code, 或 Cursor 等AI编码代理, 在写入代码前安装 Chat SDK 技能, 使其了解 SDK API, 适配器模式, 以及项目常规 .
npx skills add vercel/chat
技术参考文献 node_modules/chat/docs,加上已发布的软件包中的适配器指南和启动模板.
您也可以安装 Vercel 插件 包括Chat SDK技术与专家代理、代理斜线指令
npx plugins add vercel/vercel-plugin
插件是可选的;单是技能就足以用Chat SDK来构建.
关于可代理阅读的文件,见 聊天- sdk.dev/llms.txt (页数索引)或 聊天- sdk.dev/llms- full.txt (全文).
许可证
麻省理工学院