mirror of
https://github.com/langgenius/dify.git
synced 2026-10-04 13:47:55 +08:00
FINDING 1 (dataset visibility): DatasetService.get_datasets requires user parameter and silently omits ONLY_ME/PARTIAL_TEAM datasets on RBAC tenants. Query Dataset model directly by tenant_id for complete grounding inventory. FINDING 2 (normalization guard): Move fetch+normalize inside _safe to prevent malformed records from raising after _safe returns. Use lambda builders to wrap complete resource transformation including record normalization. FINDING 3 (test coverage): Add test_mixed_failure_partial_success (one source fails, others succeed) and test_malformed_tool_record_degrades (missing tool_name key gracefully degrades). Minor: compute model id once and reuse for both id and label fields; ensure E302 two blank lines between test functions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
79 lines
2.4 KiB
Python
79 lines
2.4 KiB
Python
"""Read-only tenant resource inventory for Dify Builder cognition/grounding.
|
|
|
|
One entry point -- list_tenant_resources -- returns the tenant's configured
|
|
models, datasets, and tools normalized to id+label. Each source degrades to
|
|
an empty list on failure so a missing provider never breaks the advance.
|
|
"""
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from core.workflow.generator.tool_catalogue import build_tool_catalogue
|
|
from extensions.ext_database import db
|
|
from models.dataset import Dataset
|
|
from services.model_provider_service import ModelProviderService
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ResourceRef:
|
|
id: str
|
|
label: str
|
|
meta: str = ""
|
|
readiness: str = "ready"
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class TenantResources:
|
|
models: list[ResourceRef]
|
|
datasets: list[ResourceRef]
|
|
tools: list[ResourceRef]
|
|
|
|
|
|
def _list_models(tenant_id: str) -> list[Any]:
|
|
return ModelProviderService().get_models_by_model_type(tenant_id=tenant_id, model_type="llm")
|
|
|
|
|
|
def _list_datasets(tenant_id: str) -> list[Any]:
|
|
with Session(db.engine) as session:
|
|
return list(session.scalars(select(Dataset).where(Dataset.tenant_id == tenant_id)).all())
|
|
|
|
|
|
def _list_tools(tenant_id: str) -> list[dict]:
|
|
return list(build_tool_catalogue(tenant_id))
|
|
|
|
|
|
def _safe(build) -> list:
|
|
try:
|
|
return build()
|
|
except Exception: # a missing/mis-configured source degrades to empty, never raises
|
|
return []
|
|
|
|
|
|
def list_tenant_resources(tenant_id: str) -> TenantResources:
|
|
def build_models():
|
|
models: list[ResourceRef] = []
|
|
for prov in _list_models(tenant_id):
|
|
for m in getattr(prov, "models", []):
|
|
name = getattr(m, "model", "")
|
|
model_id = f"{prov.provider}/{name}"
|
|
models.append(ResourceRef(id=model_id, label=model_id))
|
|
return models
|
|
|
|
def build_datasets():
|
|
return [ResourceRef(id=str(d.id), label=str(d.name)) for d in _list_datasets(tenant_id)]
|
|
|
|
def build_tools():
|
|
return [
|
|
ResourceRef(id=f"{t['provider_name']}/{t['tool_name']}", label=str(t.get("tool_label") or t["tool_name"]),
|
|
meta=str(t.get("description") or ""))
|
|
for t in _list_tools(tenant_id)
|
|
]
|
|
|
|
models = _safe(build_models)
|
|
datasets = _safe(build_datasets)
|
|
tools = _safe(build_tools)
|
|
return TenantResources(models=models, datasets=datasets, tools=tools)
|