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.
- ygo74/agent_runtime/integrations/agentframework/__init__.py +23 -0
- ygo74/agent_runtime/integrations/agentframework/approval.py +193 -0
- ygo74/agent_runtime/integrations/agentframework/binding.py +544 -0
- ygo74/agent_runtime/integrations/agentframework/conversion.py +75 -0
- ygo74/agent_runtime/integrations/agentframework/output_adapter.py +422 -0
- ygo74/agent_runtime/integrations/agentframework/progress.py +313 -0
- ygo74/agent_runtime/integrations/agentframework/py.typed +0 -0
- ygo74/agent_runtime/integrations/agentframework/stream_adapter.py +190 -0
- ygo74/agent_runtime/integrations/agentframework/worker.py +155 -0
- ygo74_agent_runtime_agentframework-0.8.0.dist-info/METADATA +39 -0
- ygo74_agent_runtime_agentframework-0.8.0.dist-info/RECORD +13 -0
- ygo74_agent_runtime_agentframework-0.8.0.dist-info/WHEEL +5 -0
- ygo74_agent_runtime_agentframework-0.8.0.dist-info/top_level.txt +1 -0
|
@@ -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
|
+
))
|