mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 00:21:58 +00:00
b2f6d8d7d2
- 依赖与锁文件:[postgres] 里装的是什么、为什么 langgraph-checkpoint 单独卡小版本、checkpoint-postgres 锁在 3.1.2;数据位置:PostgreSQL 时不再用 agentlab.db / checkpoints.db;开发与验证:scripts/test-postgres.sh - environment.yml 带上 [postgres];.env.example 补应用库一节 - deploy.sh:数据目录里没有 agentlab.db(库在 PostgreSQL)时,认 .secret_key、uploads/ 也算 AgentLab 的数据目录,不再拒绝改属主 - 断点库的连接串不带只给 asyncpg 看的参数(prepared_statement_cache_size 之类,psycopg 不认);地址规范化、启动日志不带密码的测试
60 lines
3.0 KiB
Python
60 lines
3.0 KiB
Python
"""应用库、断点库的地址:规范写法、换算成 psycopg 用的连接串、写进日志的样子(不带密码)。
|
||
|
||
不依赖 app.core.config:配置的校验器要调这里,这里再导 config 就成了循环导入。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from sqlalchemy.engine import URL, make_url
|
||
from sqlalchemy.exc import ArgumentError
|
||
|
||
#: 应用库认的写法。asyncpg 是 SQLAlchemy 这边的驱动;postgresql:// / postgres:// 是常见的通用写法,一律换成 asyncpg
|
||
_APP_SCHEMES = {"postgresql+asyncpg", "postgresql", "postgres"}
|
||
#: 断点库认的写法:psycopg 只认 libpq 的 postgresql:// / postgres://;带驱动名的 SQLAlchemy 写法也收,去掉驱动名
|
||
_CHECKPOINT_SCHEMES = {"postgresql", "postgres", "postgresql+psycopg", "postgresql+asyncpg"}
|
||
|
||
#: asyncpg 和 libpq 对 SSL 的参数名不一样:asyncpg 是 ssl=require,libpq 是 sslmode=require(取值同一套)
|
||
_ASYNCPG_TO_LIBPQ = {"ssl": "sslmode"}
|
||
#: 只给 SQLAlchemy / asyncpg 看的参数,libpq 不认(psycopg 连的时候会报「invalid connection option」)
|
||
_ASYNCPG_ONLY = {"prepared_statement_cache_size", "prepared_statement_name_func", "command_timeout", "async_fallback"}
|
||
|
||
|
||
def _parse(raw: str, schemes: set[str], example: str) -> URL:
|
||
try:
|
||
url = make_url(raw)
|
||
except ArgumentError:
|
||
raise ValueError(f"认不出这个地址,应当写成 {example}") from None
|
||
if url.drivername not in schemes:
|
||
raise ValueError(f"只认 PostgreSQL({example}),不认 {url.drivername}://;用 SQLite 就不要设这一项")
|
||
if not url.database:
|
||
raise ValueError(f"地址里要写库名,例如 {example}")
|
||
return url
|
||
|
||
|
||
def normalize_database_url(raw: str) -> str:
|
||
"""AGENTLAB_DATABASE_URL → postgresql+asyncpg://…(密码原样保留)。认不出的写法抛 ValueError。"""
|
||
url = _parse(raw, _APP_SCHEMES, "postgresql+asyncpg://用户:密码@主机:5432/库名")
|
||
return url.set(drivername="postgresql+asyncpg").render_as_string(hide_password=False)
|
||
|
||
|
||
def normalize_checkpoint_url(raw: str) -> str:
|
||
"""AGENTLAB_CHECKPOINT_URL → libpq 认的 postgresql://…。认不出的写法抛 ValueError。"""
|
||
url = _parse(raw, _CHECKPOINT_SCHEMES, "postgresql://用户:密码@主机:5432/库名")
|
||
return libpq_conninfo(url.render_as_string(hide_password=False))
|
||
|
||
|
||
def libpq_conninfo(database_url: str) -> str:
|
||
"""SQLAlchemy 的地址 → psycopg 的连接串:去掉驱动名,asyncpg 写法的参数换成 libpq 的。"""
|
||
url = make_url(database_url)
|
||
query = {_ASYNCPG_TO_LIBPQ.get(k, k): v for k, v in url.query.items() if k not in _ASYNCPG_ONLY}
|
||
return url.set(drivername="postgresql", query=query).render_as_string(hide_password=False)
|
||
|
||
|
||
def describe(database_url: str) -> str:
|
||
"""写进日志的样子:主机、端口、库名、用户,不写密码。"""
|
||
url = make_url(database_url)
|
||
host = url.host or "本机套接字"
|
||
port = f":{url.port}" if url.port else ""
|
||
user = f",用户 {url.username}" if url.username else ""
|
||
return f"{host}{port}/{url.database}{user}"
|