-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathtest_copilot_request_session_id_e2e.py
More file actions
138 lines (112 loc) · 4.92 KB
/
Copy pathtest_copilot_request_session_id_e2e.py
File metadata and controls
138 lines (112 loc) · 4.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# --------------------------------------------------------------------------------------------
"""E2E tests asserting the runtime threads its session id into the
CopilotRequestHandler for both CAPI and BYOK sessions.
Mirrors ``nodejs/test/e2e/copilot_request_session_id.e2e.test.ts``. The handler
alone services every model-layer request (no upstream server, no CAPI proxy
acting as the inference endpoint), so the only source of ``ctx.session_id`` is
the runtime's own per-client threading.
"""
from __future__ import annotations
from dataclasses import dataclass
import httpx
import pytest
from copilot import CopilotRequestContext, CopilotRequestHandler
from copilot.session import PermissionHandler
from ._copilot_request_helpers import (
assistant_text,
build_inference_response,
build_non_inference_response,
is_inference_url,
isolated_client_fixture,
)
pytestmark = pytest.mark.asyncio(loop_scope="module")
@dataclass
class _InterceptedRequest:
url: str
session_id: str | None
agent_id: str | None
parent_agent_id: str | None
interaction_type: str | None
class _SessionIdHandler(CopilotRequestHandler):
def __init__(self) -> None:
self.records: list[_InterceptedRequest] = []
async def send_request(
self, request: httpx.Request, ctx: CopilotRequestContext
) -> httpx.Response:
url = str(request.url)
self.records.append(
_InterceptedRequest(
url=url,
session_id=ctx.session_id,
agent_id=ctx.agent_id,
parent_agent_id=ctx.parent_agent_id,
interaction_type=ctx.interaction_type,
)
)
if is_inference_url(url):
return build_inference_response(request)
# Force /responses transport so the inference URL is predictable.
return build_non_inference_response(url, supported_endpoints=["/responses"])
session_id_client = isolated_client_fixture(_SessionIdHandler)
def _assert_agent_metadata(record: _InterceptedRequest) -> None:
assert record.agent_id
assert record.interaction_type
class TestCopilotRequestSessionId:
capi_session_id: str | None = None
async def test_threads_session_id_into_capi_session(self, session_id_client):
client, handler = session_id_client
await client.start()
baseline = len(handler.records)
session = await client.create_session(on_permission_request=PermissionHandler.approve_all)
TestCopilotRequestSessionId.capi_session_id = session.session_id
text = ""
try:
result = await session.send_and_wait("Say OK.")
text = assistant_text(result)
finally:
await session.disconnect()
inference = [r for r in handler.records[baseline:] if is_inference_url(r.url)]
assert len(inference) > 0, "expected at least one intercepted inference request"
for r in inference:
assert r.session_id == session.session_id, (
"CAPI inference request must carry the runtime session id"
)
_assert_agent_metadata(r)
# Validate the final assistant response arrived (guards against truncated captures)
assert "OK from the synthetic" in text
async def test_threads_session_id_into_byok_session(self, session_id_client):
client, handler = session_id_client
await client.start()
baseline = len(handler.records)
session = await client.create_session(
on_permission_request=PermissionHandler.approve_all,
model="claude-sonnet-4.5",
provider={
"type": "openai",
"wire_api": "responses",
"base_url": "https://byok.invalid/v1",
"api_key": "byok-secret",
"model_id": "claude-sonnet-4.5",
"wire_model": "claude-sonnet-4.5",
},
)
byok_session_id = session.session_id
text = ""
try:
result = await session.send_and_wait("Say OK.")
text = assistant_text(result)
finally:
await session.disconnect()
inference = [r for r in handler.records[baseline:] if is_inference_url(r.url)]
assert len(inference) > 0, "expected at least one intercepted BYOK inference request"
for r in inference:
assert r.session_id == byok_session_id, (
"BYOK inference request must carry the runtime session id"
)
_assert_agent_metadata(r)
# Session ids are per-session, so the two turns must differ.
assert byok_session_id != TestCopilotRequestSessionId.capi_session_id
# Validate the final assistant response arrived (guards against truncated captures)
assert "OK from the synthetic" in text