mirror of
https://github.com/langgenius/dify.git
synced 2026-06-07 16:32:01 +08:00
fix: normalize json_schema from string to dict in VariableEntity (#36777)
This commit is contained in:
parent
1e76b9e1b8
commit
5a0ad4ecd9
@ -1,4 +1,6 @@
|
||||
import json
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
from core.app.app_config.entities import RagPipelineVariableEntity
|
||||
from graphon.variables.input_entities import VariableEntity
|
||||
@ -20,10 +22,32 @@ class WorkflowVariablesConfigManager:
|
||||
|
||||
# variables
|
||||
for variable in user_input_form:
|
||||
cls._normalize_json_schema(variable)
|
||||
variables.append(VariableEntity.model_validate(variable))
|
||||
|
||||
return variables
|
||||
|
||||
@staticmethod
|
||||
def _normalize_json_schema(variable: dict[str, Any]) -> None:
|
||||
"""
|
||||
Normalize ``json_schema`` from a JSON string to a dict.
|
||||
|
||||
The workflow graph is stored as JSON in the database. When a JSON
|
||||
object variable carries a ``json_schema`` field, nested dicts are
|
||||
preserved correctly, but older data or certain serialization paths
|
||||
may store it as a JSON *string* instead of a native dict.
|
||||
|
||||
``VariableEntity.json_schema`` expects ``dict | None``, so we
|
||||
deserialize the string here before handing it to Pydantic.
|
||||
"""
|
||||
json_schema = variable.get("json_schema")
|
||||
if isinstance(json_schema, str):
|
||||
try:
|
||||
variable["json_schema"] = json.loads(json_schema)
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
# Leave as-is; Pydantic validation will surface the error.
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def convert_rag_pipeline_variable(cls, workflow: Workflow, start_node_id: str) -> list[RagPipelineVariableEntity]:
|
||||
"""
|
||||
|
||||
@ -74,6 +74,87 @@ class TestWorkflowVariablesConfigManagerConvert:
|
||||
with pytest.raises(ValueError):
|
||||
WorkflowVariablesConfigManager.convert(mock_workflow)
|
||||
|
||||
def test_convert_normalizes_json_schema_string_to_dict(self, mock_workflow, mock_variable_entity):
|
||||
"""Regression test for #36766: json_schema stored as a JSON string."""
|
||||
import json
|
||||
|
||||
schema_dict = {"type": "object", "properties": {"name": {"type": "string"}}}
|
||||
input_variables = [
|
||||
{
|
||||
"variable": "profile",
|
||||
"label": "Profile",
|
||||
"type": "json_object",
|
||||
"json_schema": json.dumps(schema_dict),
|
||||
}
|
||||
]
|
||||
mock_workflow.user_input_form.return_value = input_variables
|
||||
mock_variable_entity.model_validate.side_effect = lambda x: x
|
||||
|
||||
# Act
|
||||
result = WorkflowVariablesConfigManager.convert(mock_workflow)
|
||||
|
||||
# Assert — the string was deserialized before reaching model_validate
|
||||
assert result[0]["json_schema"] == schema_dict
|
||||
assert isinstance(result[0]["json_schema"], dict)
|
||||
|
||||
def test_convert_normalizes_json_schema_dict_passthrough(self, mock_workflow, mock_variable_entity):
|
||||
"""json_schema already a dict should pass through unchanged."""
|
||||
schema_dict = {"type": "object", "properties": {"age": {"type": "number"}}}
|
||||
input_variables = [
|
||||
{
|
||||
"variable": "profile",
|
||||
"label": "Profile",
|
||||
"type": "json_object",
|
||||
"json_schema": schema_dict,
|
||||
}
|
||||
]
|
||||
mock_workflow.user_input_form.return_value = input_variables
|
||||
mock_variable_entity.model_validate.side_effect = lambda x: x
|
||||
|
||||
# Act
|
||||
result = WorkflowVariablesConfigManager.convert(mock_workflow)
|
||||
|
||||
# Assert
|
||||
assert result[0]["json_schema"] == schema_dict
|
||||
|
||||
def test_convert_normalizes_json_schema_none_passthrough(self, mock_workflow, mock_variable_entity):
|
||||
"""json_schema=None should pass through unchanged."""
|
||||
input_variables = [
|
||||
{
|
||||
"variable": "name",
|
||||
"label": "Name",
|
||||
"type": "text-input",
|
||||
"json_schema": None,
|
||||
}
|
||||
]
|
||||
mock_workflow.user_input_form.return_value = input_variables
|
||||
mock_variable_entity.model_validate.side_effect = lambda x: x
|
||||
|
||||
# Act
|
||||
result = WorkflowVariablesConfigManager.convert(mock_workflow)
|
||||
|
||||
# Assert
|
||||
assert result[0]["json_schema"] is None
|
||||
|
||||
def test_convert_normalizes_json_schema_invalid_string_passthrough(self, mock_workflow, mock_variable_entity):
|
||||
"""Invalid JSON string should be left as-is for Pydantic to reject."""
|
||||
input_variables = [
|
||||
{
|
||||
"variable": "profile",
|
||||
"label": "Profile",
|
||||
"type": "json_object",
|
||||
"json_schema": "{invalid-json",
|
||||
}
|
||||
]
|
||||
mock_workflow.user_input_form.return_value = input_variables
|
||||
mock_variable_entity.model_validate.side_effect = lambda x: x
|
||||
|
||||
# Act
|
||||
result = WorkflowVariablesConfigManager.convert(mock_workflow)
|
||||
|
||||
# Assert — string left as-is
|
||||
assert result[0]["json_schema"] == "{invalid-json"
|
||||
|
||||
|
||||
# =============================
|
||||
# Test convert_rag_pipeline_variable
|
||||
|
||||
Loading…
Reference in New Issue
Block a user