mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-04-25 08:26:21 +02:00
1517 lines
53 KiB
Markdown
1517 lines
53 KiB
Markdown
|
|
---
|
||
|
|
layout: default
|
||
|
|
title: "Python API Refactor Technical Specification"
|
||
|
|
parent: "Portuguese (Beta)"
|
||
|
|
---
|
||
|
|
|
||
|
|
# Python API Refactor Technical Specification
|
||
|
|
|
||
|
|
> **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.
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
Esta especificação descreve uma refatoração abrangente da biblioteca de cliente da API Python TrustGraph para alcançar a paridade de recursos com o API Gateway e adicionar suporte para padrões de comunicação em tempo real modernos.
|
||
|
|
|
||
|
|
A refatoração aborda quatro casos de uso primários:
|
||
|
|
|
||
|
|
1. **Interações de LLM em Streaming**: Habilitar o streaming em tempo real de respostas de LLM (agente, RAG de grafo, RAG de documento, conclusão de texto, prompts) com uma latência ~60 vezes menor (500ms vs 30s para o primeiro token).
|
||
|
|
2. **Operações em Lote**: Suportar a importação/exportação eficiente em lote de triplas, incorporações de grafos e incorporações de documentos para gerenciamento de grafos de conhecimento em grande escala.
|
||
|
|
3. **Paridade de Recursos**: Garantir que cada endpoint do API Gateway tenha um método de API Python correspondente, incluindo a consulta de incorporações de grafos.
|
||
|
|
4. **Conexões Persistentes**: Habilitar a comunicação baseada em WebSocket para solicitações multiplexadas e redução da sobrecarga de conexão.
|
||
|
|
|
||
|
|
## Objetivos
|
||
|
|
|
||
|
|
**Paridade de Recursos**: Cada serviço do Gateway API tem um método de API Python correspondente.
|
||
|
|
**Suporte para Streaming**: Todos os serviços capazes de streaming (agente, RAG, conclusão de texto, prompt) suportam streaming na API Python.
|
||
|
|
**Transporte WebSocket**: Adicionar uma camada de transporte WebSocket opcional para conexões persistentes e multiplexação.
|
||
|
|
**Operações em Lote**: Adicionar importação/exportação eficiente em lote para triplas, incorporações de grafos e incorporações de documentos.
|
||
|
|
**Suporte Completo Async**: Implementação completa de async/await para todas as interfaces (REST, WebSocket, operações em lote, métricas).
|
||
|
|
**Compatibilidade com Versões Anteriores**: O código existente continua a funcionar sem modificação.
|
||
|
|
**Segurança de Tipos**: Manter interfaces com segurança de tipos com dataclasses e dicas de tipo.
|
||
|
|
**Aprimoramento Progressivo**: Streaming e async são opcionais por meio da seleção explícita da interface.
|
||
|
|
**Desempenho**: Alcançar uma melhoria de latência de 60 vezes para operações de streaming.
|
||
|
|
**Python Moderno**: Suporte para paradigmas sync e async para máxima flexibilidade.
|
||
|
|
|
||
|
|
## Contexto
|
||
|
|
|
||
|
|
### Estado Atual
|
||
|
|
|
||
|
|
A API Python (`trustgraph-base/trustgraph/api/`) é uma biblioteca de cliente REST somente com os seguintes módulos:
|
||
|
|
|
||
|
|
`flow.py`: Gerenciamento de fluxo e serviços com escopo de fluxo (50 métodos).
|
||
|
|
`library.py`: Operações da biblioteca de documentos (9 métodos).
|
||
|
|
`knowledge.py`: Gerenciamento central do KG (4 métodos).
|
||
|
|
`collection.py`: Metadados da coleção (3 métodos).
|
||
|
|
`config.py`: Gerenciamento de configuração (6 métodos).
|
||
|
|
`types.py`: Definições de tipo de dados (5 dataclasses).
|
||
|
|
|
||
|
|
**Operações Totais**: 50/59 (85% de cobertura).
|
||
|
|
|
||
|
|
### Limitações Atuais
|
||
|
|
|
||
|
|
**Operações Ausentes**:
|
||
|
|
Consulta de incorporações de grafos (busca semântica sobre entidades de grafos).
|
||
|
|
Importação/exportação em lote para triplas, incorporações de grafos, incorporações de documentos, contextos de entidades, objetos.
|
||
|
|
Endpoint de métricas.
|
||
|
|
|
||
|
|
**Capacidades Ausentes**:
|
||
|
|
Suporte para streaming para serviços de LLM.
|
||
|
|
Transporte WebSocket.
|
||
|
|
Solicitações concorrentes multiplexadas.
|
||
|
|
Conexões persistentes.
|
||
|
|
|
||
|
|
**Problemas de Desempenho**:
|
||
|
|
Alta latência para interações de LLM (~30s para o primeiro token).
|
||
|
|
Transferência de dados em lote ineficiente (requisição REST por item).
|
||
|
|
Sobrecarga de conexão para várias operações sequenciais.
|
||
|
|
|
||
|
|
**Problemas de Experiência do Usuário**:
|
||
|
|
Sem feedback em tempo real durante a geração de LLM.
|
||
|
|
Não é possível cancelar operações de LLM em execução prolongada.
|
||
|
|
Má escalabilidade para operações em lote.
|
||
|
|
|
||
|
|
### Impacto
|
||
|
|
|
||
|
|
O aprimoramento de streaming do API Gateway em novembro de 2024 proporcionou uma melhoria de latência de 60 vezes (500ms vs 30s para o primeiro token) para interações de LLM, mas os usuários da API Python não podem aproveitar essa capacidade. Isso cria uma lacuna significativa de experiência entre usuários de Python e não usuários de Python.
|
||
|
|
|
||
|
|
## Design Técnico
|
||
|
|
|
||
|
|
### Arquitetura
|
||
|
|
|
||
|
|
A API Python refatorada usa uma **abordagem de interface modular** com objetos separados para diferentes padrões de comunicação. Todas as interfaces estão disponíveis tanto em variantes **síncronas quanto assíncronas**:
|
||
|
|
|
||
|
|
1. **Interface REST** (existente, aprimorada).
|
||
|
|
**Sync**: `api.flow()`, `api.library()`, `api.knowledge()`, `api.collection()`, `api.config()`.
|
||
|
|
**Async**: `api.async_flow()`.
|
||
|
|
Requisição/resposta síncrona/assíncrona.
|
||
|
|
Modelo de conexão simples.
|
||
|
|
Padrão para compatibilidade com versões anteriores.
|
||
|
|
|
||
|
|
2. **Interface WebSocket** (nova).
|
||
|
|
**Sync**: `api.socket()`.
|
||
|
|
**Async**: `api.async_socket()`.
|
||
|
|
Conexão persistente.
|
||
|
|
Solicitações multiplexadas.
|
||
|
|
Suporte para streaming.
|
||
|
|
Mesmas assinaturas de método do REST onde a funcionalidade se sobrepõe.
|
||
|
|
|
||
|
|
3. **Interface de Operações em Lote** (nova).
|
||
|
|
**Sync**: `api.bulk()`.
|
||
|
|
**Async**: `api.async_bulk()`.
|
||
|
|
Baseado em WebSocket para eficiência.
|
||
|
|
Importação/exportação baseada em iterador/AsyncIterator.
|
||
|
|
Lida com grandes conjuntos de dados.
|
||
|
|
|
||
|
|
4. **Interface de Métricas** (nova).
|
||
|
|
**Sync**: `api.metrics()`.
|
||
|
|
**Async**: `api.async_metrics()`.
|
||
|
|
Acesso a métricas 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())
|
||
|
|
```
|
||
|
|
|
||
|
|
**Princípios de Design Chave:**
|
||
|
|
**Mesmo URL para todas as interfaces:** `Api(url="http://localhost:8088/")` funciona para todas.
|
||
|
|
**Simetria Síncrona/Assíncrona:** Cada interface possui variantes síncronas e assíncronas com assinaturas de método idênticas.
|
||
|
|
**Assinaturas Idênticas:** Onde a funcionalidade se sobrepõe, as assinaturas dos métodos são idênticas entre REST e WebSocket, síncronas e assíncronas.
|
||
|
|
**Melhoria Progressiva:** Escolha a interface com base nas necessidades (REST para tarefas simples, WebSocket para streaming, Bulk para grandes conjuntos de dados, assíncrono para frameworks modernos).
|
||
|
|
**Intenção Explícita:** `api.socket()` sinaliza WebSocket, `api.async_socket()` sinaliza WebSocket assíncrono.
|
||
|
|
**Compatível com versões anteriores:** Código existente permanece inalterado.
|
||
|
|
|
||
|
|
### Componentes
|
||
|
|
|
||
|
|
#### 1. Classe de API Principal (Modificada)
|
||
|
|
|
||
|
|
Módulo: `trustgraph-base/trustgraph/api/api.py`
|
||
|
|
|
||
|
|
**Classe de API Aprimorada:**
|
||
|
|
|
||
|
|
```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. Cliente WebSocket Síncrono
|
||
|
|
|
||
|
|
Módulo: `trustgraph-base/trustgraph/api/socket_client.py` (novo)
|
||
|
|
|
||
|
|
**Classe 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.
|
||
|
|
```
|
||
|
|
|
||
|
|
**Principais Características:**
|
||
|
|
Conexão preguiçosa (conecta apenas quando o primeiro pedido é enviado)
|
||
|
|
Multiplexação de requisições (até 15 simultâneas)
|
||
|
|
Reconexão automática em caso de desconexão
|
||
|
|
Análise de resposta em streaming
|
||
|
|
Operação thread-safe
|
||
|
|
Wrapper síncrono em torno da biblioteca assíncrona de WebSockets
|
||
|
|
|
||
|
|
#### 3. Cliente WebSocket Assíncrono
|
||
|
|
|
||
|
|
Módulo: `trustgraph-base/trustgraph/api/async_socket_client.py` (novo)
|
||
|
|
|
||
|
|
**Classe 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.
|
||
|
|
```
|
||
|
|
|
||
|
|
**Principais Características:**
|
||
|
|
Suporte nativo para async/await
|
||
|
|
Eficiente para aplicações assíncronas (FastAPI, aiohttp)
|
||
|
|
Sem bloqueio de threads
|
||
|
|
Mesma interface da versão síncrona
|
||
|
|
AsyncIterator para streaming
|
||
|
|
|
||
|
|
#### 4. Cliente de Operações em Lote Síncronas
|
||
|
|
|
||
|
|
Módulo: `trustgraph-base/trustgraph/api/bulk_client.py` (novo)
|
||
|
|
|
||
|
|
**Classe 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
|
||
|
|
```
|
||
|
|
|
||
|
|
**Principais Características:**
|
||
|
|
Baseado em iteradores para uso constante de memória.
|
||
|
|
Conexões WebSocket dedicadas por operação.
|
||
|
|
Rastreamento de progresso (callback opcional).
|
||
|
|
Tratamento de erros com relatórios de sucesso parcial.
|
||
|
|
|
||
|
|
#### 5. Cliente de Operações em Massa Assíncronas
|
||
|
|
|
||
|
|
Módulo: `trustgraph-base/trustgraph/api/async_bulk_client.py` (novo)
|
||
|
|
|
||
|
|
**Classe 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
|
||
|
|
```
|
||
|
|
|
||
|
|
**Principais Características:**
|
||
|
|
Baseado em AsyncIterator para uso constante de memória
|
||
|
|
Eficiente para aplicações assíncronas
|
||
|
|
Suporte nativo para async/await
|
||
|
|
Mesma interface da versão síncrona
|
||
|
|
|
||
|
|
#### 6. API REST Flow (Síncrona - Inalterada)
|
||
|
|
|
||
|
|
Módulo: `trustgraph-base/trustgraph/api/flow.py`
|
||
|
|
|
||
|
|
A API REST Flow permanece **completamente inalterada** para compatibilidade com versões anteriores. Todos os métodos existentes continuam a funcionar:
|
||
|
|
|
||
|
|
`Flow.list()`, `Flow.start()`, `Flow.stop()`, etc.
|
||
|
|
`FlowInstance.agent()`, `FlowInstance.text_completion()`, `FlowInstance.graph_rag()`, etc.
|
||
|
|
Todas as assinaturas e tipos de retorno existentes preservados
|
||
|
|
|
||
|
|
**Novo:** Adicione `graph_embeddings_query()` a REST FlowInstance para paridade de recursos:
|
||
|
|
|
||
|
|
```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. API de Fluxo REST Assíncrono
|
||
|
|
|
||
|
|
Módulo: `trustgraph-base/trustgraph/api/async_flow.py` (novo)
|
||
|
|
|
||
|
|
**Classes AsyncFlow e 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
|
||
|
|
```
|
||
|
|
|
||
|
|
**Principais Características:**
|
||
|
|
HTTP assíncrono nativo usando `aiohttp` ou `httpx`
|
||
|
|
Mesmas assinaturas de método da API REST síncrona
|
||
|
|
Sem streaming (use `async_socket()` para streaming)
|
||
|
|
Eficiente para aplicações assíncronas
|
||
|
|
|
||
|
|
#### 8. API de Métricas
|
||
|
|
|
||
|
|
Módulo: `trustgraph-base/trustgraph/api/metrics.py` (novo)
|
||
|
|
|
||
|
|
**Métricas Síncronas:**
|
||
|
|
|
||
|
|
```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
|
||
|
|
```
|
||
|
|
|
||
|
|
**Métricas Assíncronas**:
|
||
|
|
|
||
|
|
```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. Tipos Aprimorados
|
||
|
|
|
||
|
|
Módulo: `trustgraph-base/trustgraph/api/types.py` (modificado)
|
||
|
|
|
||
|
|
**Novos Tipos**:
|
||
|
|
|
||
|
|
```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 de Métricas
|
||
|
|
|
||
|
|
Módulo: `trustgraph-base/trustgraph/api/metrics.py` (novo)
|
||
|
|
|
||
|
|
```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
|
||
|
|
```
|
||
|
|
|
||
|
|
### Abordagem de Implementação
|
||
|
|
|
||
|
|
#### Fase 1: Aprimoramento da API Central (Semana 1)
|
||
|
|
|
||
|
|
1. Adicionar métodos `socket()`, `bulk()` e `metrics()` à classe `Api`
|
||
|
|
2. Implementar inicialização preguiçosa para clientes WebSocket e em lote
|
||
|
|
3. Adicionar suporte a gerenciador de contexto (`__enter__`, `__exit__`)
|
||
|
|
4. Adicionar método `close()` para limpeza
|
||
|
|
5. Adicionar testes unitários para aprimoramentos da classe da API
|
||
|
|
6. Verificar compatibilidade com versões anteriores
|
||
|
|
|
||
|
|
**Compatibilidade com versões anteriores**: Nenhuma alteração disruptiva. Apenas novos métodos.
|
||
|
|
|
||
|
|
#### Fase 2: Cliente WebSocket (Semana 2-3)
|
||
|
|
|
||
|
|
1. Implementar classe `SocketClient` com gerenciamento de conexão
|
||
|
|
2. Implementar `SocketFlowInstance` com as mesmas assinaturas de método de `FlowInstance`
|
||
|
|
3. Adicionar suporte para multiplexação de solicitações (até 15 simultâneas)
|
||
|
|
4. Adicionar análise de resposta em streaming para diferentes tipos de fragmentos
|
||
|
|
5. Adicionar lógica de reconexão automática
|
||
|
|
6. Adicionar testes unitários e de integração
|
||
|
|
7. Documentar padrões de uso do WebSocket
|
||
|
|
|
||
|
|
**Compatibilidade com versões anteriores**: Nova interface apenas. Sem impacto no código existente.
|
||
|
|
|
||
|
|
#### Fase 3: Suporte a Streaming (Semana 3-4)
|
||
|
|
|
||
|
|
1. Adicionar classes de tipo de fragmento de streaming (`AgentThought`, `AgentObservation`, `AgentAnswer`, `RAGChunk`)
|
||
|
|
2. Implementar análise de resposta em streaming em `SocketClient`
|
||
|
|
3. Adicionar parâmetro de streaming a todos os métodos LLM em `SocketFlowInstance`
|
||
|
|
4. Lidar com casos de erro durante o streaming
|
||
|
|
5. Adicionar testes unitários e de integração para streaming
|
||
|
|
6. Adicionar exemplos de streaming à documentação
|
||
|
|
|
||
|
|
**Compatibilidade com versões anteriores**: Nova interface apenas. API REST existente inalterada.
|
||
|
|
|
||
|
|
#### Fase 4: Operações em Lote (Semana 4-5)
|
||
|
|
|
||
|
|
1. Implementar classe `BulkClient`
|
||
|
|
2. Adicionar métodos de importação/exportação em lote para triplas, incorporações, contextos, objetos
|
||
|
|
3. Implementar processamento baseado em iterador para uso de memória constante
|
||
|
|
4. Adicionar rastreamento de progresso (callback opcional)
|
||
|
|
5. Adicionar tratamento de erros com relatório de sucesso parcial
|
||
|
|
6. Adicionar testes unitários e de integração
|
||
|
|
7. Adicionar exemplos de operações em lote
|
||
|
|
|
||
|
|
**Compatibilidade com versões anteriores**: Nova interface apenas. Sem impacto no código existente.
|
||
|
|
|
||
|
|
#### Fase 5: Paridade de Recursos e Refinamento (Semana 5)
|
||
|
|
|
||
|
|
1. Adicionar `graph_embeddings_query()` ao REST `FlowInstance`
|
||
|
|
2. Implementar classe `Metrics`
|
||
|
|
3. Adicionar testes de integração abrangentes
|
||
|
|
4. Teste de desempenho
|
||
|
|
5. Atualizar toda a documentação
|
||
|
|
6. Criar guia de migração
|
||
|
|
|
||
|
|
**Compatibilidade com versões anteriores**: Novos métodos apenas. Sem impacto no código existente.
|
||
|
|
|
||
|
|
### Modelos de Dados
|
||
|
|
|
||
|
|
#### Seleção de Interface
|
||
|
|
|
||
|
|
```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
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Tipos de Resposta de Streaming
|
||
|
|
|
||
|
|
**Streaming do Agente**:
|
||
|
|
|
||
|
|
```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
|
||
|
|
```
|
||
|
|
|
||
|
|
**Streaming 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
|
||
|
|
```
|
||
|
|
|
||
|
|
**Operações em Lote (Síncronas):**
|
||
|
|
|
||
|
|
```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}")
|
||
|
|
```
|
||
|
|
|
||
|
|
**Operações em Lote (Assíncronas):**
|
||
|
|
|
||
|
|
```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())
|
||
|
|
```
|
||
|
|
|
||
|
|
**Exemplo de REST Assíncrono:**
|
||
|
|
|
||
|
|
```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())
|
||
|
|
```
|
||
|
|
|
||
|
|
**Exemplo de Streaming WebSocket Assíncrono:**
|
||
|
|
|
||
|
|
```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. **Classe de API Core**:
|
||
|
|
**Síncrono**:
|
||
|
|
`Api.socket()` - Obter cliente WebSocket síncrono
|
||
|
|
`Api.bulk()` - Obter cliente de operações em lote síncrono
|
||
|
|
`Api.metrics()` - Obter cliente de métricas síncrono
|
||
|
|
`Api.close()` - Fechar todas as conexões síncronas
|
||
|
|
Suporte a gerenciador de contexto (`__enter__`, `__exit__`)
|
||
|
|
**Assíncrono**:
|
||
|
|
`Api.async_flow()` - Obter cliente de fluxo REST assíncrono
|
||
|
|
`Api.async_socket()` - Obter cliente WebSocket assíncrono
|
||
|
|
`Api.async_bulk()` - Obter cliente de operações em lote assíncrono
|
||
|
|
`Api.async_metrics()` - Obter cliente de métricas assíncrono
|
||
|
|
`Api.aclose()` - Fechar todas as conexões assíncronas
|
||
|
|
Suporte a gerenciador de contexto assíncrono (`__aenter__`, `__aexit__`)
|
||
|
|
|
||
|
|
2. **Cliente WebSocket Síncrono**:
|
||
|
|
`SocketClient.flow(flow_id)` - Obter instância de fluxo WebSocket
|
||
|
|
`SocketFlowInstance.agent(..., streaming: bool = False)` - Agente com streaming opcional
|
||
|
|
`SocketFlowInstance.text_completion(..., streaming: bool = False)` - Conclusão de texto com streaming opcional
|
||
|
|
`SocketFlowInstance.graph_rag(..., streaming: bool = False)` - Graph RAG com streaming opcional
|
||
|
|
`SocketFlowInstance.document_rag(..., streaming: bool = False)` - Document RAG com streaming opcional
|
||
|
|
`SocketFlowInstance.prompt(..., streaming: bool = False)` - Prompt com streaming opcional
|
||
|
|
`SocketFlowInstance.graph_embeddings_query()` - Consulta de embeddings de grafo
|
||
|
|
Todos os outros métodos FlowInstance com assinaturas idênticas
|
||
|
|
|
||
|
|
3. **Cliente WebSocket Assíncrono**:
|
||
|
|
`AsyncSocketClient.flow(flow_id)` - Obter instância de fluxo WebSocket assíncrono
|
||
|
|
`AsyncSocketFlowInstance.agent(..., streaming: bool = False)` - Agente assíncrono com streaming opcional
|
||
|
|
`AsyncSocketFlowInstance.text_completion(..., streaming: bool = False)` - Conclusão de texto assíncrona com streaming opcional
|
||
|
|
`AsyncSocketFlowInstance.graph_rag(..., streaming: bool = False)` - Graph RAG assíncrono com streaming opcional
|
||
|
|
`AsyncSocketFlowInstance.document_rag(..., streaming: bool = False)` - Document RAG assíncrono com streaming opcional
|
||
|
|
`AsyncSocketFlowInstance.prompt(..., streaming: bool = False)` - Prompt assíncrono com streaming opcional
|
||
|
|
`AsyncSocketFlowInstance.graph_embeddings_query()` - Consulta de embeddings de grafo assíncrona
|
||
|
|
Todos os outros métodos FlowInstance como versões assíncronas
|
||
|
|
|
||
|
|
4. **Cliente de Operações em Lote Síncrono**:
|
||
|
|
`BulkClient.import_triples(flow, triples)` - Importação em lote de triplas
|
||
|
|
`BulkClient.export_triples(flow)` - Exportação em lote de triplas
|
||
|
|
`BulkClient.import_graph_embeddings(flow, embeddings)` - Importação em lote de embeddings de grafo
|
||
|
|
`BulkClient.export_graph_embeddings(flow)` - Exportação em lote de embeddings de grafo
|
||
|
|
`BulkClient.import_document_embeddings(flow, embeddings)` - Importação em lote de embeddings de documentos
|
||
|
|
`BulkClient.export_document_embeddings(flow)` - Exportação em lote de embeddings de documentos
|
||
|
|
`BulkClient.import_entity_contexts(flow, contexts)` - Importação em lote de contextos de entidades
|
||
|
|
`BulkClient.export_entity_contexts(flow)` - Exportação em lote de contextos de entidades
|
||
|
|
`BulkClient.import_objects(flow, objects)` - Importação em lote de objetos
|
||
|
|
|
||
|
|
5. **Cliente de Operações em Lote Assíncrono**:
|
||
|
|
`AsyncBulkClient.import_triples(flow, triples)` - Importação assíncrona em lote de triplas
|
||
|
|
`AsyncBulkClient.export_triples(flow)` - Exportação assíncrona em lote de triplas
|
||
|
|
`AsyncBulkClient.import_graph_embeddings(flow, embeddings)` - Importação assíncrona em lote de embeddings de grafo
|
||
|
|
`AsyncBulkClient.export_graph_embeddings(flow)` - Exportação assíncrona em lote de embeddings de grafo
|
||
|
|
`AsyncBulkClient.import_document_embeddings(flow, embeddings)` - Importação assíncrona em lote de embeddings de documentos
|
||
|
|
`AsyncBulkClient.export_document_embeddings(flow)` - Exportação assíncrona em lote de embeddings de documentos
|
||
|
|
`AsyncBulkClient.import_entity_contexts(flow, contexts)` - Importação assíncrona em lote de contextos de entidades
|
||
|
|
`AsyncBulkClient.export_entity_contexts(flow)` - Exportação assíncrona em lote de contextos de entidades
|
||
|
|
`AsyncBulkClient.import_objects(flow, objects)` - Importação assíncrona em lote de objetos
|
||
|
|
|
||
|
|
6. **Cliente de Fluxo REST Assíncrono**:
|
||
|
|
`AsyncFlow.list()` - Listar todos os fluxos de forma assíncrona
|
||
|
|
`AsyncFlow.get(id)` - Obter definição de fluxo de forma assíncrona
|
||
|
|
`AsyncFlow.start(...)` - Iniciar fluxo de forma assíncrona
|
||
|
|
`AsyncFlow.stop(id)` - Parar fluxo de forma assíncrona
|
||
|
|
`AsyncFlow.id(flow_id)` - Obter instância de fluxo de forma assíncrona
|
||
|
|
`AsyncFlowInstance.agent(...)` - Execução de agente assíncrona
|
||
|
|
`AsyncFlowInstance.text_completion(...)` - Conclusão de texto assíncrona
|
||
|
|
`AsyncFlowInstance.graph_rag(...)` - Graph RAG assíncrono
|
||
|
|
Todos os outros métodos FlowInstance como versões assíncronas
|
||
|
|
|
||
|
|
7. **Clientes de Métricas**:
|
||
|
|
`Metrics.get()` - Métricas Prometheus síncronas
|
||
|
|
`AsyncMetrics.get()` - Métricas Prometheus assíncronas
|
||
|
|
|
||
|
|
8. **Aprimoramento da API de Fluxo REST**:
|
||
|
|
`FlowInstance.graph_embeddings_query()` - Consulta de embeddings de grafo (paridade de recursos síncrona)
|
||
|
|
`AsyncFlowInstance.graph_embeddings_query()` - Consulta de embeddings de grafo (paridade de recursos assíncrona)
|
||
|
|
|
||
|
|
#### APIs Modificadas
|
||
|
|
|
||
|
|
1. **Construtor** (melhoria menor):
|
||
|
|
```python
|
||
|
|
Api(url: str, timeout: int = 60, token: Optional[str] = None)
|
||
|
|
```
|
||
|
|
Adicionado parâmetro `token` (opcional, para autenticação)
|
||
|
|
Se `None` (padrão): Nenhuma autenticação utilizada
|
||
|
|
Se especificado: Usado como token bearer para REST (`Authorization: Bearer <token>`), parâmetro de consulta para WebSocket (`?token=<token>`)
|
||
|
|
Nenhuma outra alteração - totalmente compatível com versões anteriores
|
||
|
|
|
||
|
|
2. **Nenhuma Alteração Significativa**:
|
||
|
|
Todos os métodos da API REST existentes permanecem inalterados
|
||
|
|
Todas as assinaturas existentes são preservadas
|
||
|
|
Todos os tipos de retorno existentes são preservados
|
||
|
|
|
||
|
|
### Detalhes da Implementação
|
||
|
|
|
||
|
|
#### Tratamento de Erros
|
||
|
|
|
||
|
|
**Erros de Conexão 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")
|
||
|
|
```
|
||
|
|
|
||
|
|
**Fallback Elegante**:
|
||
|
|
```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"])
|
||
|
|
```
|
||
|
|
|
||
|
|
**Erros de Streaming Parcial:**
|
||
|
|
```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)}")
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Gerenciamento de Recursos
|
||
|
|
|
||
|
|
**Suporte a Gerenciadores de Contexto**:
|
||
|
|
```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.)
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Threads e Concorrência
|
||
|
|
|
||
|
|
**Segurança de Threads**:
|
||
|
|
Cada instância de `Api` mantém sua própria conexão.
|
||
|
|
O transporte WebSocket usa bloqueios para multiplexação de solicitações com segurança de thread.
|
||
|
|
Múltiplas threads podem compartilhar uma instância de `Api` com segurança.
|
||
|
|
Iteradores de streaming não são seguros para threads (consumir de uma única thread).
|
||
|
|
|
||
|
|
**Suporte Assíncrono** (consideração futura):
|
||
|
|
```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())
|
||
|
|
```
|
||
|
|
|
||
|
|
## Considerações de Segurança
|
||
|
|
|
||
|
|
### Autenticação
|
||
|
|
|
||
|
|
**Parâmetro de Token**:
|
||
|
|
```python
|
||
|
|
# No authentication (default)
|
||
|
|
api = Api(url="http://localhost:8088/")
|
||
|
|
|
||
|
|
# With authentication
|
||
|
|
api = Api(url="http://localhost:8088/", token="mytoken")
|
||
|
|
```
|
||
|
|
|
||
|
|
**Transporte REST**:
|
||
|
|
Token do transportador via cabeçalho `Authorization`
|
||
|
|
Aplicado automaticamente a todas as requisições REST
|
||
|
|
Formato: `Authorization: Bearer <token>`
|
||
|
|
|
||
|
|
**Transporte WebSocket**:
|
||
|
|
Token via parâmetro de consulta anexado à URL do WebSocket
|
||
|
|
Aplicado automaticamente durante o estabelecimento da conexão
|
||
|
|
Formato: `ws://localhost:8088/api/v1/socket?token=<token>`
|
||
|
|
|
||
|
|
**Implementação**:
|
||
|
|
```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)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Exemplo**:
|
||
|
|
```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
|
||
|
|
```
|
||
|
|
|
||
|
|
### Comunicação Segura
|
||
|
|
|
||
|
|
Suporta os esquemas WS (WebSocket) e WSS (WebSocket Secure).
|
||
|
|
Validação de certificado TLS para conexões WSS.
|
||
|
|
Verificação de certificado opcional para desenvolvimento (com aviso).
|
||
|
|
|
||
|
|
### Validação de Entrada
|
||
|
|
|
||
|
|
Valida esquemas de URL (http, https, ws, wss).
|
||
|
|
Valida valores de parâmetros de transporte.
|
||
|
|
Valida combinações de parâmetros de streaming.
|
||
|
|
Valida tipos de dados para importação em lote.
|
||
|
|
|
||
|
|
## Considerações de Desempenho
|
||
|
|
|
||
|
|
### Melhorias de Latência
|
||
|
|
|
||
|
|
**Operações LLM em Streaming**:
|
||
|
|
**Tempo para o primeiro token**: ~500ms (vs ~30s sem streaming)
|
||
|
|
**Melhoria**: 60 vezes mais rápido em termos de desempenho percebido.
|
||
|
|
**Aplicável a**: Agente, Graph RAG, Document RAG, Geração de Texto, Prompt.
|
||
|
|
|
||
|
|
**Conexões Persistentes**:
|
||
|
|
**Sobrecarga de conexão**: Eliminada para solicitações subsequentes.
|
||
|
|
**Handshake WebSocket**: Custo único (~100ms).
|
||
|
|
**Aplicável a**: Todas as operações ao usar o transporte WebSocket.
|
||
|
|
|
||
|
|
### Melhorias de Throughput
|
||
|
|
|
||
|
|
**Operações em Lote**:
|
||
|
|
**Importação de triplas**: ~10.000 triplas/segundo (vs ~100/segundo com REST por item).
|
||
|
|
**Importação de embeddings**: ~5.000 embeddings/segundo (vs ~50/segundo com REST por item).
|
||
|
|
**Melhoria**: 100 vezes mais rápido para operações em lote.
|
||
|
|
|
||
|
|
**Multiplexação de Requisições**:
|
||
|
|
**Requisições concorrentes**: Até 15 requisições simultâneas sobre uma única conexão.
|
||
|
|
**Reutilização de conexão**: Sem sobrecarga de conexão para operações concorrentes.
|
||
|
|
|
||
|
|
### Considerações de Memória
|
||
|
|
|
||
|
|
**Respostas em Streaming**:
|
||
|
|
Uso constante de memória (processa os chunks à medida que chegam).
|
||
|
|
Sem bufferização da resposta completa.
|
||
|
|
Adequado para saídas muito longas (>1MB).
|
||
|
|
|
||
|
|
**Operações em Lote**:
|
||
|
|
Processamento baseado em iterador (uso constante de memória).
|
||
|
|
Sem carregamento do conjunto de dados inteiro na memória.
|
||
|
|
Adequado para conjuntos de dados com milhões de itens.
|
||
|
|
|
||
|
|
### Benchmarks (Esperados)
|
||
|
|
|
||
|
|
| Operação | REST (existente) | WebSocket (streaming) | Melhoria |
|
||
|
|
|-----------|----------------|----------------------|-------------|
|
||
|
|
| Agente (tempo para o primeiro token) | 30s | 0.5s | 60x |
|
||
|
|
| Graph RAG (tempo para o primeiro token) | 25s | 0.5s | 50x |
|
||
|
|
| Importação de 10K triplas | 100s | 1s | 100x |
|
||
|
|
| Importação de 1M triplas | 10.000s (2.7h) | 100s (1.6m) | 100x |
|
||
|
|
| 10 requisições pequenas concorrentes | 5s (sequencial) | 0.5s (paralelo) | 10x |
|
||
|
|
|
||
|
|
## Estratégia de Testes
|
||
|
|
|
||
|
|
### Testes Unitários
|
||
|
|
|
||
|
|
**Camada de Transporte** (`test_transport.py`):
|
||
|
|
Testar requisição/resposta de transporte REST
|
||
|
|
Testar conexão de transporte WebSocket
|
||
|
|
Testar reconexão de transporte WebSocket
|
||
|
|
Testar multiplexação de requisições
|
||
|
|
Testar análise de resposta em streaming
|
||
|
|
Simular um servidor WebSocket para testes determinísticos
|
||
|
|
|
||
|
|
**Métodos da API** (`test_flow.py`, `test_library.py`, etc.):
|
||
|
|
Testar novos métodos com transporte simulado
|
||
|
|
Testar tratamento de parâmetros em streaming
|
||
|
|
Testar iteradores de operações em lote
|
||
|
|
Testar tratamento de erros
|
||
|
|
|
||
|
|
**Tipos** (`test_types.py`):
|
||
|
|
Testar novos tipos de fragmentos em streaming
|
||
|
|
Testar serialização/desserialização de tipos
|
||
|
|
|
||
|
|
### Testes de Integração
|
||
|
|
|
||
|
|
**REST de Ponta a Ponta** (`test_integration_rest.py`):
|
||
|
|
Testar todas as operações contra o Gateway real (modo REST)
|
||
|
|
Verificar compatibilidade com versões anteriores
|
||
|
|
Testar condições de erro
|
||
|
|
|
||
|
|
**WebSocket de Ponta a Ponta** (`test_integration_websocket.py`):
|
||
|
|
Testar todas as operações contra o Gateway real (modo WebSocket)
|
||
|
|
Testar operações de streaming
|
||
|
|
Testar operações em lote
|
||
|
|
Testar requisições concorrentes
|
||
|
|
Testar recuperação de conexão
|
||
|
|
|
||
|
|
**Serviços de Streaming** (`test_streaming_integration.py`):
|
||
|
|
Testar streaming de agentes (pensamentos, observações, respostas)
|
||
|
|
Testar streaming RAG (fragmentos incrementais)
|
||
|
|
Testar streaming de conclusão de texto (token por token)
|
||
|
|
Testar streaming de prompts
|
||
|
|
Testar tratamento de erros durante o streaming
|
||
|
|
|
||
|
|
**Operações em Lote** (`test_bulk_integration.py`):
|
||
|
|
Testar importação/exportação em lote de triplas (1K, 10K, 100K itens)
|
||
|
|
Testar importação/exportação em lote de embeddings
|
||
|
|
Testar o uso de memória durante operações em lote
|
||
|
|
Testar o rastreamento de progresso
|
||
|
|
|
||
|
|
### Testes de Desempenho
|
||
|
|
|
||
|
|
**Benchmarks de Latência** (`test_performance_latency.py`):
|
||
|
|
Medir o tempo para o primeiro token (streaming vs não streaming)
|
||
|
|
Medir a sobrecarga de conexão (REST vs WebSocket)
|
||
|
|
Comparar com benchmarks esperados
|
||
|
|
|
||
|
|
**Benchmarks de Throughput** (`test_performance_throughput.py`):
|
||
|
|
Medir o throughput de importação em lote
|
||
|
|
Medir a eficiência da multiplexação de requisições
|
||
|
|
Comparar com benchmarks esperados
|
||
|
|
|
||
|
|
### Testes de Compatibilidade
|
||
|
|
|
||
|
|
**Compatibilidade com Versões Anteriores** (`test_backward_compatibility.py`):
|
||
|
|
Executar o conjunto de testes existente contra a API refatorada
|
||
|
|
Verificar a ausência de alterações que quebrem a compatibilidade
|
||
|
|
Testar o caminho de migração para padrões comuns
|
||
|
|
|
||
|
|
## Plano de Migração
|
||
|
|
|
||
|
|
### Fase 1: Migração Transparente (Padrão)
|
||
|
|
|
||
|
|
**Não são necessárias alterações no código**. O código existente continua a funcionar:
|
||
|
|
|
||
|
|
```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")
|
||
|
|
```
|
||
|
|
|
||
|
|
### Fase 2: Streaming Opcional (Simples)
|
||
|
|
|
||
|
|
**Use a interface `api.socket()`** para habilitar o streaming:
|
||
|
|
|
||
|
|
```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)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Pontos-chave:**
|
||
|
|
Mesma URL para REST e WebSocket
|
||
|
|
Mesmas assinaturas de método (fácil migração)
|
||
|
|
Basta adicionar `.socket()` e `streaming=True`
|
||
|
|
|
||
|
|
### Fase 3: Operações em Lote (Nova Funcionalidade)
|
||
|
|
|
||
|
|
**Use a interface `api.bulk()`** para grandes conjuntos de dados:
|
||
|
|
|
||
|
|
```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))
|
||
|
|
```
|
||
|
|
|
||
|
|
### Atualizações na Documentação
|
||
|
|
|
||
|
|
1. **README.md**: Adicionar exemplos de streaming e WebSocket
|
||
|
|
2. **Referência da API**: Documentar todos os novos métodos e parâmetros
|
||
|
|
3. **Guia de Migração**: Guia passo a passo para habilitar o streaming
|
||
|
|
4. **Exemplos**: Adicionar scripts de exemplo para padrões comuns
|
||
|
|
5. **Guia de Desempenho**: Documentar as melhorias de desempenho esperadas
|
||
|
|
|
||
|
|
### Política de Descontinuação
|
||
|
|
|
||
|
|
**Nenhuma descontinuação**. Todas as APIs existentes permanecem suportadas. Esta é uma melhoria pura.
|
||
|
|
|
||
|
|
## Cronograma
|
||
|
|
|
||
|
|
### Semana 1: Fundação
|
||
|
|
Camada de abstração de transporte
|
||
|
|
Refatorar o código REST existente
|
||
|
|
Testes unitários para a camada de transporte
|
||
|
|
Verificação de compatibilidade com versões anteriores
|
||
|
|
|
||
|
|
### Semana 2: Transporte WebSocket
|
||
|
|
Implementação do transporte WebSocket
|
||
|
|
Gerenciamento de conexão e reconexão
|
||
|
|
Multiplexação de requisições
|
||
|
|
Testes unitários e de integração
|
||
|
|
|
||
|
|
### Semana 3: Suporte a Streaming
|
||
|
|
Adicionar parâmetro de streaming aos métodos LLM
|
||
|
|
Implementar análise de resposta de streaming
|
||
|
|
Adicionar tipos de fragmentos de streaming
|
||
|
|
Testes de integração de streaming
|
||
|
|
|
||
|
|
### Semana 4: Operações em Lote
|
||
|
|
Adicionar métodos de importação/exportação em lote
|
||
|
|
Implementar operações baseadas em iteradores
|
||
|
|
Testes de desempenho
|
||
|
|
Testes de integração de operações em lote
|
||
|
|
|
||
|
|
### Semana 5: Paridade de Recursos e Documentação
|
||
|
|
Adicionar consulta de incorporações de grafos
|
||
|
|
Adicionar API de métricas
|
||
|
|
Documentação abrangente
|
||
|
|
Guia de migração
|
||
|
|
Versão candidata
|
||
|
|
|
||
|
|
### Semana 6: Lançamento
|
||
|
|
Testes de integração finais
|
||
|
|
Benchmarking de desempenho
|
||
|
|
Documentação de lançamento
|
||
|
|
Anúncio para a comunidade
|
||
|
|
|
||
|
|
**Duração Total**: 6 semanas
|
||
|
|
|
||
|
|
## Perguntas Abertas
|
||
|
|
|
||
|
|
### Perguntas de Design da API
|
||
|
|
|
||
|
|
1. **Suporte Assíncrono**: ✅ **RESOLVIDO** - Suporte assíncrono completo incluído no lançamento inicial
|
||
|
|
Todas as interfaces possuem variantes assíncronas: `async_flow()`, `async_socket()`, `async_bulk()`, `async_metrics()`
|
||
|
|
Fornece total simetria entre as APIs síncronas e assíncronas
|
||
|
|
Essencial para frameworks assíncronos modernos (FastAPI, aiohttp)
|
||
|
|
|
||
|
|
2. **Rastreamento de Progresso**: As operações em lote devem suportar callbacks de progresso?
|
||
|
|
```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)
|
||
|
|
```
|
||
|
|
**Recomendação**: Adicionar na Fase 2. Não é crítico para a versão inicial.
|
||
|
|
|
||
|
|
3. **Tempo Limite de Streaming**: Como devemos tratar os tempos limite para operações de streaming?
|
||
|
|
**Recomendação**: Usar o mesmo tempo limite que as operações não-streaming, mas resetar a cada bloco recebido.
|
||
|
|
|
||
|
|
4. **Bufferização de Blocos**: Devemos bufferizar os blocos ou retornar imediatamente?
|
||
|
|
**Recomendação**: Retornar imediatamente para a menor latência.
|
||
|
|
|
||
|
|
5. **Serviços Globais via WebSocket**: `api.socket()` deve suportar serviços globais (biblioteca, conhecimento, coleção, configuração) ou apenas serviços específicos do fluxo?
|
||
|
|
**Recomendação**: Começar apenas com serviços específicos do fluxo (onde o streaming é importante). Adicionar serviços globais, se necessário, na Fase 2.
|
||
|
|
|
||
|
|
### Perguntas de Implementação
|
||
|
|
|
||
|
|
1. **Biblioteca WebSocket**: Devemos usar `websockets`, `websocket-client` ou `aiohttp`?
|
||
|
|
**Recomendação**: `websockets` (assíncrono, maduro, bem mantido). Envolver em uma interface síncrona usando `asyncio.run()`.
|
||
|
|
|
||
|
|
2. **Pool de Conexões**: Devemos suportar múltiplas instâncias concorrentes de `Api` compartilhando um pool de conexões?
|
||
|
|
**Recomendação**: Adiar para a Fase 2. Cada instância de `Api` terá suas próprias conexões inicialmente.
|
||
|
|
|
||
|
|
3. **Reutilização de Conexões**: `SocketClient` e `BulkClient` devem compartilhar a mesma conexão WebSocket, ou usar conexões separadas?
|
||
|
|
**Recomendação**: Conexões separadas. Implementação mais simples, separação mais clara de responsabilidades.
|
||
|
|
|
||
|
|
4. **Conexão Preguiçosa vs. Ansiosa**: A conexão WebSocket deve ser estabelecida em `api.socket()` ou na primeira requisição?
|
||
|
|
**Recomendação**: Preguiçosa (na primeira requisição). Evita a sobrecarga da conexão se o usuário usar apenas métodos REST.
|
||
|
|
|
||
|
|
### Perguntas de Teste
|
||
|
|
|
||
|
|
1. **Gateway Mock**: Devemos criar um Gateway mock leve para testes, ou testar contra o Gateway real?
|
||
|
|
**Recomendação**: Ambos. Usar mocks para testes unitários, Gateway real para testes de integração.
|
||
|
|
|
||
|
|
2. **Testes de Regressão de Desempenho**: Devemos adicionar testes de regressão de desempenho automatizados ao CI?
|
||
|
|
**Recomendação**: Sim, mas com limites generosos para levar em conta a variabilidade do ambiente do CI.
|
||
|
|
|
||
|
|
## Referências
|
||
|
|
|
||
|
|
### Especificações Técnicas Relacionadas
|
||
|
|
`docs/tech-specs/streaming-llm-responses.md` - Implementação de streaming no Gateway
|
||
|
|
`docs/tech-specs/rag-streaming-support.md` - Suporte de streaming RAG
|
||
|
|
|
||
|
|
### Arquivos de Implementação
|
||
|
|
`trustgraph-base/trustgraph/api/` - Código fonte da API Python
|
||
|
|
`trustgraph-flow/trustgraph/gateway/` - Código fonte do Gateway
|
||
|
|
`trustgraph-flow/trustgraph/gateway/dispatch/mux.py` - Implementação de referência de multiplexador WebSocket
|
||
|
|
|
||
|
|
### Documentação
|
||
|
|
`docs/apiSpecification.md` - Referência completa da API
|
||
|
|
`docs/api-status-summary.md` - Resumo do status da API
|
||
|
|
`README.websocket` - Documentação do protocolo WebSocket
|
||
|
|
`STREAMING-IMPLEMENTATION-NOTES.txt` - Notas sobre a implementação de streaming
|
||
|
|
|
||
|
|
### Bibliotecas Externas
|
||
|
|
`websockets` - Biblioteca WebSocket Python (https://websockets.readthedocs.io/)
|
||
|
|
`requests` - Biblioteca HTTP Python (existente)
|