Skip to content

Honour llm.max_tokens on the AI analysis paths - #413

Closed
dpage wants to merge 1 commit into
mainfrom
fix/issue-399-llm-max-tokens
Closed

Honour llm.max_tokens on the AI analysis paths#413
dpage wants to merge 1 commit into
mainfrom
fix/issue-399-llm-max-tokens

Conversation

@dpage

@dpage dpage commented Aug 12, 2026

Copy link
Copy Markdown
Member

Summary

The estate summary, the Server Info database analysis, and the alerter's
tier 3 classification each hardcoded a 512-token output budget, so the
configured llm.max_tokens had no bearing on any of them. A reasoning
model spends part of its budget on an internal thinking block before it
emits any answer text, so with a local model served over an
OpenAI-compatible endpoint the cap was consumed entirely by reasoning;
the response then carried no text block, and because the callers treated
the resulting empty string as a successful generation the failure
surfaced as a blank panel with no error at all.

The changes are as follows:

  • Add (*llmproxy.Config).AnalysisMaxTokens, which reports the
    operator-configured llm.max_tokens and falls back to the new
    DefaultAnalysisMaxTokens (4096) when the setting is absent or
    non-positive, and use it for both the estate summary and the Server
    Info analysis, on the chat request and on the client options alike.

  • Give the alerter an llm.max_tokens setting, which it previously
    lacked entirely, with the same 4096 default and fallback rule, and
    plumb it through newLibReasoning into the classification request.

  • Report a response that yields no usable text rather than rendering it:
    the overview path returns an error naming the configured budget, the
    Server Info path logs and skips the cache so the next request retries
    instead of serving a blank result, and the alerter's Classify
    returns an error, which its caller already handles by failing safe to
    an alert.

  • Document the setting's effect on the analysis paths in the server and
    alerter configuration guides, update both commented example
    configurations, and add a changelog entry.

Test plan

  • New unit tests cover AnalysisMaxTokens and ReasoningMaxTokens
    across nil, unset, zero, negative, and configured values.

  • New table-driven tests drive the overview and Server Info paths
    against an OpenAI-compatible stub that decodes the request body, and
    assert that the budget on the wire is the configured value, or the
    4096 default when the setting is unset.

  • New tests assert that a response carrying no text (empty content, and
    whitespace only) produces an error naming the configured budget, and
    that the Server Info path leaves the cache untouched in that case.

  • Every changed function reports 100% line coverage:
    AnalysisMaxTokens, generateSummaryFromPrompt, both
    createLLMClient methods, getAIAnalysis, ReasoningMaxTokens,
    Classify, and newLibReasoning.

  • make lint passes with zero issues for both server and alerter,
    and gofmt reports no changes.

  • make test-all passes bar one pre-existing failure in
    server/internal/tools (TestStoreMemoryGeneratesEmbeddingIntegration
    and TestRecallMemoriesGeneratesQueryEmbeddingIntegration), which is
    a stale vector(3) fixture against the 4000-dimension padding and
    reproduces identically on a clean checkout of main; it is unrelated
    to this change and skips in CI, where Postgres lacks pgvector.

Closes #399

Summary by CodeRabbit

  • New Features

    • Added configurable LLM response token limits for chat, database analysis, estate summaries, and alert classification.
    • Added a default 4,096-token budget when limits are unset or invalid.
    • Reasoning models now account for their reasoning budget within the configured limit.
  • Bug Fixes

    • Empty or unusable AI responses are reported as errors and are not cached.
  • Documentation

    • Updated configuration guides, example files, and the changelog with token-limit behavior and settings.

The estate summary, the Server Info database analysis, and the alerter's
tier 3 classification each hardcoded a 512-token output budget, so the
configured llm.max_tokens had no bearing on any of them. A reasoning
model spends part of its budget on an internal thinking block before it
emits any answer text, and with a local model served over an
OpenAI-compatible endpoint that small cap was consumed entirely by
reasoning; the response then arrived with no text block at all, and
because the callers treated the resulting empty string as a successful
generation the failure surfaced as a blank panel with no error.

Both server paths now take their budget from the shared llmproxy config
via a new AnalysisMaxTokens helper, which reports the operator-configured
value and falls back to 4096 when the setting is absent or non-positive.
The alerter previously had no such setting at all, so it gains an
llm.max_tokens option with the same default and fallback rule.

A response that yields no usable text is now reported rather than
rendered: the overview path returns an error naming the configured
budget, the Server Info path logs and skips the cache so the next request
retries, and the alerter's Classify returns an error, which its caller
already handles by failing safe to an alert.

Closes #399
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The change adds configurable LLM output-token budgets with a 4096-token fallback. Alerter reasoning, estate summaries, and server-info analysis now use configured budgets. Empty or whitespace-only responses return errors and are not cached.

Changes

LLM token budget configuration

Layer / File(s) Summary
Alerter reasoning budget
alerter/src/internal/config/config.go, alerter/src/internal/config/config_test.go, alerter/src/internal/llm/...
The alerter adds llm.max_tokens, resolves positive or default values, passes the budget to reasoning providers, and rejects responses without text. Tests cover propagation and fallback behavior.
Server analysis budget
server/src/internal/llmproxy/proxy.go, server/src/internal/llmproxy/proxy_test.go
The server proxy adds DefaultAnalysisMaxTokens and AnalysisMaxTokens(). Tests verify fallback handling and propagation into client options.
Server analysis flows
server/src/internal/overview/..., server/src/internal/api/server_info_handlers...
Estate summaries and server-info analysis use the resolved token budget. Empty analysis responses return failures and are excluded from caching. Integration tests cover configured, default, and empty-response cases.
Configuration documentation
docs/changelog.md, docs/getting-started/configuration/..., examples/*.yaml
Documentation and examples describe the llm.max_tokens setting, its default, reasoning-token usage, and empty-response behavior.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ServerInfoHandler
  participant AnalysisConfig
  participant LLMProvider
  participant AnalysisCache
  ServerInfoHandler->>AnalysisConfig: resolve AnalysisMaxTokens()
  ServerInfoHandler->>LLMProvider: send request with configured MaxTokens
  LLMProvider-->>ServerInfoHandler: return analysis response
  alt usable text exists
    ServerInfoHandler->>AnalysisCache: cache analysis
  else no usable text
    ServerInfoHandler-->>ServerInfoHandler: log failure and return unavailable
  end
Loading

Possibly related PRs

Suggested reviewers: tsivaprasad

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.19% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary change: honoring llm.max_tokens on AI analysis paths.
Linked Issues check ✅ Passed The changes satisfy issue #399 by honoring llm.max_tokens, adding 4096-token fallbacks, handling empty responses, and documenting the behavior.
Out of Scope Changes check ✅ Passed All code, tests, documentation, examples, and changelog changes directly support configurable token budgets and empty-response handling.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-399-llm-max-tokens

Comment @coderabbitai help to get the list of available commands.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 35 complexity · 0 duplication

Metric Results
Complexity 35
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/changelog.md`:
- Around line 64-76: Update the changelog entry to state that llm.max_tokens
falls back to 4096 when absent, zero, or negative. Clarify the path-specific
empty-response behavior: Server Info database analysis reports an error, while
the alerter’s tier 3 classification reports a failed classification.

In `@docs/getting-started/configuration/server.md`:
- Around line 439-445: Update the max_tokens documentation to state that
non-positive values fall back to 4096 only for the AI estate summary and AI
database analysis requests. Do not claim this fallback applies to Ask Ellie
chat, which omits MaxTokens when llm.max_tokens is non-positive.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 24078633-806c-4125-8986-789b892d0690

📥 Commits

Reviewing files that changed from the base of the PR and between 19c645d and cc47377.

📒 Files selected for processing (16)
  • alerter/src/internal/config/config.go
  • alerter/src/internal/config/config_test.go
  • alerter/src/internal/llm/llm.go
  • alerter/src/internal/llm/reasoning.go
  • alerter/src/internal/llm/reasoning_test.go
  • docs/changelog.md
  • docs/getting-started/configuration/alerter.md
  • docs/getting-started/configuration/server.md
  • examples/ai-dba-alerter.yaml
  • examples/ai-dba-server.yaml
  • server/src/internal/api/server_info_handlers.go
  • server/src/internal/api/server_info_handlers_test.go
  • server/src/internal/llmproxy/proxy.go
  • server/src/internal/llmproxy/proxy_test.go
  • server/src/internal/overview/generator.go
  • server/src/internal/overview/generator_test.go

Comment thread docs/changelog.md
Comment on lines +64 to +76
- Honour the configured `llm.max_tokens` on the AI estate summary,
the Server Info database analysis, and the alerter's tier 3
classification, each of which previously hardcoded a 512-token
output budget. A reasoning model spends part of its budget on an
internal thinking block, so the small cap was consumed before the
model emitted any answer and the estate summary rendered as an
empty panel. Each path now reads the operator-configured budget
and falls back to `4096` when the setting is absent, and a
response that carries no text content is reported as an error
rather than being cached and rendered as an empty result. The
alerter gains a matching `llm.max_tokens` setting, which it
previously lacked. (#399)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

State the full fallback and empty-response behavior.

llm.max_tokens also falls back to 4096 for zero and negative values, not only when the setting is absent. Empty-response handling is path-specific: server analysis reports an error, while tier 3 classification reports a failed classification. Update this entry to match the configuration guides.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/changelog.md` around lines 64 - 76, Update the changelog entry to state
that llm.max_tokens falls back to 4096 when absent, zero, or negative. Clarify
the path-specific empty-response behavior: Server Info database analysis reports
an error, while the alerter’s tier 3 classification reports a failed
classification.

Comment on lines +439 to +445
The `max_tokens` option sets the output-token budget
for every LLM request the server makes. The budget
governs Ask Ellie chat, the AI estate summary on the
overview page, and the AI database analysis in the
Server Info dialog. The default value is `4096`;
values less than or equal to zero fall back to that
default.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 6 \
  'AnalysisMaxTokens|BuildClientOptions|MaxTokens' \
  server/src --glob '*.go'

Repository: pgEdge/ai-dba-workbench

Length of output: 50379


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- chat and streaming call sites ---'
rg -n -C 5 \
  'providerOptions\(|\.Chat\(|\.ChatStream\(|ChatStream|ChatRequest' \
  server/src/internal/llmproxy server/src/internal/api server/src/internal/overview \
  --glob '*.go' | head -n 260

printf '%s\n' '--- max-token assignment paths ---'
rg -n -C 4 \
  'MaxTokens|AnalysisMaxTokens' \
  server/src/internal/llmproxy/proxy.go \
  server/src/internal/api \
  server/src/internal/overview \
  --glob '*.go' | head -n 320

Repository: pgEdge/ai-dba-workbench

Length of output: 40426


Limit the fallback statement to analysis requests. Ask Ellie chat uses providerOptions, which omits MaxTokens when llm.max_tokens is non-positive and does not apply the 4096 fallback.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/getting-started/configuration/server.md` around lines 439 - 445, Update
the max_tokens documentation to state that non-positive values fall back to 4096
only for the AI estate summary and AI database analysis requests. Do not claim
this fallback applies to Ask Ellie chat, which omits MaxTokens when
llm.max_tokens is non-positive.

@dpage

dpage commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

Closing as a duplicate. PR #411 fixes the same three hardcoded output-token caps and goes further, also updating openapi.go, the generated openapi.json, the server-info documentation and the golang-expert knowledge base. There is nothing in this branch that #411 does not already cover, so #411 is the one to review for #399.

@dpage dpage closed this Aug 12, 2026
@dpage
dpage deleted the fix/issue-399-llm-max-tokens branch August 12, 2026 12:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AI analysis paths hardcode a 512-token output cap, producing empty summaries with local reasoning models

1 participant