feat(core): 添加共享 Protocol 端口
LLMProvider / VLMProvider / TelemetryRecorder,全部 runtime_checkable。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,64 @@
|
|||||||
|
"""共享 Protocol 端口定义。
|
||||||
|
|
||||||
|
LLMProvider / VLMProvider / TelemetryRecorder 是跨子包共享接口,
|
||||||
|
被 core/agent/、core/evolution/、app/ 各模块引用。
|
||||||
|
adapters/ 提供具体实现。
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Protocol, runtime_checkable
|
||||||
|
|
||||||
|
from core.types import LLMResponse
|
||||||
|
|
||||||
|
|
||||||
|
@runtime_checkable
|
||||||
|
class LLMProvider(Protocol):
|
||||||
|
"""LLM 文本调用端口。"""
|
||||||
|
|
||||||
|
async def chat(
|
||||||
|
self,
|
||||||
|
messages: list[dict[str, Any]],
|
||||||
|
*,
|
||||||
|
session_id: str | None = None,
|
||||||
|
parent_call_id: str | None = None,
|
||||||
|
) -> LLMResponse: ...
|
||||||
|
|
||||||
|
|
||||||
|
@runtime_checkable
|
||||||
|
class VLMProvider(Protocol):
|
||||||
|
"""VLM 图文调用端口。"""
|
||||||
|
|
||||||
|
async def chat_with_images(
|
||||||
|
self,
|
||||||
|
messages: list[dict[str, Any]],
|
||||||
|
images: list[str | Path],
|
||||||
|
*,
|
||||||
|
session_id: str | None = None,
|
||||||
|
parent_call_id: str | None = None,
|
||||||
|
) -> LLMResponse: ...
|
||||||
|
|
||||||
|
|
||||||
|
@runtime_checkable
|
||||||
|
class TelemetryRecorder(Protocol):
|
||||||
|
"""LLM 调用遥测记录端口。"""
|
||||||
|
|
||||||
|
async def record_llm_call(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
call_id: str,
|
||||||
|
parent_call_id: str | None,
|
||||||
|
session_id: str | None,
|
||||||
|
model_name: str,
|
||||||
|
provider: str,
|
||||||
|
messages: str,
|
||||||
|
response: str,
|
||||||
|
thinking: str,
|
||||||
|
prompt_tokens: int,
|
||||||
|
completion_tokens: int,
|
||||||
|
latency_ms: int,
|
||||||
|
ttft_ms: float | None,
|
||||||
|
max_inter_token_ms: float | None,
|
||||||
|
cache_hit: bool,
|
||||||
|
error: str | None,
|
||||||
|
) -> None: ...
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
"""core/protocols.py 单元测试 — 验证 Protocol 可 runtime_checkable。"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from core.protocols import LLMProvider, TelemetryRecorder, VLMProvider
|
||||||
|
from core.types import LLMResponse
|
||||||
|
|
||||||
|
|
||||||
|
class _FakeLLM:
|
||||||
|
async def chat(
|
||||||
|
self,
|
||||||
|
messages: list[dict[str, Any]],
|
||||||
|
*,
|
||||||
|
session_id: str | None = None,
|
||||||
|
parent_call_id: str | None = None,
|
||||||
|
) -> LLMResponse:
|
||||||
|
return LLMResponse(
|
||||||
|
content="ok", thinking="", model="m", provider="p",
|
||||||
|
prompt_tokens=1, completion_tokens=1, latency_ms=1,
|
||||||
|
ttft_ms=None, max_inter_token_ms=None, cache_hit=False, call_id="c",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class _FakeVLM:
|
||||||
|
async def chat_with_images(
|
||||||
|
self,
|
||||||
|
messages: list[dict[str, Any]],
|
||||||
|
images: list[str | Path],
|
||||||
|
*,
|
||||||
|
session_id: str | None = None,
|
||||||
|
parent_call_id: str | None = None,
|
||||||
|
) -> LLMResponse:
|
||||||
|
return LLMResponse(
|
||||||
|
content="ok", thinking="", model="m", provider="p",
|
||||||
|
prompt_tokens=1, completion_tokens=1, latency_ms=1,
|
||||||
|
ttft_ms=None, max_inter_token_ms=None, cache_hit=False, call_id="c",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class _FakeTelemetry:
|
||||||
|
async def record_llm_call(
|
||||||
|
self, *, call_id: str, parent_call_id: str | None, session_id: str | None,
|
||||||
|
model_name: str, provider: str, messages: str, response: str, thinking: str,
|
||||||
|
prompt_tokens: int, completion_tokens: int, latency_ms: int,
|
||||||
|
ttft_ms: float | None, max_inter_token_ms: float | None,
|
||||||
|
cache_hit: bool, error: str | None,
|
||||||
|
) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_fake_llm_satisfies_protocol() -> None:
|
||||||
|
assert isinstance(_FakeLLM(), LLMProvider)
|
||||||
|
|
||||||
|
def test_fake_vlm_satisfies_protocol() -> None:
|
||||||
|
assert isinstance(_FakeVLM(), VLMProvider)
|
||||||
|
|
||||||
|
def test_fake_telemetry_satisfies_protocol() -> None:
|
||||||
|
assert isinstance(_FakeTelemetry(), TelemetryRecorder)
|
||||||
|
|
||||||
|
def test_plain_object_does_not_satisfy() -> None:
|
||||||
|
assert not isinstance(object(), LLMProvider)
|
||||||
|
assert not isinstance(object(), VLMProvider)
|
||||||
|
assert not isinstance(object(), TelemetryRecorder)
|
||||||
Reference in New Issue
Block a user