docs: document caller dimensions and the RLS boundary
Issue #11 Task 8, repo files only (the Gitea wiki pages are handled separately at merge time). CHANGELOG gains an unreleased section covering the two new keyword-only parameters on all four public methods, the two new telemetry columns (JSONB on PG, TEXT on SQLite), why backfilled rows read as an empty string rather than NULL, the validation limits, and the boundary that the library ships columns only - no index, no RLS. Telemetry field count re-measured via inspect.signature: 22 -> 24, README updated accordingly. The cache-key row also dropped the sampling component and called the namespace a tenant, which now reads as the new tenant_id; both corrected.
This commit is contained in:
@@ -16,9 +16,10 @@
|
||||
| 熔断 | 双通道(连续失败 + 失败率窗口,健康证据抑制误熔);半开单探针带租约(持有者死亡自动回收);epoch fencing 拒绝迟到写回;开路时长指数递增 |
|
||||
| 自适应并发 | AIMD:429 削减、成功缓升,防止打爆上游 |
|
||||
| 背压与判死 | 配额满可选等待或快速失败;等待期按双条件判死(本地非生产性等待与全局无进展**同时**超窗)。stall 窗口只计**非生产性**等待(429 退避/配额轮询/熔断冷却),与 `TIMEOUT_S` 无耦合 |
|
||||
| 响应缓存 | Redis/内存;key 含 model + messages 摘要 + namespace/租户 + salt,多模态 content 先摘要再 hash(防毒化);可 per-call 绕过(科研重采样) |
|
||||
| 响应缓存 | Redis/内存;key 含 model + messages 摘要 + namespace(缓存隔离单位)+ salt + 采样参数,多模态 content 先摘要再 hash(防毒化);可 per-call 绕过(科研重采样) |
|
||||
| 流式看门狗 | TTFT / inter-token / 总超时三层活性;thinking token 刷活性不计结果;截断流(缺 `[DONE]`)判瞬时不入缓存 |
|
||||
| 遥测与成本 | 每次调用(含缓存命中与失败)必录 22 字段;SQLite / Postgres 后端(表已存在时**不需要** schema 建表权限,最小权限账号可直接用);按价格表折算成本落库(注意 `LLMResponse.cost` 本身恒为 `None`,成本只进遥测);多模态内容摘要落库不存原图 |
|
||||
| 遥测与成本 | 每次调用(含缓存命中与失败)必录 24 字段;SQLite / Postgres 后端(表已存在时**不需要** schema 建表权限,最小权限账号可直接用);按价格表折算成本落库(注意 `LLMResponse.cost` 本身恒为 `None`,成本只进遥测);多模态内容摘要落库不存原图 |
|
||||
| 调用方维度 | 每次调用可带 `tenant_id`(遥测表的真实列,可挂 RLS、可建复合索引)与 `meta`(≤16 个自定义 KV);四个公共方法全覆盖,校验超限即报错;**库只交付列,不启用 RLS、不建索引** |
|
||||
| 结构化输出 | json_repair 修复 / 原生 schema 双策略 + 校验失败有界带反馈重问 |
|
||||
| OCR | MonkeyOCR 双端点(文本转录 + 版面解析),bbox 数值防御下沉,逐源健康预检 `check_health()` |
|
||||
| Embedding | 分批、维度校验、与 chat 同一治理栈 |
|
||||
@@ -81,7 +82,7 @@ async def main() -> None:
|
||||
await client.aclose() # 归还连接与治理后端资源
|
||||
```
|
||||
|
||||
`chat()` 原生接受 OpenAI 多模态 content 数组(`image_url` data URL),VLM 调用无需专门客户端;`session_id` / `parent_call_id` / `cache_salt` 关键字参数用于链路追踪与缓存控制;`overlay` 传采样参数(`temperature` / `seed` / `max_tokens` 等,恒定值宜配在源的 `EXTRA_BODY` 上)——它会进缓存 key,故逐次变化的 `seed` 天然不命中缓存。
|
||||
`chat()` 原生接受 OpenAI 多模态 content 数组(`image_url` data URL),VLM 调用无需专门客户端;`session_id` / `parent_call_id` / `cache_salt` 关键字参数用于链路追踪与缓存控制,`tenant_id` / `meta` 用于遥测归属(见下文「多租户与自定义维度」);`overlay` 传采样参数(`temperature` / `seed` / `max_tokens` 等,恒定值宜配在源的 `EXTRA_BODY` 上)——它会进缓存 key,故逐次变化的 `seed` 天然不命中缓存。
|
||||
|
||||
### 3. OCR 与 Embedding
|
||||
|
||||
@@ -112,6 +113,22 @@ except RequestRejectedError:
|
||||
... # 请求本身有问题(400/格式拒绝): 不重试,直接失败
|
||||
```
|
||||
|
||||
### 5. 多租户与自定义维度
|
||||
|
||||
```python
|
||||
resp = await client.chat(
|
||||
messages,
|
||||
tenant_id="acme-corp", # 遥测表的真实列,可挂 RLS
|
||||
meta={"batch_id": "b-42", "stage": "extract"}, # 任意自定义 KV,进 meta 列
|
||||
)
|
||||
```
|
||||
|
||||
四个公共方法(`chat` / `embed` / `recognize_text` / `parse_layout`)都接受这两个关键字参数,都可省略,既有调用点无需改动。校验在入口收口、**超限报 `ValueError` 而非静默丢弃**:`tenant_id` ≤128 字符且不含首尾空白;`meta` 最多 16 个键,键须匹配 `[a-z0-9_.]{1,64}`(`pg_` 前缀保留给库),值仅限 `str` / `int` / `float` / `bool` 且字符串值 ≤256 字符。两者**都不进缓存 key**——缓存隔离由 `cache_namespace` 负责。
|
||||
|
||||
存储上 `tenant_id` 两端都是 `TEXT NOT NULL DEFAULT ''`,`meta` 在 Postgres 是 `JSONB`、在 SQLite 是 `TEXT`;老表自动补列,**老行读出是空串而非 NULL**(NULL 在任何 RLS policy 下都对所有人不可见,空串则可用一条 SQL 审出还有多少行待归属)。
|
||||
|
||||
**库只提供列,不启用 RLS、不建索引。** 要数据库层的强制隔离,请自行执行 `ENABLE` / `FORCE ROW LEVEL SECURITY` 与 `CREATE POLICY`,并建 `(tenant_id, created_at)` 复合索引;不执行则 `tenant_id` 只是一个可查可过滤的普通列。库不代劳的原因是 default-deny:启用 RLS 而没有匹配的 policy = 零行可写且静默不报错,会让非多租户部署的遥测全量写失败。
|
||||
|
||||
## 错误模型(四分类)
|
||||
|
||||
一切失败在 transport 层翻译为四类之一,治理行为由分类决定,业务侧不需要判断状态码:
|
||||
|
||||
Reference in New Issue
Block a user