feat: M1 — 立项→写章草稿(SSE)→自动保存;连一家 provider
- 薄自建 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 走通闭环
This commit is contained in:
51
packages/llm_gateway/ww_llm_gateway/adapters/base.py
Normal file
51
packages/llm_gateway/ww_llm_gateway/adapters/base.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""适配器接口与中间数据形(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]: ...
|
||||
Reference in New Issue
Block a user