| | |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.web.reactive.function.client.WebClient; |
| | | import reactor.core.publisher.Mono; |
| | | import reactor.core.publisher.Flux; |
| | | |
| | | import java.util.List; |
| | | import java.util.function.Consumer; |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | |
| | | @Slf4j |
| | | @Service |
| | |
| | | |
| | | return response.getChoices().get(0).getMessage().getContent(); |
| | | } |
| | | } |
| | | |
| | | public void chatStream(List<ChatCompletionRequest.Message> messages, |
| | | Double temperature, |
| | | Integer maxTokens, |
| | | Consumer<String> onChunk, |
| | | Runnable onComplete, |
| | | Consumer<Throwable> onError) { |
| | | |
| | | ChatCompletionRequest req = new ChatCompletionRequest(); |
| | | req.setModel(model); |
| | | req.setMessages(messages); |
| | | req.setTemperature(temperature != null ? temperature : 0.3); |
| | | req.setMax_tokens(maxTokens != null ? maxTokens : 1024); |
| | | req.setStream(true); |
| | | |
| | | Flux<String> flux = llmWebClient.post() |
| | | .uri("/chat/completions") |
| | | .header(HttpHeaders.AUTHORIZATION, "Bearer " + apiKey) |
| | | .contentType(MediaType.APPLICATION_JSON) |
| | | .accept(MediaType.TEXT_EVENT_STREAM) |
| | | .bodyValue(req) |
| | | .retrieve() |
| | | .bodyToFlux(String.class) |
| | | .doOnError(ex -> log.error("调用 LLM 流式失败", ex)); |
| | | |
| | | flux.subscribe(payload -> { |
| | | String s = payload == null ? null : payload.trim(); |
| | | if (s == null || s.isEmpty()) return; |
| | | if (s.startsWith("data:")) s = s.substring(5).trim(); |
| | | if ("[DONE]".equals(s)) return; |
| | | try { |
| | | JSONObject obj = JSON.parseObject(s); |
| | | JSONArray choices = obj.getJSONArray("choices"); |
| | | if (choices != null && !choices.isEmpty()) { |
| | | JSONObject c0 = choices.getJSONObject(0); |
| | | JSONObject delta = c0.getJSONObject("delta"); |
| | | if (delta != null) { |
| | | String content = delta.getString("content"); |
| | | if (content != null) onChunk.accept(content); |
| | | } |
| | | } |
| | | } catch (Exception ignore) {} |
| | | }, err -> { |
| | | if (onError != null) onError.accept(err); |
| | | }, () -> { |
| | | if (onComplete != null) onComplete.run(); |
| | | }); |
| | | } |
| | | } |