mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 11:13:43 +08:00
fix(llm): pin OpenAI-compatible HTTP client to HTTP/1.1 (#89)
This commit is contained in:
parent
3f1f83bbfe
commit
9fd5843dda
@ -1611,6 +1611,7 @@ public class AgentGraphBuilder {
|
||||
private RestClient.Builder applyHttpTimeouts(RestClient.Builder builder, Integer readTimeoutOverride) {
|
||||
HttpClient httpClient = HttpClient.newBuilder()
|
||||
.connectTimeout(vip.mate.llm.chatmodel.HttpTimeouts.CONNECT_TIMEOUT)
|
||||
.version(HttpClient.Version.HTTP_1_1)
|
||||
.build();
|
||||
JdkClientHttpRequestFactory rf = new JdkClientHttpRequestFactory(httpClient);
|
||||
rf.setReadTimeout(vip.mate.llm.chatmodel.HttpTimeouts.resolveReadTimeout(readTimeoutOverride));
|
||||
@ -1641,8 +1642,13 @@ public class AgentGraphBuilder {
|
||||
* {@link #applyHttpTimeouts(RestClient.Builder, Integer)}.
|
||||
*/
|
||||
private WebClient.Builder applyHttpTimeoutsToWebClient(WebClient.Builder builder, Integer readTimeoutOverride) {
|
||||
// Pin HTTP/1.1: many self-hosted OpenAI-compatible servers (vLLM, lmstudio,
|
||||
// llama.cpp, ollama — all uvicorn/ASGI based) only speak HTTP/1.1 over
|
||||
// cleartext and slam the socket on the JDK client's default H2C upgrade
|
||||
// probe, surfacing as "header parser received no bytes" with no body sent.
|
||||
HttpClient httpClient = HttpClient.newBuilder()
|
||||
.connectTimeout(vip.mate.llm.chatmodel.HttpTimeouts.CONNECT_TIMEOUT)
|
||||
.version(HttpClient.Version.HTTP_1_1)
|
||||
.build();
|
||||
org.springframework.http.client.reactive.JdkClientHttpConnector connector =
|
||||
new org.springframework.http.client.reactive.JdkClientHttpConnector(httpClient);
|
||||
|
||||
@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.JdkClientHttpRequestFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestClient;
|
||||
@ -13,6 +14,7 @@ import vip.mate.exception.MateClawException;
|
||||
import vip.mate.llm.model.*;
|
||||
import vip.mate.llm.oauth.OpenAIOAuthService;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@ -413,7 +415,7 @@ public class ModelDiscoveryService {
|
||||
}
|
||||
String apiKey = provider.getApiKey();
|
||||
|
||||
RestClient client = RestClient.builder()
|
||||
RestClient client = openAiCompatibleClientBuilder()
|
||||
.baseUrl(baseUrl)
|
||||
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
|
||||
.build();
|
||||
@ -623,7 +625,7 @@ public class ModelDiscoveryService {
|
||||
Map<String, Object> kwargs = modelProviderService.readProviderGenerateKwargs(provider);
|
||||
String completionsPath = resolveCompletionsPath(baseUrl, kwargs);
|
||||
|
||||
RestClient.RequestHeadersSpec<?> spec = RestClient.builder()
|
||||
RestClient.RequestHeadersSpec<?> spec = openAiCompatibleClientBuilder()
|
||||
.baseUrl(baseUrl)
|
||||
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
|
||||
.build()
|
||||
@ -915,6 +917,23 @@ public class ModelDiscoveryService {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a RestClient.Builder pinned to HTTP/1.1 for self-hosted OpenAI-compatible
|
||||
* servers. Java's HttpClient defaults to HTTP/2 and over cleartext attempts an
|
||||
* H2C upgrade ({@code Upgrade: h2c, Connection: Upgrade, HTTP2-Settings: ...}).
|
||||
* Uvicorn-based stacks (vLLM, lmstudio, llama.cpp, ollama) reject the upgrade
|
||||
* by closing the socket mid-handshake — surfacing as either
|
||||
* "header parser received no bytes" on the chat path or, more subtly, a
|
||||
* 400 with body=None on the test path because the body never makes it past
|
||||
* the upgrade negotiation.
|
||||
*/
|
||||
private RestClient.Builder openAiCompatibleClientBuilder() {
|
||||
HttpClient httpClient = HttpClient.newBuilder()
|
||||
.version(HttpClient.Version.HTTP_1_1)
|
||||
.build();
|
||||
return RestClient.builder().requestFactory(new JdkClientHttpRequestFactory(httpClient));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void applyCustomHeaders(RestClient.RequestHeadersSpec<?> spec, Map<String, Object> kwargs) {
|
||||
if (kwargs == null) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user