package com.vincent.rsf.server.ai.service; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.vincent.rsf.server.ai.config.AiProperties; import com.vincent.rsf.server.ai.dto.GatewayChatRequest; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; @Component public class AiGatewayClient { @Resource private AiProperties aiProperties; @Resource private ObjectMapper objectMapper; public interface StreamCallback { boolean handle(JsonNode event) throws Exception; } public void stream(GatewayChatRequest request, StreamCallback callback) throws Exception { HttpURLConnection connection = null; try { String url = aiProperties.getGatewayBaseUrl() + "/internal/chat/stream"; connection = (HttpURLConnection) new URL(url).openConnection(); connection.setRequestMethod("POST"); connection.setDoOutput(true); connection.setConnectTimeout(10000); connection.setReadTimeout(0); connection.setRequestProperty("Content-Type", "application/json"); connection.setRequestProperty("Accept", "application/x-ndjson"); try (OutputStream outputStream = connection.getOutputStream()) { outputStream.write(objectMapper.writeValueAsBytes(request)); outputStream.flush(); } InputStream inputStream = connection.getResponseCode() >= 400 ? connection.getErrorStream() : connection.getInputStream(); if (inputStream == null) { return; } try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { if (line.trim().isEmpty()) { continue; } JsonNode event = objectMapper.readTree(line); if (!callback.handle(event)) { break; } } } } finally { if (connection != null) { connection.disconnect(); } } } }