mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2026-06-07 17:31:04 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d7eb0de71 | |||
| 33c150fe7b | |||
| a2fa9a9e7b |
@@ -1,4 +1,7 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
|
||||
from flask import Blueprint, jsonify, redirect, url_for, flash
|
||||
from flask_babel import gettext
|
||||
@@ -8,6 +11,44 @@ from changedetectionio.store import ChangeDetectionStore
|
||||
from changedetectionio.auth_decorator import login_optionally_required
|
||||
|
||||
|
||||
class _LiteLLMWarningCapture(logging.Handler):
|
||||
"""Capture warnings emitted on the 'LiteLLM' stdlib logger during a single call.
|
||||
|
||||
litellm.get_valid_models() catches HTTP/auth errors internally, logs a warning,
|
||||
and returns []. Without capturing that warning we can't tell the user *why*
|
||||
no models came back (bad key vs. offline vs. genuinely empty model list).
|
||||
"""
|
||||
def __init__(self):
|
||||
super().__init__(level=logging.WARNING)
|
||||
self.messages = []
|
||||
|
||||
def emit(self, record):
|
||||
try:
|
||||
self.messages.append(record.getMessage())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _humanize_litellm_error(raw: str) -> str:
|
||||
# litellm warnings typically look like:
|
||||
# "Error getting valid models: Failed to get models: { 'error': { 'message': '...' } }"
|
||||
# Pull the inner provider message when present; otherwise trim the boilerplate.
|
||||
if not raw:
|
||||
return raw
|
||||
m = re.search(r'\{.*\}', raw, re.DOTALL)
|
||||
if m:
|
||||
try:
|
||||
body = json.loads(m.group(0))
|
||||
inner = (body.get('error') or {}).get('message') or body.get('message')
|
||||
if inner:
|
||||
return inner
|
||||
except Exception:
|
||||
pass
|
||||
cleaned = re.sub(r'^Error getting valid models:\s*', '', raw)
|
||||
cleaned = re.sub(r'^Failed to get models:\s*', '', cleaned).strip()
|
||||
return cleaned[:500]
|
||||
|
||||
|
||||
def construct_llm_blueprint(datastore: ChangeDetectionStore):
|
||||
llm_blueprint = Blueprint('llm', __name__)
|
||||
|
||||
@@ -41,13 +82,27 @@ def construct_llm_blueprint(datastore: ChangeDetectionStore):
|
||||
try:
|
||||
import litellm
|
||||
logger.debug(f"LLM model list: calling litellm.get_valid_models provider={provider!r} (litellm={litellm_provider!r}) api_base={api_base!r}")
|
||||
raw = litellm.get_valid_models(
|
||||
check_provider_endpoint=True,
|
||||
custom_llm_provider=litellm_provider,
|
||||
api_key=api_key or None,
|
||||
api_base=api_base or None,
|
||||
) or []
|
||||
|
||||
capture = _LiteLLMWarningCapture()
|
||||
litellm_logger = logging.getLogger('LiteLLM')
|
||||
litellm_logger.addHandler(capture)
|
||||
try:
|
||||
raw = litellm.get_valid_models(
|
||||
check_provider_endpoint=True,
|
||||
custom_llm_provider=litellm_provider,
|
||||
api_key=api_key or None,
|
||||
api_base=api_base or None,
|
||||
) or []
|
||||
finally:
|
||||
litellm_logger.removeHandler(capture)
|
||||
|
||||
models = sorted({(m if m.startswith(prefix) else prefix + m) for m in raw})
|
||||
|
||||
if not models and capture.messages:
|
||||
err = _humanize_litellm_error(capture.messages[-1])
|
||||
logger.debug(f"LLM model list: 0 models, surfacing captured litellm warning: {err!r}")
|
||||
return jsonify({'models': [], 'error': err}), 400
|
||||
|
||||
logger.debug(f"LLM model list: got {len(models)} models for provider={provider!r}")
|
||||
return jsonify({'models': models, 'error': None})
|
||||
except Exception as e:
|
||||
|
||||
@@ -469,7 +469,7 @@
|
||||
|
||||
if (!data.models || data.models.length === 0) {
|
||||
statusEl.style.color = '#e67e22';
|
||||
statusEl.textContent = '{{ _("No models returned — check your API key.") }}';
|
||||
statusEl.textContent = '{{ _("No models returned by the provider.") }}';
|
||||
selGroup.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -356,7 +356,7 @@ window.watchOverviewI18n = {
|
||||
{#last_checked becomes fetch-start-time#}
|
||||
<td class="last-checked" data-timestamp="{{ watch.last_checked }}" data-fetchduration={{ watch.fetch_time }} data-eta_complete="{{ watch.last_checked+watch.fetch_time }}" data-label="{{ _('Last Checked') }}">
|
||||
<div class="spinner-wrapper" style="display:none;" >
|
||||
<span class="spinner"></span><span class="status-text"> {{ _('Checking now') }}</span>
|
||||
<span class="spinner"></span><span class="status-text"> {{ watch['__check_status'] or _('Checking now') }}</span>
|
||||
</div>
|
||||
<span class="innertext">{{watch|format_last_checked_time|safe}}</span>
|
||||
</td>
|
||||
|
||||
@@ -1106,7 +1106,6 @@ class globalSettingsLLMForm(Form):
|
||||
_l('API Key'),
|
||||
validators=[validators.Optional()],
|
||||
render_kw={
|
||||
"placeholder": _l('Leave blank to use LITELLM_API_KEY env var'),
|
||||
"autocomplete": "off",
|
||||
"style": "width: 24em;",
|
||||
},
|
||||
|
||||
@@ -49,6 +49,9 @@ def completion(model: str, messages: list, api_key: str = None,
|
||||
|
||||
_retryable = (litellm.Timeout, litellm.APIConnectionError)
|
||||
|
||||
logger.trace("Sending payload to LLM.. ")
|
||||
logger.trace(messages)
|
||||
|
||||
for attempt in range(1, DEFAULT_RETRIES + 1):
|
||||
try:
|
||||
response = litellm.completion(**kwargs)
|
||||
|
||||
@@ -1064,6 +1064,7 @@ class model(EntityPersistenceMixin, watch_base):
|
||||
Prepare watch data for commit.
|
||||
|
||||
Excludes processor_config_* keys (stored in separate files).
|
||||
Excludes __-prefixed keys (transient in-memory state — must not persist to disk).
|
||||
Normalizes browser_steps to empty list if no meaningful steps.
|
||||
"""
|
||||
import copy
|
||||
@@ -1077,8 +1078,11 @@ class model(EntityPersistenceMixin, watch_base):
|
||||
else:
|
||||
snapshot = dict(self)
|
||||
|
||||
# Exclude processor config keys (stored separately)
|
||||
watch_dict = {k: copy.deepcopy(v) for k, v in snapshot.items() if not k.startswith('processor_config_')}
|
||||
# Exclude processor config keys (stored separately) and __-prefixed transient keys
|
||||
watch_dict = {
|
||||
k: copy.deepcopy(v) for k, v in snapshot.items()
|
||||
if not k.startswith('processor_config_') and not k.startswith('__')
|
||||
}
|
||||
|
||||
# Normalize browser_steps: if no meaningful steps, save as empty list
|
||||
if not self.has_browser_steps:
|
||||
|
||||
@@ -335,6 +335,13 @@ class watch_base(dict):
|
||||
if self.__watch_was_edited:
|
||||
return # Already marked as edited
|
||||
|
||||
# __-prefixed keys are transient in-memory state (e.g. __check_status set by
|
||||
# set_watch_minitext_status). They never persist to disk and must not trigger
|
||||
# the edited flag — otherwise just observing a check in progress would force
|
||||
# the next run to bypass the unchanged-content skip.
|
||||
if isinstance(key, str) and key.startswith('__'):
|
||||
return
|
||||
|
||||
# Import from shared schema utilities (no circular dependency)
|
||||
from .schema_utils import get_readonly_watch_fields
|
||||
readonly_fields = get_readonly_watch_fields()
|
||||
|
||||
@@ -1121,7 +1121,7 @@ msgid "Loading…"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
msgid "No models returned — check your API key."
|
||||
msgid "No models returned by the provider."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
@@ -3134,10 +3134,6 @@ msgstr ""
|
||||
msgid "API Key"
|
||||
msgstr "API klíč"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Leave blank to use LITELLM_API_KEY env var"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API Base URL"
|
||||
msgstr ""
|
||||
|
||||
@@ -1137,7 +1137,7 @@ msgid "Loading…"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
msgid "No models returned — check your API key."
|
||||
msgid "No models returned by the provider."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
@@ -3186,10 +3186,6 @@ msgstr ""
|
||||
msgid "API Key"
|
||||
msgstr "API-Schlüssel"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Leave blank to use LITELLM_API_KEY env var"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API Base URL"
|
||||
msgstr ""
|
||||
|
||||
@@ -1119,7 +1119,7 @@ msgid "Loading…"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
msgid "No models returned — check your API key."
|
||||
msgid "No models returned by the provider."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
@@ -3128,10 +3128,6 @@ msgstr ""
|
||||
msgid "API Key"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Leave blank to use LITELLM_API_KEY env var"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API Base URL"
|
||||
msgstr ""
|
||||
|
||||
@@ -1119,7 +1119,7 @@ msgid "Loading…"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
msgid "No models returned — check your API key."
|
||||
msgid "No models returned by the provider."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
@@ -3128,10 +3128,6 @@ msgstr ""
|
||||
msgid "API Key"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Leave blank to use LITELLM_API_KEY env var"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API Base URL"
|
||||
msgstr ""
|
||||
|
||||
@@ -1157,7 +1157,7 @@ msgid "Loading…"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
msgid "No models returned — check your API key."
|
||||
msgid "No models returned by the provider."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
@@ -3201,10 +3201,6 @@ msgstr ""
|
||||
msgid "API Key"
|
||||
msgstr "Clave API"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Leave blank to use LITELLM_API_KEY env var"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API Base URL"
|
||||
msgstr ""
|
||||
|
||||
@@ -1125,7 +1125,7 @@ msgid "Loading…"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
msgid "No models returned — check your API key."
|
||||
msgid "No models returned by the provider."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
@@ -3141,10 +3141,6 @@ msgstr ""
|
||||
msgid "API Key"
|
||||
msgstr "Clé API"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Leave blank to use LITELLM_API_KEY env var"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API Base URL"
|
||||
msgstr ""
|
||||
|
||||
@@ -1121,7 +1121,7 @@ msgid "Loading…"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
msgid "No models returned — check your API key."
|
||||
msgid "No models returned by the provider."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
@@ -3130,10 +3130,6 @@ msgstr ""
|
||||
msgid "API Key"
|
||||
msgstr "Chiave API"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Leave blank to use LITELLM_API_KEY env var"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API Base URL"
|
||||
msgstr ""
|
||||
|
||||
@@ -1126,7 +1126,7 @@ msgid "Loading…"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
msgid "No models returned — check your API key."
|
||||
msgid "No models returned by the provider."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
@@ -3147,10 +3147,6 @@ msgstr ""
|
||||
msgid "API Key"
|
||||
msgstr "APIキー"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Leave blank to use LITELLM_API_KEY env var"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API Base URL"
|
||||
msgstr ""
|
||||
|
||||
Binary file not shown.
@@ -1127,8 +1127,8 @@ msgid "Loading…"
|
||||
msgstr "불러오는 중..."
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
msgid "No models returned — check your API key."
|
||||
msgstr "반환된 모델이 없습니다. API 키를 확인하세요."
|
||||
msgid "No models returned by the provider."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
msgid "— choose a model —"
|
||||
@@ -3138,10 +3138,6 @@ msgstr "모델"
|
||||
msgid "API Key"
|
||||
msgstr "API 키"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Leave blank to use LITELLM_API_KEY env var"
|
||||
msgstr "LITELLM_API_KEY 환경 변수를 사용하려면 비워 두세요"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API Base URL"
|
||||
msgstr "API 기본 URL"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: changedetection.io 0.55.3\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2026-05-02 18:29+0900\n"
|
||||
"POT-Creation-Date: 2026-05-12 11:08+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"
|
||||
@@ -1118,7 +1118,7 @@ msgid "Loading…"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
msgid "No models returned — check your API key."
|
||||
msgid "No models returned by the provider."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
@@ -3127,10 +3127,6 @@ msgstr ""
|
||||
msgid "API Key"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Leave blank to use LITELLM_API_KEY env var"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API Base URL"
|
||||
msgstr ""
|
||||
|
||||
@@ -1144,7 +1144,7 @@ msgid "Loading…"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
msgid "No models returned — check your API key."
|
||||
msgid "No models returned by the provider."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
@@ -3178,10 +3178,6 @@ msgstr ""
|
||||
msgid "API Key"
|
||||
msgstr "Chave da API"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Leave blank to use LITELLM_API_KEY env var"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API Base URL"
|
||||
msgstr ""
|
||||
|
||||
@@ -1154,7 +1154,7 @@ msgid "Loading…"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
msgid "No models returned — check your API key."
|
||||
msgid "No models returned by the provider."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
@@ -3181,10 +3181,6 @@ msgstr ""
|
||||
msgid "API Key"
|
||||
msgstr "API Anahtarı"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Leave blank to use LITELLM_API_KEY env var"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API Base URL"
|
||||
msgstr ""
|
||||
|
||||
@@ -1134,7 +1134,7 @@ msgid "Loading…"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
msgid "No models returned — check your API key."
|
||||
msgid "No models returned by the provider."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
@@ -3160,10 +3160,6 @@ msgstr ""
|
||||
msgid "API Key"
|
||||
msgstr "Ключ API"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Leave blank to use LITELLM_API_KEY env var"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API Base URL"
|
||||
msgstr ""
|
||||
|
||||
@@ -1123,7 +1123,7 @@ msgid "Loading…"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
msgid "No models returned — check your API key."
|
||||
msgid "No models returned by the provider."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
@@ -3133,10 +3133,6 @@ msgstr ""
|
||||
msgid "API Key"
|
||||
msgstr "API密钥"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Leave blank to use LITELLM_API_KEY env var"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API Base URL"
|
||||
msgstr ""
|
||||
|
||||
@@ -1122,7 +1122,7 @@ msgid "Loading…"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
msgid "No models returned — check your API key."
|
||||
msgid "No models returned by the provider."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
|
||||
@@ -3132,10 +3132,6 @@ msgstr ""
|
||||
msgid "API Key"
|
||||
msgstr "API 金鑰"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Leave blank to use LITELLM_API_KEY env var"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API Base URL"
|
||||
msgstr ""
|
||||
|
||||
@@ -9,9 +9,16 @@ from changedetectionio.pluggy_interface import apply_update_handler_alter, apply
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
|
||||
# Allow alphanumerics, space, and a small set of punctuation that appears in legitimate
|
||||
# status strings ("Querying AI/LLM (intent)..", "Fetching page.."). Anything that could
|
||||
# be HTML-active (<, >, &, ", ', =, ;, {, }, `, \) is stripped.
|
||||
_MINITEXT_STATUS_SAFE_RE = re.compile(r'[^A-Za-z0-9 ().,/:\-]')
|
||||
_MINITEXT_STATUS_MAX_LEN = 80
|
||||
|
||||
from loguru import logger
|
||||
|
||||
# Async version of update_worker
|
||||
@@ -20,6 +27,22 @@ from loguru import logger
|
||||
IN_PYTEST = "pytest" in sys.modules or "PYTEST_CURRENT_TEST" in os.environ
|
||||
DEFER_SLEEP_TIME_ALREADY_QUEUED = 0.3 if IN_PYTEST else 10.0
|
||||
|
||||
|
||||
def set_watch_minitext_status(watch, status):
|
||||
"""
|
||||
Set a transient status line for a watch (e.g. "Fetching page..", "Querying AI/LLM..").
|
||||
|
||||
Writes to watch['__check_status'] so a client reloading the page can render the
|
||||
last known status, and fires the realtime signal so already-connected clients
|
||||
update live. __-prefixed key is filtered from disk by Watch._get_commit_data().
|
||||
|
||||
Status is sanitized to alphanumerics, space, and safe punctuation only.
|
||||
"""
|
||||
safe_status = _MINITEXT_STATUS_SAFE_RE.sub('', str(status))[:_MINITEXT_STATUS_MAX_LEN]
|
||||
watch['__check_status'] = safe_status
|
||||
signal('watch_small_status_comment').send(watch_uuid=watch['uuid'], status=safe_status)
|
||||
|
||||
|
||||
async def async_update_worker(worker_id, q, notification_q, app, datastore, executor=None):
|
||||
"""
|
||||
Async worker function that processes watch check jobs from the queue.
|
||||
@@ -159,8 +182,7 @@ async def async_update_worker(worker_id, q, notification_q, app, datastore, exec
|
||||
# Allow plugins to modify/wrap the update_handler
|
||||
update_handler = apply_update_handler_alter(update_handler, watch, datastore)
|
||||
|
||||
update_signal = signal('watch_small_status_comment')
|
||||
update_signal.send(watch_uuid=uuid, status="Fetching page..")
|
||||
set_watch_minitext_status(watch, "Fetching page..")
|
||||
|
||||
# All fetchers are now async, so call directly
|
||||
await update_handler.call_browser()
|
||||
@@ -446,6 +468,7 @@ async def async_update_worker(worker_id, q, notification_q, app, datastore, exec
|
||||
# Step 1: AI Change Intent — may suppress notification
|
||||
_llm_intent, _llm_intent_source = resolve_intent(watch, datastore)
|
||||
if _llm_intent:
|
||||
set_watch_minitext_status(watch, "AI/LLM (rules)..")
|
||||
_llm_result = await loop.run_in_executor(
|
||||
executor,
|
||||
lambda diff=_diff_text, snap=contents: evaluate_change(
|
||||
@@ -465,6 +488,7 @@ async def async_update_worker(worker_id, q, notification_q, app, datastore, exec
|
||||
|
||||
# Step 2: AI Change Summary — runs for any LLM-configured watch with a change
|
||||
if changed_detected:
|
||||
set_watch_minitext_status(watch, "AI/LLM (summary)..")
|
||||
_change_summary = await loop.run_in_executor(
|
||||
executor,
|
||||
lambda diff=_diff_text, snap=contents: summarise_change(
|
||||
@@ -669,6 +693,8 @@ async def async_update_worker(worker_id, q, notification_q, app, datastore, exec
|
||||
finally:
|
||||
# Send completion signal - retrieve by name to ensure thread-safe access
|
||||
if watch:
|
||||
# Clear transient in-memory status — check is done
|
||||
watch.pop('__check_status', None)
|
||||
watch_check_update = signal('watch_check_update')
|
||||
watch_check_update.send(watch_uuid=watch['uuid'])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user