Rifqi Hafizuddin
[KM-644] planner: infeasible plan outcome + entity-ranking few-shots
089b69d
Raw
History Blame
4.36 kB
"""Canned, language-matched refusals for the guardrail layer.
Centralized so the three refusal sites β€” the `out_of_scope` router branch, the
input-guard block, and the content-filter fallback β€” speak with one voice.
Deterministic on purpose (no LLM): the refusal must not call the same model that
just refused or failed. Language is picked with a tiny marker check (EN default,
ID when Indonesian markers are present), mirroring the lightweight detector in
`handlers/help.py` β€” enough for a one-line reply without an LLM round-trip.
"""
from __future__ import annotations
import re
# Subset of help.py's ID marker set β€” enough to pick EN vs ID for a short refusal.
# Includes the common Indonesian injection verbs ("lupakan"=forget, "abaikan"=ignore)
# so an ID-language jailbreak still gets an ID-language refusal.
_ID_MARKERS = frozenset({
"yang", "dan", "apa", "gimana", "bagaimana", "kenapa", "mengapa", "aku", "saya",
"tolong", "ini", "itu", "untuk", "dengan", "tidak", "nggak", "enggak", "bisa",
"mau", "buat", "dari", "kamu", "berapa", "kapan", "siapa", "dimana", "adalah",
"akan", "sudah", "belum", "lupakan", "abaikan", "kredensial", "tunjukkan",
})
def _is_indonesian(message: str) -> bool:
tokens = re.findall(r"[a-z']+", (message or "").lower())
return any(t in _ID_MARKERS for t in tokens)
# Off-topic / out-of-scope: the request is benign but outside what the assistant does.
_OUT_OF_SCOPE = {
"en": (
"That's outside what I can help with β€” I'm a data assistant, so I can only work "
"with the sources you've connected to Data Eyond. Ask me a question about your "
"data, or type /help to see what I can do."
),
"id": (
"Itu di luar yang bisa saya bantu β€” saya asisten data, jadi saya hanya bisa "
"bekerja dengan sumber data yang Anda hubungkan ke Data Eyond. Ajukan pertanyaan "
"tentang data Anda, atau ketik /help untuk melihat yang bisa saya lakukan."
),
}
# Blocked: a manipulation / injection / secret-extraction / abuse attempt.
_BLOCKED = {
"en": (
"I can't help with that request. I'm here to analyze the data you've connected β€” "
"ask me a question about your data and I'll take it from there."
),
"id": (
"Saya tidak bisa membantu permintaan tersebut. Saya di sini untuk menganalisis "
"data yang Anda hubungkan β€” ajukan pertanyaan tentang data Anda dan saya bantu."
),
}
# Data-gap: the planner judged the bound sources cannot answer the question
# (planner.md "When the catalog cannot answer"). Deterministic wrapper on
# purpose β€” the model that declined to plan is not re-asked to prose it up.
# Keyed on the pipeline's reply_language ("Indonesian"/"English"), not marker
# detection: the upstream language decision is authoritative here.
_DATA_GAP = {
"en": (
"I can't answer that from the data sources connected to this analysis. "
"{reason}You can bind a source that holds this data, or ask me what's "
"available (try /help or \"what data do I have?\")."
),
"id": (
"Saya tidak bisa menjawab itu dari sumber data yang terhubung ke "
"analisis ini. {reason}Anda bisa menambahkan sumber yang memuat data "
"tersebut, atau tanyakan data apa yang tersedia (coba /help atau "
"\"data apa yang saya punya?\")."
),
}
def data_gap_message(reason: str | None, reply_language: str | None = None) -> str:
"""Answer for an infeasible analysis: the bound sources lack the asked-for data.
`reason` is the planner's `infeasible_reason` (may be None/empty);
`reply_language` is the pipeline's detected language ("Indonesian"/"English").
"""
detail = (reason or "").strip()
if detail and not detail.endswith((".", "!", "?")):
detail += "."
lang = "id" if reply_language == "Indonesian" else "en"
return _DATA_GAP[lang].format(reason=f"{detail} " if detail else "")
def out_of_scope_message(message: str) -> str:
"""Refusal for a benign but out-of-scope request (the `out_of_scope` intent)."""
return _OUT_OF_SCOPE["id" if _is_indonesian(message) else "en"]
def blocked_message(message: str) -> str:
"""Refusal for a blocked request (injection / secrets / abuse / content-filter)."""
return _BLOCKED["id" if _is_indonesian(message) else "en"]