Skip to content

Commit 9a77a0e

Browse files
committed
fix(core): propagate draft_id and platform across Slack UI, fix RAG loop, and add markdown backup
Details: - Removed hardcoded 'telegram' and 'temp_id' in Slack interactions. - Passed UUID draft_id through Slack buttons and modals to maintain state. - Fixed NameError in view_submission for platform variable. - Added Makefile for simplified Taskiq worker startup. - Implemented Markdown file backup for published posts in vectorize_post task.
1 parent 57dd76d commit 9a77a0e

6 files changed

Lines changed: 62 additions & 30 deletions

File tree

Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
.PHONY: worker
1+
.PHONY: worker api
22

33
worker:
4-
poetry run taskiq worker backend.workers.broker:broker backend.workers.tasks
4+
poetry run taskiq worker backend.workers.broker:broker backend.workers.tasks
5+
6+
api:
7+
poetry run uvicorn backend.api.main:app --host 127.0.0.1 --port 8001 --reload

backend/api/routes/feedback.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import uuid
23

34
import httpx
45
import structlog
@@ -71,7 +72,10 @@ async def slack_interactions(request: Request):
7172
action = payload.get("actions", [])[0]
7273
action_id = action.get("action_id")
7374
response_url = payload.get("response_url")
74-
75+
action_value = action.get("value", "temp_id|telegram")
76+
value_parts = action_value.split("|")
77+
draft_id = value_parts[0]
78+
platform = value_parts[1] if len(value_parts) > 1 else "telegram"
7579
# --- ГРУПА 1: Дії, що відкривають модалки (response_url НЕ потрібен) ---
7680
if action_id == "action_open_upload_modal":
7781
modal_view = build_upload_modal()
@@ -116,7 +120,7 @@ async def slack_interactions(request: Request):
116120
async with httpx.AsyncClient() as client:
117121
if action_id == "action_publish_draft":
118122
await publish_post_task.kiq(
119-
post_id="temp_id", platform="telegram", content=draft_text
123+
post_id=draft_id, platform=platform, content=draft_text
120124
)
121125
await client.post(
122126
response_url,
@@ -137,7 +141,10 @@ async def slack_interactions(request: Request):
137141

138142
elif action_id == "action_edit_draft":
139143
modal_view = build_approval_modal(
140-
topic=topic, draft=draft_text, platform="telegram"
144+
topic=topic,
145+
draft=draft_text,
146+
platform=platform,
147+
draft_id=draft_id,
141148
)
142149
await client.post(
143150
"https://slack.com/api/views.open",
@@ -149,9 +156,10 @@ async def slack_interactions(request: Request):
149156
channel_id = str(payload.get("channel", {}).get("id", ""))
150157
await generate_draft_task.kiq( # type: ignore[call-overload]
151158
topic=topic,
152-
platform="telegram",
159+
platform=platform,
153160
user_id=user_id,
154161
channel_id=channel_id,
162+
draft_id=draft_id,
155163
)
156164
await client.post(
157165
response_url,
@@ -191,9 +199,13 @@ async def slack_interactions(request: Request):
191199
platform=platform,
192200
)
193201

194-
# Запускаємо генерацію
202+
new_draft_id = str(uuid.uuid4()) # ГЕНЕРУЄМО УНІКАЛЬНИЙ ID
195203
await generate_draft_task.kiq( # type: ignore[call-overload]
196-
topic=topic, platform=platform, user_id=user_id, channel_id=channel_id
204+
topic=topic,
205+
platform=platform,
206+
user_id=user_id,
207+
channel_id=channel_id,
208+
draft_id=new_draft_id,
197209
)
198210

199211
# Відправляємо підтвердження в канал
@@ -232,8 +244,13 @@ async def slack_interactions(request: Request):
232244
logger.info(
233245
"slack_edit_modal_submitted", user_id=user_id, platform=platform
234246
)
247+
metadata_parts = view.get("private_metadata", "").split("|")
248+
draft_id = (
249+
metadata_parts[1] if len(metadata_parts) > 1 else "temp_id"
250+
) # ВИТЯГУЄМО З МЕТАДАНИХ
251+
235252
await publish_post_task.kiq(
236-
post_id="temp_id", platform=platform, content=draft_content
253+
post_id=draft_id, platform=platform, content=draft_content
237254
)
238255

239256
return Response(

backend/workers/callbacks.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ async def _send_slack_message(payload: dict[str, Any]) -> None:
4343

4444

4545
async def notify_slack_on_complete(
46-
user_id: str, channel_id: str, draft: str, topic: str
46+
user_id: str, channel_id: str, draft: str, topic: str, draft_id: str
4747
) -> None:
4848
logger.info(
4949
"sending_slack_completion_notification", user_id=user_id, channel_id=channel_id
@@ -52,7 +52,9 @@ async def notify_slack_on_complete(
5252
payload = {
5353
"channel": channel_id,
5454
"text": SLACK_UI["draft_ready_fallback"].format(topic=topic),
55-
"blocks": build_draft_card(topic=topic, draft=draft, user_id=user_id),
55+
"blocks": build_draft_card(
56+
topic=topic, draft=draft, user_id=user_id, draft_id=draft_id
57+
),
5658
}
5759
await _send_slack_message(payload)
5860

backend/workers/tasks/generate_draft.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,21 @@
44
from taskiq import TaskiqDepends
55

66
from backend.services.content_generator import ContentGenerator, JudgeFailedError
7-
from backend.workers.broker import broker
87
from backend.workers.callbacks import notify_slack_on_complete, notify_slack_on_failure
98
from backend.workers.dependencies import get_content_generator
109

1110
logger = structlog.get_logger()
1211

1312

1413
# Збільшено timeout до 180 секунд через наявність циклу LLM-as-a-Judge та Retries
15-
@broker.task(task_name="generate_medical_draft", timeout=180)
1614
async def generate_draft_task(
1715
topic: str,
1816
platform: str,
1917
generator: Annotated[ContentGenerator, TaskiqDepends(get_content_generator)],
2018
source_url: str | None = None,
2119
user_id: str | None = None,
2220
channel_id: str | None = None,
21+
draft_id: str = "temp_id",
2322
) -> str:
2423
"""
2524
Фонова задача для генерації медичного контенту.
@@ -45,7 +44,11 @@ async def generate_draft_task(
4544
# ДОДАНО: Відправка готового результату у Slack
4645
if user_id and channel_id:
4746
await notify_slack_on_complete(
48-
user_id=user_id, channel_id=channel_id, draft=result, topic=topic
47+
user_id=user_id,
48+
channel_id=channel_id,
49+
draft=result,
50+
topic=topic,
51+
draft_id=draft_id,
4952
)
5053

5154
return result

backend/workers/tasks/vectorize_post.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from pathlib import Path
2+
13
import structlog
24
from llama_index.core import Document, StorageContext, VectorStoreIndex
35
from llama_index.core.settings import Settings
@@ -23,7 +25,9 @@
2325
)
2426

2527
# Налаштовуємо глобальну модель для векторизації
26-
embed_model = OpenAIEmbedding(model=settings.OPENAI_MODEL_EMBEDDING, api_key=openai_key)
28+
embed_model = OpenAIEmbedding(
29+
model=settings.OPENAI_MODEL_EMBEDDING, dimensions=768, api_key=openai_key
30+
)
2731
Settings.embed_model = embed_model
2832

2933

@@ -35,17 +39,25 @@ async def vectorize_published_post_task(content: str, platform: str) -> None:
3539
logger.info("vectorization_started", platform=platform)
3640

3741
try:
38-
# 1. Підключаємось до Qdrant
42+
# --- 1. ЗБЕРЕЖЕННЯ У MARKDOWN (Резервна копія) ---
43+
md_file_path = Path("knowledge_base/doctor_style/posts/doctor_style_posts.md")
44+
md_file_path.parent.mkdir(parents=True, exist_ok=True)
45+
46+
# Додаємо пост у кінець файлу з роздільником
47+
with md_file_path.open("a", encoding="utf-8") as f:
48+
f.write(f"\n\n---\n\n{content.strip()}")
49+
50+
logger.info("markdown_backup_success", file_path=str(md_file_path))
51+
52+
# --- 2. ВЕКТОРИЗАЦІЯ В QDRANT ---
3953
qdrant_url = getattr(settings, "QDRANT_URL", "http://127.0.0.1:6333")
4054
aclient = AsyncQdrantClient(url=qdrant_url)
4155

42-
# Передаємо AsyncQdrantClient в параметр aclient
4356
vector_store = QdrantVectorStore(
4457
aclient=aclient, collection_name="doctor_style"
4558
)
4659
storage_context = StorageContext.from_defaults(vector_store=vector_store)
4760

48-
# 2. Створюємо документ із метаданими
4961
doc = Document(
5062
text=content,
5163
metadata={
@@ -55,16 +67,10 @@ async def vectorize_published_post_task(content: str, platform: str) -> None:
5567
},
5668
)
5769

58-
# 3. Векторизуємо та зберігаємо у БД асинхронно
59-
# Якщо колекції немає, QdrantVectorStore створит її під капотом
60-
# коли ми будемо використовувати aclient
61-
62-
# Створюємо порожній індекс, прив'язаний до Qdrant
6370
index = VectorStoreIndex.from_vector_store( # type: ignore[reportUnknownMemberType]
6471
vector_store=vector_store, storage_context=storage_context
6572
)
6673

67-
# Вставляємо документ асинхронно
6874
await index.ainsert(doc)
6975

7076
logger.info("vectorization_success", platform=platform)

slack_app/utils/block_builder.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
from backend.config.lexicon import SLACK_UI
44

55

6-
def build_draft_card(topic: str, draft: str, user_id: str) -> list[dict[str, Any]]:
6+
def build_draft_card(
7+
topic: str, draft: str, user_id: str, draft_id: str
8+
) -> list[dict[str, Any]]:
79
"""Генерує картку чернетки з кнопками дій."""
810
return [
911
{
@@ -39,7 +41,7 @@ def build_draft_card(topic: str, draft: str, user_id: str) -> list[dict[str, Any
3941
"emoji": True,
4042
},
4143
"style": "primary",
42-
"value": "publish",
44+
"value": draft_id,
4345
"action_id": "action_publish_draft",
4446
},
4547
{
@@ -49,7 +51,7 @@ def build_draft_card(topic: str, draft: str, user_id: str) -> list[dict[str, Any
4951
"text": SLACK_UI["btn_edit"],
5052
"emoji": True,
5153
},
52-
"value": "edit",
54+
"value": draft_id,
5355
"action_id": "action_edit_draft",
5456
},
5557
{
@@ -79,13 +81,12 @@ def build_draft_card(topic: str, draft: str, user_id: str) -> list[dict[str, Any
7981

8082

8183
def build_approval_modal(
82-
topic: str, draft: str, platform: str = "telegram"
84+
topic: str, draft: str, platform: str = "telegram", draft_id: str = "unknown"
8385
) -> dict[str, Any]:
84-
"""Генерує модальне вікно для редагування тексту перед публікацією."""
8586
return {
8687
"type": "modal",
8788
"callback_id": "modal_edit_draft",
88-
"private_metadata": topic, # Зберігаємо тему для контексту
89+
"private_metadata": f"{topic}|{draft_id}",
8990
"title": {"type": "plain_text", "text": SLACK_UI["modal_title"], "emoji": True},
9091
"submit": {
9192
"type": "plain_text",

0 commit comments

Comments
 (0)