Initial commit: BLIGHT: CUE

Webhook listener that monitors Gitea repos for BLIGHT: triggers in
markdown files, processes them via Gemini 2.5 Flash-Lite, and writes
results back in-place.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-14 20:17:31 -05:00
commit 4c9fecda16
12 changed files with 523 additions and 0 deletions

77
processor.py Normal file
View File

@@ -0,0 +1,77 @@
import re
import time
import logging
from ai import GeminiProvider
logger = logging.getLogger(__name__)
TRIGGER_PATTERN = re.compile(r"^BLIGHT:\s+(.+)$", re.MULTILINE)
FAILED_TEMPLATE = "<!-- BLIGHT_FAILED: {instruction} -->"
_MAX_RETRIES = 3
_RETRY_DELAYS = [1, 2, 4] # seconds between attempts
_provider = GeminiProvider()
def process_document(content: str) -> tuple[str, bool]:
"""Scan content for BLIGHT triggers and process each one.
Returns:
(updated_content, changed) where changed is True if any triggers
were found and the content was modified.
"""
triggers = list(TRIGGER_PATTERN.finditer(content))
if not triggers:
return content, False
# Process triggers one by one. After each replacement the string length
# may change, so we re-search on the updated content each iteration.
changed = False
for _ in range(len(triggers)):
match = TRIGGER_PATTERN.search(content)
if not match:
break
instruction = match.group(1).strip()
trigger_line = match.group(0)
logger.info("Processing trigger: %s", instruction)
replacement = _call_with_retry(content, instruction)
content = content[:match.start()] + replacement + content[match.end():]
changed = True
return content, changed
def _call_with_retry(document: str, instruction: str) -> str:
"""Call the AI provider with up to _MAX_RETRIES attempts.
Returns the AI response on success, or a BLIGHT_FAILED comment on
exhausted retries.
"""
last_error: Exception | None = None
for attempt in range(_MAX_RETRIES):
try:
return _provider.complete(document, instruction)
except Exception as exc:
last_error = exc
if attempt < _MAX_RETRIES - 1:
delay = _RETRY_DELAYS[attempt]
logger.warning(
"Attempt %d/%d failed for instruction %r: %s — retrying in %ds",
attempt + 1,
_MAX_RETRIES,
instruction,
exc,
delay,
)
time.sleep(delay)
logger.error(
"All %d attempts failed for instruction %r: %s",
_MAX_RETRIES,
instruction,
last_error,
)
return FAILED_TEMPLATE.format(instruction=instruction)