Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ _Avoid_: отдельная роль, модератор
_Code aliases_: `low_sent_pool`
_Avoid_: обычная лента, топовая лента

**Дедупликация мемов**:
Шаг пайплайна, который распознаёт повторки мемов до попадания в рекомендации,
помечает дубль как `duplicate`, переносит неконфликтующие реакции на оригинал
и обновляет счётчики оригинала.
_Code aliases_: `storage.deduplication`, `meme.duplicate_of`, `MemeStatus.DUPLICATE`
_Avoid_: ручной cleanup, постфактум чистка очереди

**Кандидат в источники**:
Публичный источник мемов, найденный или предложенный для возможного добавления в бота.
_Code aliases_: `meme_source_candidate`, Source Candidate
Expand Down Expand Up @@ -108,6 +115,7 @@ _Avoid_: router
## Relationships

- **Модератор** может участвовать в **модераторском чате** и получать **разбор новых мемов** в своей ленте.
- **Дедупликация мемов** должна выполняться до того, как мем становится доступен обычной рекомендательной системе.
- **Модераторский чат** используется для комьюнити-циклов; **чат проверки загрузок** используется для решений по пользовательским загрузкам.
- **Проверяющий загрузки** определяется доступом к **чату проверки загрузок**, а не `UserType`.
- Только **русскоязычный кандидат в источники** может попасть в автоматическое **голосование за источник**.
Expand Down
26 changes: 20 additions & 6 deletions scripts/agent_doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,19 +73,33 @@ def check_command_available(command: str) -> CheckResult:


def check_describe_memes_models(root: Path = ROOT) -> CheckResult:
path = root / "src" / "flows" / "storage" / "describe_memes.py"
try:
model_ids = extract_vision_models(path)
except Exception as exc:
return CheckResult("describe_memes:free_models", False, str(exc))
paths = (
root / "src" / "flows" / "storage" / "describe_memes.py",
root / "src" / "flows" / "storage" / "openrouter_vision.py",
)
errors: list[str] = []
for path in paths:
try:
model_ids = extract_vision_models(path)
source_path = path
break
except Exception as exc:
errors.append(str(exc))
else:
return CheckResult("describe_memes:free_models", False, "; ".join(errors))

paid = non_free_openrouter_models(model_ids)
if paid:
return CheckResult(
"describe_memes:free_models",
False,
"non-free model ids: " + ", ".join(paid),
)
return CheckResult("describe_memes:free_models", True, f"{len(model_ids)} free model(s)")
return CheckResult(
"describe_memes:free_models",
True,
f"{len(model_ids)} free model(s) in {source_path.relative_to(root)}",
)


def check_paperclip_access_adapter(
Expand Down
3 changes: 1 addition & 2 deletions scripts/deep_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ async def main():

# Find source in DB
from sqlalchemy import text
from src.database import fetch_one

source = await fetch_one(
text("SELECT id, url, status FROM meme_source WHERE url = :url"),
Expand Down Expand Up @@ -72,7 +71,7 @@ async def main():

if posts:
await insert_parsed_posts_from_telegram(source["id"], posts)
print(f"Inserted into meme_raw_telegram")
print("Inserted into meme_raw_telegram")

await update_meme_source(
meme_source_id=source["id"], parsed_at=datetime.now(timezone.utc)
Expand Down
7 changes: 4 additions & 3 deletions scripts/e2e_smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
from telethon import TelegramClient
from telethon.sessions import StringSession


# --- Config ---
API_ID = os.environ.get("TELEGRAM_API_ID")
API_HASH = os.environ.get("TELEGRAM_API_HASH")
Expand Down Expand Up @@ -150,9 +149,11 @@ async def test_delete(client, bot):
if btn.data and b"delete" in btn.data.lower():
await msg.click(data=btn.data)
confirm_msg = await wait_for_response(client, bot, msg.id)
if confirm_msg and ("ciao" in (confirm_msg.text or "").lower() or "start" in (confirm_msg.text or "").lower()):
confirm_text = (confirm_msg.text or "").lower() if confirm_msg else ""
if confirm_msg and ("ciao" in confirm_text or "start" in confirm_text):
return "PASS", f"State deleted: {(confirm_msg.text or '')[:80]}"
return "WARN", f"Delete clicked but unexpected response: {(confirm_msg.text if confirm_msg else 'no response')[:80]}"
response = confirm_msg.text if confirm_msg else "no response"
return "WARN", f"Delete clicked but unexpected response: {response[:80]}"

if "sure" in (msg.text or "").lower() or "delete" in (msg.text or "").lower():
return "WARN", f"Got confirmation prompt but no button found: {(msg.text or '')[:80]}"
Expand Down
Loading
Loading