Skip to content

fix(llm,web): chat and completion prompts keep the active model's full context, embedding cold starts and deploy windows stop breaking chat, and API/error responses report real status codes - #52

Merged
WilliamAGH merged 14 commits into
mainfrom
dev
Jun 10, 2026

Conversation

@WilliamAGH

@WilliamAGHWilliamAGH commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Summary

Latency and correctness fixes across the LLM path and web layer: prompts now truncate to the limits of the model actually serving each request (previously every GPT-5-family chat — including gateway gpt-5.4 with 400K context — was silently cut to 7K tokens), live chat turns no longer pay 20–60s serverless embedding cold starts, error and API responses report real status codes, the SPA survives rolling deploys with a guarded auto-reload, and the frontend picks up the grouped Dependabot security bumps.

Changes

Bug Fixes

  • GPT-5-family chats through OpenAI direct/the LLM gateway keep their full context: the 7K input cap modeled GitHub Models' 8K tier but was applied family-wide on both the streaming and completion paths, truncating every gpt-5.4 chat turn to 7K tokens; the cap now applies only when GitHub Models serves a GPT-5 model, and the truncation notice no longer claims an 8K limit for a 100K truncation (OpenAiRequestFactory.prepareStreamingRequest/buildCompletionRequest)
  • Completion prompts no longer truncate to the unused provider's limit: truncation moved inside buildCompletionRequest, keyed to the active provider's model; fallback re-truncates with the fallback model's own limit (OpenAIStreamingService.complete; fixes[Detail Bug] Prompt truncation uses limits from unused provider, forcing 7K cap on high-context models #40, contributed in Scope completion truncation to active provider #49 by @pntech20)
  • o-series models keep their full input window: o1/o3/o3-mini use the default 100K cap instead of inheriting the GPT-5 tier cap (OpenAiRequestFactory)
  • Chat turns no longer absorb serverless embedding cold starts: a 4-minute keep-alive probe plus startup warmup keeps the remote embedding model resident (reproduced: 23.7s cold vs 1.0s warm); slow probes log WARN as a cold-start record (EmbeddingModelKeepAlive)
  • Missing assets and error pages return their real HTTP status instead of 200: the error-dispatch status now survives the forward to the error documentation pages, so deploy-window asset misses surface as clean 404s instead of browser strict-MIME module errors; directly-browsed docs stay 200 and the swallowed IOException logs (ErrorDocumentationController, CustomErrorController)
  • Unknown /api paths return JSON 404 instead of the SPA shell with 200: a (?!api$) guard keeps the API namespace out of the SPA fallback patterns, and bare /api joins the JSON error branch (WebMvcConfig, CustomErrorController)
  • The SPA recovers from rolling-deploy chunk failures: a vite:preloadError listener reloads once per session (sessionStorage-guarded) to pick up the fresh build instead of leaving a dead page (frontend/src/main.ts)
  • Chrome's deprecated-meta warning silenced: mobile-web-app-capable added alongside the apple variant (frontend/index.html)

Dependencies

  • Frontend npm group bump (Dependabot build(deps): bump the npm_and_yarn group across 1 directory with 8 updates #50): dompurify 3.3.1→3.4.0 (markdown sanitizer; verified its FORBID_TAGS/ADD_ATTR changes don't intersect with our single static config), svelte 5.48→5.55, vite 6.4.1→6.4.2, plus transitive security patches; validated with clean install, lint, svelte-check, production build, and 80/80 tests (frontend/package.json, lockfile)

Refactoring

  • Removed the two orphaned public truncation overloads left after the provider-scoped fix; regression tests exercise buildCompletionRequest, covering the full provider-to-model-id truncation path (OpenAiRequestFactory, OpenAiRequestFactoryTest)

Breaking Changes

None

Related Issues

Closes#40

macand others added 6 commits May 17, 2026 09:25
…dates
Bumps the npm_and_yarn group with 7 updates in the /frontend directory:
| Package | From | To |
| --- | --- | --- |
| [dompurify](https://github.com/cure53/DOMPurify) | `3.3.1` | `3.4.0` |
| [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) | `5.48.0` | `5.55.7` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `6.4.1` | `6.4.2` |
| [minimatch](https://github.com/isaacs/minimatch) | `3.1.2` | `3.1.5` |
| [minimatch](https://github.com/isaacs/minimatch) | `9.0.5` | `9.0.9` |
| [flatted](https://github.com/WebReflection/flatted) | `3.3.3` | `3.4.2` |
| [picomatch](https://github.com/micromatch/picomatch) | `2.3.1` | `4.0.4` |
| [rollup](https://github.com/rollup/rollup) | `4.56.0` | `4.60.4` |
Updates `dompurify` from 3.3.1 to 3.4.0
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](cure53/DOMPurify@3.3.1...3.4.0)
Updates `svelte` from 5.48.0 to 5.55.7
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.55.7/packages/svelte)
Updates `vite` from 6.4.1 to 6.4.2
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.4.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.4.2/packages/vite)
Updates `minimatch` from 3.1.2 to 3.1.5
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](isaacs/minimatch@v3.1.2...v3.1.5)
Updates `minimatch` from 9.0.5 to 9.0.9
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](isaacs/minimatch@v3.1.2...v3.1.5)
Updates `devalue` from 5.6.2 to 5.8.1
- [Release notes](https://github.com/sveltejs/devalue/releases)
- [Changelog](https://github.com/sveltejs/devalue/blob/main/CHANGELOG.md)
- [Commits](sveltejs/devalue@v5.6.2...v5.8.1)
Updates `flatted` from 3.3.3 to 3.4.2
- [Commits](WebReflection/flatted@v3.3.3...v3.4.2)
Updates `picomatch` from 2.3.1 to 4.0.4
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](micromatch/picomatch@2.3.1...4.0.4)
Updates `rollup` from 4.56.0 to 4.60.4
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](rollup/rollup@v4.56.0...v4.60.4)
---
updated-dependencies:
- dependency-name: dompurify
dependency-version: 3.4.0
dependency-type: direct:production
dependency-group: npm_and_yarn
- dependency-name: svelte
dependency-version: 5.55.7
dependency-type: direct:development
dependency-group: npm_and_yarn
- dependency-name: vite
dependency-version: 6.4.2
dependency-type: direct:development
dependency-group: npm_and_yarn
- dependency-name: minimatch
dependency-version: 3.1.5
dependency-type: indirect
dependency-group: npm_and_yarn
- dependency-name: minimatch
dependency-version: 9.0.9
dependency-type: indirect
dependency-group: npm_and_yarn
- dependency-name: devalue
dependency-version: 5.8.1
dependency-type: indirect
dependency-group: npm_and_yarn
- dependency-name: flatted
dependency-version: 3.4.2
dependency-type: indirect
dependency-group: npm_and_yarn
- dependency-name: picomatch
dependency-version: 4.0.4
dependency-type: indirect
dependency-group: npm_and_yarn
- dependency-name: rollup
dependency-version: 4.60.4
dependency-type: indirect
dependency-group: npm_and_yarn
...
Signed-off-by: dependabot[bot] <support@github.com>
Scope completion truncation to active provider
…ween chats
Live chat turns paid serverless cold starts on the remote embedding
provider: 53.5s observed in production, 23.7s cold vs 1.0s warm
reproduced against the same provider. A scheduled probe every 4 minutes
(below common scale-to-zero windows) plus a startup warmup keeps the
model resident; probes exceeding 5s log WARN as evidence the model went
cold, and probe failures are logged without affecting request handling.
…-scoped fix
The no-arg truncatePromptForCompletion lost its only production caller
in #49 and silently assumed the OPENAI provider; the provider-arg
overload was reachable only from tests. Both removed per [AB1d]; the
regression tests now exercise buildCompletionRequest, covering the full
provider-to-model-id truncation path.
…PT-5's 7K limit
The 7K truncation cap exists for GPT-5's 8K input tier on GitHub
Models; o-series reasoning models accept >=128K input tokens, so the
canonicalModelName startsWith("o") clause silently truncated prompts
that fit. The limit now follows the GPT-5 family check alone; the
reasoning-model distinction still governs temperature omission in
buildResponseParams.
CopilotAI review requested due to automatic review settings June 10, 2026 04:26
@coderabbitai

coderabbitaiBot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a scheduled EmbeddingModelKeepAlive component to probe embedding providers and moves completion prompt truncation into OpenAiRequestFactory so truncation uses the resolved model’s token limits; also updates a few frontend dependency versions.

Changes

Embedding Model Keep-Alive

Layer / File(s)Summary
EmbeddingModelKeepAlive component and keep-alive tests
src/main/java/com/williamcallahan/javachat/service/EmbeddingModelKeepAlive.java, src/test/java/com/williamcallahan/javachat/service/EmbeddingModelKeepAliveTest.java
Spring @Component schedules keepEmbeddingModelWarm() with fixed warmup/interval/threshold, calls EmbeddingClient.embed(...), logs WARN on provider unavailability without rethrowing, and logs WARN/DEBUG depending on probe latency. Tests verify single embed invocation and exception suppression.

Provider-Aware Prompt Truncation

Layer / File(s)Summary
Model-aware truncation in OpenAiRequestFactory
src/main/java/com/williamcallahan/javachat/service/OpenAiRequestFactory.java, src/test/java/com/williamcallahan/javachat/service/OpenAiRequestFactoryTest.java
buildCompletionRequest resolves the selected modelId and runs truncatePromptForCompletion(prompt, modelId) which applies a GPT-5-family 7K input cap only when the resolved model is GPT-5-family; truncated prompts receive the appropriate notice prefix. Tests confirm behavior for OPENAI, o-series, and GITHUB_MODELS selections.
OpenAIStreamingService removes local truncation
src/main/java/com/williamcallahan/javachat/service/OpenAIStreamingService.java
complete(...) no longer computes a local truncated prompt; it passes the original prompt into requestFactory.buildCompletionRequest(...), delegating truncation to the factory.

Frontend dependency updates

Layer / File(s)Summary
package.json dependency bumps
frontend/package.json
Bumps dompurify and upgrades vite (and adjusts svelte span) in frontend dev dependencies.

Sequence Diagram(s)

sequenceDiagram
participant SpringScheduler as Spring Scheduler
participant KeepAlive as EmbeddingModelKeepAlive
participant EmbedClient as EmbeddingClient
SpringScheduler->>KeepAlive: keepEmbeddingModelWarm() (scheduled)
KeepAlive->>KeepAlive: record probe start time
KeepAlive->>EmbedClient: embed(probe text)
alt Provider Available
EmbedClient-->>KeepAlive: embedding result
KeepAlive->>KeepAlive: compare latency to cold-start threshold
KeepAlive->>KeepAlive: log WARN (cold) or DEBUG (warm)
else Provider Unavailable
EmbedClient-->>KeepAlive: EmbeddingServiceUnavailableException
KeepAlive->>KeepAlive: log WARN, suppress exception
KeepAlive-->>SpringScheduler: no exception propagated
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • WilliamAGH/java-chat#49: Directly related truncation refactor to scope prompt limits by the active provider instead of aggregating limits across configured providers.

Poem

🌱 A tiny probe wakes sleeping models at dawn,
Prompts trimmed by the model they’re truly upon.
Dependencies polished, tests sing their part—
Warm embeddings and kinder truncation, smart! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 26.67% 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 nameStatusExplanation
Linked Issues check✅ PassedAll requirements from issue #40 are met: truncation moved to buildCompletionRequest keyed to active provider's model ID (#40), o-series models use default 100K cap instead of GPT-5's 7K, provider fallback re-truncates with fallback model's limits, orphaned overloads removed, and regression tests added covering the full provider-to-model-id truncation path.
Out of Scope Changes check✅ PassedAll changes are directly scoped to the linked issue: prompt truncation refactoring, embedding keep-alive implementation, and orphaned method removal. The minor frontend dependency bumps (dompurify, vite) are reasonable maintenance updates unrelated to the core fixes.
Title check✅ PassedThe title comprehensively captures the main changes: prompt context preservation for active models, embedding cold start fixes, and HTTP status code corrections.
Description check✅ PassedThe description is well-structured and directly relates to the changeset, providing clear summaries of bug fixes, dependencies, and refactoring across multiple files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitaicoderabbitaiBot added enhancement New feature or request java Pull requests that update java code refactor Code refactoring labels Jun 10, 2026

@chatgpt-codex-connectorchatgpt-codex-connectorBot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:b6878a450f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

CopilotAI 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.

Pull request overview

This PR fixes two LLM-path latency/correctness issues: it scopes completion prompt truncation to the active provider/model (so high-context models don’t get capped by an unused provider’s GPT‑5 defaults), and it adds an embedding keep-alive scheduler to prevent chat turns from stalling on embedding cold starts.

Changes:

  • Move completion truncation into OpenAiRequestFactory.buildCompletionRequest(...), keyed to the resolved model ID for each provider attempt (including fallback).
  • Adjust truncation rules so GPT‑5-family models use the 7K safe input budget while o‑series models no longer inherit that cap.
  • Add EmbeddingModelKeepAlive + tests to probe the embedding provider on a cadence and avoid cold-start latency on user requests.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/main/java/com/williamcallahan/javachat/service/OpenAIStreamingService.javaStops pre-truncating before provider selection; passes raw prompt so each provider attempt can truncate to its own model limit.
src/main/java/com/williamcallahan/javachat/service/OpenAiRequestFactory.javaApplies completion prompt truncation inside buildCompletionRequest using the resolved model ID; adjusts GPT‑5 vs non‑GPT‑5 token budgeting.
src/main/java/com/williamcallahan/javachat/service/EmbeddingModelKeepAlive.javaAdds scheduled embedding probe to keep serverless/self-hosted embedding models warm and avoid cold-start latency.
src/test/java/com/williamcallahan/javachat/service/OpenAiRequestFactoryTest.javaAdds regression tests ensuring provider/model-specific truncation behavior (OpenAI vs GitHub Models; GPT‑5 vs o‑series).
src/test/java/com/williamcallahan/javachat/service/EmbeddingModelKeepAliveTest.javaAdds unit tests verifying probing behavior and that provider unavailability is not propagated.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…nd/npm_and_yarn-8019164dec
build(deps): bump the npm_and_yarn group across 1 directory with 8 updates
ErrorDocumentationController served every error page with
ResponseEntity.ok(), overwriting the status CustomErrorController set on
the forward. A missing hashed asset therefore answered 200 text/html,
which browsers surface as a strict-MIME module error during deploy
windows instead of a clean 404. The controller now preserves the
error-dispatch status (ERROR_STATUS_CODE survives the forward) while
direct documentation visits stay 200; the swallowed IOException in
serveHtmlFile now logs per [RC1e].
handlePageError forwarded to the error page without setting the
ModelAndView status, leaving the forward's default 200 in place. Sets
the resolved status at the dispatch source so the error contract does
not depend solely on the forwarded controller.
…e variant
Chrome deprecated honoring apple-mobile-web-app-capable alone and logs a
console warning; the standard tag is added first while the apple tag
stays for older iOS Safari.
@WilliamAGHWilliamAGH changed the title fix(llm): completion prompts keep the active model's full context window, and chat turns no longer stall on embedding cold startsfix(llm,web): completion prompts keep the active model's full context, chat turns skip embedding cold starts, and error pages return real status codesJun 10, 2026
… of the whole GPT-5 family
Both prepareStreamingRequest and buildCompletionRequest applied the
7,000-token input cap to every GPT-5-family model, so gpt-5.4 served
via OpenAI direct or the LLM gateway (400K context) was truncated to
7K on every chat turn and completion. The cap models GitHub Models'
8K input tier and now applies only when that provider serves a GPT-5
model; everything else uses the 100K default. The truncation notice
follows the same flag so a 100K truncation no longer claims an 8K
limit. Constant renamed to MAX_TOKENS_GITHUB_MODELS_GPT5_INPUT to name
its real owner.
Unknown /api paths matched the dot-less SPA view-controller patterns
and answered 200 with the index.html shell. The (?!api$) segment guard
keeps them out of the fallback so they reach CustomErrorController's
JSON branch as real 404s; SPA deep links keep forwarding to index.html.
The JSON-vs-HTML branch matched only /api/ prefixes, so a request to
exactly /api received the HTML error page; it now gets the JSON error
contract like the rest of the namespace.
…s with one guarded reload
A rolling deploy can 404 lazily-loaded chunks referenced by the new
index.html while the old container drains. The vite:preloadError
listener reloads once per session to pick up the fresh build instead of
leaving a dead page; the sessionStorage guard prevents reload loops if
the failure persists.
@WilliamAGHWilliamAGH changed the title fix(llm,web): completion prompts keep the active model's full context, chat turns skip embedding cold starts, and error pages return real status codesfix(llm,web): chat and completion prompts keep the active model's full context, embedding cold starts and deploy windows stop breaking chat, and API/error responses report real status codesJun 10, 2026
@WilliamAGH
WilliamAGH merged commit 3159c42 into mainJun 10, 2026
3 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancementNew feature or requestjavaPull requests that update java coderefactorCode refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@WilliamAGH