agent-checkpoint-framework 0.1.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.
- agent_checkpoint/__init__.py +24 -0
- agent_checkpoint/codec.py +109 -0
- agent_checkpoint/factory.py +43 -0
- agent_checkpoint/manager.py +55 -0
- agent_checkpoint/models.py +72 -0
- agent_checkpoint/storage.py +39 -0
- agent_checkpoint/validator.py +112 -0
- agent_checkpoint_framework-0.1.0.dist-info/METADATA +37 -0
- agent_checkpoint_framework-0.1.0.dist-info/RECORD +11 -0
- agent_checkpoint_framework-0.1.0.dist-info/WHEEL +5 -0
- agent_checkpoint_framework-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from .codec import ParseResult, emit, emit_json, parse
|
|
2
|
+
from .factory import new_checkpoint, resume_from
|
|
3
|
+
from .manager import CheckpointManager
|
|
4
|
+
from .models import Checkpoint, CheckpointStatus, RetryStrategy
|
|
5
|
+
from .storage import InMemoryStorage, Storage
|
|
6
|
+
from .validator import ValidationError
|
|
7
|
+
|
|
8
|
+
__version__ = "0.1.0"
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"Checkpoint",
|
|
12
|
+
"CheckpointStatus",
|
|
13
|
+
"RetryStrategy",
|
|
14
|
+
"ValidationError",
|
|
15
|
+
"ParseResult",
|
|
16
|
+
"parse",
|
|
17
|
+
"emit",
|
|
18
|
+
"emit_json",
|
|
19
|
+
"new_checkpoint",
|
|
20
|
+
"resume_from",
|
|
21
|
+
"CheckpointManager",
|
|
22
|
+
"Storage",
|
|
23
|
+
"InMemoryStorage",
|
|
24
|
+
]
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Any, Dict, List, Optional, Union
|
|
6
|
+
|
|
7
|
+
from .models import Checkpoint, CheckpointStatus, RetryStrategy
|
|
8
|
+
from .validator import ValidationError, validate
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class ParseResult:
|
|
13
|
+
"""The result of a parse operation."""
|
|
14
|
+
|
|
15
|
+
ok: bool
|
|
16
|
+
checkpoint: Optional[Checkpoint]
|
|
17
|
+
errors: List[ValidationError]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def parse(data: Union[str, Dict[str, Any]]) -> ParseResult:
|
|
21
|
+
"""
|
|
22
|
+
Parses a JSON string or dictionary into a Checkpoint object.
|
|
23
|
+
Performs validation and returns a ParseResult.
|
|
24
|
+
"""
|
|
25
|
+
if isinstance(data, str):
|
|
26
|
+
try:
|
|
27
|
+
raw_dict = json.loads(data)
|
|
28
|
+
except json.JSONDecodeError as e:
|
|
29
|
+
return ParseResult(
|
|
30
|
+
ok=False,
|
|
31
|
+
checkpoint=None,
|
|
32
|
+
errors=[ValidationError("json", f"Invalid JSON: {str(e)}")],
|
|
33
|
+
)
|
|
34
|
+
else:
|
|
35
|
+
raw_dict = data
|
|
36
|
+
|
|
37
|
+
# 1. Validate the dictionary
|
|
38
|
+
errors = validate(raw_dict)
|
|
39
|
+
if errors:
|
|
40
|
+
return ParseResult(ok=False, checkpoint=None, errors=errors)
|
|
41
|
+
|
|
42
|
+
# 2. Build Checkpoint from dict
|
|
43
|
+
try:
|
|
44
|
+
# Convert enum strings to actual enum members for the constructor
|
|
45
|
+
status = CheckpointStatus(raw_dict["status"])
|
|
46
|
+
|
|
47
|
+
retry_strategy = None
|
|
48
|
+
if raw_dict.get("retry_strategy"):
|
|
49
|
+
retry_strategy = RetryStrategy(raw_dict["retry_strategy"])
|
|
50
|
+
|
|
51
|
+
checkpoint = Checkpoint(
|
|
52
|
+
# Required fields
|
|
53
|
+
ac_version=raw_dict["ac_version"],
|
|
54
|
+
checkpoint_id=raw_dict["checkpoint_id"],
|
|
55
|
+
task_id=raw_dict["task_id"],
|
|
56
|
+
agent_id=raw_dict["agent_id"],
|
|
57
|
+
emitted_at=raw_dict["emitted_at"],
|
|
58
|
+
status=status,
|
|
59
|
+
task_summary=raw_dict["task_summary"],
|
|
60
|
+
# Optional fields
|
|
61
|
+
completed_steps=raw_dict.get("completed_steps", []),
|
|
62
|
+
remaining_steps=raw_dict.get("remaining_steps", []),
|
|
63
|
+
partial_output=raw_dict.get("partial_output"),
|
|
64
|
+
failure_reason=raw_dict.get("failure_reason"),
|
|
65
|
+
confidence=raw_dict.get("confidence"),
|
|
66
|
+
retry_strategy=retry_strategy,
|
|
67
|
+
executor_hint=raw_dict.get("executor_hint"),
|
|
68
|
+
context_snapshot=raw_dict.get("context_snapshot"),
|
|
69
|
+
parent_checkpoint_id=raw_dict.get("parent_checkpoint_id"),
|
|
70
|
+
)
|
|
71
|
+
return ParseResult(ok=True, checkpoint=checkpoint, errors=[])
|
|
72
|
+
except (KeyError, ValueError, TypeError) as e:
|
|
73
|
+
# This catch-all handles unexpected type mismatches not caught by validate()
|
|
74
|
+
return ParseResult(
|
|
75
|
+
ok=False,
|
|
76
|
+
checkpoint=None,
|
|
77
|
+
errors=[ValidationError("internal", f"Failed to instantiate Checkpoint: {str(e)}")],
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def emit(checkpoint: Checkpoint) -> Dict[str, Any]:
|
|
82
|
+
"""
|
|
83
|
+
Converts a Checkpoint to a dictionary for serialization.
|
|
84
|
+
Omits fields with None values entirely.
|
|
85
|
+
"""
|
|
86
|
+
# Use __dict__ to avoid complex serialization of nested objects if any,
|
|
87
|
+
# but since Checkpoint is simple dataclass, this works well.
|
|
88
|
+
raw = checkpoint.__dict__.copy()
|
|
89
|
+
|
|
90
|
+
# Clean up enums and omit None values
|
|
91
|
+
clean: Dict[str, Any] = {}
|
|
92
|
+
for k, v in raw.items():
|
|
93
|
+
if v is None:
|
|
94
|
+
continue
|
|
95
|
+
|
|
96
|
+
# Handle enums
|
|
97
|
+
if isinstance(v, (CheckpointStatus, RetryStrategy)):
|
|
98
|
+
clean[k] = v.value
|
|
99
|
+
else:
|
|
100
|
+
clean[k] = v
|
|
101
|
+
|
|
102
|
+
return clean
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def emit_json(checkpoint: Checkpoint, indent: int = 2) -> str:
|
|
106
|
+
"""
|
|
107
|
+
Converts a Checkpoint to a JSON string.
|
|
108
|
+
"""
|
|
109
|
+
return json.dumps(emit(checkpoint), indent=indent, default=str)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import uuid
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
|
|
6
|
+
from .models import Checkpoint, CheckpointStatus
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def new_checkpoint(
|
|
10
|
+
task_id: str,
|
|
11
|
+
agent_id: str,
|
|
12
|
+
task_summary: str,
|
|
13
|
+
status: CheckpointStatus = CheckpointStatus.PARTIAL,
|
|
14
|
+
) -> Checkpoint:
|
|
15
|
+
"""
|
|
16
|
+
Creates a new Checkpoint for a fresh task.
|
|
17
|
+
"""
|
|
18
|
+
return Checkpoint(
|
|
19
|
+
ac_version="0.1.0",
|
|
20
|
+
checkpoint_id=str(uuid.uuid4()),
|
|
21
|
+
task_id=task_id,
|
|
22
|
+
agent_id=agent_id,
|
|
23
|
+
emitted_at=datetime.now(timezone.utc).isoformat(),
|
|
24
|
+
status=status,
|
|
25
|
+
task_summary=task_summary,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def resume_from(previous: Checkpoint, agent_id: str) -> Checkpoint:
|
|
30
|
+
"""
|
|
31
|
+
Creates a new Checkpoint linked to a previous one, preserving task context.
|
|
32
|
+
"""
|
|
33
|
+
return Checkpoint(
|
|
34
|
+
ac_version="0.1.0",
|
|
35
|
+
checkpoint_id=str(uuid.uuid4()),
|
|
36
|
+
task_id=previous.task_id,
|
|
37
|
+
agent_id=agent_id,
|
|
38
|
+
emitted_at=datetime.now(timezone.utc).isoformat(),
|
|
39
|
+
status=CheckpointStatus.PARTIAL,
|
|
40
|
+
task_summary=previous.task_summary,
|
|
41
|
+
completed_steps=list(previous.completed_steps), # copy the list
|
|
42
|
+
parent_checkpoint_id=previous.checkpoint_id,
|
|
43
|
+
)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from .models import Checkpoint, CheckpointStatus, RetryStrategy
|
|
6
|
+
from .storage import Storage
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CheckpointManager:
|
|
10
|
+
"""Coordinates checkpoint creation and retrieval based on SPEC.md."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, storage: Storage):
|
|
13
|
+
self.storage = storage
|
|
14
|
+
|
|
15
|
+
def create_checkpoint(
|
|
16
|
+
self,
|
|
17
|
+
task_id: str,
|
|
18
|
+
agent_id: str,
|
|
19
|
+
task_summary: str,
|
|
20
|
+
status: CheckpointStatus = CheckpointStatus.PARTIAL,
|
|
21
|
+
completed_steps: Optional[List[str]] = None,
|
|
22
|
+
remaining_steps: Optional[List[str]] = None,
|
|
23
|
+
partial_output: Optional[Dict[str, Any]] = None,
|
|
24
|
+
failure_reason: Optional[str] = None,
|
|
25
|
+
confidence: Optional[float] = None,
|
|
26
|
+
retry_strategy: Optional[RetryStrategy] = None,
|
|
27
|
+
executor_hint: Optional[str] = None,
|
|
28
|
+
context_snapshot: Optional[Dict[str, Any]] = None,
|
|
29
|
+
parent_checkpoint_id: Optional[str] = None,
|
|
30
|
+
) -> Checkpoint:
|
|
31
|
+
"""Creates and saves a new checkpoint following the spec."""
|
|
32
|
+
checkpoint = Checkpoint(
|
|
33
|
+
ac_version="0.1.0",
|
|
34
|
+
checkpoint_id=str(uuid.uuid4()),
|
|
35
|
+
task_id=task_id,
|
|
36
|
+
agent_id=agent_id,
|
|
37
|
+
emitted_at=datetime.now(timezone.utc).isoformat(),
|
|
38
|
+
status=status,
|
|
39
|
+
task_summary=task_summary,
|
|
40
|
+
completed_steps=completed_steps or [],
|
|
41
|
+
remaining_steps=remaining_steps or [],
|
|
42
|
+
partial_output=partial_output or {},
|
|
43
|
+
failure_reason=failure_reason,
|
|
44
|
+
confidence=confidence,
|
|
45
|
+
retry_strategy=retry_strategy,
|
|
46
|
+
executor_hint=executor_hint,
|
|
47
|
+
context_snapshot=context_snapshot or {},
|
|
48
|
+
parent_checkpoint_id=parent_checkpoint_id,
|
|
49
|
+
)
|
|
50
|
+
self.storage.save(checkpoint)
|
|
51
|
+
return checkpoint
|
|
52
|
+
|
|
53
|
+
def get_checkpoint(self, checkpoint_id: str) -> Optional[Checkpoint]:
|
|
54
|
+
"""Retrieves a checkpoint by ID."""
|
|
55
|
+
return self.storage.load(checkpoint_id)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from enum import StrEnum
|
|
5
|
+
from typing import Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CheckpointStatus(StrEnum):
|
|
9
|
+
"""Status of the checkpoint as defined in SPEC.md."""
|
|
10
|
+
|
|
11
|
+
PARTIAL = "partial"
|
|
12
|
+
FAILED = "failed"
|
|
13
|
+
PAUSED = "paused"
|
|
14
|
+
COMPLETED = "completed"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class RetryStrategy(StrEnum):
|
|
18
|
+
"""Strategy for continuation as defined in SPEC.md."""
|
|
19
|
+
|
|
20
|
+
RESUME = "resume"
|
|
21
|
+
RESTART = "restart"
|
|
22
|
+
ESCALATE = "escalate"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class Checkpoint:
|
|
27
|
+
"""
|
|
28
|
+
Represents a single point in time for an agent's state, following SPEC.md.
|
|
29
|
+
|
|
30
|
+
Validation:
|
|
31
|
+
- confidence: None or between 0.0 and 1.0 (inclusive)
|
|
32
|
+
- executor_hint: None, 'same', 'any', or starts with 'capability:'
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
# Required fields (no defaults)
|
|
36
|
+
ac_version: str
|
|
37
|
+
checkpoint_id: str
|
|
38
|
+
task_id: str
|
|
39
|
+
agent_id: str
|
|
40
|
+
emitted_at: str
|
|
41
|
+
status: CheckpointStatus
|
|
42
|
+
task_summary: str
|
|
43
|
+
|
|
44
|
+
# Optional fields
|
|
45
|
+
completed_steps: List[str] = field(default_factory=list)
|
|
46
|
+
remaining_steps: List[str] = field(default_factory=list)
|
|
47
|
+
partial_output: Optional[Dict[str, object]] = None
|
|
48
|
+
failure_reason: Optional[str] = None
|
|
49
|
+
confidence: Optional[float] = None
|
|
50
|
+
retry_strategy: Optional[RetryStrategy] = None
|
|
51
|
+
executor_hint: Optional[str] = None
|
|
52
|
+
context_snapshot: Optional[Dict[str, object]] = None
|
|
53
|
+
parent_checkpoint_id: Optional[str] = None
|
|
54
|
+
|
|
55
|
+
def __post_init__(self) -> None:
|
|
56
|
+
"""Validate input constraints."""
|
|
57
|
+
if self.confidence is not None:
|
|
58
|
+
if not (0.0 <= self.confidence <= 1.0):
|
|
59
|
+
raise ValueError(f"Confidence must be between 0.0 and 1.0; got {self.confidence}")
|
|
60
|
+
|
|
61
|
+
if self.executor_hint is not None:
|
|
62
|
+
if not (
|
|
63
|
+
self.executor_hint in ("same", "any")
|
|
64
|
+
or self.executor_hint.startswith("capability:")
|
|
65
|
+
):
|
|
66
|
+
raise ValueError(
|
|
67
|
+
f"executor_hint must be 'same', 'any', or start with 'capability:'; "
|
|
68
|
+
f"got {self.executor_hint}"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
__all__ = ["CheckpointStatus", "RetryStrategy", "Checkpoint"]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Dict, List, Optional
|
|
3
|
+
|
|
4
|
+
from .models import Checkpoint
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Storage(ABC):
|
|
8
|
+
"""Abstract base class for checkpoint storage."""
|
|
9
|
+
|
|
10
|
+
@abstractmethod
|
|
11
|
+
def save(self, checkpoint: Checkpoint) -> None:
|
|
12
|
+
"""Persist a checkpoint."""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
@abstractmethod
|
|
16
|
+
def load(self, checkpoint_id: str) -> Optional[Checkpoint]:
|
|
17
|
+
"""Retrieve a checkpoint by ID."""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def list_all(self) -> List[Checkpoint]:
|
|
22
|
+
"""List all stored checkpoints."""
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class InMemoryStorage(Storage):
|
|
27
|
+
"""Simple in-memory implementation of checkpoint storage."""
|
|
28
|
+
|
|
29
|
+
def __init__(self) -> None:
|
|
30
|
+
self._checkpoints: Dict[str, Checkpoint] = {}
|
|
31
|
+
|
|
32
|
+
def save(self, checkpoint: Checkpoint) -> None:
|
|
33
|
+
self._checkpoints[checkpoint.checkpoint_id] = checkpoint
|
|
34
|
+
|
|
35
|
+
def load(self, checkpoint_id: str) -> Optional[Checkpoint]:
|
|
36
|
+
return self._checkpoints.get(checkpoint_id)
|
|
37
|
+
|
|
38
|
+
def list_all(self) -> List[Checkpoint]:
|
|
39
|
+
return list(self._checkpoints.values())
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Any, Dict, List
|
|
7
|
+
|
|
8
|
+
from .models import CheckpointStatus, RetryStrategy
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class ValidationError:
|
|
13
|
+
"""Represents a single validation failure."""
|
|
14
|
+
|
|
15
|
+
field: str
|
|
16
|
+
message: str
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# UUID v4 pattern (simplified but strict enough for spec compliance)
|
|
20
|
+
UUID_V4_PATTERN = re.compile(
|
|
21
|
+
r"^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", re.IGNORECASE
|
|
22
|
+
)
|
|
23
|
+
SEMVER_PATTERN = re.compile(r"^\d+\.\d+\.\d+$")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def validate(data: Dict[str, Any]) -> List[ValidationError]:
|
|
27
|
+
"""
|
|
28
|
+
Validates a dictionary against the agent-checkpoint specification.
|
|
29
|
+
Returns a list of ValidationError objects; an empty list indicates success.
|
|
30
|
+
"""
|
|
31
|
+
errors: List[ValidationError] = []
|
|
32
|
+
|
|
33
|
+
# 1. Required fields presence and basic type check
|
|
34
|
+
required_fields = [
|
|
35
|
+
"ac_version",
|
|
36
|
+
"checkpoint_id",
|
|
37
|
+
"task_id",
|
|
38
|
+
"agent_id",
|
|
39
|
+
"emitted_at",
|
|
40
|
+
"status",
|
|
41
|
+
"task_summary",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
for field in required_fields:
|
|
45
|
+
if field not in data:
|
|
46
|
+
errors.append(ValidationError(field, "Field is missing"))
|
|
47
|
+
elif not isinstance(data[field], str) or not data[field].strip():
|
|
48
|
+
errors.append(ValidationError(field, "Field must be a non-empty string"))
|
|
49
|
+
|
|
50
|
+
# If basic requirements are missing, we still continue to check patterns for existing fields
|
|
51
|
+
|
|
52
|
+
# 2. Pattern and Logic Validations
|
|
53
|
+
if "ac_version" in data and isinstance(data["ac_version"], str):
|
|
54
|
+
if not SEMVER_PATTERN.match(data["ac_version"]):
|
|
55
|
+
errors.append(
|
|
56
|
+
ValidationError("ac_version", "Must match semver pattern \\d+\\.\\d+\\.\\d+")
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# UUID checks
|
|
60
|
+
for uuid_field in ["checkpoint_id", "task_id", "parent_checkpoint_id"]:
|
|
61
|
+
if uuid_field in data and data[uuid_field] is not None:
|
|
62
|
+
if not isinstance(data[uuid_field], str):
|
|
63
|
+
errors.append(ValidationError(uuid_field, "Must be a string"))
|
|
64
|
+
elif not UUID_V4_PATTERN.match(data[uuid_field]):
|
|
65
|
+
errors.append(ValidationError(uuid_field, "Must be a valid UUID v4"))
|
|
66
|
+
|
|
67
|
+
# Timestamp check
|
|
68
|
+
if "emitted_at" in data and isinstance(data["emitted_at"], str):
|
|
69
|
+
try:
|
|
70
|
+
# Handle the 'Z' suffix which fromisoformat might not like in older 3.11 patches
|
|
71
|
+
ts = data["emitted_at"].replace("Z", "+00:00")
|
|
72
|
+
datetime.fromisoformat(ts)
|
|
73
|
+
except ValueError:
|
|
74
|
+
errors.append(ValidationError("emitted_at", "Must be a valid ISO 8601 timestamp"))
|
|
75
|
+
|
|
76
|
+
# Enum checks
|
|
77
|
+
if "status" in data and isinstance(data["status"], str):
|
|
78
|
+
if data["status"] not in [s.value for s in CheckpointStatus]:
|
|
79
|
+
errors.append(
|
|
80
|
+
ValidationError(
|
|
81
|
+
"status", f"Must be one of: {', '.join(s.value for s in CheckpointStatus)}"
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if "retry_strategy" in data and data["retry_strategy"] is not None:
|
|
86
|
+
if data["retry_strategy"] not in [r.value for r in RetryStrategy]:
|
|
87
|
+
errors.append(
|
|
88
|
+
ValidationError(
|
|
89
|
+
"retry_strategy", f"Must be one of: {', '.join(r.value for r in RetryStrategy)}"
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Confidence check
|
|
94
|
+
if "confidence" in data and data["confidence"] is not None:
|
|
95
|
+
if not isinstance(data["confidence"], (int, float)):
|
|
96
|
+
errors.append(ValidationError("confidence", "Must be a number"))
|
|
97
|
+
elif not (0.0 <= float(data["confidence"]) <= 1.0):
|
|
98
|
+
errors.append(ValidationError("confidence", "Must be between 0.0 and 1.0"))
|
|
99
|
+
|
|
100
|
+
# Executor hint check
|
|
101
|
+
if "executor_hint" in data and data["executor_hint"] is not None:
|
|
102
|
+
val = data["executor_hint"]
|
|
103
|
+
if not isinstance(val, str):
|
|
104
|
+
errors.append(ValidationError("executor_hint", "Must be a string"))
|
|
105
|
+
elif not (val in ("same", "any") or val.startswith("capability:")):
|
|
106
|
+
errors.append(
|
|
107
|
+
ValidationError(
|
|
108
|
+
"executor_hint", "Must be 'same', 'any', or start with 'capability:'"
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
return errors
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-checkpoint-framework
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Never lose work when an agent fails.
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Provides-Extra: dev
|
|
8
|
+
Requires-Dist: pytest; extra == "dev"
|
|
9
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
10
|
+
Requires-Dist: ruff; extra == "dev"
|
|
11
|
+
Requires-Dist: mypy; extra == "dev"
|
|
12
|
+
|
|
13
|
+
# Agent Checkpoint
|
|
14
|
+
|
|
15
|
+
Never lose work when an agent fails.
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
- **Industrialized OOP Structure**: Clean separation of concerns between models, storage, and management.
|
|
20
|
+
- **Flexible Storage**: Abstract base class allows for multiple storage backends (In-Memory, File-system, Database).
|
|
21
|
+
- **Type Safe**: Fully type-hinted and compatible with `mypy`.
|
|
22
|
+
- **CI Ready**: Pre-configured with GitHub Actions, `ruff`, and `pytest`.
|
|
23
|
+
|
|
24
|
+
## Architecture
|
|
25
|
+
|
|
26
|
+
- `Checkpoint`: Immutable data model for agent states.
|
|
27
|
+
- `Storage`: Interface for persisting and retrieving checkpoints.
|
|
28
|
+
- `CheckpointManager`: Central API for creating and managing checkpoints.
|
|
29
|
+
|
|
30
|
+
## Development
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install -e .[dev]
|
|
34
|
+
ruff check .
|
|
35
|
+
mypy src
|
|
36
|
+
pytest
|
|
37
|
+
```
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
agent_checkpoint/__init__.py,sha256=vWeOPeehA9pgXrxPjKKJD1X5-35XhEcERFnRdGJ8vwM,581
|
|
2
|
+
agent_checkpoint/codec.py,sha256=z6TmeRLyNGhhSeYAxlWcYIXtsn-g7USn6Le-T5_VajA,3615
|
|
3
|
+
agent_checkpoint/factory.py,sha256=fBR1fxjoCAUfB3wESAwiRXNxy0wptqxK638T5NEG-9E,1219
|
|
4
|
+
agent_checkpoint/manager.py,sha256=UC4gjC7DOdP9TWOTmJmKFgLJow2iDzIcZof0X-ApfH8,2057
|
|
5
|
+
agent_checkpoint/models.py,sha256=dm96BVb9Ul7ZqBGo2hD0YScoi2_hcp4uvqgLdkL5u28,2165
|
|
6
|
+
agent_checkpoint/storage.py,sha256=L0oDYv6CeLefAq07uEoMu8LvOFRREecL51pwu33wJaQ,1082
|
|
7
|
+
agent_checkpoint/validator.py,sha256=XOFuMBEoCxyzEwgKEtZQxDb44lseypvj55Iz0rXvslM,4197
|
|
8
|
+
agent_checkpoint_framework-0.1.0.dist-info/METADATA,sha256=SCqo3AFYLdqKGUeQKWYve5PSK64YqXwb0pyfpB3Zt50,1077
|
|
9
|
+
agent_checkpoint_framework-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
10
|
+
agent_checkpoint_framework-0.1.0.dist-info/top_level.txt,sha256=PwvaVmUYakHhI7DYlzlDk2dZotS_olNFvb5xU02rvas,17
|
|
11
|
+
agent_checkpoint_framework-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
agent_checkpoint
|