agent-checkpoint-framework 0.2.0__tar.gz → 0.3.0__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.
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/PKG-INFO +1 -1
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/pyproject.toml +1 -1
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint/__init__.py +4 -0
- agent_checkpoint_framework-0.3.0/src/agent_checkpoint/manifest.py +98 -0
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint_framework.egg-info/PKG-INFO +1 -1
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint_framework.egg-info/SOURCES.txt +3 -1
- agent_checkpoint_framework-0.3.0/tests/test_manifest.py +109 -0
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/README.md +0 -0
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/setup.cfg +0 -0
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint/codec.py +0 -0
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint/factory.py +0 -0
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint/manager.py +0 -0
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint/models.py +0 -0
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint/storage.py +0 -0
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint/validator.py +0 -0
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint_framework.egg-info/dependency_links.txt +0 -0
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint_framework.egg-info/requires.txt +0 -0
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint_framework.egg-info/top_level.txt +0 -0
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/tests/test_codec.py +0 -0
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/tests/test_factory.py +0 -0
- {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/tests/test_manager.py +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from .codec import ParseResult, emit, emit_json, parse
|
|
2
2
|
from .factory import new_checkpoint, resume_from
|
|
3
3
|
from .manager import CheckpointManager
|
|
4
|
+
from .manifest import AgentManifest, emit_manifest, parse_manifest
|
|
4
5
|
from .models import Checkpoint, CheckpointStatus, RetryStrategy, TrustedValue, TrustTier
|
|
5
6
|
from .storage import InMemoryStorage, Storage
|
|
6
7
|
from .validator import ValidationError
|
|
@@ -23,4 +24,7 @@ __all__ = [
|
|
|
23
24
|
"CheckpointManager",
|
|
24
25
|
"Storage",
|
|
25
26
|
"InMemoryStorage",
|
|
27
|
+
"AgentManifest",
|
|
28
|
+
"parse_manifest",
|
|
29
|
+
"emit_manifest",
|
|
26
30
|
]
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from dataclasses import asdict, dataclass, field
|
|
5
|
+
from typing import Any, Dict, List, Optional, Union
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class ConfidenceRange:
|
|
10
|
+
"""Range of confidence scores the agent operates within."""
|
|
11
|
+
|
|
12
|
+
min: float = 0.0
|
|
13
|
+
max: float = 1.0
|
|
14
|
+
|
|
15
|
+
def __post_init__(self) -> None:
|
|
16
|
+
if not (0.0 <= self.min <= 1.0):
|
|
17
|
+
raise ValueError(f"min must be between 0.0 and 1.0; got {self.min}")
|
|
18
|
+
if not (0.0 <= self.max <= 1.0):
|
|
19
|
+
raise ValueError(f"max must be between 0.0 and 1.0; got {self.max}")
|
|
20
|
+
if self.min > self.max:
|
|
21
|
+
raise ValueError(f"min ({self.min}) cannot be greater than max ({self.max})")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class AgentManifest:
|
|
26
|
+
"""Manifest describing an agent's capabilities and constraints."""
|
|
27
|
+
|
|
28
|
+
ac_version: str
|
|
29
|
+
agent_id: str
|
|
30
|
+
capabilities: List[str]
|
|
31
|
+
refuses: List[str] = field(default_factory=list)
|
|
32
|
+
confidence_range: Optional[ConfidenceRange] = None
|
|
33
|
+
max_context_tokens: Optional[int] = None
|
|
34
|
+
contact: Optional[str] = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def parse_manifest(data: Union[str, Dict[str, Any]]) -> Optional[AgentManifest]:
|
|
38
|
+
"""
|
|
39
|
+
Parses a JSON string or dictionary into an AgentManifest object.
|
|
40
|
+
Returns None on any error or if required fields are missing.
|
|
41
|
+
"""
|
|
42
|
+
try:
|
|
43
|
+
if isinstance(data, str):
|
|
44
|
+
raw_dict = json.loads(data)
|
|
45
|
+
else:
|
|
46
|
+
raw_dict = data
|
|
47
|
+
|
|
48
|
+
if not isinstance(raw_dict, dict):
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
# Check required fields
|
|
52
|
+
if (
|
|
53
|
+
"ac_version" not in raw_dict
|
|
54
|
+
or "agent_id" not in raw_dict
|
|
55
|
+
or "capabilities" not in raw_dict
|
|
56
|
+
):
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
conf_range = None
|
|
60
|
+
raw_conf = raw_dict.get("confidence_range")
|
|
61
|
+
if isinstance(raw_conf, dict):
|
|
62
|
+
try:
|
|
63
|
+
conf_range = ConfidenceRange(
|
|
64
|
+
min=float(raw_conf.get("min", 0.0)),
|
|
65
|
+
max=float(raw_conf.get("max", 1.0)),
|
|
66
|
+
)
|
|
67
|
+
except (ValueError, TypeError):
|
|
68
|
+
# If confidence range is invalid, we continue without it
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
return AgentManifest(
|
|
72
|
+
ac_version=str(raw_dict["ac_version"]),
|
|
73
|
+
agent_id=str(raw_dict["agent_id"]),
|
|
74
|
+
capabilities=list(raw_dict["capabilities"]),
|
|
75
|
+
refuses=list(raw_dict.get("refuses", [])),
|
|
76
|
+
confidence_range=conf_range,
|
|
77
|
+
max_context_tokens=raw_dict.get("max_context_tokens"),
|
|
78
|
+
contact=raw_dict.get("contact"),
|
|
79
|
+
)
|
|
80
|
+
except Exception:
|
|
81
|
+
# Never raise, return None on any failure
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def emit_manifest(manifest: AgentManifest) -> str:
|
|
86
|
+
"""
|
|
87
|
+
Converts an AgentManifest to a JSON string.
|
|
88
|
+
Omits fields with None values.
|
|
89
|
+
"""
|
|
90
|
+
raw = asdict(manifest)
|
|
91
|
+
|
|
92
|
+
def _remove_none(obj: Any) -> Any:
|
|
93
|
+
if isinstance(obj, dict):
|
|
94
|
+
return {k: _remove_none(v) for k, v in obj.items() if v is not None}
|
|
95
|
+
return obj
|
|
96
|
+
|
|
97
|
+
clean = _remove_none(raw)
|
|
98
|
+
return json.dumps(clean, indent=2)
|
|
@@ -4,6 +4,7 @@ src/agent_checkpoint/__init__.py
|
|
|
4
4
|
src/agent_checkpoint/codec.py
|
|
5
5
|
src/agent_checkpoint/factory.py
|
|
6
6
|
src/agent_checkpoint/manager.py
|
|
7
|
+
src/agent_checkpoint/manifest.py
|
|
7
8
|
src/agent_checkpoint/models.py
|
|
8
9
|
src/agent_checkpoint/storage.py
|
|
9
10
|
src/agent_checkpoint/validator.py
|
|
@@ -14,4 +15,5 @@ src/agent_checkpoint_framework.egg-info/requires.txt
|
|
|
14
15
|
src/agent_checkpoint_framework.egg-info/top_level.txt
|
|
15
16
|
tests/test_codec.py
|
|
16
17
|
tests/test_factory.py
|
|
17
|
-
tests/test_manager.py
|
|
18
|
+
tests/test_manager.py
|
|
19
|
+
tests/test_manifest.py
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from agent_checkpoint import AgentManifest, emit_manifest, parse_manifest
|
|
6
|
+
from agent_checkpoint.manifest import ConfidenceRange
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture
|
|
10
|
+
def valid_manifest_dict():
|
|
11
|
+
return {
|
|
12
|
+
"ac_version": "0.1.0",
|
|
13
|
+
"agent_id": "reviewer-gpt-4",
|
|
14
|
+
"capabilities": ["code-review", "unit-testing"],
|
|
15
|
+
"confidence_range": {"min": 0.6, "max": 0.9},
|
|
16
|
+
"contact": "support@example.com",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_parse_manifest_valid_dict(valid_manifest_dict):
|
|
21
|
+
manifest = parse_manifest(valid_manifest_dict)
|
|
22
|
+
assert isinstance(manifest, AgentManifest)
|
|
23
|
+
assert manifest.agent_id == "reviewer-gpt-4"
|
|
24
|
+
assert manifest.capabilities == ["code-review", "unit-testing"]
|
|
25
|
+
assert manifest.confidence_range.min == 0.6
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_parse_manifest_valid_json_string(valid_manifest_dict):
|
|
29
|
+
json_str = json.dumps(valid_manifest_dict)
|
|
30
|
+
manifest = parse_manifest(json_str)
|
|
31
|
+
assert isinstance(manifest, AgentManifest)
|
|
32
|
+
assert manifest.agent_id == "reviewer-gpt-4"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_parse_manifest_missing_agent_id(valid_manifest_dict):
|
|
36
|
+
data = valid_manifest_dict.copy()
|
|
37
|
+
del data["agent_id"]
|
|
38
|
+
# Should return None, never raise
|
|
39
|
+
manifest = parse_manifest(data)
|
|
40
|
+
assert manifest is None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_parse_manifest_invalid_json():
|
|
44
|
+
manifest = parse_manifest("{invalid json")
|
|
45
|
+
assert manifest is None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_emit_manifest_produces_valid_json(valid_manifest_dict):
|
|
49
|
+
manifest = parse_manifest(valid_manifest_dict)
|
|
50
|
+
json_str = emit_manifest(manifest)
|
|
51
|
+
data = json.loads(json_str)
|
|
52
|
+
assert data["agent_id"] == "reviewer-gpt-4"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_emit_manifest_omits_none_fields(valid_manifest_dict):
|
|
56
|
+
data = valid_manifest_dict.copy()
|
|
57
|
+
if "contact" in data:
|
|
58
|
+
del data["contact"]
|
|
59
|
+
manifest = parse_manifest(data)
|
|
60
|
+
json_str = emit_manifest(manifest)
|
|
61
|
+
assert "contact" not in json_str
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_manifest_round_trip(valid_manifest_dict):
|
|
65
|
+
manifest = parse_manifest(valid_manifest_dict)
|
|
66
|
+
json_str = emit_manifest(manifest)
|
|
67
|
+
round_tripped = parse_manifest(json_str)
|
|
68
|
+
assert manifest == round_tripped
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_confidence_range_validation():
|
|
72
|
+
# Valid
|
|
73
|
+
ConfidenceRange(min=0.1, max=0.5)
|
|
74
|
+
# Invalid: min > max
|
|
75
|
+
with pytest.raises(ValueError, match="cannot be greater than max"):
|
|
76
|
+
ConfidenceRange(min=0.8, max=0.2)
|
|
77
|
+
# Invalid: out of bounds
|
|
78
|
+
with pytest.raises(ValueError, match="between 0.0 and 1.0"):
|
|
79
|
+
ConfidenceRange(min=-0.1, max=0.5)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_agent_agnostic_usage():
|
|
83
|
+
"""
|
|
84
|
+
Demonstrates that an agent doesn't need to know about manifests
|
|
85
|
+
to receive and use checkpoints.
|
|
86
|
+
"""
|
|
87
|
+
# A checkpoint with a capability hint
|
|
88
|
+
checkpoint_data = {
|
|
89
|
+
"ac_version": "0.1.0",
|
|
90
|
+
"checkpoint_id": "a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d",
|
|
91
|
+
"task_id": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
|
|
92
|
+
"agent_id": "source-agent",
|
|
93
|
+
"emitted_at": "2026-06-16T14:30:00Z",
|
|
94
|
+
"status": "partial",
|
|
95
|
+
"task_summary": "Review code",
|
|
96
|
+
"executor_hint": "capability:code-review",
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# An agent receives this. It doesn't call parse_manifest.
|
|
100
|
+
# It just looks at the hint.
|
|
101
|
+
hint = checkpoint_data.get("executor_hint")
|
|
102
|
+
assert hint == "capability:code-review"
|
|
103
|
+
|
|
104
|
+
# It can decide to handle it based on its own internal logic
|
|
105
|
+
# without ever seeing an agent-caps.json manifest.
|
|
106
|
+
if hint.startswith("capability:"):
|
|
107
|
+
capability = hint.split(":")[1]
|
|
108
|
+
assert capability == "code-review"
|
|
109
|
+
# Proceed to handle...
|
|
File without changes
|
|
File without changes
|
{agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint/codec.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint/models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|