feat(llm): support incremental Responses WebSocket transport

This commit is contained in:
chenmingxuan committed 2026-11-04 17:15:32 +08:00
1 parent 73adcf25a7
commit 2a3f43fb60
22 files changed
+1176 -12

No files matched your search

+27 -4
View File
@@ -17,7 +17,7 @@
from enum import StrEnum
import hashlib
import os
from typing import Any
from typing import Any, Literal
from langchain_core.language_models.chat_models import BaseChatModel
from pydantic import BaseModel, Field
@@ -40,6 +40,9 @@ class ModelEndpoint(BaseModel):
model_name: str = Field(default_factory=lambda: settings.OPENAI_MODEL)
api_key: str | None = Field(default=None, repr=False)
api_base: str | None = None
transport: Literal["http", "websocket"] = Field(
default_factory=lambda: settings.OPENAI_TRANSPORT
)
temperature: float | None = None
max_tokens: int | None = Field(default=None, gt=0)
timeout_seconds: float = Field(default_factory=lambda: settings.OPENAI_TIMEOUT_SECONDS, gt=0)
@@ -63,6 +66,7 @@ class ModelEndpoint(BaseModel):
self.enable_grounding,
self.api_base or settings.OPENAI_BASE_URL,
settings.OPENAI_USE_SYSTEM_PROXY,
self.transport,
hashlib.sha256(key.encode()).hexdigest(),
)
@@ -119,13 +123,32 @@ class ModelFactory:
"http_client": DefaultHttpxClient(
timeout=endpoint.timeout_seconds, **model_http_options()
),
"http_async_client": DefaultAsyncHttpxClient(
}
if endpoint.transport == "websocket":
import httpx
from artemis.llm.websocket import ResponsesWebSocketTransport
# Disable HTTP proxy mounts on this facade: they would bypass the
# WebSocket transport. Each actual transport owns its proxy policy.
kwargs["http_async_client"] = httpx.AsyncClient(
timeout=endpoint.timeout_seconds,
trust_env=False,
transport=ResponsesWebSocketTransport(
timeout=endpoint.timeout_seconds, use_proxy=settings.OPENAI_USE_SYSTEM_PROXY
),
event_hooks={
"request": [observe_request_headers],
"response": [observe_response_headers],
},
)
else:
kwargs["http_async_client"] = DefaultAsyncHttpxClient(
timeout=endpoint.timeout_seconds,
**model_http_options(),
event_hooks={
"request": [observe_request_headers],
"response": [observe_response_headers],
},
),
}
)
return ChatOpenAI(**{k: v for k, v in kwargs.items() if v is not None})