programgarden 1.23.1__tar.gz → 1.24.1__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.
- {programgarden-1.23.1 → programgarden-1.24.1}/PKG-INFO +4 -4
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/__init__.py +21 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/client.py +12 -1
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/deep_fixtures.py +71 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/executor.py +532 -29
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/resolver.py +164 -27
- programgarden-1.24.1/programgarden/semantic_rules.py +404 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/pyproject.toml +4 -4
- {programgarden-1.23.1 → programgarden-1.24.1}/README.md +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/binding_validator.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/context.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/database/__init__.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/database/checkpoint_manager.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/database/query_builder.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/database/workflow_position_tracker.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/database/workflow_risk_tracker.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/node_runner.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/plugin/__init__.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/plugin/sandbox.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/providers/__init__.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/providers/llm_errors.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/providers/llm_provider.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/reconnect_handler.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/resource/__init__.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/resource/context.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/resource/limiter.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/resource/monitor.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/resource/throttle.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/tools/__init__.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/tools/credential_tools.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/tools/definition_tools.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/tools/event_tools.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/tools/job_tools.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/tools/registry_tools.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/tools/sqlite_tools.py +0 -0
- {programgarden-1.23.1 → programgarden-1.24.1}/programgarden/validation_recommender.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: programgarden
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.24.1
|
|
4
4
|
Summary: ProgramGarden - 노드 기반 자동매매 DSL 실행 엔진
|
|
5
5
|
Author: 프로그램동산
|
|
6
6
|
Author-email: coding@programgarden.com
|
|
@@ -14,9 +14,9 @@ Requires-Dist: aiosqlite (>=0.20.0,<0.21.0)
|
|
|
14
14
|
Requires-Dist: croniter (>=6.0.0,<7.0.0)
|
|
15
15
|
Requires-Dist: litellm (>=1.40.0)
|
|
16
16
|
Requires-Dist: lxml (>=6.0.2,<7.0.0)
|
|
17
|
-
Requires-Dist: programgarden-community (>=1.13.
|
|
18
|
-
Requires-Dist: programgarden-core (>=1.
|
|
19
|
-
Requires-Dist: programgarden-finance (>=1.6.
|
|
17
|
+
Requires-Dist: programgarden-community (>=1.13.9,<2.0.0)
|
|
18
|
+
Requires-Dist: programgarden-core (>=1.15.1,<2.0.0)
|
|
19
|
+
Requires-Dist: programgarden-finance (>=1.6.11,<2.0.0)
|
|
20
20
|
Requires-Dist: psutil (>=6.0.0,<7.0.0)
|
|
21
21
|
Requires-Dist: psycopg2-binary (>=2.9.11,<3.0.0)
|
|
22
22
|
Requires-Dist: pydantic (>=2.0.0,<3.0.0)
|
|
@@ -14,6 +14,17 @@ from programgarden.executor import WorkflowExecutor
|
|
|
14
14
|
from programgarden.context import ExecutionContext
|
|
15
15
|
from programgarden.client import ProgramGarden
|
|
16
16
|
from programgarden.node_runner import NodeRunner
|
|
17
|
+
from programgarden.semantic_rules import (
|
|
18
|
+
analyze_workflow_semantics,
|
|
19
|
+
normalize_severities,
|
|
20
|
+
DEFAULT_SEMANTIC_SEVERITIES,
|
|
21
|
+
STRICT_SEMANTIC_SEVERITIES,
|
|
22
|
+
ALL_RULES,
|
|
23
|
+
RULE_ORDER_QTY_FROM_AI,
|
|
24
|
+
RULE_STRUCTURED_OUTPUT_NO_SCHEMA,
|
|
25
|
+
RULE_HARDCODED_ORDER_QTY,
|
|
26
|
+
RULE_ORDER_IGNORED_FIELD,
|
|
27
|
+
)
|
|
17
28
|
from programgarden_core.bases.listener import (
|
|
18
29
|
NodeState,
|
|
19
30
|
EdgeState,
|
|
@@ -140,4 +151,14 @@ __all__ = [
|
|
|
140
151
|
"get_events",
|
|
141
152
|
"get_job_summary",
|
|
142
153
|
"analyze_performance",
|
|
154
|
+
# Semantic / safety layer (deep_validate R1~R4)
|
|
155
|
+
"analyze_workflow_semantics",
|
|
156
|
+
"normalize_severities",
|
|
157
|
+
"DEFAULT_SEMANTIC_SEVERITIES",
|
|
158
|
+
"STRICT_SEMANTIC_SEVERITIES",
|
|
159
|
+
"ALL_RULES",
|
|
160
|
+
"RULE_ORDER_QTY_FROM_AI",
|
|
161
|
+
"RULE_STRUCTURED_OUTPUT_NO_SCHEMA",
|
|
162
|
+
"RULE_HARDCODED_ORDER_QTY",
|
|
163
|
+
"RULE_ORDER_IGNORED_FIELD",
|
|
143
164
|
]
|
|
@@ -80,6 +80,7 @@ class ProgramGarden:
|
|
|
80
80
|
*,
|
|
81
81
|
fixtures: Optional[Dict[str, Any]] = None,
|
|
82
82
|
timeout: float = 15.0,
|
|
83
|
+
semantic_rules: Optional[Dict[str, Any]] = None,
|
|
83
84
|
) -> ValidationResult:
|
|
84
85
|
"""Deep-validate a workflow via virtual full-execution (never raises).
|
|
85
86
|
|
|
@@ -96,6 +97,11 @@ class ProgramGarden:
|
|
|
96
97
|
fixtures: Optional per-node fixture overrides, keyed by node id or
|
|
97
98
|
node type (merged shallowly on top of the default fixture).
|
|
98
99
|
timeout: Hard timeout (seconds) for the single validation pass.
|
|
100
|
+
semantic_rules: Optional per-rule severity config for the configurable
|
|
101
|
+
semantic/safety layer (R1~R4). ``None`` (default) skips the layer;
|
|
102
|
+
pass ``programgarden.semantic_rules.STRICT_SEMANTIC_SEVERITIES`` (or
|
|
103
|
+
a ``{rule_id: "error"|"warning"|"off"}`` dict) to opt in. See
|
|
104
|
+
``WorkflowExecutor.deep_validate`` for details.
|
|
99
105
|
|
|
100
106
|
Returns:
|
|
101
107
|
ValidationResult — ``errors`` carry structured per-node ErrorInfo;
|
|
@@ -109,7 +115,12 @@ class ProgramGarden:
|
|
|
109
115
|
... print(err.short())
|
|
110
116
|
"""
|
|
111
117
|
return _run_coro_sync(
|
|
112
|
-
self.executor.deep_validate(
|
|
118
|
+
self.executor.deep_validate(
|
|
119
|
+
definition,
|
|
120
|
+
fixtures=fixtures,
|
|
121
|
+
timeout=timeout,
|
|
122
|
+
semantic_rules=semantic_rules,
|
|
123
|
+
)
|
|
113
124
|
)
|
|
114
125
|
|
|
115
126
|
def run(
|
|
@@ -361,6 +361,77 @@ def broker_connection_fixture(
|
|
|
361
361
|
}
|
|
362
362
|
|
|
363
363
|
|
|
364
|
+
def _schema_default_value(schema: Any) -> Any:
|
|
365
|
+
"""Best-effort default value for one ``output_schema`` entry.
|
|
366
|
+
|
|
367
|
+
An entry is either a bare type string (``"number"``) or a JSON-schema-ish
|
|
368
|
+
dict (``{"type": "...", "enum": [...], "items": {...}, "properties": {...}}``)
|
|
369
|
+
— the two shapes ``AIAgentNodeExecutor._build_output_instruction`` /
|
|
370
|
+
``_validate_structured`` already accept. Returns a type-correct placeholder
|
|
371
|
+
so a downstream ``{{ nodes.<agent>.response.<field> }}`` binding resolves to a
|
|
372
|
+
real value (an enum field resolves to its first allowed value, an object to
|
|
373
|
+
its declared properties, an array to a single shaped element).
|
|
374
|
+
"""
|
|
375
|
+
if isinstance(schema, str):
|
|
376
|
+
type_name, spec = schema, {}
|
|
377
|
+
elif isinstance(schema, dict):
|
|
378
|
+
type_name, spec = str(schema.get("type", "string")), schema
|
|
379
|
+
else:
|
|
380
|
+
return "deep_validate"
|
|
381
|
+
|
|
382
|
+
enum_vals = spec.get("enum") if isinstance(spec, dict) else None
|
|
383
|
+
if enum_vals:
|
|
384
|
+
return enum_vals[0]
|
|
385
|
+
|
|
386
|
+
if type_name in ("string", "str"):
|
|
387
|
+
return "deep_validate"
|
|
388
|
+
if type_name in ("number", "float"):
|
|
389
|
+
return 1.0
|
|
390
|
+
if type_name in ("integer", "int"):
|
|
391
|
+
return 1
|
|
392
|
+
if type_name in ("boolean", "bool"):
|
|
393
|
+
return True
|
|
394
|
+
if type_name == "array":
|
|
395
|
+
items = spec.get("items") if isinstance(spec, dict) else None
|
|
396
|
+
if isinstance(items, (dict, str)):
|
|
397
|
+
return [_schema_default_value(items)]
|
|
398
|
+
return []
|
|
399
|
+
if type_name in ("object", "dict"):
|
|
400
|
+
props = spec.get("properties") if isinstance(spec, dict) else None
|
|
401
|
+
if isinstance(props, dict):
|
|
402
|
+
return {key: _schema_default_value(sub) for key, sub in props.items()}
|
|
403
|
+
return {}
|
|
404
|
+
return "deep_validate"
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def ai_agent_fixture(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
408
|
+
"""AIAgentNode deep fixture — schema-shaped ``{"response": ...}``, no LLM call.
|
|
409
|
+
|
|
410
|
+
A deep run must never hit a live LLM (cost, non-determinism, network) nor
|
|
411
|
+
silently swallow a failed model call. This builds the ``response`` output
|
|
412
|
+
port directly from the node's declared ``output_format`` / ``output_schema``
|
|
413
|
+
so downstream consumers see the same shape they would at runtime:
|
|
414
|
+
|
|
415
|
+
- ``"structured"`` + ``output_schema`` → a dict with every declared field
|
|
416
|
+
populated by a type-correct placeholder (enum → first value, nested
|
|
417
|
+
object/array shaped recursively). This is what makes
|
|
418
|
+
``{{ nodes.<agent>.response.<field> }}`` bindings resolve in deep mode.
|
|
419
|
+
- ``"json"`` → an empty dict (no schema to shape it).
|
|
420
|
+
- ``"text"`` / anything else → a placeholder string.
|
|
421
|
+
"""
|
|
422
|
+
output_format = config.get("output_format", "text")
|
|
423
|
+
output_schema = config.get("output_schema")
|
|
424
|
+
if output_format == "structured" and isinstance(output_schema, dict) and output_schema:
|
|
425
|
+
response: Any = {
|
|
426
|
+
key: _schema_default_value(spec) for key, spec in output_schema.items()
|
|
427
|
+
}
|
|
428
|
+
elif output_format == "json":
|
|
429
|
+
response = {}
|
|
430
|
+
else: # "text" or unknown → raw string
|
|
431
|
+
response = "deep_validate: AIAgentNode response (virtual run, no live LLM call)"
|
|
432
|
+
return {"response": response}
|
|
433
|
+
|
|
434
|
+
|
|
364
435
|
def apply_override(default: Dict[str, Any], override: Optional[Dict[str, Any]]) -> Dict[str, Any]:
|
|
365
436
|
"""Shallow-merge a caller override on top of a default fixture.
|
|
366
437
|
|