macp-sdk-python 0.2.1__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.
- macp_sdk/__init__.py +207 -0
- macp_sdk/_logging.py +16 -0
- macp_sdk/agent/__init__.py +64 -0
- macp_sdk/agent/dispatcher.py +105 -0
- macp_sdk/agent/participant.py +404 -0
- macp_sdk/agent/runner.py +100 -0
- macp_sdk/agent/strategies.py +312 -0
- macp_sdk/agent/transports.py +153 -0
- macp_sdk/agent/types.py +64 -0
- macp_sdk/auth.py +74 -0
- macp_sdk/base_projection.py +52 -0
- macp_sdk/base_session.py +169 -0
- macp_sdk/client.py +625 -0
- macp_sdk/constants.py +20 -0
- macp_sdk/decision.py +154 -0
- macp_sdk/envelope.py +180 -0
- macp_sdk/errors.py +101 -0
- macp_sdk/handoff.py +241 -0
- macp_sdk/policy.py +292 -0
- macp_sdk/projections.py +183 -0
- macp_sdk/proposal.py +305 -0
- macp_sdk/proto_registry.py +150 -0
- macp_sdk/py.typed +0 -0
- macp_sdk/quorum.py +258 -0
- macp_sdk/retry.py +64 -0
- macp_sdk/task.py +368 -0
- macp_sdk/validation.py +117 -0
- macp_sdk/watchers.py +122 -0
- macp_sdk_python-0.2.1.dist-info/METADATA +165 -0
- macp_sdk_python-0.2.1.dist-info/RECORD +33 -0
- macp_sdk_python-0.2.1.dist-info/WHEEL +5 -0
- macp_sdk_python-0.2.1.dist-info/licenses/LICENSE +201 -0
- macp_sdk_python-0.2.1.dist-info/top_level.txt +1 -0
macp_sdk/__init__.py
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
from ._logging import configure_logging
|
|
2
|
+
from .auth import AuthConfig
|
|
3
|
+
from .base_projection import BaseProjection
|
|
4
|
+
from .base_session import BaseSession
|
|
5
|
+
from .client import MacpClient, MacpStream
|
|
6
|
+
from .constants import (
|
|
7
|
+
DEFAULT_CONFIGURATION_VERSION,
|
|
8
|
+
DEFAULT_MODE_VERSION,
|
|
9
|
+
DEFAULT_POLICY_VERSION,
|
|
10
|
+
MACP_VERSION,
|
|
11
|
+
MODE_DECISION,
|
|
12
|
+
MODE_HANDOFF,
|
|
13
|
+
MODE_MULTI_ROUND,
|
|
14
|
+
MODE_PROPOSAL,
|
|
15
|
+
MODE_QUORUM,
|
|
16
|
+
MODE_TASK,
|
|
17
|
+
STANDARD_MODES,
|
|
18
|
+
)
|
|
19
|
+
from .decision import DecisionSession
|
|
20
|
+
from .envelope import (
|
|
21
|
+
build_commitment_payload,
|
|
22
|
+
build_envelope,
|
|
23
|
+
build_progress_payload,
|
|
24
|
+
build_root,
|
|
25
|
+
build_session_start_payload,
|
|
26
|
+
build_signal_payload,
|
|
27
|
+
infer_outcome_positive,
|
|
28
|
+
new_commitment_id,
|
|
29
|
+
new_message_id,
|
|
30
|
+
new_session_id,
|
|
31
|
+
serialize_message,
|
|
32
|
+
)
|
|
33
|
+
from .errors import (
|
|
34
|
+
DUPLICATE_MESSAGE,
|
|
35
|
+
FORBIDDEN,
|
|
36
|
+
INTERNAL_ERROR,
|
|
37
|
+
INVALID_ENVELOPE,
|
|
38
|
+
INVALID_POLICY_DEFINITION,
|
|
39
|
+
INVALID_SESSION_ID,
|
|
40
|
+
MODE_NOT_SUPPORTED,
|
|
41
|
+
PAYLOAD_TOO_LARGE,
|
|
42
|
+
POLICY_DENIED,
|
|
43
|
+
RATE_LIMITED,
|
|
44
|
+
SESSION_ALREADY_EXISTS,
|
|
45
|
+
SESSION_NOT_FOUND,
|
|
46
|
+
SESSION_NOT_OPEN,
|
|
47
|
+
UNAUTHENTICATED,
|
|
48
|
+
UNKNOWN_POLICY_VERSION,
|
|
49
|
+
UNSUPPORTED_PROTOCOL_VERSION,
|
|
50
|
+
AckFailure,
|
|
51
|
+
MacpAckError,
|
|
52
|
+
MacpIdentityMismatchError,
|
|
53
|
+
MacpRetryError,
|
|
54
|
+
MacpSdkError,
|
|
55
|
+
MacpSessionError,
|
|
56
|
+
MacpTimeoutError,
|
|
57
|
+
MacpTransportError,
|
|
58
|
+
)
|
|
59
|
+
from .handoff import HandoffProjection, HandoffRecord, HandoffSession
|
|
60
|
+
from .policy import (
|
|
61
|
+
AbstentionRules,
|
|
62
|
+
CommitmentRules,
|
|
63
|
+
CounterProposalRules,
|
|
64
|
+
EvaluationRules,
|
|
65
|
+
HandoffAcceptanceRules,
|
|
66
|
+
ObjectionHandlingRules,
|
|
67
|
+
ProposalAcceptanceRules,
|
|
68
|
+
QuorumThreshold,
|
|
69
|
+
RejectionRules,
|
|
70
|
+
TaskAssignmentRules,
|
|
71
|
+
TaskCompletionRules,
|
|
72
|
+
VotingRules,
|
|
73
|
+
build_decision_policy,
|
|
74
|
+
build_handoff_policy,
|
|
75
|
+
build_proposal_policy,
|
|
76
|
+
build_quorum_policy,
|
|
77
|
+
build_task_policy,
|
|
78
|
+
)
|
|
79
|
+
from .projections import DecisionProjection
|
|
80
|
+
from .proposal import ProposalProjection, ProposalSession, RejectRecord
|
|
81
|
+
from .proto_registry import ProtoRegistry
|
|
82
|
+
from .quorum import QuorumProjection, QuorumSession
|
|
83
|
+
from .retry import RetryPolicy, retry_send
|
|
84
|
+
from .task import TaskProjection, TaskSession
|
|
85
|
+
from .validation import (
|
|
86
|
+
validate_confidence,
|
|
87
|
+
validate_participant_count,
|
|
88
|
+
validate_participants,
|
|
89
|
+
validate_recommendation,
|
|
90
|
+
validate_required_field,
|
|
91
|
+
validate_session_id,
|
|
92
|
+
validate_session_start,
|
|
93
|
+
validate_severity,
|
|
94
|
+
validate_signal_type,
|
|
95
|
+
validate_ttl_ms,
|
|
96
|
+
validate_vote,
|
|
97
|
+
)
|
|
98
|
+
from .watchers import (
|
|
99
|
+
ModeRegistryWatcher,
|
|
100
|
+
PolicyChange,
|
|
101
|
+
PolicyWatcher,
|
|
102
|
+
RootsWatcher,
|
|
103
|
+
SignalWatcher,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
__all__ = [
|
|
107
|
+
"DEFAULT_CONFIGURATION_VERSION",
|
|
108
|
+
"DEFAULT_MODE_VERSION",
|
|
109
|
+
"DEFAULT_POLICY_VERSION",
|
|
110
|
+
"DUPLICATE_MESSAGE",
|
|
111
|
+
"FORBIDDEN",
|
|
112
|
+
"INTERNAL_ERROR",
|
|
113
|
+
"INVALID_ENVELOPE",
|
|
114
|
+
"INVALID_POLICY_DEFINITION",
|
|
115
|
+
"INVALID_SESSION_ID",
|
|
116
|
+
"MACP_VERSION",
|
|
117
|
+
"MODE_DECISION",
|
|
118
|
+
"MODE_HANDOFF",
|
|
119
|
+
"MODE_MULTI_ROUND",
|
|
120
|
+
"MODE_NOT_SUPPORTED",
|
|
121
|
+
"MODE_PROPOSAL",
|
|
122
|
+
"MODE_QUORUM",
|
|
123
|
+
"MODE_TASK",
|
|
124
|
+
"PAYLOAD_TOO_LARGE",
|
|
125
|
+
"POLICY_DENIED",
|
|
126
|
+
"RATE_LIMITED",
|
|
127
|
+
"SESSION_ALREADY_EXISTS",
|
|
128
|
+
"SESSION_NOT_FOUND",
|
|
129
|
+
"SESSION_NOT_OPEN",
|
|
130
|
+
"STANDARD_MODES",
|
|
131
|
+
"UNAUTHENTICATED",
|
|
132
|
+
"UNKNOWN_POLICY_VERSION",
|
|
133
|
+
"UNSUPPORTED_PROTOCOL_VERSION",
|
|
134
|
+
"AbstentionRules",
|
|
135
|
+
"AckFailure",
|
|
136
|
+
"AuthConfig",
|
|
137
|
+
"BaseProjection",
|
|
138
|
+
"BaseSession",
|
|
139
|
+
"CommitmentRules",
|
|
140
|
+
"CounterProposalRules",
|
|
141
|
+
"DecisionProjection",
|
|
142
|
+
"DecisionSession",
|
|
143
|
+
"EvaluationRules",
|
|
144
|
+
"HandoffAcceptanceRules",
|
|
145
|
+
"HandoffProjection",
|
|
146
|
+
"HandoffRecord",
|
|
147
|
+
"HandoffSession",
|
|
148
|
+
"MacpAckError",
|
|
149
|
+
"MacpClient",
|
|
150
|
+
"MacpIdentityMismatchError",
|
|
151
|
+
"MacpRetryError",
|
|
152
|
+
"MacpSdkError",
|
|
153
|
+
"MacpSessionError",
|
|
154
|
+
"MacpStream",
|
|
155
|
+
"MacpTimeoutError",
|
|
156
|
+
"MacpTransportError",
|
|
157
|
+
"ModeRegistryWatcher",
|
|
158
|
+
"ObjectionHandlingRules",
|
|
159
|
+
"PolicyChange",
|
|
160
|
+
"PolicyWatcher",
|
|
161
|
+
"ProposalAcceptanceRules",
|
|
162
|
+
"ProposalProjection",
|
|
163
|
+
"ProposalSession",
|
|
164
|
+
"ProtoRegistry",
|
|
165
|
+
"QuorumProjection",
|
|
166
|
+
"QuorumSession",
|
|
167
|
+
"QuorumThreshold",
|
|
168
|
+
"RejectRecord",
|
|
169
|
+
"RejectionRules",
|
|
170
|
+
"RetryPolicy",
|
|
171
|
+
"RootsWatcher",
|
|
172
|
+
"SignalWatcher",
|
|
173
|
+
"TaskAssignmentRules",
|
|
174
|
+
"TaskCompletionRules",
|
|
175
|
+
"TaskProjection",
|
|
176
|
+
"TaskSession",
|
|
177
|
+
"VotingRules",
|
|
178
|
+
"build_commitment_payload",
|
|
179
|
+
"build_decision_policy",
|
|
180
|
+
"build_envelope",
|
|
181
|
+
"build_handoff_policy",
|
|
182
|
+
"build_progress_payload",
|
|
183
|
+
"build_proposal_policy",
|
|
184
|
+
"build_quorum_policy",
|
|
185
|
+
"build_root",
|
|
186
|
+
"build_session_start_payload",
|
|
187
|
+
"build_signal_payload",
|
|
188
|
+
"build_task_policy",
|
|
189
|
+
"configure_logging",
|
|
190
|
+
"infer_outcome_positive",
|
|
191
|
+
"new_commitment_id",
|
|
192
|
+
"new_message_id",
|
|
193
|
+
"new_session_id",
|
|
194
|
+
"retry_send",
|
|
195
|
+
"serialize_message",
|
|
196
|
+
"validate_confidence",
|
|
197
|
+
"validate_participant_count",
|
|
198
|
+
"validate_participants",
|
|
199
|
+
"validate_recommendation",
|
|
200
|
+
"validate_required_field",
|
|
201
|
+
"validate_session_id",
|
|
202
|
+
"validate_session_start",
|
|
203
|
+
"validate_severity",
|
|
204
|
+
"validate_signal_type",
|
|
205
|
+
"validate_ttl_ms",
|
|
206
|
+
"validate_vote",
|
|
207
|
+
]
|
macp_sdk/_logging.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger("macp_sdk")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def configure_logging(
|
|
9
|
+
level: int = logging.INFO,
|
|
10
|
+
fmt: str = "%(asctime)s %(name)s %(levelname)s %(message)s",
|
|
11
|
+
) -> None:
|
|
12
|
+
"""Configure the macp_sdk logger with a stream handler."""
|
|
13
|
+
handler = logging.StreamHandler()
|
|
14
|
+
handler.setFormatter(logging.Formatter(fmt))
|
|
15
|
+
logger.addHandler(handler)
|
|
16
|
+
logger.setLevel(level)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from .dispatcher import Dispatcher
|
|
2
|
+
from .participant import Participant, ParticipantActions
|
|
3
|
+
from .runner import from_bootstrap
|
|
4
|
+
from .strategies import (
|
|
5
|
+
CommitmentDecision,
|
|
6
|
+
CommitmentStrategy,
|
|
7
|
+
EvaluationResult,
|
|
8
|
+
EvaluationStrategy,
|
|
9
|
+
VoteDecision,
|
|
10
|
+
VotingStrategy,
|
|
11
|
+
commitment_handler,
|
|
12
|
+
evaluation_handler,
|
|
13
|
+
function_committer,
|
|
14
|
+
function_evaluator,
|
|
15
|
+
function_voter,
|
|
16
|
+
majority_committer,
|
|
17
|
+
majority_voter,
|
|
18
|
+
voting_handler,
|
|
19
|
+
)
|
|
20
|
+
from .transports import (
|
|
21
|
+
GrpcTransportAdapter,
|
|
22
|
+
HttpTransportAdapter,
|
|
23
|
+
TransportAdapter,
|
|
24
|
+
)
|
|
25
|
+
from .types import (
|
|
26
|
+
HandlerContext,
|
|
27
|
+
IncomingMessage,
|
|
28
|
+
MessageHandler,
|
|
29
|
+
PhaseChangeHandler,
|
|
30
|
+
SessionInfo,
|
|
31
|
+
TerminalHandler,
|
|
32
|
+
TerminalResult,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"CommitmentDecision",
|
|
37
|
+
"CommitmentStrategy",
|
|
38
|
+
"Dispatcher",
|
|
39
|
+
"EvaluationResult",
|
|
40
|
+
"EvaluationStrategy",
|
|
41
|
+
"GrpcTransportAdapter",
|
|
42
|
+
"HandlerContext",
|
|
43
|
+
"HttpTransportAdapter",
|
|
44
|
+
"IncomingMessage",
|
|
45
|
+
"MessageHandler",
|
|
46
|
+
"Participant",
|
|
47
|
+
"ParticipantActions",
|
|
48
|
+
"PhaseChangeHandler",
|
|
49
|
+
"SessionInfo",
|
|
50
|
+
"TerminalHandler",
|
|
51
|
+
"TerminalResult",
|
|
52
|
+
"TransportAdapter",
|
|
53
|
+
"VoteDecision",
|
|
54
|
+
"VotingStrategy",
|
|
55
|
+
"commitment_handler",
|
|
56
|
+
"evaluation_handler",
|
|
57
|
+
"from_bootstrap",
|
|
58
|
+
"function_committer",
|
|
59
|
+
"function_evaluator",
|
|
60
|
+
"function_voter",
|
|
61
|
+
"majority_committer",
|
|
62
|
+
"majority_voter",
|
|
63
|
+
"voting_handler",
|
|
64
|
+
]
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
|
|
5
|
+
from .._logging import logger
|
|
6
|
+
from .types import (
|
|
7
|
+
HandlerContext,
|
|
8
|
+
IncomingMessage,
|
|
9
|
+
MessageHandler,
|
|
10
|
+
PhaseChangeHandler,
|
|
11
|
+
TerminalHandler,
|
|
12
|
+
TerminalResult,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Dispatcher:
|
|
17
|
+
"""Routes incoming MACP messages to registered handlers.
|
|
18
|
+
|
|
19
|
+
Supports per-message-type handlers, per-phase-change handlers,
|
|
20
|
+
wildcard ``'*'`` handlers (invoked for every event), and a single
|
|
21
|
+
terminal handler.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self) -> None:
|
|
25
|
+
self._handlers: dict[str, list[MessageHandler]] = defaultdict(list)
|
|
26
|
+
self._wildcard_handlers: list[MessageHandler] = []
|
|
27
|
+
self._phase_handlers: dict[str, list[PhaseChangeHandler]] = defaultdict(list)
|
|
28
|
+
self._wildcard_phase_handlers: list[PhaseChangeHandler] = []
|
|
29
|
+
self._terminal_handler: TerminalHandler | None = None
|
|
30
|
+
|
|
31
|
+
def on(self, message_type: str, handler: MessageHandler) -> None:
|
|
32
|
+
"""Register a handler for a specific message type.
|
|
33
|
+
|
|
34
|
+
Use ``'*'`` as the message_type to register a wildcard handler that
|
|
35
|
+
is invoked for every message after type-specific handlers.
|
|
36
|
+
"""
|
|
37
|
+
if message_type == "*":
|
|
38
|
+
self._wildcard_handlers.append(handler)
|
|
39
|
+
else:
|
|
40
|
+
self._handlers[message_type].append(handler)
|
|
41
|
+
|
|
42
|
+
def on_phase_change(self, phase: str, handler: PhaseChangeHandler) -> None:
|
|
43
|
+
"""Register a handler for a specific phase transition.
|
|
44
|
+
|
|
45
|
+
Use ``'*'`` as the phase to register a wildcard handler that is
|
|
46
|
+
invoked for every phase change after phase-specific handlers.
|
|
47
|
+
"""
|
|
48
|
+
if phase == "*":
|
|
49
|
+
self._wildcard_phase_handlers.append(handler)
|
|
50
|
+
else:
|
|
51
|
+
self._phase_handlers[phase].append(handler)
|
|
52
|
+
|
|
53
|
+
def on_terminal(self, handler: TerminalHandler) -> None:
|
|
54
|
+
"""Register the terminal state handler (only one is allowed)."""
|
|
55
|
+
self._terminal_handler = handler
|
|
56
|
+
|
|
57
|
+
def dispatch(self, message: IncomingMessage, ctx: HandlerContext) -> None:
|
|
58
|
+
"""Dispatch a message to all matching handlers.
|
|
59
|
+
|
|
60
|
+
Type-specific handlers are invoked first (in registration order),
|
|
61
|
+
followed by wildcard ``'*'`` handlers. If no handler matches at
|
|
62
|
+
all, a debug-level log is emitted.
|
|
63
|
+
"""
|
|
64
|
+
handlers = self._handlers.get(message.message_type)
|
|
65
|
+
if not handlers and not self._wildcard_handlers:
|
|
66
|
+
logger.debug("no handler for message_type=%s", message.message_type)
|
|
67
|
+
return
|
|
68
|
+
for handler in handlers or []:
|
|
69
|
+
handler(message, ctx)
|
|
70
|
+
for handler in self._wildcard_handlers:
|
|
71
|
+
handler(message, ctx)
|
|
72
|
+
|
|
73
|
+
def dispatch_phase_change(self, phase: str, ctx: HandlerContext) -> None:
|
|
74
|
+
"""Dispatch a phase change event to matching handlers.
|
|
75
|
+
|
|
76
|
+
Phase-specific handlers are invoked first, followed by wildcard
|
|
77
|
+
``'*'`` phase handlers.
|
|
78
|
+
"""
|
|
79
|
+
handlers = self._phase_handlers.get(phase)
|
|
80
|
+
for handler in handlers or []:
|
|
81
|
+
handler(phase, ctx)
|
|
82
|
+
for handler in self._wildcard_phase_handlers:
|
|
83
|
+
handler(phase, ctx)
|
|
84
|
+
|
|
85
|
+
def dispatch_terminal(self, result: TerminalResult) -> None:
|
|
86
|
+
"""Invoke the terminal handler if registered."""
|
|
87
|
+
if self._terminal_handler is not None:
|
|
88
|
+
self._terminal_handler(result)
|
|
89
|
+
else:
|
|
90
|
+
logger.debug("terminal result with no handler: state=%s", result.state)
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def registered_message_types(self) -> list[str]:
|
|
94
|
+
"""Return all message types that have at least one handler."""
|
|
95
|
+
return list(self._handlers.keys())
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def registered_phases(self) -> list[str]:
|
|
99
|
+
"""Return all phases that have at least one handler."""
|
|
100
|
+
return list(self._phase_handlers.keys())
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def has_terminal_handler(self) -> bool:
|
|
104
|
+
"""Return whether a terminal handler is registered."""
|
|
105
|
+
return self._terminal_handler is not None
|