Files

112 lines
3.7 KiB
Python
Raw Permalink Normal View History

2025-02-13 18:10:46 +08:00
import json
import socket
import time
import uuid
from typing import Generator
import logging
import threading
logger = logging.getLogger("llm_client")
2025-02-18 11:40:03 +08:00
logger.setLevel(logging.DEBUG)
2025-02-13 18:10:46 +08:00
class LLMClient:
def __repr__(self):
attrs = ", ".join(f"{k}={v}" for k, v in self.__dict__.items() if not k.startswith("_"))
return f"LLMClient({attrs})"
2025-02-13 18:10:46 +08:00
def __init__(self, host: str = "localhost", port: int = 10001):
2025-02-18 11:40:03 +08:00
self._lock = threading.Lock()
2025-02-13 18:10:46 +08:00
self.host = host
self.port = port
self.sock = None
2025-02-18 11:40:03 +08:00
self.work_id = None
self._initialized = False
self._connect()
2025-02-13 18:10:46 +08:00
def __enter__(self):
self.connect()
return self
def __exit__(self, _exc_type, _exc_val, _exc_tb):
self.close()
def _connect(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.sock.connect((self.host, self.port))
except ConnectionRefusedError as e:
2025-02-20 17:40:30 +08:00
raise RuntimeError(f"Failed to connect to {self.host}:{self.port}") from e
2025-02-13 18:10:46 +08:00
def close(self):
if self.sock:
self.sock.close()
self.sock = None
2025-02-14 16:37:25 +08:00
def _send_request(self, action: str, object: str, data: dict) -> str:
2025-02-13 18:10:46 +08:00
request_id = str(uuid.uuid4())
2025-02-14 18:02:32 +08:00
object_type = object.split('.')[0] if '.' in object else "llm"
2025-02-13 18:10:46 +08:00
payload = {
"request_id": request_id,
2025-02-14 18:02:32 +08:00
"work_id": self.work_id or object_type,
2025-02-13 18:10:46 +08:00
"action": action,
2025-02-14 16:37:25 +08:00
"object": object,
2025-02-13 18:10:46 +08:00
"data": data
}
logger.debug(
f"Sending request: [ID:{request_id}] "
f"Action:{action} WorkID:{payload['work_id']}\n"
f"Data: {str(data)[:100]}..."
)
self.sock.sendall(json.dumps(payload, ensure_ascii=False).encode('utf-8'))
return request_id
2025-02-14 16:37:25 +08:00
def setup(self, object: str, model_config: dict) -> dict:
2025-02-13 18:10:46 +08:00
if not self.sock:
self._connect()
2025-02-14 16:37:25 +08:00
request_id = self._send_request("setup", object, model_config)
2025-02-13 18:10:46 +08:00
return self._wait_response(request_id)
2025-02-14 18:02:32 +08:00
def inference_stream(self, query: str, object_type: str = "llm.utf-8") -> Generator[str, None, None]:
request_id = self._send_request("inference", object_type, query)
2025-02-13 18:10:46 +08:00
while True:
response = json.loads(self.sock.recv(4096).decode())
if response["request_id"] != request_id:
continue
yield response["data"]["delta"]
if response["data"].get("finish", False):
self.work_id = response["work_id"]
break
2025-02-18 17:13:25 +08:00
def stop_inference(self) -> dict:
request_id = self._send_request("pause", "llm.utf-8", {})
return request_id
def send_jpeg(self, query: str, object_type: str = "vlm.jpeg.base64") -> str:
request_id = self._send_request("inference", object_type, query)
return request_id
2025-02-13 18:10:46 +08:00
def exit(self) -> dict:
2025-02-14 16:37:25 +08:00
request_id = self._send_request("exit", "llm.utf-8", {})
2025-02-13 18:10:46 +08:00
result = self._wait_response(request_id)
self._initialized = False
return result
def _wait_response(self, request_id: str) -> dict:
start_time = time.time()
while time.time() - start_time < 10:
response = json.loads(self.sock.recv(4096).decode())
if response["request_id"] == request_id:
if response["error"]["code"] != 0:
raise RuntimeError(f"Server error: {response['error']['message']}")
self.work_id = response["work_id"]
return response
raise TimeoutError("No response from server")
def connect(self):
with self._lock:
if not self.sock:
2025-04-08 18:07:54 +08:00
self._connect()