ygo74-agent-runtime-agentframework 0.8.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.
@@ -0,0 +1,23 @@
1
+ """Microsoft Agent Framework 1.18 native execution and output integration."""
2
+
3
+ from .binding import AgentFrameworkSession
4
+ from .conversion import (
5
+ ConversionDecision,
6
+ ConversionReason,
7
+ ConversionStatus,
8
+ OutputConversion,
9
+ UpdateConversion,
10
+ )
11
+ from .output_adapter import AgentFrameworkOutputAdapter
12
+ from .stream_adapter import AgentFrameworkStreamAdapter
13
+
14
+ __all__ = [
15
+ "AgentFrameworkOutputAdapter",
16
+ "AgentFrameworkSession",
17
+ "AgentFrameworkStreamAdapter",
18
+ "ConversionDecision",
19
+ "ConversionReason",
20
+ "ConversionStatus",
21
+ "OutputConversion",
22
+ "UpdateConversion",
23
+ ]
@@ -0,0 +1,193 @@
1
+ """Optional MAF approval-to-ticket bridge; domain gates remain injected."""
2
+
3
+ import json
4
+ from collections.abc import Mapping, Sequence
5
+ from dataclasses import dataclass
6
+ from typing import Any, Protocol
7
+
8
+ from agent_framework import AgentResponse, Content, Message
9
+ from ygo74.agent_runtime.domains.errors import DomainError
10
+ from ygo74.agent_runtime.domains.humanapproval.confirmation import (
11
+ ConfirmationDecision,
12
+ ConfirmationRequest,
13
+ )
14
+ from ygo74.agent_runtime.domains.humanapproval.confirmed_operations import (
15
+ ConfirmationPresenter,
16
+ )
17
+ from ygo74.agent_runtime.domains.humanapproval.tickets import (
18
+ ConfirmationTicket,
19
+ PendingConfirmationStore,
20
+ )
21
+ from ygo74.agent_runtime.domains.security.security_errors import SecurityError
22
+ from ygo74.agent_runtime.domains.security.user_context import UserContext
23
+
24
+ APPROVAL_REQUEST_TYPE = "function_approval_request"
25
+
26
+
27
+ class PendingToolApproval:
28
+ """A suspended native call with exact proposed arguments.
29
+
30
+ Args:
31
+ content: MAF function approval request; malformed arguments are rejected.
32
+ """
33
+
34
+ def __init__(self, content: Content) -> None:
35
+ """Retain the request.
36
+
37
+ Args:
38
+ content: Native request to answer.
39
+ """
40
+ self._content = content
41
+
42
+ @property
43
+ def content(self) -> Content:
44
+ """Return the original native request."""
45
+ return self._content
46
+
47
+ @property
48
+ def tool_name(self) -> str:
49
+ """Return the requested native tool name."""
50
+ call = self._content.function_call
51
+ return "" if call is None else call.name or ""
52
+
53
+ @property
54
+ def arguments(self) -> Mapping[str, Any]:
55
+ """Decode exact wire arguments, refusing malformed JSON rather than guessing."""
56
+ call = self._content.function_call
57
+ raw = None if call is None else call.arguments
58
+ if raw is None:
59
+ return {}
60
+ decoded = json.loads(raw) if isinstance(raw, str) else raw
61
+ if not isinstance(decoded, Mapping):
62
+ raise TypeError("approval arguments must be a JSON object")
63
+ return dict(decoded)
64
+
65
+ def answer(self, *, approved: bool) -> Content:
66
+ """Answer this specific request.
67
+
68
+ Args:
69
+ approved: Deterministic resolver decision, never inferred from text.
70
+ """
71
+ return self._content.to_function_approval_response(approved=approved)
72
+
73
+
74
+ class MafApprovalTranslator:
75
+ """Translate native approval state without interpreting authorization."""
76
+
77
+ def pending_approvals(self, response: AgentResponse[Any]) -> tuple[PendingToolApproval, ...]:
78
+ """Read suspended calls.
79
+
80
+ Args:
81
+ response: Completed native sub-run.
82
+ """
83
+ return tuple(PendingToolApproval(item) for item in response.user_input_requests
84
+ if item.type == APPROVAL_REQUEST_TYPE)
85
+
86
+ def answer_message(self, answers: Sequence[Content]) -> Message:
87
+ """Produce MAF's complete batch response.
88
+
89
+ Args:
90
+ answers: One native answer per suspended request.
91
+ """
92
+ return Message(role="user", contents=list(answers))
93
+
94
+ def decision_for(self, request: ConfirmationRequest, user: UserContext, *, approved: bool) -> ConfirmationDecision:
95
+ """Produce the corresponding deterministic domain decision.
96
+
97
+ Args:
98
+ request: Domain confirmation being answered.
99
+ user: Acting user supplied by the host.
100
+ approved: Explicit decision.
101
+ """
102
+ return ConfirmationDecision(request_id=request.request_id, approved=approved, decided_by=user.user_id)
103
+
104
+
105
+ @dataclass(frozen=True, slots=True)
106
+ class ApprovalRound:
107
+ """Answers for a complete native batch.
108
+
109
+ Args:
110
+ answers: Native responses in request order.
111
+ questioned_user: Whether a person was actually prompted.
112
+ """
113
+
114
+ answers: tuple[Content, ...]
115
+ questioned_user: bool
116
+
117
+
118
+ class ApprovalResolver(Protocol):
119
+ """Optional domain or console resolver; not required for simple agents."""
120
+
121
+ def will_question(self, pending: Sequence[PendingToolApproval]) -> bool:
122
+ """Classify a batch before charging the prompt budget.
123
+
124
+ Args:
125
+ pending: Suspended calls.
126
+ """
127
+ ...
128
+
129
+ async def resolve(self, pending: Sequence[PendingToolApproval]) -> ApprovalRound:
130
+ """Resolve every suspended call using deterministic policy.
131
+
132
+ Args:
133
+ pending: Suspended calls.
134
+ """
135
+ ...
136
+
137
+
138
+ class TicketApprovalResolver:
139
+ """Decline native execution and retain tickets for later explicit commands.
140
+
141
+ Args:
142
+ presenter: Domain-owned authorization and confirmation description.
143
+ store: Conversation-scoped ticket storage.
144
+ user: Explicit acting identity.
145
+ conversation_id: Ticket partition within the identity.
146
+ """
147
+
148
+ def __init__(self, presenter: ConfirmationPresenter, store: PendingConfirmationStore,
149
+ user: UserContext, *, conversation_id: str) -> None:
150
+ """Retain injected domain collaborators.
151
+
152
+ Args:
153
+ presenter: Domain-owned authorization and description.
154
+ store: Ticket persistence.
155
+ user: Explicit identity.
156
+ conversation_id: Ticket partition.
157
+ """
158
+ self._presenter, self._store, self._user = presenter, store, user
159
+ self._conversation_id = conversation_id
160
+
161
+ def will_question(self, pending: Sequence[PendingToolApproval]) -> bool:
162
+ """Never block the HTTP turn for a human.
163
+
164
+ Args:
165
+ pending: Calls to defer.
166
+ """
167
+ return False
168
+
169
+ async def resolve(self, pending: Sequence[PendingToolApproval]) -> ApprovalRound:
170
+ """Store readable, authorized tickets and decline all calls to MAF.
171
+
172
+ Args:
173
+ pending: Complete suspended batch.
174
+ """
175
+ for approval in pending:
176
+ await self._raise_ticket(approval)
177
+ return ApprovalRound(tuple(approval.answer(approved=False) for approval in pending), False)
178
+
179
+ async def _raise_ticket(self, approval: PendingToolApproval) -> None:
180
+ """Only offer confirmations passing the injected domain presenter.
181
+
182
+ Args:
183
+ approval: Suspended call whose exact arguments must be preserved.
184
+ """
185
+ try:
186
+ arguments = approval.arguments
187
+ request = await self._presenter.present(approval.tool_name, arguments, self._user)
188
+ except (DomainError, SecurityError, ValueError, TypeError):
189
+ return
190
+ self._store.issue(ConfirmationTicket.issue(
191
+ subject=self._user.user_id, conversation_id=self._conversation_id,
192
+ tool_name=approval.tool_name, request=request, arguments=arguments,
193
+ ))