Compare commits

...

6 Commits

Author SHA1 Message Date
dgtlmoon 6f4cc2d6c1 Updating language catalog
Build and push containers / metadata (push) Has been cancelled
Build and push containers / build-push-containers (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
ChangeDetection.io App Test / lint-code (push) Has been cancelled
ChangeDetection.io App Test / lint-translations (push) Has been cancelled
ChangeDetection.io App Test / lint-template-i18n (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built package works basically. (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-14 (push) Has been cancelled
2026-06-03 12:07:40 +02:00
Jaroslav Lichtblau 9adf6a478e feat: Czech translation updated and refined (#4203)
Build and push containers / metadata (push) Has been cancelled
Build and push containers / build-push-containers (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
ChangeDetection.io App Test / lint-code (push) Has been cancelled
ChangeDetection.io App Test / lint-translations (push) Has been cancelled
ChangeDetection.io App Test / lint-template-i18n (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built package works basically. (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-14 (push) Has been cancelled
2026-06-02 09:25:43 +02:00
dgtlmoon dd56a502c0 Update docker-compose.yml - adding LLM_FEATURES_DISABLED example
Build and push containers / metadata (push) Has been cancelled
Build and push containers / build-push-containers (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
ChangeDetection.io App Test / lint-code (push) Has been cancelled
ChangeDetection.io App Test / lint-translations (push) Has been cancelled
ChangeDetection.io App Test / lint-template-i18n (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built package works basically. (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-14 (push) Has been cancelled
2026-05-29 15:12:41 +02:00
dgtlmoon baae46deed 0.55.7
Build and push containers / metadata (push) Has been cancelled
Build and push containers / build-push-containers (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/amd64 (alpine) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm64 (alpine) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/amd64 (main) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm/v7 (main) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm/v8 (main) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm64 (main) (push) Has been cancelled
ChangeDetection.io App Test / lint-code (push) Has been cancelled
ChangeDetection.io App Test / lint-translations (push) Has been cancelled
ChangeDetection.io App Test / lint-template-i18n (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built package works basically. (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-14 (push) Has been cancelled
2026-05-25 18:47:29 +02:00
dgtlmoon d7a1b67c5a UI - LLM - Fix for settings (wtforms vs pydantic) (#4184) 2026-05-25 18:43:33 +02:00
dgtlmoon b7bb67fac4 LLM - Smarter reasoning budget logic for gemini models 2026-05-25 18:03:11 +02:00
8 changed files with 146 additions and 47 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
# Read more https://github.com/dgtlmoon/changedetection.io/wiki
# Semver means never use .01, or 00. Should be .1.
__version__ = '0.55.6'
__version__ = '0.55.7'
from changedetectionio.strtobool import strtobool
from json.decoder import JSONDecodeError
@@ -99,6 +99,12 @@ def construct_blueprint(datastore: ChangeDetectionStore):
llm_form_input = dict(form.data.get('llm') or {})
# Empty IntegerField submissions come back as None from WTForms;
# the schema declares those fields as strict `int`, so passing
# them through would fail validation. Treat None like the
# absent-key case: keep the stored value, don't merge.
llm_form_input = {k: v for k, v in llm_form_input.items() if v is not None}
# PasswordField never re-renders, so a blank submitted value means
# "keep stored key" — drop it from the merge.
if not (llm_form_input.get('api_key') or '').strip():
+16 -3
View File
@@ -82,10 +82,23 @@ def _check_input_size(text: str, max_chars: int) -> None:
def _thinking_extra_body(model: str, budget: int) -> dict | None:
"""Return litellm extra_body to control thinking for models that support it.
For Gemini 2.5+: passes thinkingConfig with the given budget (0 = disabled).
For all other models: returns None (no-op).
The `thinkingConfig.thinkingBudget` payload is Gemini-specific (Anthropic and
OpenAI reasoning models use different parameters), so we gate on the gemini/
provider prefix first, then defer to litellm's model registry for the actual
"does this model think?" decision. That picks up new Gemini variants and
rolling aliases (`gemini-flash-latest`, etc.) as litellm's registry tracks
them, without us hardcoding model names here.
"""
if not model.startswith('gemini/gemini-2.5'):
if not model.startswith('gemini/'):
return None
try:
import litellm
if not litellm.get_model_info(model).get('supports_reasoning'):
return None
except Exception:
# Unknown model or registry lookup failed — skip the thinking config
# rather than guess. Worst case: thinking stays at the provider default.
return None
return {'generationConfig': {'thinkingConfig': {'thinkingBudget': budget}}}
@@ -196,6 +196,81 @@ def test_settings_form_preserves_token_counters(
delete_all_watches(client)
def test_settings_form_blank_llm_integer_fields_preserve_stored_values(
client, live_server, measure_memory_usage, datastore_path):
"""
Empty IntegerField submissions come back as None from WTForms. LLMSettings
declares token_budget_month / max_input_chars / max_tokens_per_count_period /
local_token_multiplier as strict `int`, so a None passed through to
model_validate raises ValidationError and 500s the settings save.
Regression for settings/__init__.py — the LLM merge must drop None values
(treat them like absent keys) so blank IntegerField submissions preserve
the stored value instead of crashing the form.
"""
ds = client.application.config.get('DATASTORE')
ds.data['settings']['application']['llm'] = {
'model': 'gpt-4o',
'api_key': 'sk-existing',
'token_budget_month': 50000,
'max_input_chars': 200000,
'max_tokens_per_count_period': 1000,
'local_token_multiplier': 3,
}
res = client.post(
url_for('settings.settings_page'),
data={
'llm-model': 'gpt-4o',
'llm-api_key': '',
'llm-api_base': '',
# The bug-trigger: every LLM IntegerField submitted blank
'llm-token_budget_month': '',
'llm-max_input_chars': '',
'llm-max_tokens_per_count_period': '',
'llm-local_token_multiplier': '',
# Minimal required fields for the rest of the form to validate.
# 'System default' is popped from notification_format choices for the
# global form, so it must be one of the real codes (e.g. 'html').
'application-pager_size': '50',
'application-notification_format': 'html',
'application-fetch_backend': 'html_requests',
'application-rss_diff_length': '5',
'application-filter_failure_notification_threshold_attempts': '0',
'requests-time_between_check-days': '0',
'requests-time_between_check-hours': '0',
'requests-time_between_check-minutes': '5',
'requests-time_between_check-seconds': '0',
'requests-time_between_check-weeks': '0',
'requests-jitter_seconds': '0',
'requests-workers': '10',
'requests-timeout': '60',
},
follow_redirects=True,
)
assert res.status_code == 200, \
f"Settings save crashed on blank LLM IntegerField submission (got {res.status_code})"
# Sanity: the form must have actually validated and reached the LLM save path
# — without this the test would trivially pass because the buggy code never ran.
assert b'Settings updated.' in res.data, \
"Settings form did not validate — the bug-path was never exercised. Check fixture fields."
body = res.data.decode('utf-8', errors='replace')
assert 'ValidationError' not in body, \
"Pydantic ValidationError leaked into the response — blank IntegerField wasn't filtered"
llm = ds.data['settings']['application'].get('llm') or {}
assert llm.get('token_budget_month') == 50000, \
f"Blank submission must preserve stored token_budget_month (got {llm.get('token_budget_month')!r})"
assert llm.get('max_input_chars') == 200000, \
f"Blank submission must preserve stored max_input_chars (got {llm.get('max_input_chars')!r})"
assert llm.get('max_tokens_per_count_period') == 1000, \
f"Blank submission must preserve stored max_tokens_per_count_period (got {llm.get('max_tokens_per_count_period')!r})"
assert llm.get('local_token_multiplier') == 3, \
f"Blank submission must preserve stored local_token_multiplier (got {llm.get('local_token_multiplier')!r})"
delete_all_watches(client)
def test_settings_form_cannot_inject_fake_token_counts(
client, live_server, measure_memory_usage, datastore_path):
"""
@@ -584,7 +584,7 @@ msgstr "Pozn.: Toto je aplikováno globálně dodatečně k pravidlům nastaven
#: changedetectionio/blueprint/settings/templates/settings.html changedetectionio/templates/edit/text-options.html
msgid "Matching text will be ignored in the text snapshot (you can still see it but it wont trigger a change)"
msgstr ""
msgstr "Text shody bude ignorován ve snímku textu (bude i nadále vidět, ale nebude spouštět upozornění na změnu)"
#: changedetectionio/blueprint/settings/templates/settings.html changedetectionio/templates/edit/text-options.html
msgid "Each line processed separately, any line matching will be ignored (removed before creating the checksum)"
@@ -772,23 +772,23 @@ msgstr "Využití"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Overview"
msgstr ""
msgstr "Přehled"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Provider"
msgstr ""
msgstr "Poskytovatel"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Prompts"
msgstr ""
msgstr "Prompty"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Behaviour"
msgstr ""
msgstr "Chování"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "AI-powered change monitoring"
msgstr ""
msgstr "AI podporované sledování změn"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Connect an LLM to move from \"something changed\" to \"only the thing you care about changed\"."
@@ -925,31 +925,31 @@ msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Load available models"
msgstr ""
msgstr "Načíst dostupné modely"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Available models"
msgstr ""
msgstr "Dostupné modely"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "choose a model"
msgstr ""
msgstr "vybrat model"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Enter API key and click 'Load available models'"
msgstr ""
msgstr "Vložit API klíč a kliknout na 'Načíst dostupné modely'"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Remove AI / LLM configuration?"
msgstr ""
msgstr "Odstranit AI / LLM konfiguraci?"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "This will remove your saved AI provider, model, and API key."
msgstr ""
msgstr "Toto odstraní uloženého poskytovatele AI, model a API klíč."
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Remove"
msgstr ""
msgstr "Odstranit"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
#: changedetectionio/blueprint/ui/templates/clear_all_history.html changedetectionio/templates/base.html
@@ -958,7 +958,7 @@ msgstr "Zrušit"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Test connection"
msgstr ""
msgstr "Otestovat připojení"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid ""
@@ -1294,7 +1294,7 @@ msgstr "Sledovat skupinu / Značka"
#: changedetectionio/blueprint/tags/templates/groups-overview.html
msgid "Groups allows you to manage filters and notifications for multiple watches under a single organisational tag."
msgstr ""
msgstr "Skupiny umožňují spravovat filtry a upozornění pro vícero sledování seskupené pod jedním organizačním tagem."
#: changedetectionio/blueprint/tags/templates/groups-overview.html
msgid "# Watches"
@@ -1329,7 +1329,7 @@ msgstr "Smazat skupinu?"
#: changedetectionio/blueprint/tags/templates/groups-overview.html
#, python-format
msgid "<p>Are you sure you want to delete group <strong>%(title)s</strong>?</p><p>This action cannot be undone.</p>"
msgstr ""
msgstr "<p>Opravdu chcete smazat skupinu <strong>%(title)s</strong>?</p><p>Tuto akci nelze vzít zpět.</p>"
#: changedetectionio/blueprint/tags/templates/groups-overview.html changedetectionio/blueprint/ui/templates/edit.html
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
@@ -1350,6 +1350,8 @@ msgid ""
"<p>Are you sure you want to unlink all watches from group <strong>%(title)s</strong>?</p><p>The tag will be kept but "
"watches will be removed from it.</p>"
msgstr ""
"<p>Opravud chcete odpojit všechna sledování pod skupinou <strong>%(title)s</strong>?</p><p>Tag bude zachován, ale "
"podřazená sledování budou odstraněna.</p>"
#: changedetectionio/blueprint/tags/templates/groups-overview.html
msgid "Unlink"
@@ -1391,22 +1393,22 @@ msgstr "{} sledování ztlumeno"
#: changedetectionio/blueprint/ui/__init__.py
#, python-brace-format
msgid "{} watches un-muted"
msgstr ""
msgstr "{} sledování zesíleno"
#: changedetectionio/blueprint/ui/__init__.py
#, python-brace-format
msgid "{} watches queued for rechecking"
msgstr ""
msgstr "{} sledování ve frontě ke kontrole"
#: changedetectionio/blueprint/ui/__init__.py
#, python-brace-format
msgid "{} watches errors cleared"
msgstr ""
msgstr "{} chyb sledování vyčištěno"
#: changedetectionio/blueprint/ui/__init__.py
#, python-brace-format
msgid "{} watches cleared/reset."
msgstr ""
msgstr "{} sledování vyčištěno/resetováno."
#: changedetectionio/blueprint/ui/__init__.py
#, python-brace-format
@@ -1416,7 +1418,7 @@ msgstr "{} monitorů nastaveno na použití výchozího nastavení oznámení"
#: changedetectionio/blueprint/ui/__init__.py
#, python-brace-format
msgid "{} watches were tagged"
msgstr ""
msgstr "{} sledování otagováno"
#: changedetectionio/blueprint/ui/__init__.py
msgid "Watch not found"
@@ -1433,7 +1435,7 @@ msgstr "jasný"
#: changedetectionio/blueprint/ui/__init__.py
msgid "History clearing started in background"
msgstr ""
msgstr "Čištění historie spuštěno na pozadí"
#: changedetectionio/blueprint/ui/__init__.py
msgid "Incorrect confirmation text."
@@ -1442,7 +1444,7 @@ msgstr "Žádné informace"
#: changedetectionio/blueprint/ui/__init__.py
#, python-brace-format
msgid "The watch by UUID {} does not exist."
msgstr ""
msgstr "Sledování s UUID {} neexistuje."
#: changedetectionio/blueprint/ui/__init__.py
msgid "Deleted."
@@ -1450,15 +1452,15 @@ msgstr "Smazáno"
#: changedetectionio/blueprint/ui/__init__.py
msgid "Cloned, you are editing the new watch."
msgstr ""
msgstr "Naklonováno, upravujete nové sledování."
#: changedetectionio/blueprint/ui/__init__.py
msgid "Watch is already queued or being checked."
msgstr ""
msgstr "Sledování je již zařazeno do fronty ke kontrole."
#: changedetectionio/blueprint/ui/__init__.py
msgid "Queued 1 watch for rechecking."
msgstr ""
msgstr "Zařazeno 1 sledování ke kontrole."
#: changedetectionio/blueprint/ui/__init__.py
#, python-brace-format
@@ -1477,7 +1479,7 @@ msgstr "Přidává se sledování do fronty pro opětovnou kontrolu na pozadí..
#: changedetectionio/blueprint/ui/__init__.py
#, python-brace-format
msgid "Could not share, something went wrong while communicating with the share server - {}"
msgstr ""
msgstr "Sdílení selhalo, něco se pokazilo při komunikaci se sdílecím serverem = {}"
#: changedetectionio/blueprint/ui/__init__.py
msgid "Language set to auto-detect from browser"
@@ -1485,52 +1487,52 @@ msgstr "Jazyk nastaven na automatickou detekci z prohlížeče"
#: changedetectionio/blueprint/ui/diff.py changedetectionio/blueprint/ui/preview.py
msgid "No history found for the specified link, bad link?"
msgstr ""
msgstr "Historie pro vybraný odkaz nenalezena, chybný odkaz?"
#: changedetectionio/blueprint/ui/diff.py
msgid "Not enough history (2 snapshots required) to show difference page for this watch."
msgstr ""
msgstr "Nedostatečná historie (vyžadovány 2 záchyty) pro zobrazení rozdílů tohoto sledování."
#: changedetectionio/blueprint/ui/diff.py
#, python-format
msgid "Monthly AI token budget of %(budget)s tokens reached (%(used)s used). Resets next month."
msgstr ""
msgstr "Dosažen měsíční počet %(budget)s AI tokenů (%(used)s použito). Resetuje se příští měsíc."
#: changedetectionio/blueprint/ui/edit.py
msgid "No watches to edit"
msgstr ""
msgstr "Žádná sledování k úpravě"
#: changedetectionio/blueprint/ui/edit.py
#, python-brace-format
msgid "No watch with the UUID {} found."
msgstr ""
msgstr "Sledování s UUID {} nenalezeno."
#: changedetectionio/blueprint/ui/edit.py
#, python-brace-format
msgid "Switched to mode - {}."
msgstr ""
msgstr "Přepnuto na mód - {}."
#: changedetectionio/blueprint/ui/edit.py
#, python-brace-format
msgid "Could not load '{}' processor, processor plugin might be missing. Please select a different processor."
msgstr ""
msgstr "Nelze načíst '{}' procesor, zásuvný modul procesoru nejspíše chybí. Vyberte prosím jiný procesor."
#: changedetectionio/blueprint/ui/edit.py
#, python-brace-format
msgid "Could not load '{}' processor, processor plugin might be missing."
msgstr ""
msgstr "Nelze načíst '{}' procesor, zásuvný modul procesoru nejspíše chybí."
#: changedetectionio/blueprint/ui/edit.py
msgid "System settings default"
msgstr ""
msgstr "Výchozí systémová nastavení"
#: changedetectionio/blueprint/ui/edit.py
msgid "Default"
msgstr ""
msgstr "Výchozí"
#: changedetectionio/blueprint/ui/edit.py
msgid "Updated watch - unpaused!"
msgstr ""
msgstr "Sledování aktualizováno - znovu spuštěno!"
#: changedetectionio/blueprint/ui/edit.py
msgid "Updated watch."
@@ -1538,15 +1540,15 @@ msgstr "Sledování aktualizováno."
#: changedetectionio/blueprint/ui/preview.py
msgid "Preview unavailable - No fetch/check completed or triggers not reached"
msgstr ""
msgstr "Náhled nedostupný - stažení/kontrola nedokončena nebo nebyly splněny podmínky kontroly"
#: changedetectionio/blueprint/ui/preview.py
msgid "Diff"
msgstr ""
msgstr "Rozdíly"
#: changedetectionio/blueprint/ui/templates/clear_all_history.html
msgid "This will remove version history (snapshots) for ALL watches, but keep your list of URLs!"
msgstr ""
msgstr "Toto odstraní historii verzí (snímky) pro VŠECHNA sledování, ale ponechá seznam URL adres!"
#: changedetectionio/blueprint/ui/templates/clear_all_history.html
msgid "You may like to use the"
+2 -2
View File
@@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: changedetection.io 0.55.6\n"
"Project-Id-Version: changedetection.io 0.55.7\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-05-25 17:59+0200\n"
"POT-Creation-Date: 2026-06-03 12:07+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
+3
View File
@@ -70,6 +70,9 @@ services:
# For complete privacy if you don't want to use the 'check version' / telemetry service
# - DISABLE_VERSION_CHECK=true
#
# Disable all LLM / AI features, prompts etc
# - LLM_FEATURES_DISABLED=true
#
# A valid timezone name to run as (for scheduling watch checking) see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# - TZ=America/Los_Angeles
#