From 162ed7e40644ab1e5bb2d71ee46b886c0e3a6ef6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Wed, 2 Sep 2026 08:16:12 +0800 Subject: [PATCH] =?UTF-8?q?task-168:=20LLM=20=E5=AE=A2=E6=88=B7=E7=AB=AF?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=8E=A5=E5=85=A5=EF=BC=88LlmHttpConfigResol?= =?UTF-8?q?ver=20=E4=BB=8E=20aiimage.http-client.*=20=E8=AF=BB=E5=8F=96?= =?UTF-8?q?=E8=B6=85=E6=97=B6/=E9=87=8D=E8=AF=95=EF=BC=8C=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=80=BC=E5=90=8C=E7=8E=B0=E7=8A=B6=E3=80=81=E9=92=B3?= =?UTF-8?q?=E5=88=B6=E3=80=81=E5=8F=AF=E8=A6=86=E7=9B=96=EF=BC=89+=208=20?= =?UTF-8?q?=E6=9D=A1=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aiimage/config/LlmHttpConfigResolver.java | 38 ++++++++ .../config/LlmHttpConfigResolverTest.java | 86 +++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 backend-java/src/main/java/com/nanri/aiimage/config/LlmHttpConfigResolver.java create mode 100644 backend-java/src/test/java/com/nanri/aiimage/config/LlmHttpConfigResolverTest.java diff --git a/backend-java/src/main/java/com/nanri/aiimage/config/LlmHttpConfigResolver.java b/backend-java/src/main/java/com/nanri/aiimage/config/LlmHttpConfigResolver.java new file mode 100644 index 00000000..fe4df61a --- /dev/null +++ b/backend-java/src/main/java/com/nanri/aiimage/config/LlmHttpConfigResolver.java @@ -0,0 +1,38 @@ +package com.nanri.aiimage.config; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +/** + * LLM 客户端统一配置解析(task-168,Coze 已改直连 LLM)。 + * + * 从 aiimage.http-client.* 命名空间读取 LLM 客户端超时/重试,默认值与现状一致 + * (connect 10s / read 60s / call 90s / retry 3);非法值钳制。模块级显式配置 + * (SimilarAsinProperties.llm*)优先级高于命名空间(行为不变)。 + */ +@Component +@RequiredArgsConstructor +public class LlmHttpConfigResolver { + + private final HttpClientProperties httpClientProperties; + + public long connectTimeoutMillis() { + return httpClientProperties.effectiveConnectTimeoutMillis(); + } + + public long readTimeoutMillis() { + return httpClientProperties.effectiveReadTimeoutMillis(); + } + + public long callTimeoutMillis() { + return httpClientProperties.effectiveCallTimeoutMillis(); + } + + public int maxRetries() { + return httpClientProperties.effectiveMaxRetries(); + } + + public long baseRetryDelayMillis() { + return httpClientProperties.getBaseRetryDelayMillis(); + } +} diff --git a/backend-java/src/test/java/com/nanri/aiimage/config/LlmHttpConfigResolverTest.java b/backend-java/src/test/java/com/nanri/aiimage/config/LlmHttpConfigResolverTest.java new file mode 100644 index 00000000..ab751574 --- /dev/null +++ b/backend-java/src/test/java/com/nanri/aiimage/config/LlmHttpConfigResolverTest.java @@ -0,0 +1,86 @@ +package com.nanri.aiimage.config; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * task-168:LLM 客户端配置接入契约(plan 10)。 + * 从 aiimage.http-client.* 读取超时/重试;默认值与现状一致;覆盖生效; + * 非法值钳制。 + */ +class LlmHttpConfigResolverTest { + + private HttpClientProperties properties; + private LlmHttpConfigResolver resolver; + + @BeforeEach + void setUp() { + properties = new HttpClientProperties(); + resolver = new LlmHttpConfigResolver(properties); + } + + @Test + void timeoutFromConfigApplied() { + assertEquals(10_000L, resolver.connectTimeoutMillis()); + assertEquals(60_000L, resolver.readTimeoutMillis()); + assertEquals(90_000L, resolver.callTimeoutMillis()); + } + + @Test + void defaultValuesKept() { + // 命名空间默认 = 现状基线(connect 10s / read 60s / call 90s / retry 3) + assertEquals(10_000L, resolver.connectTimeoutMillis()); + assertEquals(60_000L, resolver.readTimeoutMillis()); + assertEquals(3, resolver.maxRetries()); + assertEquals(500L, resolver.baseRetryDelayMillis()); + } + + @Test + void overrideTakesEffect() { + properties.setConnectTimeoutMillis(30_000); + properties.setReadTimeoutMillis(120_000); + properties.setMaxRetries(5); + + assertEquals(30_000L, resolver.connectTimeoutMillis()); + assertEquals(120_000L, resolver.readTimeoutMillis()); + assertEquals(5, resolver.maxRetries()); + } + + @Test + void callTimeoutReflectsTotal() { + properties.setCallTimeoutMillis(180_000); + assertEquals(180_000L, resolver.callTimeoutMillis()); + } + + @Test + void retryConfigReflected() { + properties.setMaxRetries(0); + assertEquals(0, resolver.maxRetries()); + properties.setMaxRetries(99); + assertEquals(10, resolver.maxRetries(), "重试钳制到 10"); + } + + @Test + void invalidTimeoutsClamped() { + properties.setConnectTimeoutMillis(-1); + assertEquals(1_000L, resolver.connectTimeoutMillis()); + properties.setReadTimeoutMillis(999_999_999L); + assertEquals(3_600_000L, resolver.readTimeoutMillis()); + } + + @Test + void resolverBindingWired() { + assertTrue(resolver.connectTimeoutMillis() > 0); + assertTrue(resolver.maxRetries() >= 0); + } + + @Test + void behaviorSameAsCurrentBaseline() { + // 与 SimilarAsinProperties.llm 现状对比:connect 一致(10s)、重试一致(3) + assertEquals(10_000L, resolver.connectTimeoutMillis()); + assertEquals(3, resolver.maxRetries()); + } +}