Files
dify/api/services/dify_builder/agent/model_resolver.py
T
Charles YaoandClaude Opus 4.8 c73dcc7720 fix(dify_builder): final review fixes — wiring test, stop char-split, ping log, lint sweep
- test_wiring.py: expected dict was missing SessionView's new `model` field
- model_resolver.normalize_completion_params: a string `stop` was being
  char-split into a list of letters instead of wrapped in a single-element
  list; added a regression test
- controllers/console/dify_builder._ping: log the exception server-side
  before returning the ok:false envelope
- ruff-clean the slice's new/extended test files (ARG001/ARG002/ARG005 via
  precise noqa, PT018 compound-assert splits, E306, E501) with no change
  to test behavior or assertion meaning

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 22:51:27 -07:00

62 lines
2.7 KiB
Python

"""Resolve the user's per-session model choice to a live ModelInstance.
The choice is Dify's model-selector shape (a ``model_config`` dict:
{provider, name, mode, completion_params}). Empty/None means "use the
tenant's configured default LLM". This module is the only place in the
Dify Builder services layer that touches the model runtime for resolution.
"""
from typing import Any
from core.app.app_config.entities import ModelConfig
from core.dify_builder.errors import BadRequestError
from core.model_manager import ModelInstance, ModelManager
from graphon.model_runtime.entities.model_entities import ModelType
def normalize_completion_params(completion_params: dict[str, Any]) -> tuple[dict[str, Any], list[str] | None]:
"""Split ``stop`` out of completion_params (invoke_llm takes it separately)."""
params = dict(completion_params or {})
stop = params.pop("stop", None)
if isinstance(stop, str):
stop = [stop]
elif stop is not None and not isinstance(stop, list):
stop = list(stop)
return params, stop
def resolve_model_instance(tenant_id: str, model_config: dict[str, Any] | None) -> ModelInstance:
"""A chosen model_config -> that model; empty/None -> the tenant default LLM."""
manager = ModelManager.for_tenant(tenant_id)
if model_config:
mc = ModelConfig.model_validate(model_config)
return manager.get_model_instance(
tenant_id=tenant_id, provider=mc.provider, model_type=ModelType.LLM, model=mc.name
)
return manager.get_default_model_instance(tenant_id=tenant_id, model_type=ModelType.LLM)
def validate_model_config(tenant_id: str, model_config: dict[str, Any]) -> None:
"""Fail fast at session creation if an explicit pick can't be resolved.
Surfaces the failure as BadRequestError (-> 400) rather than silently
downgrading to canned behavior.
"""
try:
mc = ModelConfig.model_validate(model_config)
ModelManager.for_tenant(tenant_id).get_model_instance(
tenant_id=tenant_id, provider=mc.provider, model_type=ModelType.LLM, model=mc.name
)
except Exception as exc:
raise BadRequestError(f"invalid model_config: {exc}") from exc
def resolved_model_names(tenant_id: str, model_config: dict[str, Any] | None) -> dict[str, str]:
"""Provider+name for display: the explicit pick, or the tenant default names."""
if model_config:
return {"provider": model_config.get("provider", ""), "name": model_config.get("name", "")}
provider, name = ModelManager.for_tenant(tenant_id).get_default_provider_model_name(
tenant_id=tenant_id, model_type=ModelType.LLM
)
return {"provider": provider or "", "name": name or ""}