Skip to content

Prompt framework and backend consolidation#41

Open
beastyrabbit wants to merge 9 commits intomainfrom
feature/prompt-framework-analysis-backend-consolidation
Open

Prompt framework and backend consolidation#41
beastyrabbit wants to merge 9 commits intomainfrom
feature/prompt-framework-analysis-backend-consolidation

Conversation

@beastyrabbit
Copy link
Copy Markdown
Owner

Summary

  • Queue runtime monitoring and Codex timeout configuration

Somewhere a GPU is overheating so I don't have to think.

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Mar 22, 2026

Greptile Summary

This PR delivers two major workstreams: a prompt framework overhaul (profile/mode/distance composability, per-field sanitization, prompt budget contracts, diagnostic helpers) and a backend consolidation that removes duplicated LLM logic from web routes, replacing it with a thin proxyAutoplayerRequest proxy to the server. On top of these, it adds worker/queue runtime monitoring (periodic diagnostics snapshots, slow-item warnings, playlist buffer alerts), improved observability (multi-target logger with API-key redaction, per-request IDs, noisy-route aggregation), and a pipeline truth UI panel surfacing live generation state.

Key changes:

  • apps/server/src/external/llm.ts: Prompt sections are now composable — PromptProfile (strict/balanced/creative/compact), PromptMode (full/minimal/none), and PromptDistance (close/general/faithful/album) combine to build the system prompt. getSongPromptContract enables diagnostics and test coverage. New LLM functions (enhancePlaylistPrompt, enhanceSongRequest, refineSessionPrompt, enhanceSessionParams) consolidated from web to server.
  • apps/web/src/lib/autoplayer-proxy.ts: Single-function proxy replaces ~500 lines of duplicated handler logic across 8+ web routes.
  • apps/server/src/worker/index.ts: Worker diagnostics added — SQLite queries for song status counts, oldest transient songs, playlist buffer health, and queue age warnings. stopWorkerDiagnostics exported for graceful shutdown.
  • apps/server/src/worker/song-worker.ts: Playlist manager refresh moved from awaited (blocking) to best-effort fire-and-forget, allowing song generation to start immediately.
  • apps/server/src/logger.ts: Upgraded to multi-target (pino-pretty + ndjson file), with API key redaction and session metadata.
  • apps/web/src/lib/pipeline-truth.ts / autoplayer_.queue.tsx: Client-side pipeline state cross-correlation between song statuses and queue items, surfaced as a sparkline panel on the queue view.

Confidence Score: 4/5

  • Safe to merge with minor follow-up cleanups on sanitization consistency and the magic constant in pipeline-truth.
  • Large-scope refactor (32 files) but well-structured — the proxy consolidation is mechanical and thoroughly tested by the updated llm-sync test. The prompt framework changes are unit-tested in the new prompt-contract test suite. The song-worker behavioral change (fire-and-forget refresh) is intentional and documented in code comments. No data loss, security, or critical runtime risks identified. Three P2 style concerns remain: implicit deduplication reliance in ensurePlaylistManagerRefresh, missing per-field sanitization in buildAlbumPrompt, and a magic number in pipeline-truth.
  • apps/server/src/routes/autoplayer.ts (buildAlbumPrompt sanitization consistency), apps/web/src/lib/pipeline-truth.ts (hardcoded priority threshold)

Important Files Changed

Filename Overview
apps/server/src/external/llm.ts Major prompt framework refactor: monolithic system prompts replaced with composable sections (PromptProfile, PromptMode, PromptDistance), prompt sanitization helpers added, new LLM functions (enhancePlaylistPrompt, enhanceSongRequest, refineSessionPrompt, enhanceSessionParams) consolidated from web, and getSongPromptContract added for diagnostics. The buildAlbumPrompt in the autoplayer route (not here) lacks per-field sanitization for source song data, though sanitization does happen at the prompt level downstream.
apps/server/src/routes/autoplayer.ts New server-side endpoints for generate-song, generate-album-track, extract-persona, enhance-prompt, enhance-request, refine-prompt, enhance-session, and prompt-contract. The buildAlbumPrompt function embeds sourceSong fields (title, description, lyrics, etc.) into the prompt without per-field sanitization, relying on downstream sanitization in buildSongUserPrompt.
apps/server/src/worker/index.ts Adds comprehensive worker diagnostics: queue status snapshots, song status counts from SQLite, oldest transient songs, and playlist buffer health checks. Scheduled interval with configurable env vars. stopWorkerDiagnostics exported for graceful shutdown. Well-structured with safeMs guard for NaN/invalid timestamps.
apps/server/src/worker/song-worker.ts Playlist manager refresh moved from awaited call (blocking song generation) to best-effort fire-and-forget via new ensurePlaylistManagerRefresh method. The abort check after the old await is removed since refresh is now independent. Deduplication relies on refreshPlaylistManager's own managerRefreshInFlight guard.
apps/web/src/lib/autoplayer-proxy.ts New thin proxy utility: forwards web route requests to backend API server. Passes query string, content-type, method, body, and client abort signal. Intentionally minimal — only forwards content-type to avoid leaking client headers. Error propagation handled by framework.
apps/web/src/lib/pipeline-truth.ts New utility computing cross-cutting pipeline state from songs array + worker queue status. Hardcodes 20_000 as the persona-job priority threshold — a magic number that should match the server-side PERSONA_PRIORITY constant.
apps/server/src/logger.ts Logger upgraded to multi-target: pino-pretty in dev and optional ndjson file output. Adds API key redaction (authorization, apiKey, openrouterApiKey) and service/session metadata in base. Exports loggingConfig for startup diagnostics. isTest guard prevents file logs and pretty output in test runs.
apps/server/src/index.ts Adds request lifecycle middleware with per-request IDs, slow-request warnings, and aggregated summaries for noisy polling routes. Adds unhandledRejection/uncaughtException handlers. stopWorkerDiagnostics called on shutdown. Well-structured with configurable thresholds via env vars.
apps/server/src/events/event-bus.ts Adds configurable event and handler tracing: LOG_EVENT_BUS controls emit-level debug logging, LOG_EVENT_HANDLER_TRACE enables per-handler completion logs, LOG_EVENT_HANDLER_SLOW_MS emits warnings for slow handlers. Sequence counter added for correlating event logs.
apps/server/src/tests/prompt-contract.test.ts New test suite for getSongPromptContract and resolveSongPromptProfile. Tests cover default profile resolution, compact vs full prompt length comparison, faithful → balanced profile relaxation when prompt includes flex keywords, and mode overrides. Good coverage of the new prompt framework.

Comments Outside Diff (2)

  1. apps/server/src/routes/autoplayer.ts, line 2007-2028 (link)

    P2 buildAlbumPrompt skips per-field sanitization

    Every other prompt-building function in llm.ts (buildPersonaUserPrompt, buildSongUserPrompt, generatePlaylistManagerPlan) calls sanitizePromptOptional on each individual field before embedding it. Here, source.title, source.description, source.lyrics, etc. from the raw request body are spliced into the prompt string directly without sanitization.

    The combined prompt is sanitized downstream when buildSongUserPrompt receives it as options.prompt, but that strips control/format characters from the assembled string rather than from each embedded field in isolation. This is a weaker form of protection — a Unicode control character embedded mid-field won't be caught until after it's already placed adjacent to structural prompt text.

    Consider applying sanitizePromptOptional / sanitizePromptList to the source fields before building the lines, consistent with the rest of the codebase.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/server/src/routes/autoplayer.ts
    Line: 2007-2028
    
    Comment:
    **`buildAlbumPrompt` skips per-field sanitization**
    
    Every other prompt-building function in `llm.ts` (`buildPersonaUserPrompt`, `buildSongUserPrompt`, `generatePlaylistManagerPlan`) calls `sanitizePromptOptional` on each individual field before embedding it. Here, `source.title`, `source.description`, `source.lyrics`, etc. from the raw request body are spliced into the prompt string directly without sanitization.
    
    The combined prompt is sanitized downstream when `buildSongUserPrompt` receives it as `options.prompt`, but that strips control/format characters from the assembled string rather than from each embedded field in isolation. This is a weaker form of protection — a Unicode control character embedded mid-field won't be caught until after it's already placed adjacent to structural prompt text.
    
    Consider applying `sanitizePromptOptional` / `sanitizePromptList` to the `source` fields before building the lines, consistent with the rest of the codebase.
    
    How can I resolve this? If you propose a fix, please make it concise.
  2. apps/server/src/worker/song-worker.ts, line 2836-2864 (link)

    P2 Implicit deduplication reliance in ensurePlaylistManagerRefresh

    The method logs "Scheduling playlist manager refresh in background" only when the refreshKey is NOT already in managerRefreshInFlight, but then unconditionally calls this.refreshPlaylistManager(...) regardless of whether the key is in-flight. This relies on refreshPlaylistManager internally guarding against concurrent refreshes via the managerRefreshInFlight set.

    This is functional if refreshPlaylistManager does its own check (which it almost certainly does given the naming), but the pattern is implicit and the intent isn't obvious from reading ensurePlaylistManagerRefresh alone. A brief comment or an early-return guard would make the design explicit:

    // refreshPlaylistManager guards against concurrent refreshes via managerRefreshInFlight;
    // this fire-and-forget call is always safe even when a refresh is in progress.
    void this.refreshPlaylistManager(currentEpoch, provider, model).catch(...);
    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/server/src/worker/song-worker.ts
    Line: 2836-2864
    
    Comment:
    **Implicit deduplication reliance in `ensurePlaylistManagerRefresh`**
    
    The method logs "Scheduling playlist manager refresh in background" only when the `refreshKey` is NOT already in `managerRefreshInFlight`, but then unconditionally calls `this.refreshPlaylistManager(...)` regardless of whether the key is in-flight. This relies on `refreshPlaylistManager` internally guarding against concurrent refreshes via the `managerRefreshInFlight` set.
    
    This is functional if `refreshPlaylistManager` does its own check (which it almost certainly does given the naming), but the pattern is implicit and the intent isn't obvious from reading `ensurePlaylistManagerRefresh` alone. A brief comment or an early-return guard would make the design explicit:
    
    ```ts
    // refreshPlaylistManager guards against concurrent refreshes via managerRefreshInFlight;
    // this fire-and-forget call is always safe even when a refresh is in progress.
    void this.refreshPlaylistManager(currentEpoch, provider, model).catch(...);
    ```
    
    How can I resolve this? If you propose a fix, please make it concise.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix All With AI
This is a comment left during a code review.
Path: apps/web/src/lib/pipeline-truth.ts
Line: 16-22

Comment:
**Hardcoded persona priority magic number**

`20_000` is used as the threshold to detect persona LLM jobs, but this value mirrors the server-side `PERSONA_PRIORITY` constant (imported in `apps/server/src/worker/index.ts`). If that constant ever changes, this client-side check will silently drift out of sync.

Consider extracting this to a named constant with a comment, or sharing the value via a shared package:

```suggestion
const PERSONA_PRIORITY_THRESHOLD = 20_000; // Must match server PERSONA_PRIORITY

	let personaLlmJobs = 0;
	if (status) {
		for (const item of status.queues.llm.activeItems) {
			if (item.priority >= PERSONA_PRIORITY_THRESHOLD) personaLlmJobs += 1;
		}
		for (const item of status.queues.llm.pendingItems) {
			if (item.priority >= PERSONA_PRIORITY_THRESHOLD) personaLlmJobs += 1;
		}
	}
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: apps/server/src/routes/autoplayer.ts
Line: 2007-2028

Comment:
**`buildAlbumPrompt` skips per-field sanitization**

Every other prompt-building function in `llm.ts` (`buildPersonaUserPrompt`, `buildSongUserPrompt`, `generatePlaylistManagerPlan`) calls `sanitizePromptOptional` on each individual field before embedding it. Here, `source.title`, `source.description`, `source.lyrics`, etc. from the raw request body are spliced into the prompt string directly without sanitization.

The combined prompt is sanitized downstream when `buildSongUserPrompt` receives it as `options.prompt`, but that strips control/format characters from the assembled string rather than from each embedded field in isolation. This is a weaker form of protection — a Unicode control character embedded mid-field won't be caught until after it's already placed adjacent to structural prompt text.

Consider applying `sanitizePromptOptional` / `sanitizePromptList` to the `source` fields before building the lines, consistent with the rest of the codebase.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: apps/server/src/worker/song-worker.ts
Line: 2836-2864

Comment:
**Implicit deduplication reliance in `ensurePlaylistManagerRefresh`**

The method logs "Scheduling playlist manager refresh in background" only when the `refreshKey` is NOT already in `managerRefreshInFlight`, but then unconditionally calls `this.refreshPlaylistManager(...)` regardless of whether the key is in-flight. This relies on `refreshPlaylistManager` internally guarding against concurrent refreshes via the `managerRefreshInFlight` set.

This is functional if `refreshPlaylistManager` does its own check (which it almost certainly does given the naming), but the pattern is implicit and the intent isn't obvious from reading `ensurePlaylistManagerRefresh` alone. A brief comment or an early-return guard would make the design explicit:

```ts
// refreshPlaylistManager guards against concurrent refreshes via managerRefreshInFlight;
// this fire-and-forget call is always safe even when a refresh is in progress.
void this.refreshPlaylistManager(currentEpoch, provider, model).catch(...);
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "Increase Codex timeout and add queue run..." | Re-trigger Greptile

Comment on lines +16 to +22
}

function collectQueueSongIds(status: WorkerStatus | null) {
const llm = new Set<string>();
const audio = new Set<string>();

if (!status) return { llm, audio };
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Hardcoded persona priority magic number

20_000 is used as the threshold to detect persona LLM jobs, but this value mirrors the server-side PERSONA_PRIORITY constant (imported in apps/server/src/worker/index.ts). If that constant ever changes, this client-side check will silently drift out of sync.

Consider extracting this to a named constant with a comment, or sharing the value via a shared package:

Suggested change
}
function collectQueueSongIds(status: WorkerStatus | null) {
const llm = new Set<string>();
const audio = new Set<string>();
if (!status) return { llm, audio };
const PERSONA_PRIORITY_THRESHOLD = 20_000; // Must match server PERSONA_PRIORITY
let personaLlmJobs = 0;
if (status) {
for (const item of status.queues.llm.activeItems) {
if (item.priority >= PERSONA_PRIORITY_THRESHOLD) personaLlmJobs += 1;
}
for (const item of status.queues.llm.pendingItems) {
if (item.priority >= PERSONA_PRIORITY_THRESHOLD) personaLlmJobs += 1;
}
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/src/lib/pipeline-truth.ts
Line: 16-22

Comment:
**Hardcoded persona priority magic number**

`20_000` is used as the threshold to detect persona LLM jobs, but this value mirrors the server-side `PERSONA_PRIORITY` constant (imported in `apps/server/src/worker/index.ts`). If that constant ever changes, this client-side check will silently drift out of sync.

Consider extracting this to a named constant with a comment, or sharing the value via a shared package:

```suggestion
const PERSONA_PRIORITY_THRESHOLD = 20_000; // Must match server PERSONA_PRIORITY

	let personaLlmJobs = 0;
	if (status) {
		for (const item of status.queues.llm.activeItems) {
			if (item.priority >= PERSONA_PRIORITY_THRESHOLD) personaLlmJobs += 1;
		}
		for (const item of status.queues.llm.pendingItems) {
			if (item.priority >= PERSONA_PRIORITY_THRESHOLD) personaLlmJobs += 1;
		}
	}
```

How can I resolve this? If you propose a fix, please make it concise.

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.

1 participant