Skip to content

Use minimal timestamps in LLM prompts to help reduce token usage - #886

Merged
ttlequals0 merged 1 commit into
ttlequals0:mainfrom
mrfootoyou:feature/minimal-timestamps
Oct 23, 2026
Merged

ttlequals0 merged 1 commit into
ttlequals0:mainfrom
mrfootoyou:feature/minimal-timestamps

Conversation

@mrfootoyou

@mrfootoyou mrfootoyou commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Remove spaces and units from timestamps which appear frequently in LLM prompts, i.e., transcript cues. The effect is 3 fewer tokens per instance.

See #885 for more context.

Test plan

  • PYTHONPATH=src pytest tests/ passes
  • ruff check src/ tests/ passes
  • Frontend (if touched): npx tsc --noEmit, npm run lint, npm run test
  • UI changes: npm run design:check and npm run test:browser pass
  • UI changes: before/after evidence identifies routes, viewport widths, input modes, and data limitations
  • UI changes: failure/retry, keyboard, dirty-state, and destructive-action behavior checked where affected
  • CHANGELOG.md updated

Comment thread src/database/__init__.py
Return ONLY a valid JSON array. No explanation, no markdown.

Each ad segment: {{"start": FLOAT_SECONDS, "end": FLOAT_SECONDS, "confidence": FLOAT_0_TO_1, "category": "sponsor|cross_promo|self_promo|interaction|intro|outro|recap", "reason": "brief description", "end_text": "last 3-5 words"}}
Each ad segment: {"start": FLOAT_SECONDS, "end": FLOAT_SECONDS, "confidence": FLOAT_0_TO_1, "category": "sponsor|cross_promo|self_promo|interaction|intro|outro|recap", "reason": "brief description", "end_text": "last 3-5 words"}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Remove rogue double-curlies from the default system prompts. As mentioned on line 295 and in the header of src/utils/prompt.py, these strings never use str.format() since doing so could be a security vulnerability. So, 1) there is no reason to escape the curlies since they will never be "unescaped", and 2) telling the LLM to emit invalid JSON can only lead to confusion.

@mrfootoyou
mrfootoyou force-pushed the feature/minimal-timestamps branch from b5d2262 to c6ae526 Compare October 22, 2026 14:49
Comment thread src/systemone/adapter.py
Comment on lines +59 to +62
r"(?m)^[ \t]*Effective category actions:[ \t]*([^\r\n]*?)[ \t]*$"
)
_PODCAST_RE = re.compile(r"(?m)^[ \t]*Podcast:[ \t]*([^\r\n]+)[ \t]*$")
_EPISODE_RE = re.compile(r"(?m)^[ \t]*Episode:[ \t]*([^\r\n]+)[ \t]*$")
_PODCAST_RE = re.compile(r"(?m)^[ \t]*Podcast:[ \t]*([^\r\n]+?)[ \t]*$")
_EPISODE_RE = re.compile(r"(?m)^[ \t]*Episode:[ \t]*([^\r\n]+?)[ \t]*$")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Use non-greedy consumption for a small performance gain (minimizes backtracking).

Comment thread src/ad_reviewer.py
Comment on lines +680 to +682
f"[{g_lo:.1f}-{g_hi:.1f}] (gap of {g_hi - g_lo:.1f}s - no transcript, likely silence, jingle, or unintelligible speech)"
if cut == 0
else f"[{g_lo:.1f}-{g_hi:.1f}] (gap {g_hi - g_lo:.1f}s)"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Modified to resembles the format used in #884.

Remove spaces and units (`s`) from timestamps that appear frequently in LLM prompts, i.e., transcript cues. The net effect is 3 fewer tokens per instance (9-11 vs 12-14).
@mrfootoyou
mrfootoyou force-pushed the feature/minimal-timestamps branch from c6ae526 to 0e03010 Compare October 22, 2026 15:13
Comment thread tests/unit/test_settings_registry.py Resolved
@ttlequals0

Copy link
Copy Markdown
Owner

This is the right direction and I am merging it now. Two notes, one of them the thing you flagged on the registry snapshot.

You are right that something more is needed there. Those hashes are the shipped defaults, and existing installs keep the old text in the settings table. The app only refreshes a stored prompt when is_default is set and the row lacks a marker phrase that is unique to the new default, see the reviewer prompt migration in src/database/schema/__init__.py around line 1402 and the one after it. Without that step, an upgraded instance keeps [1240.0s-1245.0s] examples and the doubled curlies while the live transcript lines use the new form. I am adding that migration in the current patch PR, so you do not need to. It covers system_prompt, verification_prompt, review_prompt and resurrect_prompt, touches is_default = 1 rows only, and is keyed on a phrase that only exists in the new text.

On the doubled curlies: agreed. render_prompt in src/utils/prompt.py substitutes with str.replace, so {{ was never unescaped and the model has been seeing it verbatim. Removing it is correct, and it is one more reason stored defaults need the refresh.

The parser changes look good. The _TS_LINE and heading regexes accept both the old and new forms, so stored prompt logs still parse.

@ttlequals0
ttlequals0 merged commit fa6e4db into ttlequals0:main Oct 23, 2026
6 checks passed
ttlequals0 added a commit that referenced this pull request Oct 23, 2026
ttlequals0 added a commit that referenced this pull request Oct 23, 2026
ttlequals0 added a commit that referenced this pull request Oct 23, 2026
ttlequals0 added a commit that referenced this pull request Oct 23, 2026
ttlequals0 added a commit that referenced this pull request Oct 24, 2026
ttlequals0 added a commit that referenced this pull request Oct 24, 2026
ttlequals0 added a commit that referenced this pull request Oct 24, 2026
… UI consistency pass (2.100.22) (#889)

## Why

Three detection reports, seven open issues, a batch of phone layout problems and the foldable-phone viewports, shipped as 2.100.19. A second round on the same branch, 2.100.20, fixes what a UI audit found after the first deploy. A third, 2.100.21, applies the full audit. A fourth, 2.100.22, fixes two things 2.100.21 got wrong. #886 and #894 are already on main.

Closes #874, #875, #876, #879, #888, #891, #895.

## What changed

- Same-sponsor merge: a fold only extends an end. When the later fragment sat inside the first, the merged marker took the inner end and a cross-fetch differential span lost its measured tail (2.7 s at an episode start, 4.5 s before the credits on one episode).
- Reviewer: an inward trim now stops at a removable template cue inside the LLM span, not only at an edge the cue snap already moved. An ad-break stinger 7 s into a span was being left in the output.
- Detection notes: per-feed notes go to the detector, the verification pass and the reviewer as their own prompt block marked as operator instructions, outside the 800-character description budget. Notes are stripped of HTML only, so a cited time survives.
- Cue templates can cut every match on their own (#874). Global setting, feed override and per-template value, explicit beating inherited. Each match becomes a marker with the cue type's category, and the feed's segment category actions decide the outcome. Content transition cues are excluded. Cue-only safety still holds an unproven template.
- Issue fixes. The pattern dialog's podcast link went to the API path (#876). The current-position pin drag follows the play-while-dragging setting (#875). The cue pair override is named like the global setting (#879). ffmpeg's big-endian ID3v2.4 frame sizes no longer fail chapter restore, and a chapter tag that cannot be read is skipped instead of failing the episode (#888, fix from ChaosHat). Recut works for cue-only feeds without a transcript (#891).
- Web UI: Calls by phase is a table. The pattern details dialog uses the shared button recipes. On phones its footer is a row of equal-width text buttons, with Save as the edit label like every other dialog. The Feeds header shows compact actions below 768 px and two columns from 600 px. Copy buttons center their icon when the label is hidden. Play buttons are one square size everywhere, 32 px inside the 44 px target on phones. Overlapping cut markers render as one block with a segments toggle. Dialog footers, close buttons, destructive outlines and icon buttons share one set of recipes, documented in the design guide. The transcript viewer groups lines into paragraphs at render time, with a Lines toggle, and the stored transcript is unchanged (#887).
- Settings > System Status drops Time Saved, LLM Tokens and Cost and links to the Stats page. Reject buttons on non-sponsor markers read "Keep" (#877 follow-up).
- Foldable phones (466x678 folded, 890x626 unfolded). The shell and fixed bars pad for the side safe-area insets. Grids use even column counts between 640 and 1023 px. Dialogs stay inside one viewport segment when the browser reports two.
- MP3 stream copy fixes ported from batram's plusminuspod fork, kept as their own commits so the squash credits the author. Review follow-ups on top: the disk-space estimate no longer counts PCM files that are not written, the final-cut extension checks protected audio, and ffmpeg's stderr is drained so a chatty decode cannot stall.
- After #886, the startup refresh of shipped defaults moves never-customized prompt rows to the new timestamp form.
- After #884, the System One adapter skips the new gap cue rows when it reads a segment-ID prompt back into segments; before, the first gap row ended the transcript.
- Image build: the 30 upstream security patches for ffmpeg, x264, libass, libsndfile, glibc, cairo, cjson and glib ship in the repo instead of being downloaded from their git forges at build time. One forge now answers automated fetches with a bot challenge page and another changed its patch rendering, so the pinned checksums no longer matched.
- 2.100.20: the coarse-pointer floor was stretching four icon buttons and leaving their icons off center: edit feed title, open in editor, and the ad editor's add and close. They now use the icon recipe or flex centering, and lucide icons replace the hand-rolled paths. Held-marker and Ad Review action rows share one button height. A browser contract measures icon centering on the routes the contract suite reaches without a backend. The data-driven pages were measured with the mocked harness. The reject button and badges read "Not ad". Settings > System Status drops the sentence pointing at the Stats page.
- 2.100.21: UI consistency pass. Headings, buttons and labels are sentence case. Errors read "Could not <verb> <noun>." and empty lists "No <things> yet." Held markers are "Needs review"; decisions are "Confirm" or "Not ad" everywhere, including the editor footer; reruns are "Re-detect ads" or "Full reprocess"; the nav says "Feeds" and "Add feed"; destructive confirms repeat verb and noun under question titles. Percents and durations share two formatters ("99% confidence", "2m 5s"). Every button, field, label and badge composes a shared recipe. Stat grids use one tile, episode status one badge, close buttons one component, and lucide replaces the hand-drawn icons. Dialogs pin their header and footer and scroll only the body. Stats and Search use the underline tabs. On phones the Add feed modes stack, the status bar shows the stage only, the Sponsors search spans the row, episode lists drop the artwork, review action rows are 44 px tall and icon buttons fill their target.
- Auto-process window (#895). New episodes were auto-queued only when published in the last 48 hours, and the value was hardcoded. An instance that runs a few times a week never processed what arrived while it was off. The window is now a global setting, default 48 hours and up to 720, with a per-feed override that inherits when empty.
- 2.100.22: the reviewer trimmed a cut back to the last spoken word whenever the wordless stretch past it was under 8 s. A pre-roll's closing music (2.7 s) and a post-roll's tagline music (4.7 s) survived the cut that way. It now holds the edge at the file head or tail and at a differential-measured edge at any length; an edge snapped to a labelled boundary cue keeps the cue. On phones, Detected ads cards keep their four controls on one line; only the five-control Ad review card wraps below 430 px. The Podping listener polls the Hive node through one keep-alive session instead of a new connection and DNS lookup per call (two calls every 3 s); health probes still open their own connections, since they fan out across threads.
- brotli 1.2.1 for PYSEC-2026-4322, filelock 4.2.4 for CVE-2026-112940, zod 4.6.7 for GHSA-mrhx-c7xc-m3w5, happy-dom 21.0.4 for GHSA-834v-8h66-3h79, plus fast-uri 3.1.9 and serialize-javascript 7.1.4.

## Verification

- `PYTHONPATH=src pytest tests/` (12246 passed, 2 skipped) and `ruff check src/ tests/` pass.
- `npx tsc --noEmit`, `npm run lint`, `npm run test` (158 files, 1883 tests), `npm run design:check` and `npm audit --audit-level=high` pass. The browser contract suite passes on the four Chromium projects (34 tests).
- Browser checks with a mocked API and coarse pointer at 320, 360, 390, 466, 626, 678, 768, 890 and 1280 px for the Feeds page, episode page, pattern dialog (view, edit and delete states), settings sections, cue templates panel and play buttons.
- Before and after captures of 19 states at 390 px (coarse pointer) and 1280 px with a mocked API. At 390 no state has a button under 44 px, horizontal overflow or clipped text, and the three dialogs keep their footer inside the viewport.
- One episode was reprocessed against sharper detection notes and came back as one contiguous block instead of three cuts.
- Trivy on the 2.100.19 through 2.100.22 GPU images: 0 critical, 0 high, 348 medium, all Ubuntu ffmpeg-family packages with no fix available. The previous image scans today at 5 critical and 23 high. The secret scan's only hits are Anthropic SDK docstrings, now allowed by rule.


Co-authored-by: batram <[email protected]>
Co-authored-by: ChaosHat <[email protected]>
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.

2 participants