|
|
@@ -77,22 +77,33 @@ public class LLMService {
|
|
|
log.info("[chatStream] POST /chat/completions, model={}, msgs={}, bodyBuilt={}ms",
|
|
|
props.getModel(), messages.size(), System.currentTimeMillis() - t0);
|
|
|
|
|
|
- final long t1 = System.currentTimeMillis();
|
|
|
+ final long postAt = System.currentTimeMillis();
|
|
|
+ // 真实 TTFB = 首个非空内容 token 到达 - 发起 POST;doFirst 测的是订阅开销(~ms),不是首字
|
|
|
+ final java.util.concurrent.atomic.AtomicLong firstTokenAt = new java.util.concurrent.atomic.AtomicLong(0);
|
|
|
+ final java.util.concurrent.atomic.AtomicInteger tokenCount = new java.util.concurrent.atomic.AtomicInteger(0);
|
|
|
return chatClient.post()
|
|
|
.uri("/chat/completions")
|
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
|
.bodyValue(body)
|
|
|
.retrieve()
|
|
|
.bodyToFlux(String.class)
|
|
|
- .doFirst(() -> {
|
|
|
- long elapsed = System.currentTimeMillis() - t1;
|
|
|
- log.info("[chatStream] 百炼已响应, latency={}ms (从POST到收到第一个字节)", elapsed);
|
|
|
- })
|
|
|
.filter(data -> !"[DONE]".equals(data.trim()))
|
|
|
.map(this::extractDeltaContent)
|
|
|
+ .doOnNext(s -> {
|
|
|
+ if (s != null && !s.isEmpty()) {
|
|
|
+ tokenCount.incrementAndGet();
|
|
|
+ firstTokenAt.compareAndSet(0, System.currentTimeMillis());
|
|
|
+ }
|
|
|
+ })
|
|
|
.doOnComplete(() -> {
|
|
|
- long total = System.currentTimeMillis() - t0;
|
|
|
- log.info("[chatStream] 流结束, total={}ms", total);
|
|
|
+ long now = System.currentTimeMillis();
|
|
|
+ long total = now - t0;
|
|
|
+ long ttfb = firstTokenAt.get() > 0 ? firstTokenAt.get() - postAt : -1;
|
|
|
+ long genMs = firstTokenAt.get() > 0 ? now - firstTokenAt.get() : total;
|
|
|
+ int n = tokenCount.get();
|
|
|
+ double tokPerSec = genMs > 0 ? (n * 1000.0 / genMs) : 0;
|
|
|
+ log.info("[chatStream] done, model={}, total={}ms, ttfb={}ms, tokens={}, genMs={}, tok/s={}",
|
|
|
+ props.getModel(), total, ttfb, n, genMs, String.format("%.1f", tokPerSec));
|
|
|
})
|
|
|
.doOnError(e -> {
|
|
|
long total = System.currentTimeMillis() - t0;
|