mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: add agent-framework-hosting-discord channel (#6081)
* Add Discord hosting channel Add an alpha agent-framework-hosting-discord package backed by Discord HTTP Interactions. The channel verifies signed slash-command requests, registers commands, runs hosted agents and ChannelCommand handlers, supports originating response hooks, streams by editing the original interaction response, and can push through Discord channel ids. Factor standard channel response-hook context application into hosting core so both host fan-out and originating channel replies use one helper. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address Discord review chunking feedback Ensure Discord command replies are chunked and streaming preview edits stay under Discord's content limit while final streamed replies continue through the chunked reply path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * small fix in init * updated lock --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
6b822853eb
commit
e8c22caaeb
@@ -7,12 +7,16 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
from agent_framework_hosting import (
|
||||
ChannelContribution,
|
||||
ChannelIdentity,
|
||||
ChannelRequest,
|
||||
ChannelResponseContext,
|
||||
ChannelSession,
|
||||
DurableTaskPayloadMode,
|
||||
HostedRunResult,
|
||||
ResponseTarget,
|
||||
ResponseTargetKind,
|
||||
apply_channel_response_hook,
|
||||
apply_run_hook,
|
||||
)
|
||||
|
||||
@@ -117,6 +121,88 @@ class _DummyTarget:
|
||||
"""
|
||||
|
||||
|
||||
class _DummyChannel:
|
||||
name = "dummy"
|
||||
path = "/dummy"
|
||||
|
||||
def contribute(self, _context: Any) -> ChannelContribution:
|
||||
return ChannelContribution()
|
||||
|
||||
|
||||
class TestApplyChannelResponseHook:
|
||||
async def test_originating_hook_receives_standard_context(self) -> None:
|
||||
request = ChannelRequest(channel="discord", operation="message.create", input="hi")
|
||||
payload = HostedRunResult("original")
|
||||
captured: list[ChannelResponseContext] = []
|
||||
|
||||
async def hook(
|
||||
result: HostedRunResult[Any],
|
||||
*,
|
||||
context: ChannelResponseContext,
|
||||
) -> HostedRunResult[Any]:
|
||||
captured.append(context)
|
||||
return result.replace(result="hooked")
|
||||
|
||||
channel = _DummyChannel()
|
||||
channel.response_hook = hook # type: ignore[attr-defined]
|
||||
|
||||
shaped = await apply_channel_response_hook(channel, payload, request=request, originating=True)
|
||||
|
||||
assert shaped.result == "hooked"
|
||||
assert captured[0].request is request
|
||||
assert captured[0].channel_name == "dummy"
|
||||
assert captured[0].destination_identity is None
|
||||
assert captured[0].originating is True
|
||||
assert captured[0].is_echo is False
|
||||
|
||||
async def test_non_originating_hook_can_clone_before_shaping(self) -> None:
|
||||
request = ChannelRequest(channel="responses", operation="message.create", input="hi")
|
||||
identity = ChannelIdentity(channel="dummy", native_id="user-1")
|
||||
payload = HostedRunResult("original")
|
||||
seen_payloads: list[HostedRunResult[Any]] = []
|
||||
seen_contexts: list[ChannelResponseContext] = []
|
||||
|
||||
def hook(
|
||||
result: HostedRunResult[Any],
|
||||
*,
|
||||
context: ChannelResponseContext,
|
||||
) -> HostedRunResult[Any]:
|
||||
seen_payloads.append(result)
|
||||
seen_contexts.append(context)
|
||||
return result.replace(result="hooked")
|
||||
|
||||
channel = _DummyChannel()
|
||||
channel.response_hook = hook # type: ignore[attr-defined]
|
||||
|
||||
shaped = await apply_channel_response_hook(
|
||||
channel,
|
||||
payload,
|
||||
request=request,
|
||||
destination_identity=identity,
|
||||
originating=False,
|
||||
is_echo=True,
|
||||
clone=True,
|
||||
)
|
||||
|
||||
assert seen_payloads[0] is not payload
|
||||
assert shaped.result == "hooked"
|
||||
assert seen_contexts[0].destination_identity is identity
|
||||
assert seen_contexts[0].originating is False
|
||||
assert seen_contexts[0].is_echo is True
|
||||
|
||||
async def test_missing_hook_returns_payload_or_clone(self) -> None:
|
||||
request = ChannelRequest(channel="responses", operation="message.create", input="hi")
|
||||
payload = HostedRunResult("original")
|
||||
channel = _DummyChannel()
|
||||
|
||||
same = await apply_channel_response_hook(channel, payload, request=request, originating=True)
|
||||
cloned = await apply_channel_response_hook(channel, payload, request=request, originating=True, clone=True)
|
||||
|
||||
assert same is payload
|
||||
assert cloned is not payload
|
||||
assert cloned.result == payload.result
|
||||
|
||||
|
||||
class TestApplyRunHook:
|
||||
"""`apply_run_hook` is the channel-side helper that invokes a
|
||||
`ChannelRunHook` with the standard kwargs (`request` positional,
|
||||
|
||||
Reference in New Issue
Block a user