- 薄自建 LLM 网关:OpenAI 兼容适配器(DeepSeek) + instructor 结构化输出 + usage_ledger 记账 + 档位路由 - 记忆服务 assemble:确定性选择(显式+主角+近况) + 渲染卡 + 缓存断点(中性文本) - LangGraph 写章节点 + Postgres checkpointer + SSE 归一(token/done/error) - API:立项 + 写章 draft(SSE) + PUT 自动保存 + 提供商凭据(Fernet 加密/测试连接) - 前端:AppShell + 作品库 + 5 步立项向导 + 写作工作台(流式打字机+自动保存) + 设置页 - M1 E2E:真实 DB + mock 网关零 token 走通闭环
52 lines
1.3 KiB
Python
52 lines
1.3 KiB
Python
"""适配器接口与中间数据形(ARCH §4.2/§4.4)。
|
||
|
||
适配器把 `LlmRequest` 翻译成目标厂商请求,并把响应/流/usage 翻译回统一中间形。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from collections.abc import AsyncIterator
|
||
from typing import Protocol, runtime_checkable
|
||
|
||
from pydantic import BaseModel, ConfigDict
|
||
|
||
from ..types import LlmRequest
|
||
|
||
|
||
class Capabilities(BaseModel):
|
||
structured_output: bool = False
|
||
prefix_cache: bool = False
|
||
thinking: bool = False
|
||
|
||
|
||
class ProviderUsage(BaseModel):
|
||
input_tokens: int
|
||
output_tokens: int
|
||
cache_read_tokens: int = 0
|
||
|
||
|
||
class ProviderResult(BaseModel):
|
||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||
|
||
text: str
|
||
usage: ProviderUsage
|
||
parsed: BaseModel | None = None # output_schema 命中时的结构化结果(§4.4)
|
||
|
||
|
||
class StreamChunk(BaseModel):
|
||
"""流式块:文本增量(usage=None),或末尾用量块(text="")。"""
|
||
|
||
text: str = ""
|
||
usage: ProviderUsage | None = None
|
||
|
||
|
||
@runtime_checkable
|
||
class ProviderAdapter(Protocol):
|
||
provider: str
|
||
|
||
def capabilities(self) -> Capabilities: ...
|
||
|
||
async def complete(self, req: LlmRequest, model: str) -> ProviderResult: ...
|
||
|
||
def stream(self, req: LlmRequest, model: str) -> AsyncIterator[StreamChunk]: ...
|