atomicguard 0.1.0__py3-none-any.whl → 1.2.0__py3-none-any.whl
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.
- atomicguard/__init__.py +8 -3
- atomicguard/application/action_pair.py +7 -1
- atomicguard/application/agent.py +46 -6
- atomicguard/application/workflow.py +494 -11
- atomicguard/domain/__init__.py +4 -1
- atomicguard/domain/exceptions.py +19 -0
- atomicguard/domain/interfaces.py +137 -6
- atomicguard/domain/models.py +120 -6
- atomicguard/guards/__init__.py +16 -5
- atomicguard/guards/composite/__init__.py +11 -0
- atomicguard/guards/dynamic/__init__.py +13 -0
- atomicguard/guards/dynamic/test_runner.py +207 -0
- atomicguard/guards/interactive/__init__.py +11 -0
- atomicguard/guards/static/__init__.py +13 -0
- atomicguard/guards/static/imports.py +177 -0
- atomicguard/infrastructure/__init__.py +4 -1
- atomicguard/infrastructure/llm/__init__.py +3 -1
- atomicguard/infrastructure/llm/huggingface.py +180 -0
- atomicguard/infrastructure/llm/mock.py +32 -6
- atomicguard/infrastructure/llm/ollama.py +40 -17
- atomicguard/infrastructure/persistence/__init__.py +7 -1
- atomicguard/infrastructure/persistence/checkpoint.py +361 -0
- atomicguard/infrastructure/persistence/filesystem.py +69 -5
- atomicguard/infrastructure/persistence/memory.py +25 -3
- atomicguard/infrastructure/registry.py +126 -0
- atomicguard/schemas/__init__.py +142 -0
- {atomicguard-0.1.0.dist-info → atomicguard-1.2.0.dist-info}/METADATA +75 -13
- atomicguard-1.2.0.dist-info/RECORD +37 -0
- {atomicguard-0.1.0.dist-info → atomicguard-1.2.0.dist-info}/WHEEL +1 -1
- atomicguard-1.2.0.dist-info/entry_points.txt +4 -0
- atomicguard/guards/test_runner.py +0 -176
- atomicguard-0.1.0.dist-info/RECORD +0 -27
- /atomicguard/guards/{base.py → composite/base.py} +0 -0
- /atomicguard/guards/{human.py → interactive/human.py} +0 -0
- /atomicguard/guards/{syntax.py → static/syntax.py} +0 -0
- {atomicguard-0.1.0.dist-info → atomicguard-1.2.0.dist-info}/licenses/LICENSE +0 -0
- {atomicguard-0.1.0.dist-info → atomicguard-1.2.0.dist-info}/top_level.txt +0 -0
atomicguard/__init__.py
CHANGED
|
@@ -25,7 +25,7 @@ from atomicguard.application.agent import DualStateAgent
|
|
|
25
25
|
from atomicguard.application.workflow import Workflow, WorkflowStep
|
|
26
26
|
|
|
27
27
|
# Domain exceptions
|
|
28
|
-
from atomicguard.domain.exceptions import RmaxExhausted
|
|
28
|
+
from atomicguard.domain.exceptions import EscalationRequired, RmaxExhausted
|
|
29
29
|
|
|
30
30
|
# Domain interfaces (for type hints and custom implementations)
|
|
31
31
|
from atomicguard.domain.interfaces import (
|
|
@@ -43,6 +43,7 @@ from atomicguard.domain.models import (
|
|
|
43
43
|
GuardResult,
|
|
44
44
|
WorkflowResult,
|
|
45
45
|
WorkflowState,
|
|
46
|
+
WorkflowStatus,
|
|
46
47
|
)
|
|
47
48
|
|
|
48
49
|
# Prompts and tasks (structures only - content defined by calling applications)
|
|
@@ -57,6 +58,7 @@ from atomicguard.guards import (
|
|
|
57
58
|
CompositeGuard,
|
|
58
59
|
DynamicTestGuard,
|
|
59
60
|
HumanReviewGuard,
|
|
61
|
+
ImportGuard,
|
|
60
62
|
SyntaxGuard,
|
|
61
63
|
TestGuard,
|
|
62
64
|
)
|
|
@@ -71,7 +73,7 @@ from atomicguard.infrastructure.persistence import (
|
|
|
71
73
|
InMemoryArtifactDAG,
|
|
72
74
|
)
|
|
73
75
|
|
|
74
|
-
__version__ = "
|
|
76
|
+
__version__ = "1.2.0"
|
|
75
77
|
|
|
76
78
|
__all__ = [
|
|
77
79
|
# Version
|
|
@@ -86,6 +88,7 @@ __all__ = [
|
|
|
86
88
|
"GuardResult",
|
|
87
89
|
"WorkflowState",
|
|
88
90
|
"WorkflowResult",
|
|
91
|
+
"WorkflowStatus",
|
|
89
92
|
# Prompts and tasks (structures only)
|
|
90
93
|
"PromptTemplate",
|
|
91
94
|
"StepDefinition",
|
|
@@ -96,6 +99,7 @@ __all__ = [
|
|
|
96
99
|
"ArtifactDAGInterface",
|
|
97
100
|
# Domain exceptions
|
|
98
101
|
"RmaxExhausted",
|
|
102
|
+
"EscalationRequired",
|
|
99
103
|
# Application layer
|
|
100
104
|
"ActionPair",
|
|
101
105
|
"DualStateAgent",
|
|
@@ -105,11 +109,12 @@ __all__ = [
|
|
|
105
109
|
"InMemoryArtifactDAG",
|
|
106
110
|
"FilesystemArtifactDAG",
|
|
107
111
|
# Infrastructure - LLM
|
|
108
|
-
"OllamaGenerator",
|
|
109
112
|
"MockGenerator",
|
|
113
|
+
"OllamaGenerator",
|
|
110
114
|
# Guards
|
|
111
115
|
"CompositeGuard",
|
|
112
116
|
"SyntaxGuard",
|
|
117
|
+
"ImportGuard",
|
|
113
118
|
"TestGuard",
|
|
114
119
|
"DynamicTestGuard",
|
|
115
120
|
"HumanReviewGuard",
|
|
@@ -48,6 +48,8 @@ class ActionPair:
|
|
|
48
48
|
self,
|
|
49
49
|
context: Context,
|
|
50
50
|
dependencies: dict[str, Artifact] | None = None,
|
|
51
|
+
action_pair_id: str = "unknown",
|
|
52
|
+
workflow_id: str = "unknown",
|
|
51
53
|
) -> tuple[Artifact, GuardResult]:
|
|
52
54
|
"""
|
|
53
55
|
Execute the atomic generate-then-validate transaction.
|
|
@@ -55,11 +57,15 @@ class ActionPair:
|
|
|
55
57
|
Args:
|
|
56
58
|
context: Generation context
|
|
57
59
|
dependencies: Artifacts from prior workflow steps
|
|
60
|
+
action_pair_id: Identifier for this action pair
|
|
61
|
+
workflow_id: UUID of the workflow execution instance
|
|
58
62
|
|
|
59
63
|
Returns:
|
|
60
64
|
Tuple of (generated artifact, guard result)
|
|
61
65
|
"""
|
|
62
66
|
dependencies = dependencies or {}
|
|
63
|
-
artifact = self._generator.generate(
|
|
67
|
+
artifact = self._generator.generate(
|
|
68
|
+
context, self._prompt_template, action_pair_id, workflow_id
|
|
69
|
+
)
|
|
64
70
|
result = self._guard.validate(artifact, **dependencies)
|
|
65
71
|
return artifact, result
|
atomicguard/application/agent.py
CHANGED
|
@@ -5,13 +5,17 @@ Manages only EnvironmentState (the retry loop).
|
|
|
5
5
|
WorkflowState is managed by Workflow.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from dataclasses import replace
|
|
9
|
+
|
|
8
10
|
from atomicguard.application.action_pair import ActionPair
|
|
9
|
-
from atomicguard.domain.exceptions import RmaxExhausted
|
|
11
|
+
from atomicguard.domain.exceptions import EscalationRequired, RmaxExhausted
|
|
10
12
|
from atomicguard.domain.interfaces import ArtifactDAGInterface
|
|
11
13
|
from atomicguard.domain.models import (
|
|
12
14
|
AmbientEnvironment,
|
|
13
15
|
Artifact,
|
|
16
|
+
ArtifactStatus,
|
|
14
17
|
Context,
|
|
18
|
+
FeedbackEntry,
|
|
15
19
|
)
|
|
16
20
|
|
|
17
21
|
|
|
@@ -33,6 +37,8 @@ class DualStateAgent:
|
|
|
33
37
|
artifact_dag: ArtifactDAGInterface,
|
|
34
38
|
rmax: int = 3,
|
|
35
39
|
constraints: str = "",
|
|
40
|
+
action_pair_id: str = "unknown",
|
|
41
|
+
workflow_id: str = "unknown",
|
|
36
42
|
):
|
|
37
43
|
"""
|
|
38
44
|
Args:
|
|
@@ -40,11 +46,15 @@ class DualStateAgent:
|
|
|
40
46
|
artifact_dag: Repository for storing artifacts
|
|
41
47
|
rmax: Maximum retry attempts (default: 3)
|
|
42
48
|
constraints: Global constraints for the ambient environment
|
|
49
|
+
action_pair_id: Identifier for this action pair (e.g., 'g_test')
|
|
50
|
+
workflow_id: UUID of the workflow execution instance
|
|
43
51
|
"""
|
|
44
52
|
self._action_pair = action_pair
|
|
45
53
|
self._artifact_dag = artifact_dag
|
|
46
54
|
self._rmax = rmax
|
|
47
55
|
self._constraints = constraints
|
|
56
|
+
self._action_pair_id = action_pair_id
|
|
57
|
+
self._workflow_id = workflow_id
|
|
48
58
|
|
|
49
59
|
def execute(
|
|
50
60
|
self,
|
|
@@ -69,18 +79,44 @@ class DualStateAgent:
|
|
|
69
79
|
context = self._compose_context(specification, dependencies)
|
|
70
80
|
feedback_history: list[tuple[Artifact, str]] = []
|
|
71
81
|
retry_count = 0
|
|
82
|
+
previous_id: str | None = None # Track chain linkage
|
|
72
83
|
|
|
73
84
|
while retry_count <= self._rmax:
|
|
74
|
-
artifact, result = self._action_pair.execute(
|
|
85
|
+
artifact, result = self._action_pair.execute(
|
|
86
|
+
context, dependencies, self._action_pair_id, self._workflow_id
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Build feedback history for context snapshot
|
|
90
|
+
fb_entries = tuple(
|
|
91
|
+
FeedbackEntry(artifact_id=a.artifact_id, feedback=f)
|
|
92
|
+
for a, f in feedback_history
|
|
93
|
+
)
|
|
75
94
|
|
|
76
|
-
|
|
77
|
-
|
|
95
|
+
# Update artifact with guard result AND provenance metadata
|
|
96
|
+
artifact = replace(
|
|
97
|
+
artifact,
|
|
98
|
+
previous_attempt_id=previous_id,
|
|
99
|
+
status=ArtifactStatus.ACCEPTED
|
|
100
|
+
if result.passed
|
|
101
|
+
else ArtifactStatus.REJECTED,
|
|
102
|
+
guard_result=result.passed,
|
|
103
|
+
feedback=result.feedback,
|
|
104
|
+
context=replace(
|
|
105
|
+
artifact.context,
|
|
106
|
+
feedback_history=fb_entries,
|
|
107
|
+
),
|
|
78
108
|
)
|
|
109
|
+
self._artifact_dag.store(artifact)
|
|
79
110
|
|
|
80
111
|
if result.passed:
|
|
81
112
|
return artifact
|
|
113
|
+
elif result.fatal:
|
|
114
|
+
# Non-recoverable failure - escalate immediately
|
|
115
|
+
raise EscalationRequired(artifact, result.feedback)
|
|
82
116
|
else:
|
|
117
|
+
# Recoverable failure - retry
|
|
83
118
|
feedback_history.append((artifact, result.feedback))
|
|
119
|
+
previous_id = artifact.artifact_id # Track for next iteration
|
|
84
120
|
retry_count += 1
|
|
85
121
|
context = self._refine_context(
|
|
86
122
|
specification, artifact, feedback_history, dependencies
|
|
@@ -105,7 +141,9 @@ class DualStateAgent:
|
|
|
105
141
|
specification=specification,
|
|
106
142
|
current_artifact=None,
|
|
107
143
|
feedback_history=(),
|
|
108
|
-
|
|
144
|
+
dependency_artifacts=tuple(
|
|
145
|
+
(k, v.artifact_id) for k, v in dependencies.items()
|
|
146
|
+
), # Store IDs, not full artifacts
|
|
109
147
|
)
|
|
110
148
|
|
|
111
149
|
def _refine_context(
|
|
@@ -125,5 +163,7 @@ class DualStateAgent:
|
|
|
125
163
|
specification=specification,
|
|
126
164
|
current_artifact=artifact.content,
|
|
127
165
|
feedback_history=tuple((a.content, f) for a, f in feedback_history),
|
|
128
|
-
|
|
166
|
+
dependency_artifacts=tuple(
|
|
167
|
+
(k, v.artifact_id) for k, v in dependencies.items()
|
|
168
|
+
), # Preserve dependencies on retry
|
|
129
169
|
)
|