fix: populate completion_params from model schema defaults in Agent node (#39590)

This commit is contained in:
Shakti
2026-07-28 07:06:49 +00:00
committed by GitHub
parent 35b539e35b
commit 7ec6a57ddf
2 changed files with 138 additions and 1 deletions
@@ -198,8 +198,23 @@ class AgentRuntimeSupport:
if model_schema:
model_schema = self._remove_unsupported_model_features_for_old_version(model_schema)
value["entity"] = model_schema.model_dump(mode="json")
# The model selector value from the workflow frontend only
# carries provider/model/mode — it does NOT include
# completion_params. AgentStrategy plugins (cot_agent,
# function_calling) read completion_params to build the
# LLMModelConfig that is backwards-invoked, and some model
# providers raise KeyError('required') when
# completion_params is empty because their parameter_rules
# declare required fields with no default. Populate
# completion_params with the defaults declared in the model
# schema so the plugin daemon always receives a valid set
# of model parameters.
if "completion_params" not in value:
value["completion_params"] = self._extract_default_completion_params(model_schema)
else:
value["entity"] = None
if "completion_params" not in value:
value["completion_params"] = {}
result[parameter_name] = value
return result
@@ -275,6 +290,24 @@ class AgentRuntimeSupport:
model_schema.features.remove(feature)
return model_schema
@staticmethod
def _extract_default_completion_params(model_schema: AIModelEntity) -> dict[str, Any]:
"""Build a completion_params dict from the model schema's parameter_rules.
The workflow Agent node's model-selector parameter only stores
provider/model/mode — it never carries completion_params. When the
value is forwarded to the plugin daemon, AgentModelConfig defaults
completion_params to ``{}``, which causes some model providers to fail
because their parameter_rules declare required fields. This helper
collects the ``default`` value of every parameter_rule that has one so
the plugin daemon receives a valid, non-empty set of model parameters.
"""
completion_params: dict[str, Any] = {}
for rule in model_schema.parameter_rules:
if rule.default is not None:
completion_params[rule.name] = rule.default
return completion_params
@staticmethod
def _filter_mcp_type_tool(
strategy: ResolvedAgentStrategy,
@@ -2,7 +2,16 @@ from types import SimpleNamespace
from unittest.mock import Mock, patch
from core.workflow.nodes.agent.runtime_support import AgentRuntimeSupport
from graphon.model_runtime.entities.model_entities import ModelType
from graphon.model_runtime.entities.common_entities import I18nObject
from graphon.model_runtime.entities.model_entities import (
AIModelEntity,
FetchFrom,
ModelFeature,
ModelPropertyKey,
ModelType,
ParameterRule,
ParameterType,
)
def test_fetch_model_reuses_single_model_assembly():
@@ -47,3 +56,98 @@ def test_fetch_model_reuses_single_model_assembly():
model_type=ModelType.LLM,
model="gpt-4o-mini",
)
def _make_model_schema_with_defaults() -> AIModelEntity:
"""Return a minimal AIModelEntity whose parameter_rules carry defaults."""
return AIModelEntity(
model="qwen-max",
label=I18nObject(en_US="Qwen Max"),
model_type=ModelType.LLM,
features=[ModelFeature.AGENT_THOUGHT, ModelFeature.MULTI_TOOL_CALL],
fetch_from=FetchFrom.PREDEFINED_MODEL,
model_properties={
ModelPropertyKey.MODE: "chat",
ModelPropertyKey.CONTEXT_SIZE: 32768,
},
parameter_rules=[
ParameterRule(
name="temperature",
use_template="temperature",
label=I18nObject(en_US="Temperature"),
type=ParameterType.FLOAT,
required=False,
default=0.7,
min=0.0,
max=2.0,
precision=2,
),
ParameterRule(
name="max_tokens",
use_template="max_tokens",
label=I18nObject(en_US="Max Tokens"),
type=ParameterType.INT,
required=False,
default=2048,
min=1,
max=32768,
),
ParameterRule(
name="top_p",
use_template="top_p",
label=I18nObject(en_US="Top P"),
type=ParameterType.FLOAT,
required=False,
default=1.0,
),
],
)
def test_extract_default_completion_params_collects_rule_defaults():
"""_extract_default_completion_params should gather every rule.default."""
schema = _make_model_schema_with_defaults()
params = AgentRuntimeSupport._extract_default_completion_params(schema)
assert params == {"temperature": 0.7, "max_tokens": 2048, "top_p": 1.0}
def test_extract_default_completion_params_skips_rules_without_default():
"""Rules whose default is None must not appear in the result."""
schema = AIModelEntity(
model="test-model",
label=I18nObject(en_US="Test"),
model_type=ModelType.LLM,
fetch_from=FetchFrom.PREDEFINED_MODEL,
model_properties={ModelPropertyKey.MODE: "chat"},
parameter_rules=[
ParameterRule(
name="seed",
label=I18nObject(en_US="Seed"),
type=ParameterType.INT,
required=False,
default=None,
),
ParameterRule(
name="temperature",
label=I18nObject(en_US="Temperature"),
type=ParameterType.FLOAT,
required=False,
default=0.5,
),
],
)
params = AgentRuntimeSupport._extract_default_completion_params(schema)
assert params == {"temperature": 0.5}
def test_extract_default_completion_params_empty_when_no_defaults():
"""An empty parameter_rules list yields an empty dict."""
schema = AIModelEntity(
model="test-model",
label=I18nObject(en_US="Test"),
model_type=ModelType.LLM,
fetch_from=FetchFrom.PREDEFINED_MODEL,
model_properties={ModelPropertyKey.MODE: "chat"},
parameter_rules=[],
)
assert AgentRuntimeSupport._extract_default_completion_params(schema) == {}