mppi-agent 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.
- mppi_agent/__init__.py +53 -0
- mppi_agent/checkpoint.py +113 -0
- mppi_agent/circle.py +579 -0
- mppi_agent/cli.py +118 -0
- mppi_agent/client.py +2047 -0
- mppi_agent/discovery.py +77 -0
- mppi_agent/hooks.py +22 -0
- mppi_agent/interfaces.py +73 -0
- mppi_agent/onboarding.py +242 -0
- mppi_agent/py.typed +0 -0
- mppi_agent/state.py +450 -0
- mppi_agent/testing.py +116 -0
- mppi_agent-0.1.0.dist-info/METADATA +144 -0
- mppi_agent-0.1.0.dist-info/RECORD +17 -0
- mppi_agent-0.1.0.dist-info/WHEEL +4 -0
- mppi_agent-0.1.0.dist-info/entry_points.txt +2 -0
- mppi_agent-0.1.0.dist-info/licenses/LICENSE +202 -0
mppi_agent/__init__.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""MPPI agent payment and inference client."""
|
|
2
|
+
|
|
3
|
+
from mppi_protocol import (
|
|
4
|
+
Catalog,
|
|
5
|
+
Rates,
|
|
6
|
+
TokenCounts,
|
|
7
|
+
funding_amount,
|
|
8
|
+
parse_catalog,
|
|
9
|
+
required_coverage,
|
|
10
|
+
)
|
|
11
|
+
from mppi_protocol.signing import LocalSessionSigner
|
|
12
|
+
|
|
13
|
+
from .client import (
|
|
14
|
+
Client,
|
|
15
|
+
ClientError,
|
|
16
|
+
CloseAccountingError,
|
|
17
|
+
CompletionStream,
|
|
18
|
+
InferenceError,
|
|
19
|
+
PaymentError,
|
|
20
|
+
)
|
|
21
|
+
from .discovery import ProviderDiscovery, discover, verify_challenge
|
|
22
|
+
from .hooks import HookError, Hooks, ObserverStats
|
|
23
|
+
from .interfaces import AgentSessions, FundingWallet, OpenResult, SessionSigner
|
|
24
|
+
from .onboarding import OnboardingError, OnboardingResult, onboard
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"HookError",
|
|
28
|
+
"Hooks",
|
|
29
|
+
"ObserverStats",
|
|
30
|
+
"AgentSessions",
|
|
31
|
+
"Catalog",
|
|
32
|
+
"Client",
|
|
33
|
+
"ClientError",
|
|
34
|
+
"CloseAccountingError",
|
|
35
|
+
"CompletionStream",
|
|
36
|
+
"FundingWallet",
|
|
37
|
+
"LocalSessionSigner",
|
|
38
|
+
"InferenceError",
|
|
39
|
+
"OpenResult",
|
|
40
|
+
"OnboardingError",
|
|
41
|
+
"OnboardingResult",
|
|
42
|
+
"PaymentError",
|
|
43
|
+
"ProviderDiscovery",
|
|
44
|
+
"Rates",
|
|
45
|
+
"SessionSigner",
|
|
46
|
+
"TokenCounts",
|
|
47
|
+
"funding_amount",
|
|
48
|
+
"discover",
|
|
49
|
+
"parse_catalog",
|
|
50
|
+
"required_coverage",
|
|
51
|
+
"verify_challenge",
|
|
52
|
+
"onboard",
|
|
53
|
+
]
|
mppi_agent/checkpoint.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Agent-owned local intent journal; no keys, prompts or provider storage.
|
|
2
|
+
|
|
3
|
+
An exclusive lock protects one client's checkpoint for its lifetime. Writes
|
|
4
|
+
replace the complete record atomically after fsync, before economic submission.
|
|
5
|
+
Python's Unix flock and os.replace/fsync semantics are required (macOS/Linux).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import fcntl
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import stat
|
|
14
|
+
import tempfile
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from mppi_protocol.errors import ValidationCode, ValidationError
|
|
19
|
+
|
|
20
|
+
MAX_CHECKPOINT_BYTES = 64 << 20
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _invalid(message: str) -> ValidationError:
|
|
24
|
+
return ValidationError(ValidationCode.INVALID_VALUE, message)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _unique(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
|
|
28
|
+
result: dict[str, Any] = {}
|
|
29
|
+
for key, value in pairs:
|
|
30
|
+
if key in result:
|
|
31
|
+
raise _invalid("duplicate checkpoint field")
|
|
32
|
+
result[key] = value
|
|
33
|
+
return result
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Checkpoint:
|
|
37
|
+
"""One mode-0600, bounded, exclusively owned non-secret agent record."""
|
|
38
|
+
|
|
39
|
+
def __init__(self, path: Path) -> None:
|
|
40
|
+
self.path = Path(path).absolute()
|
|
41
|
+
self._lock: int | None = None
|
|
42
|
+
try:
|
|
43
|
+
self.path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
|
|
44
|
+
lock_path = self.path.with_name(self.path.name + ".lock")
|
|
45
|
+
fd = os.open(lock_path, os.O_RDWR | os.O_CREAT | os.O_NOFOLLOW, 0o600)
|
|
46
|
+
self._lock = fd
|
|
47
|
+
info = os.fstat(fd)
|
|
48
|
+
if not stat.S_ISREG(info.st_mode) or info.st_mode & 0o077:
|
|
49
|
+
raise _invalid("checkpoint lock permissions must be 0600")
|
|
50
|
+
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
51
|
+
self.load()
|
|
52
|
+
except Exception:
|
|
53
|
+
self.close()
|
|
54
|
+
raise _invalid("checkpoint is unavailable, unsafe or already in use") from None
|
|
55
|
+
|
|
56
|
+
def load(self) -> dict[str, Any] | None:
|
|
57
|
+
if self._lock is None:
|
|
58
|
+
raise _invalid("checkpoint is closed")
|
|
59
|
+
try:
|
|
60
|
+
fd = os.open(self.path, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK)
|
|
61
|
+
except FileNotFoundError:
|
|
62
|
+
return None
|
|
63
|
+
try:
|
|
64
|
+
with os.fdopen(fd, "rb") as source:
|
|
65
|
+
info = os.fstat(source.fileno())
|
|
66
|
+
if not stat.S_ISREG(info.st_mode) or info.st_mode & 0o077:
|
|
67
|
+
raise _invalid("checkpoint permissions must be 0600")
|
|
68
|
+
raw = source.read(MAX_CHECKPOINT_BYTES + 1)
|
|
69
|
+
if len(raw) > MAX_CHECKPOINT_BYTES:
|
|
70
|
+
raise _invalid("checkpoint exceeds its byte limit")
|
|
71
|
+
result = json.loads(raw.decode("utf-8"), object_pairs_hook=_unique)
|
|
72
|
+
if (
|
|
73
|
+
not isinstance(result, dict)
|
|
74
|
+
or type(result.get("version")) is not int
|
|
75
|
+
or result["version"] != 1
|
|
76
|
+
):
|
|
77
|
+
raise _invalid("unsupported checkpoint record")
|
|
78
|
+
return result
|
|
79
|
+
except Exception:
|
|
80
|
+
raise _invalid("checkpoint cannot be validated") from None
|
|
81
|
+
|
|
82
|
+
def save(self, record: dict[str, Any]) -> None:
|
|
83
|
+
if self._lock is None or type(record.get("version")) is not int or record["version"] != 1:
|
|
84
|
+
raise _invalid("checkpoint is closed or has an invalid version")
|
|
85
|
+
raw = json.dumps(record, ensure_ascii=True, separators=(",", ":"), allow_nan=False).encode()
|
|
86
|
+
if len(raw) > MAX_CHECKPOINT_BYTES:
|
|
87
|
+
raise _invalid("checkpoint exceeds its byte limit")
|
|
88
|
+
temporary: str | None = None
|
|
89
|
+
try:
|
|
90
|
+
with tempfile.NamedTemporaryFile(
|
|
91
|
+
dir=self.path.parent, prefix="." + self.path.name, delete=False
|
|
92
|
+
) as target:
|
|
93
|
+
temporary = target.name
|
|
94
|
+
target.write(raw)
|
|
95
|
+
target.flush()
|
|
96
|
+
os.fsync(target.fileno())
|
|
97
|
+
os.replace(temporary, self.path)
|
|
98
|
+
temporary = None
|
|
99
|
+
directory = os.open(self.path.parent, os.O_RDONLY | os.O_DIRECTORY)
|
|
100
|
+
try:
|
|
101
|
+
os.fsync(directory)
|
|
102
|
+
finally:
|
|
103
|
+
os.close(directory)
|
|
104
|
+
except Exception:
|
|
105
|
+
raise _invalid("checkpoint could not be durably written") from None
|
|
106
|
+
finally:
|
|
107
|
+
if temporary is not None:
|
|
108
|
+
os.unlink(temporary)
|
|
109
|
+
|
|
110
|
+
def close(self) -> None:
|
|
111
|
+
if self._lock is not None:
|
|
112
|
+
fd, self._lock = self._lock, None
|
|
113
|
+
os.close(fd)
|