Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
52 changes: 28 additions & 24 deletions hospexplorer/ask/admin.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import logging
import threading

from django.contrib import admin
from django.db import transaction

from ask.models import Conversation, TermsAcceptance, QARecord, SimWorkflow, WebsiteResource, PDFResource
from ask.kb_connector import add_website_to_kb, add_pdf_to_kb, delete_kb_document
from ask.kb_connector import delete_kb_document
from ask.tasks import run_kb_resource_upload

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -181,17 +184,19 @@ def save_model(self, request, obj, form, change):
obj.modifier = request.user
super().save_model(request, obj, form, change)

# send the website URL to the MCP KB server
# errors are logged but don't block the save
# is still saved in the internal DB even if the KB is unreachable
try:
result = add_website_to_kb(obj.url)
obj.mcp_kb_document_id = result.get("doc_id")
obj.save(update_fields=["mcp_kb_document_id"])
self.message_user(request, f"Website '{obj.title}' sent to Knowledge Base (doc_id={obj.mcp_kb_document_id}).")
except Exception as e:
logger.exception("Failed to send website to KB: %s", obj.url)
self.message_user(request, f"Website saved but failed to send to Knowledge Base: {e}", level="warning")
# start MCP KB upload in a background thread AFTER the admin's
# transaction commits, so a slow MCP round trip wont time out the save
transaction.on_commit(
lambda: threading.Thread(
target=run_kb_resource_upload,
args=("website", obj.pk),
daemon=True,
).start()
)
self.message_user(
request,
f"Website '{obj.title}' saved. Upload to Knowledge Base is running in the background.",
)


@admin.register(PDFResource)
Expand All @@ -218,15 +223,14 @@ def save_model(self, request, obj, form, change):
obj.modifier = request.user
super().save_model(request, obj, form, change)

try:
obj.file.open("rb")
file_bytes = obj.file.read()
obj.file.close()
result = add_pdf_to_kb(file_bytes, obj.file.name.split("/")[-1], obj.title)
obj.mcp_kb_document_id = result.get("doc_id")
obj.save(update_fields=["mcp_kb_document_id"])
self.message_user(request, f"PDF '{obj.title}' sent to Knowledge Base (doc_id={obj.mcp_kb_document_id}).")
except Exception as e:
logger.exception("Failed to send PDF to KB: %s", obj.file.name)
self.message_user(request, f"PDF saved but failed to send to Knowledge Base: {e}", level="warning")

transaction.on_commit(
lambda: threading.Thread(
target=run_kb_resource_upload,
args=("pdf", obj.pk),
daemon=True,
).start()
)
self.message_user(
request,
f"PDF '{obj.title}' saved. Upload to Knowledge Base is running in the background.",
)
36 changes: 36 additions & 0 deletions hospexplorer/ask/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,39 @@ def run_llm_task(task_id, record_id, conversation_id):
logger.exception("Failed to mark task as failed, task_id=%s", task_id)
finally:
close_old_connections()


def run_kb_resource_upload(model_label, resource_id):
"""Background thread: push a resource to the MCP KB and record its doc_id.

Runs outside the admin's atomic save transaction so a slow or timing-out
MCP call can't roll back the local row. On failure mcp_kb_document_id
stays null and the existing reconciliation UI surfaces the mismatch.

model_label is "website" or "pdf" picks the model and the connector
call, which is the only per-type difference.
"""
from ask.models import WebsiteResource, PDFResource
from ask.kb_connector import add_pdf_to_kb, add_website_to_kb

try:
if model_label == "pdf":
obj = PDFResource.objects.get(pk=resource_id)
obj.file.open("rb")
try:
file_bytes = obj.file.read()
finally:
obj.file.close()
result = add_pdf_to_kb(file_bytes, obj.file.name.split("/")[-1], obj.title)
elif model_label == "website":
obj = WebsiteResource.objects.get(pk=resource_id)
result = add_website_to_kb(obj.url)
else:
raise ValueError(f"Unknown model_label: {model_label!r}")

obj.mcp_kb_document_id = result.get("doc_id")
obj.save(update_fields=["mcp_kb_document_id"])
except Exception:
logger.exception("Background KB %s upload failed for resource_id=%s", model_label, resource_id)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

errors need to be communicated back to the user. we probably want to attach a status to the objects that indicate success, processing, error and maybe warning, with a message that we can then show in the admin interface.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bigger files still time out, so there is a use case to test this with.

finally:
close_old_connections()