apache / apache/hertzbeat

[BUG] <title>Missing Model–Provider Compatibility Validation

Open
#3,991 5 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Java
Stars
7.4k
Forks
1.3k
Avg merge
1d 20h
Merged PRs (30d)
32

Description

### Is there an existing issue for this?

- [x] I have searched the existing issues

### Current Behavior

HertzBeat AI is configured with:
AI Provider: DeepSeek
Base URL: https://api.deepseek.com/v1
Model: deepseek-reasoner
Valid API Key
Backend logs show repeated errors:
400 Bad Request from POST https://api.deepseek.com/v1/chat/completions
org.springframework.ai.chat.model.MessageAggregator - Aggregation Error
No actionable error message is returned to the user:
The response body from DeepSeek is not logged
The root cause (invalid request schema / unsupported model) is hidden
HertzBeat allows selecting deepseek-reasoner even though it is not fully compatible with the current Spring AI chat request format.

### Expected Behavior

HertzBeat should validate model–provider compatibility before sending requests:
Incompatible models (e.g., deepseek-reasoner) should be blocked or warned about Supported models (e.g., deepseek-chat) should work without errors.
If an incompatible model is selected:
HertzBeat should fail gracefully with a clear message, such as:

### Steps To Reproduce

Deploy or run HertzBeat v1.8.0.

Open the HertzBeat Web UI.

Navigate to Settings → AI Assistant (or AI Provider configuration page).

Configure the AI provider with the following values:

Provider: DeepSeek

Base URL: https://api.deepseek.com/v1

API Key: (valid DeepSeek API key)

Model: deepseek-reasoner

Save the configuration.

Open the AI Assistant / Chatbot in the UI.

Enter any prompt (for example: Hello or Test message).

### Environment

```markdown
HertzBeat version(s): v1.8.0

AI Integration: HertzBeat AI Assistant (Spring AI based)

AI Provider: DeepSeek

Model: deepseek-reasoner

API Base URL: https://api.deepseek.com/v1

Deployment Mode:

Standalone / Docker (choose one if needed)

Backend Runtime:

Java 17

Spring Boot (Reactive WebClient)

Frontend: HertzBeat Web UI

Operating System: Linux (or your actual OS if different)

Network: Direct internet access (no proxy)

Control Environment (Working Configuration)

AI Provider: DeepSeek

Model: deepseek-chat

Result: Works as expected

If you want, I can:

Adjust this to match Docker / Kubernetes specifically

Shorten it to a minimal issue template

Add log level / debug flags

Just say 👍
```

### Debug logs

2026-01-15 10:59:54 [reactor-http-epoll-6] ERROR org.springframework.ai.chat.model.MessageAggregator - Aggregation Error
2026-01-15T10:59:54.454975323+08:00 org.springframework.web.reactive.function.client.WebClientResponseException$BadRequest: 400 Bad Request from POST https://api.deepseek.com/v1/chat/completions
2026-01-15T10:59:54.454980895+08:00 at org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:321)
2026-01-15T10:59:54.454984579+08:00 Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
2026-01-15T10:59:54.454988119+08:00 Error has been observed at the following site(s):
2026-01-15T10:59:54.454993791+08:00 *__checkpoint ⇢ 400 BAD_REQUEST from POST https://api.deepseek.com/v1/chat/completions [DefaultWebClient]
2026-01-15T10:59:54.454997134+08:00 Original Stack Trace:
2026-01-15T10:59:54.455000107+08:00 at org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:321)
2026-01-15T10:59:54.455004059+08:00 at org.springframework.web.reactive.function.client.DefaultClientResponse.lambda$createException$1(DefaultClientResponse.java:214)
2026-01-15T10:59:54.455009769+08:00 at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:106)
2026-01-15T10:59:54.455013059+08:00 at io.opentelemetry.instrumentation.reactor.v3_1.TracingSubscriber.onNext(TracingSubscriber.java:68)
2026-01-15T10:59:54.455017461+08:00 at reactor.core.publisher.FluxOnErrorReturn$ReturnSubscriber.onNext(FluxOnErrorReturn.java:162)
2026-01-15T10:59:54.455021209+08:00 at io.opentelemetry.instrumentation.reactor.v3_1.TracingSubscriber.onNext(TracingSubscriber.java:68)
2026-01-15T10:59:54.455024290+08:00 at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:122)
2026-01-15T10:59:54.455027221+08:00 at io.opentelemetry.instrumentation.reactor.v3_1.TracingSubscriber.onNext(TracingSubscriber.java:68)
2026-01-15T10:59:54.455030505+08:00 at reactor.core.publisher.FluxHide$SuppressFuseableSubscriber.onNext(FluxHide.java:137)
2026-01-15T10:59:54.455033460+08:00 at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129)
2026-01-15T10:59:54.455036399+08:00 at io.opentelemetry.instrumentation.reactor.v3_1.TracingSubscriber.onNext(TracingSubscriber.java:68)
2026-01-15T10:59:54.455039333+08:00 at reactor.core.publisher.FluxHide$SuppressFuseableSubscriber.onNext(FluxHide.java:137)
2026-01-15T10:59:54.455047352+08:00 at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107)
2026-01-15T10:59:54.455050492+08:00 at io.opentelemetry.instrumentation.reactor.v3_1.TracingSubscriber.onNext(TracingSubscriber.java:68)
2026-01-15T10:59:54.455094141+08:00 at reactor.core.publisher.FluxHide$SuppressFuseableSubscriber.onNext(FluxHide.java:137)
2026-01-15T10:59:54.455097479+08:00 at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129)
2026-01-15T10:59:54.455100441+08:00 at io.opentelemetry.instrumentation.reactor.v3_1.TracingSubscriber.onNext(TracingSubscriber.java:68)
2026-01-15T10:59:54.455103420+08:00 at reactor.core.publisher.FluxHide$SuppressFuseableSubscriber.onNext(FluxHide.java:137)
2026-01-15T10:59:54.455106315+08:00 at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:118)
2026-01-15T10:59:54.455109264+08:00 at io.opentelemetry.instrumentation.reactor.v3_1.TracingSubscriber.onNext(TracingSubscriber.java:68)
2026-01-15T10:59:54.455112217+08:00 at reactor.core.publisher.FluxHide$SuppressFuseableSubscriber.onNext(FluxHide.java:137)
2026-01-15T10:59:54.455115304+08:00 at reactor.core.publisher.Operators$BaseFluxToMonoOperator.completePossiblyEmpty(Operators.java:2097)
2026-01-15T10:59:54.455118507+08:00 at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:145)
2026-01-15T10:59:54.455121434+08:00 at io.opentelemetry.instrumentation.reactor.v3_1.TracingSubscriber.onComplete(TracingSubscriber.java:92)
2026-01-15T10:59:54.455124791+08:00 at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
2026-01-15T10:59:54.455127657+08:00 at io.opentelemetry.instrumentation.reactor.v3_1.TracingSubscriber.onComplete(TracingSubscriber.java:92)
2026-01-15T10:59:54.455130597+08:00 at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260)
2026-01-15T10:59:54.455133667+08:00 at io.opentelemetry.instrumentation.reactor.v3_1.TracingSubscriber.onComplete(TracingSubscriber.java:92)
2026-01-15T10:59:54.455136638+08:00 at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
2026-01-15T10:59:54.455139612+08:00 at reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:413)
2026-01-15T10:59:54.455142561+08:00 at reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:455)
2026-01-15T10:59:54.455145454+08:00 at reactor.netty.channel.ChannelOperations.terminate(ChannelOperations.java:509)
2026-01-15T10:59:54.455148455+08:00 at reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:819)

### Anything else?

Observed Behavior When Switching Model

Model deepseek-chat works fine

Model deepseek-reasoner fails with 400

This confirms the issue is model compatibility, not API key or URL.

Possible Root Cause (Optional but Helpful)

You can add this to help maintainers quickly understand the issue:

Possible root cause: HertzBeat uses Spring AI’s OpenAI-style chat request schema.
DeepSeek deepseek-reasoner model expects a different request format and rejects the request, resulting in 400 Bad Request.
Spring AI Message Aggregator may generate unsupported fields.

Contributor guide

Open the contributing guide

Research direction

Start at the AI Provider configuration page and the Spring AI chat integration, then reproduce the request with deepseek-chat and deepseek-reasoner using the supplied DeepSeek URL. Trace how the 400 response is aggregated and surfaced. Done means incompatible model/provider selections are blocked or warned about and failures provide an actionable message instead of hiding the response body.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring-boot
Domain
ai, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.