dify-player 0.3.2__tar.gz → 0.3.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. {dify_player-0.3.2 → dify_player-0.3.3}/PKG-INFO +1 -1
  2. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/__init__.py +1 -1
  3. dify_player-0.3.3/dify_player/dify_importer/node_converters/start.py +49 -0
  4. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/dify_importer/workflow_loader.py +1 -1
  5. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/plan_loader.py +32 -0
  6. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/runtime.py +18 -1
  7. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player.egg-info/PKG-INFO +1 -1
  8. {dify_player-0.3.2 → dify_player-0.3.3}/pyproject.toml +1 -1
  9. dify_player-0.3.2/dify_player/dify_importer/node_converters/start.py +0 -7
  10. {dify_player-0.3.2 → dify_player-0.3.3}/MANIFEST.in +0 -0
  11. {dify_player-0.3.2 → dify_player-0.3.3}/README.md +0 -0
  12. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/__main__.py +0 -0
  13. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/cli.py +0 -0
  14. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/dify_importer/__init__.py +0 -0
  15. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/dify_importer/graph_parser.py +0 -0
  16. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/dify_importer/http_body_converter.py +0 -0
  17. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/dify_importer/node_converters/__init__.py +0 -0
  18. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/dify_importer/node_converters/assigner.py +0 -0
  19. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/dify_importer/node_converters/code.py +0 -0
  20. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/dify_importer/node_converters/end.py +0 -0
  21. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/dify_importer/node_converters/http_request.py +0 -0
  22. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/dify_importer/node_converters/if_else.py +0 -0
  23. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/dify_importer/node_converters/llm.py +0 -0
  24. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/dify_importer/node_converters/loop.py +0 -0
  25. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/dify_importer/node_converters/template_transform.py +0 -0
  26. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/dify_importer/node_converters/variable_aggregator.py +0 -0
  27. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/dify_importer/plan_serializer.py +0 -0
  28. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/dify_importer/reference_converter.py +0 -0
  29. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/dify_importer/workflow_normalizer.py +0 -0
  30. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/dify_workflow_importer.py +0 -0
  31. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/event_logger.py +0 -0
  32. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/exceptions.py +0 -0
  33. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/input_resolver.py +0 -0
  34. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/llm_cache.py +0 -0
  35. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/models.py +0 -0
  36. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/nodes/__init__.py +0 -0
  37. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/nodes/assigner.py +0 -0
  38. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/nodes/code.py +0 -0
  39. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/nodes/end.py +0 -0
  40. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/nodes/http.py +0 -0
  41. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/nodes/if_else.py +0 -0
  42. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/nodes/llm_azure_chat.py +0 -0
  43. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/nodes/llm_groq_chat.py +0 -0
  44. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/nodes/llm_xai_chat.py +0 -0
  45. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/nodes/start.py +0 -0
  46. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/nodes/template.py +0 -0
  47. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/nodes/variable_aggregator.py +0 -0
  48. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/value_renderer.py +0 -0
  49. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/workflow_engine.py +0 -0
  50. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player/workflow_executor.py +0 -0
  51. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player.egg-info/SOURCES.txt +0 -0
  52. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player.egg-info/dependency_links.txt +0 -0
  53. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player.egg-info/requires.txt +0 -0
  54. {dify_player-0.3.2 → dify_player-0.3.3}/dify_player.egg-info/top_level.txt +0 -0
  55. {dify_player-0.3.2 → dify_player-0.3.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dify-player
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Minimal workflow runner for hand-authored Dify-like plans.
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: Jinja2<4,>=3.1
@@ -5,4 +5,4 @@ from dify_player.workflow_engine import WorkflowEngine
5
5
 
6
6
  __all__ = ["__version__", "LLMCacheStore", "NullLLMCacheStore", "WorkflowEngine"]
7
7
 
8
- __version__ = "0.3.2"
8
+ __version__ = "0.3.3"
@@ -0,0 +1,49 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from dify_player.exceptions import PlanValidationError
6
+ from dify_player.models import Node
7
+
8
+
9
+ def convert_start_node(*, node_id: str, node_name: str | None, data: dict[str, Any] | None = None) -> Node:
10
+ input_schema = _convert_input_schema(node_id=node_id, data=data or {})
11
+ config = {"input_schema": input_schema} if input_schema else {}
12
+ return Node(id=node_id, kind="start", name=node_name, config=config)
13
+
14
+
15
+ def _convert_input_schema(*, node_id: str, data: dict[str, Any]) -> dict[str, dict[str, Any]]:
16
+ raw_variables = data.get("variables", [])
17
+ if raw_variables is None:
18
+ raw_variables = []
19
+ if not isinstance(raw_variables, list):
20
+ raise PlanValidationError(f"Dify start node {node_id!r} variables must be an array")
21
+
22
+ input_schema: dict[str, dict[str, Any]] = {}
23
+ for index, raw_variable in enumerate(raw_variables):
24
+ if not isinstance(raw_variable, dict):
25
+ raise PlanValidationError(f"Dify start node {node_id!r} variables[{index}] must be an object")
26
+ variable = raw_variable.get("variable")
27
+ if not isinstance(variable, str) or not variable:
28
+ raise PlanValidationError(f"Dify start node {node_id!r} variables[{index}].variable must be a non-empty string")
29
+ if variable in input_schema:
30
+ raise PlanValidationError(f"Dify start node {node_id!r} defines duplicate variable {variable!r}")
31
+
32
+ required = raw_variable.get("required", True)
33
+ if not isinstance(required, bool):
34
+ raise PlanValidationError(f"Dify start node {node_id!r} variables[{index}].required must be a boolean when provided")
35
+
36
+ field_schema: dict[str, Any] = {"required": required}
37
+ if _has_meaningful_default(raw_variable=raw_variable, required=required):
38
+ field_schema["default"] = raw_variable["default"]
39
+ input_schema[variable] = field_schema
40
+ return input_schema
41
+
42
+
43
+ def _has_meaningful_default(*, raw_variable: dict[str, Any], required: bool) -> bool:
44
+ if "default" not in raw_variable:
45
+ return False
46
+ default = raw_variable["default"]
47
+ if required and default == "":
48
+ return False
49
+ return True
@@ -42,7 +42,7 @@ def _convert_node(*, node_id: str, data: dict[str, Any], parsed_graph: ParsedDif
42
42
  node_type = data["type"]
43
43
  node_name = _extract_node_name(node_id=node_id, data=data)
44
44
  if node_type == "start":
45
- return convert_start_node(node_id=node_id, node_name=node_name)
45
+ return convert_start_node(node_id=node_id, node_name=node_name, data=data)
46
46
  if node_type == "http-request":
47
47
  return convert_http_request_node(node_id=node_id, node_name=node_name, data=data, node_specs=parsed_graph.node_specs)
48
48
  if node_type == "llm":
@@ -78,6 +78,8 @@ def parse_plan(data: Any) -> Plan:
78
78
  if not isinstance(config, dict):
79
79
  raise PlanValidationError(f"node.config must be an object for node {node_label!r}")
80
80
  normalized_config = dict(config)
81
+ if kind == "start":
82
+ _validate_start_config(node_id=node_id, node_name=name, config=normalized_config)
81
83
  if kind == "template" and not isinstance(normalized_config.get("template"), str):
82
84
  raise PlanValidationError(f"template node {node_label!r} must define config.template as a string")
83
85
  if kind == "http":
@@ -305,6 +307,36 @@ def _collect_roots(value: Any) -> set[str]:
305
307
  return set()
306
308
 
307
309
 
310
+ def _validate_start_config(*, node_id: str, node_name: str | None, config: dict[str, Any]) -> None:
311
+ node_label = format_node_label(node_id, node_name)
312
+ allowed_keys = {"input_schema"}
313
+ extra_keys = sorted(set(config) - allowed_keys)
314
+ if extra_keys:
315
+ raise PlanValidationError(f"start node {node_label!r} config has unsupported keys: {', '.join(extra_keys)}")
316
+ if "input_schema" not in config:
317
+ return
318
+
319
+ input_schema = config["input_schema"]
320
+ if not isinstance(input_schema, dict):
321
+ raise PlanValidationError(f"start node {node_label!r} config.input_schema must be an object")
322
+
323
+ normalized_schema: dict[str, dict[str, Any]] = {}
324
+ for variable, field_schema in input_schema.items():
325
+ if not isinstance(variable, str) or not variable:
326
+ raise PlanValidationError(f"start node {node_label!r} config.input_schema keys must be non-empty strings")
327
+ if not isinstance(field_schema, dict):
328
+ raise PlanValidationError(f"start node {node_label!r} config.input_schema.{variable} must be an object")
329
+ required = field_schema.get("required", True)
330
+ if not isinstance(required, bool):
331
+ raise PlanValidationError(
332
+ f"start node {node_label!r} config.input_schema.{variable}.required must be a boolean when provided"
333
+ )
334
+ normalized_field_schema = dict(field_schema)
335
+ normalized_field_schema["required"] = required
336
+ normalized_schema[variable] = normalized_field_schema
337
+ config["input_schema"] = normalized_schema
338
+
339
+
308
340
  def _validate_http_config(*, node_id: str, node_name: str | None, config: dict[str, Any]) -> None:
309
341
  node_label = format_node_label(node_id, node_name)
310
342
  allowed_keys = {"method", "timeout_sec"}
@@ -52,7 +52,7 @@ class WorkflowRuntime:
52
52
  ) -> RunResult:
53
53
  effective_started_at = started_at or _utc_now()
54
54
  effective_started_monotonic = started_monotonic if started_monotonic is not None else time.monotonic()
55
- state = RunState(run_id=self.logger.run_id, inputs=inputs)
55
+ state = RunState(run_id=self.logger.run_id, inputs=_normalize_workflow_inputs(compiled_plan=compiled_plan, inputs=inputs))
56
56
  effective_http_client = http_client if http_client is not None else httpx.AsyncClient()
57
57
  owns_http_client = http_client is None
58
58
  try:
@@ -755,6 +755,23 @@ def _utc_now() -> str:
755
755
  return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
756
756
 
757
757
 
758
+ def _normalize_workflow_inputs(*, compiled_plan: CompiledPlan, inputs: dict[str, Any]) -> dict[str, Any]:
759
+ normalized_inputs = dict(inputs)
760
+ start_node = compiled_plan.node_by_id[compiled_plan.start_node_id]
761
+ input_schema = start_node.config.get("input_schema", {})
762
+ if not isinstance(input_schema, dict):
763
+ return normalized_inputs
764
+
765
+ for variable, field_schema in input_schema.items():
766
+ if variable in normalized_inputs or not isinstance(field_schema, dict):
767
+ continue
768
+ if "default" in field_schema:
769
+ normalized_inputs[variable] = deepcopy(field_schema["default"])
770
+ elif field_schema.get("required", True) is False:
771
+ normalized_inputs[variable] = ""
772
+ return normalized_inputs
773
+
774
+
758
775
  class _LoopRuntimeError(ValueError):
759
776
  def __init__(self, error: WorkflowError) -> None:
760
777
  super().__init__(error.message)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dify-player
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Minimal workflow runner for hand-authored Dify-like plans.
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: Jinja2<4,>=3.1
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "dify-player"
10
- version = "0.3.2"
10
+ version = "0.3.3"
11
11
  description = "Minimal workflow runner for hand-authored Dify-like plans."
12
12
  requires-python = ">=3.11"
13
13
  dependencies = [
@@ -1,7 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from dify_player.models import Node
4
-
5
-
6
- def convert_start_node(*, node_id: str, node_name: str | None) -> Node:
7
- return Node(id=node_id, kind="start", name=node_name)
File without changes
File without changes
File without changes