fix(assistant): resolve dashboard refs via LLM entities and remove deterministic parser fallback
This commit is contained in:
@@ -548,6 +548,37 @@ def _resolve_dashboard_id_by_ref(
|
||||
# [/DEF:_resolve_dashboard_id_by_ref:Function]
|
||||
|
||||
|
||||
# [DEF:_resolve_dashboard_id_entity:Function]
|
||||
# @PURPOSE: Resolve dashboard id from intent entities using numeric id or dashboard_ref fallback.
|
||||
# @PRE: entities may contain dashboard_id as int/str and optional dashboard_ref.
|
||||
# @POST: Returns resolved dashboard id or None when ambiguous/unresolvable.
|
||||
def _resolve_dashboard_id_entity(
|
||||
entities: Dict[str, Any],
|
||||
config_manager: ConfigManager,
|
||||
env_hint: Optional[str] = None,
|
||||
) -> Optional[int]:
|
||||
raw_dashboard_id = entities.get("dashboard_id")
|
||||
dashboard_ref = entities.get("dashboard_ref")
|
||||
|
||||
if isinstance(raw_dashboard_id, int):
|
||||
return raw_dashboard_id
|
||||
|
||||
if isinstance(raw_dashboard_id, str):
|
||||
token = raw_dashboard_id.strip()
|
||||
if token.isdigit():
|
||||
return int(token)
|
||||
if token and not dashboard_ref:
|
||||
dashboard_ref = token
|
||||
|
||||
if not dashboard_ref:
|
||||
return None
|
||||
|
||||
env_token = env_hint or entities.get("environment") or entities.get("source_env") or entities.get("target_env")
|
||||
env_id = _resolve_env_id(env_token, config_manager) if env_token else _get_default_environment_id(config_manager)
|
||||
return _resolve_dashboard_id_by_ref(str(dashboard_ref), env_id, config_manager)
|
||||
# [/DEF:_resolve_dashboard_id_entity:Function]
|
||||
|
||||
|
||||
# [DEF:_parse_command:Function]
|
||||
# @PURPOSE: Deterministically parse RU/EN command text into intent payload.
|
||||
# @PRE: message contains raw user text and config manager resolves environments.
|
||||
@@ -793,53 +824,53 @@ def _build_tool_catalog(current_user: User, config_manager: ConfigManager, db: S
|
||||
{
|
||||
"operation": "create_branch",
|
||||
"domain": "git",
|
||||
"description": "Create git branch for dashboard",
|
||||
"required_entities": ["dashboard_id", "branch_name"],
|
||||
"optional_entities": [],
|
||||
"description": "Create git branch for dashboard by id/slug/title",
|
||||
"required_entities": ["branch_name"],
|
||||
"optional_entities": ["dashboard_id", "dashboard_ref"],
|
||||
"risk_level": "guarded",
|
||||
"requires_confirmation": False,
|
||||
},
|
||||
{
|
||||
"operation": "commit_changes",
|
||||
"domain": "git",
|
||||
"description": "Commit dashboard repository changes",
|
||||
"required_entities": ["dashboard_id"],
|
||||
"optional_entities": ["message"],
|
||||
"description": "Commit dashboard repository changes by dashboard id/slug/title",
|
||||
"required_entities": [],
|
||||
"optional_entities": ["dashboard_id", "dashboard_ref", "message"],
|
||||
"risk_level": "guarded",
|
||||
"requires_confirmation": False,
|
||||
},
|
||||
{
|
||||
"operation": "deploy_dashboard",
|
||||
"domain": "git",
|
||||
"description": "Deploy dashboard to target environment",
|
||||
"required_entities": ["dashboard_id", "environment"],
|
||||
"optional_entities": [],
|
||||
"description": "Deploy dashboard (id/slug/title) to target environment",
|
||||
"required_entities": ["environment"],
|
||||
"optional_entities": ["dashboard_id", "dashboard_ref"],
|
||||
"risk_level": "guarded",
|
||||
"requires_confirmation": False,
|
||||
},
|
||||
{
|
||||
"operation": "execute_migration",
|
||||
"domain": "migration",
|
||||
"description": "Run dashboard migration between environments",
|
||||
"required_entities": ["dashboard_id", "source_env", "target_env"],
|
||||
"optional_entities": [],
|
||||
"description": "Run dashboard migration (id/slug/title) between environments",
|
||||
"required_entities": ["source_env", "target_env"],
|
||||
"optional_entities": ["dashboard_id", "dashboard_ref"],
|
||||
"risk_level": "guarded",
|
||||
"requires_confirmation": False,
|
||||
},
|
||||
{
|
||||
"operation": "run_backup",
|
||||
"domain": "backup",
|
||||
"description": "Run backup for environment or specific dashboard",
|
||||
"description": "Run backup for environment or specific dashboard by id/slug/title",
|
||||
"required_entities": ["environment"],
|
||||
"optional_entities": ["dashboard_id"],
|
||||
"optional_entities": ["dashboard_id", "dashboard_ref"],
|
||||
"risk_level": "guarded",
|
||||
"requires_confirmation": False,
|
||||
},
|
||||
{
|
||||
"operation": "run_llm_validation",
|
||||
"domain": "llm",
|
||||
"description": "Run LLM dashboard validation",
|
||||
"required_entities": ["dashboard_id"],
|
||||
"description": "Run LLM dashboard validation by dashboard id/slug/title",
|
||||
"required_entities": [],
|
||||
"optional_entities": ["dashboard_ref", "environment", "provider"],
|
||||
"defaults": {"environment": default_env_id, "provider": validation_provider},
|
||||
"risk_level": "guarded",
|
||||
@@ -900,11 +931,11 @@ def _clarification_text_for_intent(intent: Optional[Dict[str, Any]], detail_text
|
||||
"run_llm_documentation": (
|
||||
"Нужно уточнение для генерации документации: Укажите dataset_id, окружение и провайдер LLM."
|
||||
),
|
||||
"create_branch": "Нужно уточнение: укажите dashboard_id и имя ветки.",
|
||||
"commit_changes": "Нужно уточнение: укажите dashboard_id для коммита.",
|
||||
"deploy_dashboard": "Нужно уточнение: укажите dashboard_id и целевое окружение.",
|
||||
"execute_migration": "Нужно уточнение: укажите dashboard_id, source_env и target_env.",
|
||||
"run_backup": "Нужно уточнение: укажите окружение для бэкапа.",
|
||||
"create_branch": "Нужно уточнение: укажите дашборд (id/slug/title) и имя ветки.",
|
||||
"commit_changes": "Нужно уточнение: укажите дашборд (id/slug/title) для коммита.",
|
||||
"deploy_dashboard": "Нужно уточнение: укажите дашборд (id/slug/title) и целевое окружение.",
|
||||
"execute_migration": "Нужно уточнение: укажите дашборд (id/slug/title), source_env и target_env.",
|
||||
"run_backup": "Нужно уточнение: укажите окружение и при необходимости дашборд (id/slug/title).",
|
||||
}
|
||||
return guidance_by_operation.get(operation, detail_text)
|
||||
# [/DEF:_clarification_text_for_intent:Function]
|
||||
@@ -958,6 +989,7 @@ async def _plan_intent_with_llm(
|
||||
"Rules:\n"
|
||||
"- Use only operation names from available_tools.\n"
|
||||
"- If input is ambiguous, operation must be \"clarify\" with low confidence.\n"
|
||||
"- If dashboard is provided as name/slug (e.g., COVID), put it into entities.dashboard_ref.\n"
|
||||
"- Keep entities minimal and factual.\n"
|
||||
)
|
||||
payload = {
|
||||
@@ -1091,35 +1123,35 @@ async def _dispatch_intent(
|
||||
|
||||
if operation == "create_branch":
|
||||
_check_any_permission(current_user, [("plugin:git", "EXECUTE")])
|
||||
dashboard_id = entities.get("dashboard_id")
|
||||
dashboard_id = _resolve_dashboard_id_entity(entities, config_manager)
|
||||
branch_name = entities.get("branch_name")
|
||||
if not dashboard_id or not branch_name:
|
||||
raise HTTPException(status_code=400, detail="Missing dashboard_id or branch_name")
|
||||
git_service.create_branch(int(dashboard_id), branch_name, "main")
|
||||
raise HTTPException(status_code=422, detail="Missing dashboard_id/dashboard_ref or branch_name")
|
||||
git_service.create_branch(dashboard_id, branch_name, "main")
|
||||
return f"Ветка `{branch_name}` создана для дашборда {dashboard_id}.", None, []
|
||||
|
||||
if operation == "commit_changes":
|
||||
_check_any_permission(current_user, [("plugin:git", "EXECUTE")])
|
||||
dashboard_id = entities.get("dashboard_id")
|
||||
dashboard_id = _resolve_dashboard_id_entity(entities, config_manager)
|
||||
commit_message = entities.get("message")
|
||||
if not dashboard_id:
|
||||
raise HTTPException(status_code=400, detail="Missing dashboard_id")
|
||||
git_service.commit_changes(int(dashboard_id), commit_message, None)
|
||||
raise HTTPException(status_code=422, detail="Missing dashboard_id/dashboard_ref")
|
||||
git_service.commit_changes(dashboard_id, commit_message, None)
|
||||
return "Коммит выполнен успешно.", None, []
|
||||
|
||||
if operation == "deploy_dashboard":
|
||||
_check_any_permission(current_user, [("plugin:git", "EXECUTE")])
|
||||
dashboard_id = entities.get("dashboard_id")
|
||||
env_token = entities.get("environment")
|
||||
env_id = _resolve_env_id(env_token, config_manager)
|
||||
dashboard_id = _resolve_dashboard_id_entity(entities, config_manager, env_hint=env_token)
|
||||
if not dashboard_id or not env_id:
|
||||
raise HTTPException(status_code=400, detail="Missing dashboard_id or environment")
|
||||
raise HTTPException(status_code=422, detail="Missing dashboard_id/dashboard_ref or environment")
|
||||
|
||||
task = await task_manager.create_task(
|
||||
plugin_id="git-integration",
|
||||
params={
|
||||
"operation": "deploy",
|
||||
"dashboard_id": int(dashboard_id),
|
||||
"dashboard_id": dashboard_id,
|
||||
"environment_id": env_id,
|
||||
},
|
||||
user_id=current_user.id,
|
||||
@@ -1135,16 +1167,17 @@ async def _dispatch_intent(
|
||||
|
||||
if operation == "execute_migration":
|
||||
_check_any_permission(current_user, [("plugin:migration", "EXECUTE"), ("plugin:superset-migration", "EXECUTE")])
|
||||
dashboard_id = entities.get("dashboard_id")
|
||||
src = _resolve_env_id(entities.get("source_env"), config_manager)
|
||||
src_token = entities.get("source_env")
|
||||
dashboard_id = _resolve_dashboard_id_entity(entities, config_manager, env_hint=src_token)
|
||||
src = _resolve_env_id(src_token, config_manager)
|
||||
tgt = _resolve_env_id(entities.get("target_env"), config_manager)
|
||||
if not dashboard_id or not src or not tgt:
|
||||
raise HTTPException(status_code=400, detail="Missing dashboard_id/source_env/target_env")
|
||||
raise HTTPException(status_code=422, detail="Missing dashboard_id/dashboard_ref/source_env/target_env")
|
||||
|
||||
task = await task_manager.create_task(
|
||||
plugin_id="superset-migration",
|
||||
params={
|
||||
"selected_ids": [int(dashboard_id)],
|
||||
"selected_ids": [dashboard_id],
|
||||
"source_env_id": src,
|
||||
"target_env_id": tgt,
|
||||
"replace_db_config": False,
|
||||
@@ -1162,13 +1195,17 @@ async def _dispatch_intent(
|
||||
|
||||
if operation == "run_backup":
|
||||
_check_any_permission(current_user, [("plugin:superset-backup", "EXECUTE"), ("plugin:backup", "EXECUTE")])
|
||||
env_id = _resolve_env_id(entities.get("environment"), config_manager)
|
||||
env_token = entities.get("environment")
|
||||
env_id = _resolve_env_id(env_token, config_manager)
|
||||
if not env_id:
|
||||
raise HTTPException(status_code=400, detail="Missing or unknown environment")
|
||||
|
||||
params: Dict[str, Any] = {"environment_id": env_id}
|
||||
if entities.get("dashboard_id"):
|
||||
params["dashboard_ids"] = [int(entities["dashboard_id"])]
|
||||
if entities.get("dashboard_id") or entities.get("dashboard_ref"):
|
||||
dashboard_id = _resolve_dashboard_id_entity(entities, config_manager, env_hint=env_token)
|
||||
if not dashboard_id:
|
||||
raise HTTPException(status_code=422, detail="Missing dashboard_id/dashboard_ref")
|
||||
params["dashboard_ids"] = [dashboard_id]
|
||||
|
||||
task = await task_manager.create_task(
|
||||
plugin_id="superset-backup",
|
||||
@@ -1186,14 +1223,9 @@ async def _dispatch_intent(
|
||||
|
||||
if operation == "run_llm_validation":
|
||||
_check_any_permission(current_user, [("plugin:llm_dashboard_validation", "EXECUTE")])
|
||||
env_id = _resolve_env_id(entities.get("environment"), config_manager) or _get_default_environment_id(config_manager)
|
||||
dashboard_id = entities.get("dashboard_id")
|
||||
if not dashboard_id:
|
||||
dashboard_id = _resolve_dashboard_id_by_ref(
|
||||
entities.get("dashboard_ref"),
|
||||
env_id,
|
||||
config_manager,
|
||||
)
|
||||
env_token = entities.get("environment")
|
||||
env_id = _resolve_env_id(env_token, config_manager) or _get_default_environment_id(config_manager)
|
||||
dashboard_id = _resolve_dashboard_id_entity(entities, config_manager, env_hint=env_token)
|
||||
provider_id = _resolve_provider_id(
|
||||
entities.get("provider"),
|
||||
db,
|
||||
@@ -1296,7 +1328,14 @@ async def send_message(
|
||||
except Exception as exc:
|
||||
logger.warning(f"[assistant.planner][fallback] Planner error: {exc}")
|
||||
if not intent:
|
||||
intent = _parse_command(request.message, config_manager)
|
||||
intent = {
|
||||
"domain": "unknown",
|
||||
"operation": "clarify",
|
||||
"entities": {},
|
||||
"confidence": 0.0,
|
||||
"risk_level": "safe",
|
||||
"requires_confirmation": False,
|
||||
}
|
||||
confidence = float(intent.get("confidence", 0.0))
|
||||
|
||||
if intent.get("domain") == "unknown" or confidence < 0.6:
|
||||
|
||||
Reference in New Issue
Block a user