mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-04-25 08:26:21 +02:00
1517 lines
47 KiB
Markdown
1517 lines
47 KiB
Markdown
|
|
---
|
|||
|
|
layout: default
|
|||
|
|
title: "Python API 重构技术规范"
|
|||
|
|
parent: "Chinese (Beta)"
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Python API 重构技术规范
|
|||
|
|
|
|||
|
|
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
本规范描述了 TrustGraph Python API 客户端库的全面重构,旨在实现与 API 网关的功能对齐,并添加对现代实时通信模式的支持。
|
|||
|
|
|
|||
|
|
该重构解决了四个主要用例:
|
|||
|
|
|
|||
|
|
1. **流式 LLM 交互**: 启用 LLM 响应的实时流式传输(代理、图 RAG、文档 RAG、文本补全、提示),延迟降低约 60 倍(第一个 token 为 500 毫秒,而不是 30 秒)。
|
|||
|
|
2. **批量数据操作**: 支持高效的大规模知识图谱管理的 triples、图嵌入和文档嵌入的批量导入/导出。
|
|||
|
|
3. **功能对齐**: 确保每个 API 网关端点都有相应的 Python API 方法,包括图嵌入查询。
|
|||
|
|
4. **持久连接**: 启用基于 WebSocket 的通信,以实现多路复用请求和降低连接开销。
|
|||
|
|
|
|||
|
|
## 目标
|
|||
|
|
|
|||
|
|
**功能对齐**: 每个网关 API 服务都有相应的 Python API 方法。
|
|||
|
|
**流式支持**: 所有支持流式传输的服务(代理、RAG、文本补全、提示)在 Python API 中支持流式传输。
|
|||
|
|
**WebSocket 传输**: 添加可选的 WebSocket 传输层,用于持久连接和多路复用。
|
|||
|
|
**批量操作**: 添加 triples、图嵌入和文档嵌入的高效批量导入/导出功能。
|
|||
|
|
**完全异步支持**: 为所有接口(REST、WebSocket、批量操作、指标)实现完整的 async/await。
|
|||
|
|
**向后兼容性**: 现有代码无需修改即可继续工作。
|
|||
|
|
**类型安全**: 维护类型安全的接口,使用 dataclasses 和类型提示。
|
|||
|
|
**渐进式增强**: 流式传输和异步是可选的,通过显式接口选择启用。
|
|||
|
|
**性能**: 实现流式操作的 60 倍延迟改进。
|
|||
|
|
**现代 Python**: 支持同步和异步范式,以实现最大的灵活性。
|
|||
|
|
|
|||
|
|
## 背景
|
|||
|
|
|
|||
|
|
### 当前状态
|
|||
|
|
|
|||
|
|
Python API (`trustgraph-base/trustgraph/api/`) 是一个仅支持 REST 的客户端库,包含以下模块:
|
|||
|
|
|
|||
|
|
`flow.py`: 流管理和流范围服务(50 个方法)。
|
|||
|
|
`library.py`: 文档库操作(9 个方法)。
|
|||
|
|
`knowledge.py`: KG 核心管理(4 个方法)。
|
|||
|
|
`collection.py`: 集合元数据(3 个方法)。
|
|||
|
|
`config.py`: 配置管理(6 个方法)。
|
|||
|
|
`types.py`: 数据类型定义(5 个 dataclasses)。
|
|||
|
|
|
|||
|
|
**总操作**: 50/59 (覆盖率 85%)
|
|||
|
|
|
|||
|
|
### 当前限制
|
|||
|
|
|
|||
|
|
**缺失的操作**:
|
|||
|
|
图嵌入查询(图实体上的语义搜索)。
|
|||
|
|
triples、图嵌入、文档嵌入、实体上下文、对象的批量导入/导出。
|
|||
|
|
指标端点。
|
|||
|
|
|
|||
|
|
**缺失的功能**:
|
|||
|
|
LLM 服务的流式传输支持。
|
|||
|
|
WebSocket 传输。
|
|||
|
|
多路复用的并发请求。
|
|||
|
|
持久连接。
|
|||
|
|
|
|||
|
|
**性能问题**:
|
|||
|
|
LLM 交互的延迟高(第一个 token 需要 30 秒)。
|
|||
|
|
批量数据传输效率低下(每个项目一个 REST 请求)。
|
|||
|
|
多个连续操作的连接开销。
|
|||
|
|
|
|||
|
|
**用户体验问题**:
|
|||
|
|
LLM 生成过程中没有实时反馈。
|
|||
|
|
无法取消长时间运行的 LLM 操作。
|
|||
|
|
批量操作的可扩展性差。
|
|||
|
|
|
|||
|
|
### 影响
|
|||
|
|
|
|||
|
|
2024 年 11 月对网关 API 的流式增强,将 LLM 交互的延迟提高了 60 倍(第一个 token 为 500 毫秒,而不是 30 秒),但 Python API 用户无法利用此功能。 这在 Python 用户和非 Python 用户之间造成了显著的体验差距。
|
|||
|
|
|
|||
|
|
## 技术设计
|
|||
|
|
|
|||
|
|
### 架构
|
|||
|
|
|
|||
|
|
重构的 Python API 采用 **模块化接口方法**,具有用于不同通信模式的单独对象。 所有接口都提供 **同步和异步** 两种版本:
|
|||
|
|
|
|||
|
|
1. **REST 接口** (现有,增强)
|
|||
|
|
**同步**: `api.flow()`, `api.library()`, `api.knowledge()`, `api.collection()`, `api.config()`
|
|||
|
|
**异步**: `api.async_flow()`
|
|||
|
|
同步/异步请求/响应。
|
|||
|
|
简单的连接模型。
|
|||
|
|
默认用于向后兼容。
|
|||
|
|
|
|||
|
|
2. **WebSocket 接口** (新)
|
|||
|
|
**同步**: `api.socket()`
|
|||
|
|
**异步**: `api.async_socket()`
|
|||
|
|
持久连接。
|
|||
|
|
多路复用的请求。
|
|||
|
|
流式传输支持。
|
|||
|
|
功能重叠时,方法签名与 REST 相同。
|
|||
|
|
|
|||
|
|
3. **批量操作接口** (新)
|
|||
|
|
**同步**: `api.bulk()`
|
|||
|
|
**异步**: `api.async_bulk()`
|
|||
|
|
基于 WebSocket,提高效率。
|
|||
|
|
基于 Iterator/AsyncIterator 的导入/导出。
|
|||
|
|
处理大型数据集。
|
|||
|
|
|
|||
|
|
4. **指标接口** (新)
|
|||
|
|
**同步**: `api.metrics()`
|
|||
|
|
**异步**: `api.async_metrics()`
|
|||
|
|
Prometheus 指标访问。
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import asyncio
|
|||
|
|
|
|||
|
|
# Synchronous interfaces
|
|||
|
|
api = Api(url="http://localhost:8088/")
|
|||
|
|
|
|||
|
|
# REST (existing, unchanged)
|
|||
|
|
flow = api.flow().id("default")
|
|||
|
|
response = flow.agent(question="...", user="...")
|
|||
|
|
|
|||
|
|
# WebSocket (new)
|
|||
|
|
socket_flow = api.socket().flow("default")
|
|||
|
|
response = socket_flow.agent(question="...", user="...")
|
|||
|
|
for chunk in socket_flow.agent(question="...", user="...", streaming=True):
|
|||
|
|
print(chunk)
|
|||
|
|
|
|||
|
|
# Bulk operations (new)
|
|||
|
|
bulk = api.bulk()
|
|||
|
|
bulk.import_triples(flow="default", triples=triple_generator())
|
|||
|
|
|
|||
|
|
# Asynchronous interfaces
|
|||
|
|
async def main():
|
|||
|
|
api = Api(url="http://localhost:8088/")
|
|||
|
|
|
|||
|
|
# Async REST (new)
|
|||
|
|
flow = api.async_flow().id("default")
|
|||
|
|
response = await flow.agent(question="...", user="...")
|
|||
|
|
|
|||
|
|
# Async WebSocket (new)
|
|||
|
|
socket_flow = api.async_socket().flow("default")
|
|||
|
|
async for chunk in socket_flow.agent(question="...", streaming=True):
|
|||
|
|
print(chunk)
|
|||
|
|
|
|||
|
|
# Async bulk operations (new)
|
|||
|
|
bulk = api.async_bulk()
|
|||
|
|
await bulk.import_triples(flow="default", triples=async_triple_generator())
|
|||
|
|
|
|||
|
|
asyncio.run(main())
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**关键设计原则**:
|
|||
|
|
**所有接口使用相同的URL**: `Api(url="http://localhost:8088/")` 对所有接口都有效。
|
|||
|
|
**同步/异步对称**: 每个接口都具有同步和异步变体,方法签名相同。
|
|||
|
|
**相同的签名**: 当功能重叠时,REST 和 WebSocket 之间的同步和异步方法签名相同。
|
|||
|
|
**渐进式增强**: 根据需求选择接口(REST 用于简单操作,WebSocket 用于流式传输,Bulk 用于大型数据集,async 用于现代框架)。
|
|||
|
|
**明确意图**: `api.socket()` 表示 WebSocket,`api.async_socket()` 表示异步 WebSocket。
|
|||
|
|
**向后兼容**: 现有代码保持不变。
|
|||
|
|
|
|||
|
|
### 组件
|
|||
|
|
|
|||
|
|
#### 1. 核心 API 类 (修改版)
|
|||
|
|
|
|||
|
|
模块: `trustgraph-base/trustgraph/api/api.py`
|
|||
|
|
|
|||
|
|
**增强的 API 类**:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class Api:
|
|||
|
|
def __init__(self, url: str, timeout: int = 60, token: Optional[str] = None):
|
|||
|
|
self.url = url
|
|||
|
|
self.timeout = timeout
|
|||
|
|
self.token = token # Optional bearer token for REST, query param for WebSocket
|
|||
|
|
self._socket_client = None
|
|||
|
|
self._bulk_client = None
|
|||
|
|
self._async_flow = None
|
|||
|
|
self._async_socket_client = None
|
|||
|
|
self._async_bulk_client = None
|
|||
|
|
|
|||
|
|
# Existing synchronous methods (unchanged)
|
|||
|
|
def flow(self) -> Flow:
|
|||
|
|
"""Synchronous REST-based flow interface"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def library(self) -> Library:
|
|||
|
|
"""Synchronous REST-based library interface"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def knowledge(self) -> Knowledge:
|
|||
|
|
"""Synchronous REST-based knowledge interface"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def collection(self) -> Collection:
|
|||
|
|
"""Synchronous REST-based collection interface"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def config(self) -> Config:
|
|||
|
|
"""Synchronous REST-based config interface"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
# New synchronous methods
|
|||
|
|
def socket(self) -> SocketClient:
|
|||
|
|
"""Synchronous WebSocket-based interface for streaming operations"""
|
|||
|
|
if self._socket_client is None:
|
|||
|
|
self._socket_client = SocketClient(self.url, self.timeout, self.token)
|
|||
|
|
return self._socket_client
|
|||
|
|
|
|||
|
|
def bulk(self) -> BulkClient:
|
|||
|
|
"""Synchronous bulk operations interface for import/export"""
|
|||
|
|
if self._bulk_client is None:
|
|||
|
|
self._bulk_client = BulkClient(self.url, self.timeout, self.token)
|
|||
|
|
return self._bulk_client
|
|||
|
|
|
|||
|
|
def metrics(self) -> Metrics:
|
|||
|
|
"""Synchronous metrics interface"""
|
|||
|
|
return Metrics(self.url, self.timeout, self.token)
|
|||
|
|
|
|||
|
|
# New asynchronous methods
|
|||
|
|
def async_flow(self) -> AsyncFlow:
|
|||
|
|
"""Asynchronous REST-based flow interface"""
|
|||
|
|
if self._async_flow is None:
|
|||
|
|
self._async_flow = AsyncFlow(self.url, self.timeout, self.token)
|
|||
|
|
return self._async_flow
|
|||
|
|
|
|||
|
|
def async_socket(self) -> AsyncSocketClient:
|
|||
|
|
"""Asynchronous WebSocket-based interface for streaming operations"""
|
|||
|
|
if self._async_socket_client is None:
|
|||
|
|
self._async_socket_client = AsyncSocketClient(self.url, self.timeout, self.token)
|
|||
|
|
return self._async_socket_client
|
|||
|
|
|
|||
|
|
def async_bulk(self) -> AsyncBulkClient:
|
|||
|
|
"""Asynchronous bulk operations interface for import/export"""
|
|||
|
|
if self._async_bulk_client is None:
|
|||
|
|
self._async_bulk_client = AsyncBulkClient(self.url, self.timeout, self.token)
|
|||
|
|
return self._async_bulk_client
|
|||
|
|
|
|||
|
|
def async_metrics(self) -> AsyncMetrics:
|
|||
|
|
"""Asynchronous metrics interface"""
|
|||
|
|
return AsyncMetrics(self.url, self.timeout, self.token)
|
|||
|
|
|
|||
|
|
# Resource management
|
|||
|
|
def close(self) -> None:
|
|||
|
|
"""Close all synchronous connections"""
|
|||
|
|
if self._socket_client:
|
|||
|
|
self._socket_client.close()
|
|||
|
|
if self._bulk_client:
|
|||
|
|
self._bulk_client.close()
|
|||
|
|
|
|||
|
|
async def aclose(self) -> None:
|
|||
|
|
"""Close all asynchronous connections"""
|
|||
|
|
if self._async_socket_client:
|
|||
|
|
await self._async_socket_client.aclose()
|
|||
|
|
if self._async_bulk_client:
|
|||
|
|
await self._async_bulk_client.aclose()
|
|||
|
|
if self._async_flow:
|
|||
|
|
await self._async_flow.aclose()
|
|||
|
|
|
|||
|
|
def __enter__(self):
|
|||
|
|
return self
|
|||
|
|
|
|||
|
|
def __exit__(self, *args):
|
|||
|
|
self.close()
|
|||
|
|
|
|||
|
|
async def __aenter__(self):
|
|||
|
|
return self
|
|||
|
|
|
|||
|
|
async def __aexit__(self, *args):
|
|||
|
|
await self.aclose()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 2. 同步 WebSocket 客户端
|
|||
|
|
|
|||
|
|
模块:`trustgraph-base/trustgraph/api/socket_client.py` (新)
|
|||
|
|
|
|||
|
|
**SocketClient 类**:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class SocketClient:
|
|||
|
|
"""Synchronous WebSocket client"""
|
|||
|
|
def __init__(self, url: str, timeout: int, token: Optional[str]):
|
|||
|
|
self.url = self._convert_to_ws_url(url)
|
|||
|
|
self.timeout = timeout
|
|||
|
|
self.token = token
|
|||
|
|
self._connection = None
|
|||
|
|
self._request_counter = 0
|
|||
|
|
|
|||
|
|
def flow(self, flow_id: str) -> SocketFlowInstance:
|
|||
|
|
"""Get flow instance for WebSocket operations"""
|
|||
|
|
return SocketFlowInstance(self, flow_id)
|
|||
|
|
|
|||
|
|
def _connect(self) -> WebSocket:
|
|||
|
|
"""Establish WebSocket connection (lazy)"""
|
|||
|
|
# Uses asyncio.run() internally to wrap async websockets library
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def _send_request(
|
|||
|
|
self,
|
|||
|
|
service: str,
|
|||
|
|
flow: Optional[str],
|
|||
|
|
request: Dict[str, Any],
|
|||
|
|
streaming: bool = False
|
|||
|
|
) -> Union[Dict[str, Any], Iterator[Dict[str, Any]]]:
|
|||
|
|
"""Send request and handle response/streaming"""
|
|||
|
|
# Synchronous wrapper around async WebSocket calls
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def close(self) -> None:
|
|||
|
|
"""Close WebSocket connection"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
class SocketFlowInstance:
|
|||
|
|
"""Synchronous WebSocket flow instance with same interface as REST FlowInstance"""
|
|||
|
|
def __init__(self, client: SocketClient, flow_id: str):
|
|||
|
|
self.client = client
|
|||
|
|
self.flow_id = flow_id
|
|||
|
|
|
|||
|
|
# Same method signatures as FlowInstance
|
|||
|
|
def agent(
|
|||
|
|
self,
|
|||
|
|
question: str,
|
|||
|
|
user: str,
|
|||
|
|
state: Optional[Dict[str, Any]] = None,
|
|||
|
|
group: Optional[str] = None,
|
|||
|
|
history: Optional[List[Dict[str, Any]]] = None,
|
|||
|
|
streaming: bool = False,
|
|||
|
|
**kwargs
|
|||
|
|
) -> Union[Dict[str, Any], Iterator[Dict[str, Any]]]:
|
|||
|
|
"""Agent with optional streaming"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def text_completion(
|
|||
|
|
self,
|
|||
|
|
system: str,
|
|||
|
|
prompt: str,
|
|||
|
|
streaming: bool = False,
|
|||
|
|
**kwargs
|
|||
|
|
) -> Union[str, Iterator[str]]:
|
|||
|
|
"""Text completion with optional streaming"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
# ... similar for graph_rag, document_rag, prompt, etc.
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**主要特性:**
|
|||
|
|
延迟连接(仅在首次发送请求时建立连接)
|
|||
|
|
请求复用(最多 15 个并发请求)
|
|||
|
|
断开连接时自动重连
|
|||
|
|
流式响应解析
|
|||
|
|
线程安全操作
|
|||
|
|
异步 WebSocket 库的同步封装
|
|||
|
|
|
|||
|
|
#### 3. 异步 WebSocket 客户端
|
|||
|
|
|
|||
|
|
模块:`trustgraph-base/trustgraph/api/async_socket_client.py` (新)
|
|||
|
|
|
|||
|
|
**AsyncSocketClient 类:**
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class AsyncSocketClient:
|
|||
|
|
"""Asynchronous WebSocket client"""
|
|||
|
|
def __init__(self, url: str, timeout: int, token: Optional[str]):
|
|||
|
|
self.url = self._convert_to_ws_url(url)
|
|||
|
|
self.timeout = timeout
|
|||
|
|
self.token = token
|
|||
|
|
self._connection = None
|
|||
|
|
self._request_counter = 0
|
|||
|
|
|
|||
|
|
def flow(self, flow_id: str) -> AsyncSocketFlowInstance:
|
|||
|
|
"""Get async flow instance for WebSocket operations"""
|
|||
|
|
return AsyncSocketFlowInstance(self, flow_id)
|
|||
|
|
|
|||
|
|
async def _connect(self) -> WebSocket:
|
|||
|
|
"""Establish WebSocket connection (lazy)"""
|
|||
|
|
# Native async websockets library
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
async def _send_request(
|
|||
|
|
self,
|
|||
|
|
service: str,
|
|||
|
|
flow: Optional[str],
|
|||
|
|
request: Dict[str, Any],
|
|||
|
|
streaming: bool = False
|
|||
|
|
) -> Union[Dict[str, Any], AsyncIterator[Dict[str, Any]]]:
|
|||
|
|
"""Send request and handle response/streaming"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
async def aclose(self) -> None:
|
|||
|
|
"""Close WebSocket connection"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
class AsyncSocketFlowInstance:
|
|||
|
|
"""Asynchronous WebSocket flow instance"""
|
|||
|
|
def __init__(self, client: AsyncSocketClient, flow_id: str):
|
|||
|
|
self.client = client
|
|||
|
|
self.flow_id = flow_id
|
|||
|
|
|
|||
|
|
# Same method signatures as FlowInstance (but async)
|
|||
|
|
async def agent(
|
|||
|
|
self,
|
|||
|
|
question: str,
|
|||
|
|
user: str,
|
|||
|
|
state: Optional[Dict[str, Any]] = None,
|
|||
|
|
group: Optional[str] = None,
|
|||
|
|
history: Optional[List[Dict[str, Any]]] = None,
|
|||
|
|
streaming: bool = False,
|
|||
|
|
**kwargs
|
|||
|
|
) -> Union[Dict[str, Any], AsyncIterator[Dict[str, Any]]]:
|
|||
|
|
"""Agent with optional streaming"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
async def text_completion(
|
|||
|
|
self,
|
|||
|
|
system: str,
|
|||
|
|
prompt: str,
|
|||
|
|
streaming: bool = False,
|
|||
|
|
**kwargs
|
|||
|
|
) -> Union[str, AsyncIterator[str]]:
|
|||
|
|
"""Text completion with optional streaming"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
# ... similar for graph_rag, document_rag, prompt, etc.
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**主要特性:**
|
|||
|
|
原生支持 async/await
|
|||
|
|
适用于异步应用程序(FastAPI, aiohttp)
|
|||
|
|
无线程阻塞
|
|||
|
|
与同步版本具有相同的接口
|
|||
|
|
AsyncIterator 用于流式传输
|
|||
|
|
|
|||
|
|
#### 4. 同步批量操作客户端
|
|||
|
|
|
|||
|
|
模块:`trustgraph-base/trustgraph/api/bulk_client.py` (新)
|
|||
|
|
|
|||
|
|
**BulkClient 类:**
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class BulkClient:
|
|||
|
|
"""Synchronous bulk operations client"""
|
|||
|
|
def __init__(self, url: str, timeout: int, token: Optional[str]):
|
|||
|
|
self.url = self._convert_to_ws_url(url)
|
|||
|
|
self.timeout = timeout
|
|||
|
|
self.token = token
|
|||
|
|
|
|||
|
|
def import_triples(
|
|||
|
|
self,
|
|||
|
|
flow: str,
|
|||
|
|
triples: Iterator[Triple],
|
|||
|
|
**kwargs
|
|||
|
|
) -> None:
|
|||
|
|
"""Bulk import triples via WebSocket"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def export_triples(
|
|||
|
|
self,
|
|||
|
|
flow: str,
|
|||
|
|
**kwargs
|
|||
|
|
) -> Iterator[Triple]:
|
|||
|
|
"""Bulk export triples via WebSocket"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def import_graph_embeddings(
|
|||
|
|
self,
|
|||
|
|
flow: str,
|
|||
|
|
embeddings: Iterator[Dict[str, Any]],
|
|||
|
|
**kwargs
|
|||
|
|
) -> None:
|
|||
|
|
"""Bulk import graph embeddings via WebSocket"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def export_graph_embeddings(
|
|||
|
|
self,
|
|||
|
|
flow: str,
|
|||
|
|
**kwargs
|
|||
|
|
) -> Iterator[Dict[str, Any]]:
|
|||
|
|
"""Bulk export graph embeddings via WebSocket"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
# ... similar for document embeddings, entity contexts, objects
|
|||
|
|
|
|||
|
|
def close(self) -> None:
|
|||
|
|
"""Close connections"""
|
|||
|
|
pass
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**主要特性:**
|
|||
|
|
基于迭代器,内存占用恒定
|
|||
|
|
每个操作都使用专用的 WebSocket 连接
|
|||
|
|
进度跟踪(可选的回调函数)
|
|||
|
|
错误处理,支持部分成功报告
|
|||
|
|
|
|||
|
|
#### 5. 异步批量操作客户端
|
|||
|
|
|
|||
|
|
模块:`trustgraph-base/trustgraph/api/async_bulk_client.py` (新)
|
|||
|
|
|
|||
|
|
**AsyncBulkClient 类:**
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class AsyncBulkClient:
|
|||
|
|
"""Asynchronous bulk operations client"""
|
|||
|
|
def __init__(self, url: str, timeout: int, token: Optional[str]):
|
|||
|
|
self.url = self._convert_to_ws_url(url)
|
|||
|
|
self.timeout = timeout
|
|||
|
|
self.token = token
|
|||
|
|
|
|||
|
|
async def import_triples(
|
|||
|
|
self,
|
|||
|
|
flow: str,
|
|||
|
|
triples: AsyncIterator[Triple],
|
|||
|
|
**kwargs
|
|||
|
|
) -> None:
|
|||
|
|
"""Bulk import triples via WebSocket"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
async def export_triples(
|
|||
|
|
self,
|
|||
|
|
flow: str,
|
|||
|
|
**kwargs
|
|||
|
|
) -> AsyncIterator[Triple]:
|
|||
|
|
"""Bulk export triples via WebSocket"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
async def import_graph_embeddings(
|
|||
|
|
self,
|
|||
|
|
flow: str,
|
|||
|
|
embeddings: AsyncIterator[Dict[str, Any]],
|
|||
|
|
**kwargs
|
|||
|
|
) -> None:
|
|||
|
|
"""Bulk import graph embeddings via WebSocket"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
async def export_graph_embeddings(
|
|||
|
|
self,
|
|||
|
|
flow: str,
|
|||
|
|
**kwargs
|
|||
|
|
) -> AsyncIterator[Dict[str, Any]]:
|
|||
|
|
"""Bulk export graph embeddings via WebSocket"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
# ... similar for document embeddings, entity contexts, objects
|
|||
|
|
|
|||
|
|
async def aclose(self) -> None:
|
|||
|
|
"""Close connections"""
|
|||
|
|
pass
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**主要特性:**
|
|||
|
|
基于 AsyncIterator,内存占用恒定
|
|||
|
|
适用于异步应用程序,效率高
|
|||
|
|
原生支持 async/await
|
|||
|
|
与同步版本具有相同的接口
|
|||
|
|
|
|||
|
|
#### 6. REST Flow API (同步 - 未改变)
|
|||
|
|
|
|||
|
|
模块:`trustgraph-base/trustgraph/api/flow.py`
|
|||
|
|
|
|||
|
|
REST Flow API 保持 **完全不变**,以确保向后兼容。所有现有方法均可继续使用:
|
|||
|
|
|
|||
|
|
`Flow.list()`, `Flow.start()`, `Flow.stop()`, 等等。
|
|||
|
|
`FlowInstance.agent()`, `FlowInstance.text_completion()`, `FlowInstance.graph_rag()`, 等等。
|
|||
|
|
所有现有签名和返回类型均已保留
|
|||
|
|
|
|||
|
|
**新增:** 为了实现功能对等,向 REST FlowInstance 添加 `graph_embeddings_query()`:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class FlowInstance:
|
|||
|
|
# All existing methods unchanged...
|
|||
|
|
|
|||
|
|
# New: Graph embeddings query (REST)
|
|||
|
|
def graph_embeddings_query(
|
|||
|
|
self,
|
|||
|
|
text: str,
|
|||
|
|
user: str,
|
|||
|
|
collection: str,
|
|||
|
|
limit: int = 10,
|
|||
|
|
**kwargs
|
|||
|
|
) -> List[Dict[str, Any]]:
|
|||
|
|
"""Query graph embeddings for semantic search"""
|
|||
|
|
# Calls POST /api/v1/flow/{flow}/service/graph-embeddings
|
|||
|
|
pass
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 7. 异步 REST 流程 API
|
|||
|
|
|
|||
|
|
模块:`trustgraph-base/trustgraph/api/async_flow.py` (新)
|
|||
|
|
|
|||
|
|
**AsyncFlow 和 AsyncFlowInstance 类:**
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class AsyncFlow:
|
|||
|
|
"""Asynchronous REST-based flow interface"""
|
|||
|
|
def __init__(self, url: str, timeout: int, token: Optional[str]):
|
|||
|
|
self.url = url
|
|||
|
|
self.timeout = timeout
|
|||
|
|
self.token = token
|
|||
|
|
|
|||
|
|
async def list(self) -> List[Dict[str, Any]]:
|
|||
|
|
"""List all flows"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
async def get(self, id: str) -> Dict[str, Any]:
|
|||
|
|
"""Get flow definition"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
async def start(self, class_name: str, id: str, description: str, parameters: Dict) -> None:
|
|||
|
|
"""Start a flow"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
async def stop(self, id: str) -> None:
|
|||
|
|
"""Stop a flow"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def id(self, flow_id: str) -> AsyncFlowInstance:
|
|||
|
|
"""Get async flow instance"""
|
|||
|
|
return AsyncFlowInstance(self.url, self.timeout, self.token, flow_id)
|
|||
|
|
|
|||
|
|
async def aclose(self) -> None:
|
|||
|
|
"""Close connection"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
class AsyncFlowInstance:
|
|||
|
|
"""Asynchronous REST flow instance"""
|
|||
|
|
|
|||
|
|
async def agent(
|
|||
|
|
self,
|
|||
|
|
question: str,
|
|||
|
|
user: str,
|
|||
|
|
state: Optional[Dict[str, Any]] = None,
|
|||
|
|
group: Optional[str] = None,
|
|||
|
|
history: Optional[List[Dict[str, Any]]] = None,
|
|||
|
|
**kwargs
|
|||
|
|
) -> Dict[str, Any]:
|
|||
|
|
"""Async agent execution"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
async def text_completion(
|
|||
|
|
self,
|
|||
|
|
system: str,
|
|||
|
|
prompt: str,
|
|||
|
|
**kwargs
|
|||
|
|
) -> str:
|
|||
|
|
"""Async text completion"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
async def graph_rag(
|
|||
|
|
self,
|
|||
|
|
question: str,
|
|||
|
|
user: str,
|
|||
|
|
collection: str,
|
|||
|
|
**kwargs
|
|||
|
|
) -> str:
|
|||
|
|
"""Async graph RAG"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
# ... all other FlowInstance methods as async versions
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**主要特性:**
|
|||
|
|
使用 `aiohttp` 或 `httpx` 实现原生异步 HTTP。
|
|||
|
|
方法签名与同步 REST API 相同。
|
|||
|
|
不支持流式传输(使用 `async_socket()` 进行流式传输)。
|
|||
|
|
适用于异步应用程序。
|
|||
|
|
|
|||
|
|
#### 8. Metrics API
|
|||
|
|
|
|||
|
|
模块:`trustgraph-base/trustgraph/api/metrics.py` (新)
|
|||
|
|
|
|||
|
|
**同步 Metrics:**
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class Metrics:
|
|||
|
|
def __init__(self, url: str, timeout: int, token: Optional[str]):
|
|||
|
|
self.url = url
|
|||
|
|
self.timeout = timeout
|
|||
|
|
self.token = token
|
|||
|
|
|
|||
|
|
def get(self) -> str:
|
|||
|
|
"""Get Prometheus metrics as text"""
|
|||
|
|
# Call GET /api/metrics
|
|||
|
|
pass
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**异步指标:**
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class AsyncMetrics:
|
|||
|
|
def __init__(self, url: str, timeout: int, token: Optional[str]):
|
|||
|
|
self.url = url
|
|||
|
|
self.timeout = timeout
|
|||
|
|
self.token = token
|
|||
|
|
|
|||
|
|
async def get(self) -> str:
|
|||
|
|
"""Get Prometheus metrics as text"""
|
|||
|
|
# Call GET /api/metrics
|
|||
|
|
pass
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 9. 增强的类型
|
|||
|
|
|
|||
|
|
模块:`trustgraph-base/trustgraph/api/types.py` (已修改)
|
|||
|
|
|
|||
|
|
**新类型:**
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from typing import Iterator, Union, Dict, Any
|
|||
|
|
import dataclasses
|
|||
|
|
|
|||
|
|
@dataclasses.dataclass
|
|||
|
|
class StreamingChunk:
|
|||
|
|
"""Base class for streaming chunks"""
|
|||
|
|
content: str
|
|||
|
|
end_of_message: bool = False
|
|||
|
|
|
|||
|
|
@dataclasses.dataclass
|
|||
|
|
class AgentThought(StreamingChunk):
|
|||
|
|
"""Agent reasoning chunk"""
|
|||
|
|
chunk_type: str = "thought"
|
|||
|
|
|
|||
|
|
@dataclasses.dataclass
|
|||
|
|
class AgentObservation(StreamingChunk):
|
|||
|
|
"""Agent tool observation chunk"""
|
|||
|
|
chunk_type: str = "observation"
|
|||
|
|
|
|||
|
|
@dataclasses.dataclass
|
|||
|
|
class AgentAnswer(StreamingChunk):
|
|||
|
|
"""Agent final answer chunk"""
|
|||
|
|
chunk_type: str = "final-answer"
|
|||
|
|
end_of_dialog: bool = False
|
|||
|
|
|
|||
|
|
@dataclasses.dataclass
|
|||
|
|
class RAGChunk(StreamingChunk):
|
|||
|
|
"""RAG streaming chunk"""
|
|||
|
|
end_of_stream: bool = False
|
|||
|
|
error: Optional[Dict[str, str]] = None
|
|||
|
|
|
|||
|
|
# Type aliases for clarity
|
|||
|
|
AgentStream = Iterator[Union[AgentThought, AgentObservation, AgentAnswer]]
|
|||
|
|
RAGStream = Iterator[RAGChunk]
|
|||
|
|
CompletionStream = Iterator[str]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 6. 指标 API
|
|||
|
|
|
|||
|
|
模块:`trustgraph-base/trustgraph/api/metrics.py` (新)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class Metrics:
|
|||
|
|
def __init__(self, url: str, timeout: int, token: Optional[str]):
|
|||
|
|
self.url = url
|
|||
|
|
self.timeout = timeout
|
|||
|
|
self.token = token
|
|||
|
|
|
|||
|
|
def get(self) -> str:
|
|||
|
|
"""Get Prometheus metrics as text"""
|
|||
|
|
# Call GET /api/metrics
|
|||
|
|
pass
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 实现方案
|
|||
|
|
|
|||
|
|
#### 第一阶段:核心 API 增强 (第一周)
|
|||
|
|
|
|||
|
|
1. 向 `Api` 类添加 `socket()`、`bulk()` 和 `metrics()` 方法
|
|||
|
|
2. 实现 WebSocket 和批量客户端的延迟初始化
|
|||
|
|
3. 添加上下文管理器支持 (`__enter__`, `__exit__`)
|
|||
|
|
4. 添加 `close()` 方法进行清理
|
|||
|
|
5. 为 API 类增强添加单元测试
|
|||
|
|
6. 验证向后兼容性
|
|||
|
|
|
|||
|
|
**向后兼容性**: 没有破坏性更改。仅添加了新方法。
|
|||
|
|
|
|||
|
|
#### 第二阶段:WebSocket 客户端 (第二周 - 第三周)
|
|||
|
|
|
|||
|
|
1. 实现 `SocketClient` 类,用于连接管理
|
|||
|
|
2. 实现 `SocketFlowInstance`,方法签名与 `FlowInstance` 相同
|
|||
|
|
3. 添加请求复用支持 (最多 15 个并发)
|
|||
|
|
4. 添加流式响应解析,支持不同的分块类型
|
|||
|
|
5. 添加自动重连逻辑
|
|||
|
|
6. 添加单元和集成测试
|
|||
|
|
7. 记录 WebSocket 使用模式
|
|||
|
|
|
|||
|
|
**向后兼容性**: 仅为新接口。对现有代码没有影响。
|
|||
|
|
|
|||
|
|
#### 第三阶段:流式支持 (第三周 - 第四周)
|
|||
|
|
|
|||
|
|
1. 添加流式分块类型类 (`AgentThought`, `AgentObservation`, `AgentAnswer`, `RAGChunk`)
|
|||
|
|
2. 在 `SocketClient` 中实现流式响应解析
|
|||
|
|
3. 将流式参数添加到 `SocketFlowInstance` 中的所有 LLM 方法
|
|||
|
|
4. 处理流式过程中的错误情况
|
|||
|
|
5. 添加流式功能的单元和集成测试
|
|||
|
|
6. 在文档中添加流式示例
|
|||
|
|
|
|||
|
|
**向后兼容性**: 仅为新接口。现有 REST API 未改变。
|
|||
|
|
|
|||
|
|
#### 第四阶段:批量操作 (第四周 - 第五周)
|
|||
|
|
|
|||
|
|
1. 实现 `BulkClient` 类
|
|||
|
|
2. 添加三元组、嵌入、上下文、对象的批量导入/导出方法
|
|||
|
|
3. 实现基于迭代器的处理,以实现恒定内存
|
|||
|
|
4. 添加进度跟踪 (可选的回调)
|
|||
|
|
5. 添加错误处理,并报告部分成功
|
|||
|
|
6. 添加单元和集成测试
|
|||
|
|
7. 添加批量操作示例
|
|||
|
|
|
|||
|
|
**向后兼容性**: 仅为新接口。对现有代码没有影响。
|
|||
|
|
|
|||
|
|
#### 第五阶段:功能完善与优化 (第五周)
|
|||
|
|
|
|||
|
|
1. 向 REST `FlowInstance` 添加 `graph_embeddings_query()`
|
|||
|
|
2. 实现 `Metrics` 类
|
|||
|
|
3. 添加全面的集成测试
|
|||
|
|
4. 性能基准测试
|
|||
|
|
5. 更新所有文档
|
|||
|
|
6. 创建迁移指南
|
|||
|
|
|
|||
|
|
**向后兼容性**: 仅添加新方法。对现有代码没有影响。
|
|||
|
|
|
|||
|
|
### 数据模型
|
|||
|
|
|
|||
|
|
#### 接口选择
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Single API instance, same URL for all interfaces
|
|||
|
|
api = Api(url="http://localhost:8088/")
|
|||
|
|
|
|||
|
|
# Synchronous interfaces
|
|||
|
|
rest_flow = api.flow().id("default") # Sync REST
|
|||
|
|
socket_flow = api.socket().flow("default") # Sync WebSocket
|
|||
|
|
bulk = api.bulk() # Sync bulk operations
|
|||
|
|
metrics = api.metrics() # Sync metrics
|
|||
|
|
|
|||
|
|
# Asynchronous interfaces
|
|||
|
|
async_rest_flow = api.async_flow().id("default") # Async REST
|
|||
|
|
async_socket_flow = api.async_socket().flow("default") # Async WebSocket
|
|||
|
|
async_bulk = api.async_bulk() # Async bulk operations
|
|||
|
|
async_metrics = api.async_metrics() # Async metrics
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 流式响应类型
|
|||
|
|
|
|||
|
|
**代理流式传输**:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
api = Api(url="http://localhost:8088/")
|
|||
|
|
|
|||
|
|
# REST interface - non-streaming (existing)
|
|||
|
|
rest_flow = api.flow().id("default")
|
|||
|
|
response = rest_flow.agent(question="What is ML?", user="user123")
|
|||
|
|
print(response["response"])
|
|||
|
|
|
|||
|
|
# WebSocket interface - non-streaming (same signature)
|
|||
|
|
socket_flow = api.socket().flow("default")
|
|||
|
|
response = socket_flow.agent(question="What is ML?", user="user123")
|
|||
|
|
print(response["response"])
|
|||
|
|
|
|||
|
|
# WebSocket interface - streaming (new)
|
|||
|
|
for chunk in socket_flow.agent(question="What is ML?", user="user123", streaming=True):
|
|||
|
|
if isinstance(chunk, AgentThought):
|
|||
|
|
print(f"Thinking: {chunk.content}")
|
|||
|
|
elif isinstance(chunk, AgentObservation):
|
|||
|
|
print(f"Observed: {chunk.content}")
|
|||
|
|
elif isinstance(chunk, AgentAnswer):
|
|||
|
|
print(f"Answer: {chunk.content}")
|
|||
|
|
if chunk.end_of_dialog:
|
|||
|
|
break
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**RAG 流式传输**:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
api = Api(url="http://localhost:8088/")
|
|||
|
|
|
|||
|
|
# REST interface - non-streaming (existing)
|
|||
|
|
rest_flow = api.flow().id("default")
|
|||
|
|
response = rest_flow.graph_rag(question="What is Python?", user="user123", collection="default")
|
|||
|
|
print(response)
|
|||
|
|
|
|||
|
|
# WebSocket interface - streaming (new)
|
|||
|
|
socket_flow = api.socket().flow("default")
|
|||
|
|
for chunk in socket_flow.graph_rag(
|
|||
|
|
question="What is Python?",
|
|||
|
|
user="user123",
|
|||
|
|
collection="default",
|
|||
|
|
streaming=True
|
|||
|
|
):
|
|||
|
|
print(chunk.content, end="", flush=True)
|
|||
|
|
if chunk.end_of_stream:
|
|||
|
|
break
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**批量操作 (同步)**:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
api = Api(url="http://localhost:8088/")
|
|||
|
|
|
|||
|
|
# Bulk import triples
|
|||
|
|
def triple_generator():
|
|||
|
|
yield Triple(s="http://ex.com/alice", p="http://ex.com/type", o="Person")
|
|||
|
|
yield Triple(s="http://ex.com/alice", p="http://ex.com/name", o="Alice")
|
|||
|
|
yield Triple(s="http://ex.com/bob", p="http://ex.com/type", o="Person")
|
|||
|
|
|
|||
|
|
bulk = api.bulk()
|
|||
|
|
bulk.import_triples(flow="default", triples=triple_generator())
|
|||
|
|
|
|||
|
|
# Bulk export triples
|
|||
|
|
for triple in bulk.export_triples(flow="default"):
|
|||
|
|
print(f"{triple.s} -> {triple.p} -> {triple.o}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**批量操作(异步)**:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import asyncio
|
|||
|
|
|
|||
|
|
async def main():
|
|||
|
|
api = Api(url="http://localhost:8088/")
|
|||
|
|
|
|||
|
|
# Async bulk import triples
|
|||
|
|
async def async_triple_generator():
|
|||
|
|
yield Triple(s="http://ex.com/alice", p="http://ex.com/type", o="Person")
|
|||
|
|
yield Triple(s="http://ex.com/alice", p="http://ex.com/name", o="Alice")
|
|||
|
|
yield Triple(s="http://ex.com/bob", p="http://ex.com/type", o="Person")
|
|||
|
|
|
|||
|
|
bulk = api.async_bulk()
|
|||
|
|
await bulk.import_triples(flow="default", triples=async_triple_generator())
|
|||
|
|
|
|||
|
|
# Async bulk export triples
|
|||
|
|
async for triple in bulk.export_triples(flow="default"):
|
|||
|
|
print(f"{triple.s} -> {triple.p} -> {triple.o}")
|
|||
|
|
|
|||
|
|
asyncio.run(main())
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**异步 REST 示例:**
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import asyncio
|
|||
|
|
|
|||
|
|
async def main():
|
|||
|
|
api = Api(url="http://localhost:8088/")
|
|||
|
|
|
|||
|
|
# Async REST flow operations
|
|||
|
|
flow = api.async_flow().id("default")
|
|||
|
|
response = await flow.agent(question="What is ML?", user="user123")
|
|||
|
|
print(response["response"])
|
|||
|
|
|
|||
|
|
asyncio.run(main())
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**异步 WebSocket 流式传输示例:**
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import asyncio
|
|||
|
|
|
|||
|
|
async def main():
|
|||
|
|
api = Api(url="http://localhost:8088/")
|
|||
|
|
|
|||
|
|
# Async WebSocket streaming
|
|||
|
|
socket = api.async_socket()
|
|||
|
|
flow = socket.flow("default")
|
|||
|
|
|
|||
|
|
async for chunk in flow.agent(question="What is ML?", user="user123", streaming=True):
|
|||
|
|
if isinstance(chunk, AgentAnswer):
|
|||
|
|
print(chunk.content, end="", flush=True)
|
|||
|
|
if chunk.end_of_dialog:
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
asyncio.run(main())
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### APIs
|
|||
|
|
|
|||
|
|
#### New APIs
|
|||
|
|
|
|||
|
|
1. **Core API Class**:
|
|||
|
|
**Synchronous**:
|
|||
|
|
`Api.socket()` - Get synchronous WebSocket client
|
|||
|
|
`Api.bulk()` - Get synchronous bulk operations client
|
|||
|
|
`Api.metrics()` - Get synchronous metrics client
|
|||
|
|
`Api.close()` - Close all synchronous connections
|
|||
|
|
Context manager support (`__enter__`, `__exit__`)
|
|||
|
|
**Asynchronous**:
|
|||
|
|
`Api.async_flow()` - Get asynchronous REST flow client
|
|||
|
|
`Api.async_socket()` - Get asynchronous WebSocket client
|
|||
|
|
`Api.async_bulk()` - Get asynchronous bulk operations client
|
|||
|
|
`Api.async_metrics()` - Get asynchronous metrics client
|
|||
|
|
`Api.aclose()` - Close all asynchronous connections
|
|||
|
|
Async context manager support (`__aenter__`, `__aexit__`)
|
|||
|
|
|
|||
|
|
2. **Synchronous WebSocket Client**:
|
|||
|
|
`SocketClient.flow(flow_id)` - Get WebSocket flow instance
|
|||
|
|
`SocketFlowInstance.agent(..., streaming: bool = False)` - Agent with optional streaming
|
|||
|
|
`SocketFlowInstance.text_completion(..., streaming: bool = False)` - Text completion with optional streaming
|
|||
|
|
`SocketFlowInstance.graph_rag(..., streaming: bool = False)` - Graph RAG with optional streaming
|
|||
|
|
`SocketFlowInstance.document_rag(..., streaming: bool = False)` - Document RAG with optional streaming
|
|||
|
|
`SocketFlowInstance.prompt(..., streaming: bool = False)` - Prompt with optional streaming
|
|||
|
|
`SocketFlowInstance.graph_embeddings_query()` - Graph embeddings query
|
|||
|
|
All other FlowInstance methods with identical signatures
|
|||
|
|
|
|||
|
|
3. **Asynchronous WebSocket Client**:
|
|||
|
|
`AsyncSocketClient.flow(flow_id)` - Get async WebSocket flow instance
|
|||
|
|
`AsyncSocketFlowInstance.agent(..., streaming: bool = False)` - Async agent with optional streaming
|
|||
|
|
`AsyncSocketFlowInstance.text_completion(..., streaming: bool = False)` - Async text completion with optional streaming
|
|||
|
|
`AsyncSocketFlowInstance.graph_rag(..., streaming: bool = False)` - Async graph RAG with optional streaming
|
|||
|
|
`AsyncSocketFlowInstance.document_rag(..., streaming: bool = False)` - Async document RAG with optional streaming
|
|||
|
|
`AsyncSocketFlowInstance.prompt(..., streaming: bool = False)` - Async prompt with optional streaming
|
|||
|
|
`AsyncSocketFlowInstance.graph_embeddings_query()` - Async graph embeddings query
|
|||
|
|
All other FlowInstance methods as async versions
|
|||
|
|
|
|||
|
|
4. **Synchronous Bulk Operations Client**:
|
|||
|
|
`BulkClient.import_triples(flow, triples)` - Bulk triple import
|
|||
|
|
`BulkClient.export_triples(flow)` - Bulk triple export
|
|||
|
|
`BulkClient.import_graph_embeddings(flow, embeddings)` - Bulk graph embeddings import
|
|||
|
|
`BulkClient.export_graph_embeddings(flow)` - Bulk graph embeddings export
|
|||
|
|
`BulkClient.import_document_embeddings(flow, embeddings)` - Bulk document embeddings import
|
|||
|
|
`BulkClient.export_document_embeddings(flow)` - Bulk document embeddings export
|
|||
|
|
`BulkClient.import_entity_contexts(flow, contexts)` - Bulk entity contexts import
|
|||
|
|
`BulkClient.export_entity_contexts(flow)` - Bulk entity contexts export
|
|||
|
|
`BulkClient.import_objects(flow, objects)` - Bulk objects import
|
|||
|
|
|
|||
|
|
5. **Asynchronous Bulk Operations Client**:
|
|||
|
|
`AsyncBulkClient.import_triples(flow, triples)` - Async bulk triple import
|
|||
|
|
`AsyncBulkClient.export_triples(flow)` - Async bulk triple export
|
|||
|
|
`AsyncBulkClient.import_graph_embeddings(flow, embeddings)` - Async bulk graph embeddings import
|
|||
|
|
`AsyncBulkClient.export_graph_embeddings(flow)` - Async bulk graph embeddings export
|
|||
|
|
`AsyncBulkClient.import_document_embeddings(flow, embeddings)` - Async bulk document embeddings import
|
|||
|
|
`AsyncBulkClient.export_document_embeddings(flow)` - Async bulk document embeddings export
|
|||
|
|
`AsyncBulkClient.import_entity_contexts(flow, contexts)` - Async bulk entity contexts import
|
|||
|
|
`AsyncBulkClient.export_entity_contexts(flow)` - Async bulk entity contexts export
|
|||
|
|
`AsyncBulkClient.import_objects(flow, objects)` - Async bulk objects import
|
|||
|
|
|
|||
|
|
6. **Asynchronous REST Flow Client**:
|
|||
|
|
`AsyncFlow.list()` - Async list all flows
|
|||
|
|
`AsyncFlow.get(id)` - Async get flow definition
|
|||
|
|
`AsyncFlow.start(...)` - Async start flow
|
|||
|
|
`AsyncFlow.stop(id)` - Async stop flow
|
|||
|
|
`AsyncFlow.id(flow_id)` - Get async flow instance
|
|||
|
|
`AsyncFlowInstance.agent(...)` - Async agent execution
|
|||
|
|
`AsyncFlowInstance.text_completion(...)` - Async text completion
|
|||
|
|
`AsyncFlowInstance.graph_rag(...)` - Async graph RAG
|
|||
|
|
All other FlowInstance methods as async versions
|
|||
|
|
|
|||
|
|
7. **Metrics Clients**:
|
|||
|
|
`Metrics.get()` - Synchronous Prometheus metrics
|
|||
|
|
`AsyncMetrics.get()` - Asynchronous Prometheus metrics
|
|||
|
|
|
|||
|
|
8. **REST Flow API Enhancement**:
|
|||
|
|
`FlowInstance.graph_embeddings_query()` - Graph embeddings query (sync feature parity)
|
|||
|
|
`AsyncFlowInstance.graph_embeddings_query()` - Graph embeddings query (async feature parity)
|
|||
|
|
|
|||
|
|
#### Modified APIs
|
|||
|
|
|
|||
|
|
1. **Constructor** (minor enhancement):
|
|||
|
|
```python
|
|||
|
|
Api(url: str, timeout: int = 60, token: Optional[str] = None)
|
|||
|
|
```
|
|||
|
|
添加了 `token` 参数(可选,用于身份验证)
|
|||
|
|
如果未指定 `None`(默认):未使用身份验证
|
|||
|
|
如果已指定:用作 REST 的 bearer token(`Authorization: Bearer <token>`),用作 WebSocket 的查询参数(`?token=<token>`)
|
|||
|
|
没有其他更改 - 完全向后兼容
|
|||
|
|
|
|||
|
|
2. **无破坏性更改:**
|
|||
|
|
所有现有的 REST API 方法未更改
|
|||
|
|
所有现有的签名均保留
|
|||
|
|
所有现有的返回类型均保留
|
|||
|
|
|
|||
|
|
### 实现细节
|
|||
|
|
|
|||
|
|
#### 错误处理
|
|||
|
|
|
|||
|
|
**WebSocket 连接错误:**
|
|||
|
|
```python
|
|||
|
|
try:
|
|||
|
|
api = Api(url="http://localhost:8088/")
|
|||
|
|
socket = api.socket()
|
|||
|
|
socket_flow = socket.flow("default")
|
|||
|
|
response = socket_flow.agent(question="...", user="user123")
|
|||
|
|
except ConnectionError as e:
|
|||
|
|
print(f"WebSocket connection failed: {e}")
|
|||
|
|
print("Hint: Ensure Gateway is running and WebSocket endpoint is accessible")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**优雅的降级方案**:
|
|||
|
|
```python
|
|||
|
|
api = Api(url="http://localhost:8088/")
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# Try WebSocket streaming first
|
|||
|
|
socket_flow = api.socket().flow("default")
|
|||
|
|
for chunk in socket_flow.agent(question="...", user="...", streaming=True):
|
|||
|
|
print(chunk.content)
|
|||
|
|
except ConnectionError:
|
|||
|
|
# Fall back to REST non-streaming
|
|||
|
|
print("WebSocket unavailable, falling back to REST")
|
|||
|
|
rest_flow = api.flow().id("default")
|
|||
|
|
response = rest_flow.agent(question="...", user="...")
|
|||
|
|
print(response["response"])
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**部分流式错误:**
|
|||
|
|
```python
|
|||
|
|
api = Api(url="http://localhost:8088/")
|
|||
|
|
socket_flow = api.socket().flow("default")
|
|||
|
|
|
|||
|
|
accumulated = []
|
|||
|
|
try:
|
|||
|
|
for chunk in socket_flow.graph_rag(question="...", streaming=True):
|
|||
|
|
accumulated.append(chunk.content)
|
|||
|
|
if chunk.error:
|
|||
|
|
print(f"Error occurred: {chunk.error}")
|
|||
|
|
print(f"Partial response: {''.join(accumulated)}")
|
|||
|
|
break
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"Streaming error: {e}")
|
|||
|
|
print(f"Partial response: {''.join(accumulated)}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 资源管理
|
|||
|
|
|
|||
|
|
**上下文管理器支持:**
|
|||
|
|
```python
|
|||
|
|
# Automatic cleanup
|
|||
|
|
with Api(url="http://localhost:8088/") as api:
|
|||
|
|
socket_flow = api.socket().flow("default")
|
|||
|
|
response = socket_flow.agent(question="...", user="user123")
|
|||
|
|
# All connections automatically closed
|
|||
|
|
|
|||
|
|
# Manual cleanup
|
|||
|
|
api = Api(url="http://localhost:8088/")
|
|||
|
|
try:
|
|||
|
|
socket_flow = api.socket().flow("default")
|
|||
|
|
response = socket_flow.agent(question="...", user="user123")
|
|||
|
|
finally:
|
|||
|
|
api.close() # Explicitly close all connections (WebSocket, bulk, etc.)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 线程和并发
|
|||
|
|
|
|||
|
|
**线程安全**:
|
|||
|
|
每个 `Api` 实例维护自己的连接。
|
|||
|
|
WebSocket 传输使用锁进行线程安全的请求复用。
|
|||
|
|
多个线程可以安全地共享一个 `Api` 实例。
|
|||
|
|
流式迭代器不具备线程安全特性(只能从单个线程消费)。
|
|||
|
|
|
|||
|
|
**异步支持**(未来考虑):
|
|||
|
|
```python
|
|||
|
|
# Phase 2 enhancement (not in initial scope)
|
|||
|
|
import asyncio
|
|||
|
|
|
|||
|
|
async def main():
|
|||
|
|
api = await AsyncApi(url="ws://localhost:8088/")
|
|||
|
|
flow = api.flow().id("default")
|
|||
|
|
|
|||
|
|
async for chunk in flow.agent(question="...", streaming=True):
|
|||
|
|
print(chunk.content)
|
|||
|
|
|
|||
|
|
await api.close()
|
|||
|
|
|
|||
|
|
asyncio.run(main())
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 安全注意事项
|
|||
|
|
|
|||
|
|
### 身份验证
|
|||
|
|
|
|||
|
|
**令牌参数**:
|
|||
|
|
```python
|
|||
|
|
# No authentication (default)
|
|||
|
|
api = Api(url="http://localhost:8088/")
|
|||
|
|
|
|||
|
|
# With authentication
|
|||
|
|
api = Api(url="http://localhost:8088/", token="mytoken")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**REST 传输:**
|
|||
|
|
通过 `Authorization` 头部传递认证令牌
|
|||
|
|
自动应用于所有 REST 请求
|
|||
|
|
格式:`Authorization: Bearer <token>`
|
|||
|
|
|
|||
|
|
**WebSocket 传输:**
|
|||
|
|
通过附加到 WebSocket URL 的查询参数传递认证令牌
|
|||
|
|
在连接建立期间自动应用
|
|||
|
|
格式:`ws://localhost:8088/api/v1/socket?token=<token>`
|
|||
|
|
|
|||
|
|
**实现:**
|
|||
|
|
```python
|
|||
|
|
class SocketClient:
|
|||
|
|
def _connect(self) -> WebSocket:
|
|||
|
|
# Construct WebSocket URL with optional token
|
|||
|
|
ws_url = f"{self.url}/api/v1/socket"
|
|||
|
|
if self.token:
|
|||
|
|
ws_url = f"{ws_url}?token={self.token}"
|
|||
|
|
# Connect to WebSocket
|
|||
|
|
return websocket.connect(ws_url)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**示例:**
|
|||
|
|
```python
|
|||
|
|
# REST with auth
|
|||
|
|
api = Api(url="http://localhost:8088/", token="mytoken")
|
|||
|
|
flow = api.flow().id("default")
|
|||
|
|
# All REST calls include: Authorization: Bearer mytoken
|
|||
|
|
|
|||
|
|
# WebSocket with auth
|
|||
|
|
socket = api.socket()
|
|||
|
|
# Connects to: ws://localhost:8088/api/v1/socket?token=mytoken
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 安全通信
|
|||
|
|
|
|||
|
|
支持 WS (WebSocket) 和 WSS (WebSocket Secure) 两种协议。
|
|||
|
|
WSS 连接的 TLS 证书验证。
|
|||
|
|
可选的开发环境下的证书验证禁用(带有警告)。
|
|||
|
|
|
|||
|
|
### 输入验证
|
|||
|
|
|
|||
|
|
验证 URL 协议 (http, https, ws, wss)。
|
|||
|
|
验证传输参数值。
|
|||
|
|
验证流式传输参数组合。
|
|||
|
|
验证批量导入的数据类型。
|
|||
|
|
|
|||
|
|
## 性能考虑
|
|||
|
|
|
|||
|
|
### 延迟优化
|
|||
|
|
|
|||
|
|
**流式 LLM 操作**:
|
|||
|
|
**首次 token 时间**: ~500 毫秒 (vs ~30 秒 非流式)
|
|||
|
|
**改进**: 性能提升 60 倍
|
|||
|
|
**适用范围**: Agent, Graph RAG, Document RAG, 文本补全, Prompt
|
|||
|
|
|
|||
|
|
**持久连接**:
|
|||
|
|
**连接开销**: 后续请求消除连接开销
|
|||
|
|
**WebSocket 握手**: 一次性成本 (~100 毫秒)
|
|||
|
|
**适用范围**: 使用 WebSocket 传输的所有操作
|
|||
|
|
|
|||
|
|
### 吞吐量优化
|
|||
|
|
|
|||
|
|
**批量操作**:
|
|||
|
|
**三元组导入**: ~10,000 三元组/秒 (vs ~100/秒 使用 REST 单个项目)
|
|||
|
|
**嵌入向量导入**: ~5,000 嵌入向量/秒 (vs ~50/秒 使用 REST 单个项目)
|
|||
|
|
**改进**: 批量操作的吞吐量提升 100 倍
|
|||
|
|
|
|||
|
|
**请求复用**:
|
|||
|
|
**并发请求**: 单个连接可支持最多 15 个并发请求
|
|||
|
|
**连接重用**: 并发操作无需连接开销
|
|||
|
|
|
|||
|
|
### 内存考虑
|
|||
|
|
|
|||
|
|
**流式响应**:
|
|||
|
|
恒定内存使用 (按接收到的块处理)
|
|||
|
|
不缓冲完整的响应
|
|||
|
|
适用于非常长的输出 (>1MB)
|
|||
|
|
|
|||
|
|
**批量操作**:
|
|||
|
|
基于迭代器的处理 (恒定内存)
|
|||
|
|
不将整个数据集加载到内存中
|
|||
|
|
适用于包含数百万项的数据集
|
|||
|
|
|
|||
|
|
### 基准测试 (预期)
|
|||
|
|
|
|||
|
|
| 操作 | REST (现有) | WebSocket (流式传输) | 改进 |
|
|||
|
|
|-----------|----------------|----------------------|-------------|
|
|||
|
|
| Agent (首次 token 时间) | 30 秒 | 0.5 秒 | 60 倍 |
|
|||
|
|
| Graph RAG (首次 token 时间) | 25 秒 | 0.5 秒 | 50 倍 |
|
|||
|
|
| 导入 10K 三元组 | 100 秒 | 1 秒 | 100 倍 |
|
|||
|
|
| 导入 1M 三元组 | 10,000 秒 (2.7 小时) | 100 秒 (1.6 分钟) | 100 倍 |
|
|||
|
|
| 10 个并发小请求 | 5 秒 (顺序) | 0.5 秒 (并行) | 10 倍 |
|
|||
|
|
|
|||
|
|
## 测试策略
|
|||
|
|
|
|||
|
|
### 单元测试
|
|||
|
|
|
|||
|
|
**传输层** (`test_transport.py`):
|
|||
|
|
测试 REST 传输的请求/响应
|
|||
|
|
测试 WebSocket 传输连接
|
|||
|
|
测试 WebSocket 传输重连
|
|||
|
|
测试请求复用
|
|||
|
|
测试流式响应解析
|
|||
|
|
模拟 WebSocket 服务器以进行确定性测试
|
|||
|
|
|
|||
|
|
**API 方法** (`test_flow.py`, `test_library.py`, 等):
|
|||
|
|
使用模拟传输测试新方法
|
|||
|
|
测试流式参数处理
|
|||
|
|
测试批量操作迭代器
|
|||
|
|
测试错误处理
|
|||
|
|
|
|||
|
|
**类型** (`test_types.py`):
|
|||
|
|
测试新的流式分块类型
|
|||
|
|
测试类型序列化/反序列化
|
|||
|
|
|
|||
|
|
### 集成测试
|
|||
|
|
|
|||
|
|
**端到端 REST** (`test_integration_rest.py`):
|
|||
|
|
测试所有操作,针对真实的网关(REST 模式)
|
|||
|
|
验证向后兼容性
|
|||
|
|
测试错误条件
|
|||
|
|
|
|||
|
|
**端到端 WebSocket** (`test_integration_websocket.py`):
|
|||
|
|
测试所有操作,针对真实的网关(WebSocket 模式)
|
|||
|
|
测试流式操作
|
|||
|
|
测试批量操作
|
|||
|
|
测试并发请求
|
|||
|
|
测试连接恢复
|
|||
|
|
|
|||
|
|
**流式服务** (`test_streaming_integration.py`):
|
|||
|
|
测试代理流式传输(想法、观察、答案)
|
|||
|
|
测试 RAG 流式传输(增量分块)
|
|||
|
|
测试文本补全流式传输(逐个 token)
|
|||
|
|
测试提示流式传输
|
|||
|
|
测试流式传输过程中的错误处理
|
|||
|
|
|
|||
|
|
**批量操作** (`test_bulk_integration.py`):
|
|||
|
|
测试三元组的批量导入/导出(1K、10K、100K 个条目)
|
|||
|
|
测试嵌入的批量导入/导出
|
|||
|
|
测试批量操作期间的内存使用情况
|
|||
|
|
测试进度跟踪
|
|||
|
|
|
|||
|
|
### 性能测试
|
|||
|
|
|
|||
|
|
**延迟基准测试** (`test_performance_latency.py`):
|
|||
|
|
测量第一个 token 的时间(流式传输与非流式传输)
|
|||
|
|
测量连接开销(REST 与 WebSocket)
|
|||
|
|
与预期基准进行比较
|
|||
|
|
|
|||
|
|
**吞吐量基准测试** (`test_performance_throughput.py`):
|
|||
|
|
测量批量导入吞吐量
|
|||
|
|
测量请求复用效率
|
|||
|
|
与预期基准进行比较
|
|||
|
|
|
|||
|
|
### 兼容性测试
|
|||
|
|
|
|||
|
|
**向后兼容性** (`test_backward_compatibility.py`):
|
|||
|
|
运行现有测试套件,针对重构的 API
|
|||
|
|
验证没有破坏性更改
|
|||
|
|
测试常见模式的迁移路径
|
|||
|
|
|
|||
|
|
## 迁移计划
|
|||
|
|
|
|||
|
|
### 第一阶段:透明迁移(默认)
|
|||
|
|
|
|||
|
|
**无需任何代码更改**。 现有代码继续工作:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Existing code works unchanged
|
|||
|
|
api = Api(url="http://localhost:8088/")
|
|||
|
|
flow = api.flow().id("default")
|
|||
|
|
response = flow.agent(question="What is ML?", user="user123")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 第二阶段:选择加入的流媒体(简单)
|
|||
|
|
|
|||
|
|
**使用 `api.socket()` 接口** 启用流媒体:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Before: Non-streaming REST
|
|||
|
|
api = Api(url="http://localhost:8088/")
|
|||
|
|
rest_flow = api.flow().id("default")
|
|||
|
|
response = rest_flow.agent(question="What is ML?", user="user123")
|
|||
|
|
print(response["response"])
|
|||
|
|
|
|||
|
|
# After: Streaming WebSocket (same parameters!)
|
|||
|
|
api = Api(url="http://localhost:8088/") # Same URL
|
|||
|
|
socket_flow = api.socket().flow("default")
|
|||
|
|
|
|||
|
|
for chunk in socket_flow.agent(question="What is ML?", user="user123", streaming=True):
|
|||
|
|
if isinstance(chunk, AgentAnswer):
|
|||
|
|
print(chunk.content, end="", flush=True)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**关键点:**
|
|||
|
|
REST 和 WebSocket 都使用相同的 URL
|
|||
|
|
相同的函数签名(易于迁移)
|
|||
|
|
只需要添加 `.socket()` 和 `streaming=True`
|
|||
|
|
|
|||
|
|
### 第三阶段:批量操作(新功能)
|
|||
|
|
|
|||
|
|
**使用 `api.bulk()` 接口** 处理大型数据集:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Before: Inefficient per-item operations
|
|||
|
|
api = Api(url="http://localhost:8088/")
|
|||
|
|
flow = api.flow().id("default")
|
|||
|
|
|
|||
|
|
for triple in my_large_triple_list:
|
|||
|
|
# Slow per-item operations
|
|||
|
|
# (no direct bulk insert in REST API)
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
# After: Efficient bulk loading
|
|||
|
|
api = Api(url="http://localhost:8088/") # Same URL
|
|||
|
|
bulk = api.bulk()
|
|||
|
|
|
|||
|
|
# This is fast (10,000 triples/second)
|
|||
|
|
bulk.import_triples(flow="default", triples=iter(my_large_triple_list))
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 文档更新
|
|||
|
|
|
|||
|
|
1. **README.md**: 添加流式传输和 WebSocket 示例
|
|||
|
|
2. **API 参考**: 记录所有新的方法和参数
|
|||
|
|
3. **迁移指南**: 启用流式传输的分步指南
|
|||
|
|
4. **示例**: 添加常用模式的示例脚本
|
|||
|
|
5. **性能指南**: 记录预期的性能改进
|
|||
|
|
|
|||
|
|
### 弃用策略
|
|||
|
|
|
|||
|
|
**无弃用**. 所有现有的 API 均继续支持。 这是一个纯粹的增强。
|
|||
|
|
|
|||
|
|
## 时间线
|
|||
|
|
|
|||
|
|
### 第一周:基础
|
|||
|
|
传输抽象层
|
|||
|
|
重构现有的 REST 代码
|
|||
|
|
传输层的单元测试
|
|||
|
|
向后兼容性验证
|
|||
|
|
|
|||
|
|
### 第二周:WebSocket 传输
|
|||
|
|
WebSocket 传输实现
|
|||
|
|
连接管理和重连
|
|||
|
|
请求复用
|
|||
|
|
单元和集成测试
|
|||
|
|
|
|||
|
|
### 第三周:流式传输支持
|
|||
|
|
向 LLM 方法添加流式传输参数
|
|||
|
|
实现流式响应解析
|
|||
|
|
添加流式传输块类型
|
|||
|
|
流式传输集成测试
|
|||
|
|
|
|||
|
|
### 第四周:批量操作
|
|||
|
|
添加批量导入/导出方法
|
|||
|
|
实现基于迭代器的操作
|
|||
|
|
性能测试
|
|||
|
|
批量操作集成测试
|
|||
|
|
|
|||
|
|
### 第五周:功能对齐与文档
|
|||
|
|
添加图嵌入查询
|
|||
|
|
添加指标 API
|
|||
|
|
综合文档
|
|||
|
|
迁移指南
|
|||
|
|
候选版本
|
|||
|
|
|
|||
|
|
### 第六周:发布
|
|||
|
|
最终集成测试
|
|||
|
|
性能基准测试
|
|||
|
|
发布文档
|
|||
|
|
社区公告
|
|||
|
|
|
|||
|
|
**总时长**: 6 周
|
|||
|
|
|
|||
|
|
## 开放问题
|
|||
|
|
|
|||
|
|
### API 设计问题
|
|||
|
|
|
|||
|
|
1. **异步支持**: ✅ **已解决** - 异步支持已包含在初始版本中
|
|||
|
|
所有接口都具有异步变体:`async_flow()`, `async_socket()`, `async_bulk()`, `async_metrics()`
|
|||
|
|
为同步和异步 API 提供完全的对称性
|
|||
|
|
对于现代异步框架(FastAPI, aiohttp)至关重要
|
|||
|
|
|
|||
|
|
2. **进度跟踪**: 批量操作是否应支持进度回调?
|
|||
|
|
```python
|
|||
|
|
def progress_callback(processed: int, total: Optional[int]):
|
|||
|
|
print(f"Processed {processed} items")
|
|||
|
|
|
|||
|
|
bulk.import_triples(flow="default", triples=triples, on_progress=progress_callback)
|
|||
|
|
```
|
|||
|
|
**建议**: 在第二阶段添加。对于初始发布,这不是关键。
|
|||
|
|
|
|||
|
|
3. **流式传输超时**: 我们应该如何处理流式传输操作的超时?
|
|||
|
|
**建议**: 使用与非流式传输相同的超时时间,但在接收每个数据块时重置。
|
|||
|
|
|
|||
|
|
4. **数据块缓冲**: 我们应该缓冲数据块,还是立即返回?
|
|||
|
|
**建议**: 立即返回,以获得最低延迟。
|
|||
|
|
|
|||
|
|
5. **WebSocket 上的全局服务**: `api.socket()` 是否应该支持全局服务(库、知识、集合、配置),或者仅支持作用域服务?
|
|||
|
|
**建议**: 仅从作用域服务开始(流式传输相关)。如果需要,可以在第二阶段添加全局服务。
|
|||
|
|
|
|||
|
|
### 实现问题
|
|||
|
|
|
|||
|
|
1. **WebSocket 库**: 我们应该使用 `websockets`、`websocket-client` 还是 `aiohttp`?
|
|||
|
|
**建议**: `websockets` (异步,成熟,维护良好)。使用 `asyncio.run()` 包装成同步接口。
|
|||
|
|
|
|||
|
|
2. **连接池**: 我们是否应该支持多个并发的 `Api` 实例共享连接池?
|
|||
|
|
**建议**: 延期到第二阶段。最初,每个 `Api` 实例都有自己的连接。
|
|||
|
|
|
|||
|
|
3. **连接重用**: `SocketClient` 和 `BulkClient` 是否应该共享同一个 WebSocket 连接,或者使用单独的连接?
|
|||
|
|
**建议**: 使用单独的连接。实现更简单,职责分离更清晰。
|
|||
|
|
|
|||
|
|
4. **延迟连接 vs 立即连接**: WebSocket 连接是在 `api.socket()` 中建立,还是在首次请求时建立?
|
|||
|
|
**建议**: 延迟到首次请求时建立。避免连接开销,如果用户仅使用 REST 方法。
|
|||
|
|
|
|||
|
|
### 测试问题
|
|||
|
|
|
|||
|
|
1. **模拟网关**: 我们是否应该创建一个轻量级的模拟网关进行测试,或者直接测试真实网关?
|
|||
|
|
**建议**: 都应该。使用模拟进行单元测试,使用真实网关进行集成测试。
|
|||
|
|
|
|||
|
|
2. **性能回归测试**: 我们是否应该将自动化性能回归测试添加到 CI 中?
|
|||
|
|
**建议**: 是的,但应设置宽松的阈值,以考虑 CI 环境的差异。
|
|||
|
|
|
|||
|
|
## 引用
|
|||
|
|
|
|||
|
|
### 相关技术规范
|
|||
|
|
`docs/tech-specs/streaming-llm-responses.md` - 网关中的流式传输实现
|
|||
|
|
`docs/tech-specs/rag-streaming-support.md` - RAG 流式传输支持
|
|||
|
|
|
|||
|
|
### 实现文件
|
|||
|
|
`trustgraph-base/trustgraph/api/` - Python API 源代码
|
|||
|
|
`trustgraph-flow/trustgraph/gateway/` - 网关源代码
|
|||
|
|
`trustgraph-flow/trustgraph/gateway/dispatch/mux.py` - WebSocket 多路复用参考实现
|
|||
|
|
|
|||
|
|
### 文档
|
|||
|
|
`docs/apiSpecification.md` - 完整的 API 参考
|
|||
|
|
`docs/api-status-summary.md` - API 状态摘要
|
|||
|
|
`README.websocket` - WebSocket 协议文档
|
|||
|
|
`STREAMING-IMPLEMENTATION-NOTES.txt` - 流式传输实现说明
|
|||
|
|
|
|||
|
|
### 外部库
|
|||
|
|
`websockets` - Python WebSocket 库 (https://websockets.readthedocs.io/)
|
|||
|
|
`requests` - Python HTTP 库 (现有)
|