comparison mrjunejune/inference/sdk_provider_integration_test.py @ 260:1f9877b637e9

Add Copilot-powered cyberpunk JRPG chat Integrate the production JRPG chat with Seobeo streaming, Deita persistence, and a Bazel-managed Copilot SDK and LiteLLM inference stack. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <mrjunejune@users.noreply.github.com>
date Wed, 05 Aug 2026 09:19:41 -0700
parents
children
comparison
equal deleted inserted replaced
259:667156fcd3e3 260:1f9877b637e9
1 import asyncio
2 import json
3 import os
4 import sys
5 import tempfile
6 import threading
7 import time
8 import unittest
9 from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
10
11 from copilot import CopilotClient, RuntimeConnection
12 from copilot.rpc import PermissionDecisionReject
13
14 CLI_PATH = os.path.abspath(sys.argv.pop(1))
15
16
17 class ProviderHandler(BaseHTTPRequestHandler):
18 requests = []
19
20 def do_POST(self) -> None:
21 length = int(self.headers.get("Content-Length", "0"))
22 payload = json.loads(self.rfile.read(length))
23 self.__class__.requests.append((self.path, payload))
24 response_id = f"chatcmpl-{len(self.__class__.requests)}"
25 chunks = [
26 {
27 "id": response_id,
28 "object": "chat.completion.chunk",
29 "created": int(time.time()),
30 "model": "gpt-4",
31 "choices": [
32 {
33 "index": 0,
34 "delta": {"role": "assistant", "content": "hello "},
35 "finish_reason": None,
36 }
37 ],
38 },
39 {
40 "id": response_id,
41 "object": "chat.completion.chunk",
42 "created": int(time.time()),
43 "model": "gpt-4",
44 "choices": [
45 {
46 "index": 0,
47 "delta": {"content": "traveler"},
48 "finish_reason": None,
49 }
50 ],
51 },
52 {
53 "id": response_id,
54 "object": "chat.completion.chunk",
55 "created": int(time.time()),
56 "model": "gpt-4",
57 "choices": [
58 {
59 "index": 0,
60 "delta": {},
61 "finish_reason": "stop",
62 }
63 ],
64 },
65 ]
66 self.send_response(200)
67 self.send_header("Content-Type", "text/event-stream")
68 self.send_header("Cache-Control", "no-cache")
69 self.send_header("Connection", "close")
70 self.end_headers()
71 for chunk in chunks:
72 self.wfile.write(
73 f"data: {json.dumps(chunk, separators=(',', ':'))}\n\n".encode()
74 )
75 self.wfile.flush()
76 self.wfile.write(b"data: [DONE]\n\n")
77 self.wfile.flush()
78 self.close_connection = True
79
80 def log_message(self, _format: str, *_args: object) -> None:
81 return
82
83
84 def deny_permission(*_args: object, **_kwargs: object) -> PermissionDecisionReject:
85 return PermissionDecisionReject(feedback="No tools are allowed.")
86
87
88 class SdkProviderIntegrationTest(unittest.IsolatedAsyncioTestCase):
89 async def test_two_streamed_turns_use_openai_compatible_provider(self) -> None:
90 self.assertTrue(os.path.isfile(CLI_PATH))
91 ProviderHandler.requests = []
92 server = ThreadingHTTPServer(("127.0.0.1", 0), ProviderHandler)
93 server_thread = threading.Thread(target=server.serve_forever, daemon=True)
94 server_thread.start()
95 events = []
96 idle = asyncio.Event()
97
98 with tempfile.TemporaryDirectory() as base_directory:
99 client = CopilotClient(
100 connection=RuntimeConnection.for_stdio(path=CLI_PATH),
101 base_directory=base_directory,
102 use_logged_in_user=False,
103 log_level="error",
104 mode="empty",
105 )
106 try:
107 await client.start()
108 session = await client.create_session(
109 session_id="provider-integration",
110 on_permission_request=deny_permission,
111 model="gpt-4",
112 provider={
113 "type": "openai",
114 "base_url": (
115 f"http://127.0.0.1:{server.server_port}/v1"
116 ),
117 "wire_api": "completions",
118 "api_key": "local-test-key",
119 },
120 streaming=True,
121 tools=[],
122 available_tools=[],
123 mcp_servers={},
124 enable_config_discovery=False,
125 skip_custom_instructions=True,
126 enable_skills=False,
127 enable_session_store=True,
128 on_event=lambda event: (
129 events.append(event),
130 idle.set()
131 if event.type.value == "session.idle"
132 else None,
133 ),
134 )
135 for prompt in ("first", "second"):
136 idle.clear()
137 await session.send(prompt)
138 await asyncio.wait_for(idle.wait(), timeout=15)
139 await session.disconnect()
140 finally:
141 await client.stop()
142 server.shutdown()
143 server.server_close()
144 server_thread.join()
145
146 self.assertEqual(len(ProviderHandler.requests), 2)
147 self.assertTrue(
148 all(path.endswith("/chat/completions")
149 for path, _ in ProviderHandler.requests)
150 )
151 deltas = [
152 event.data.delta_content
153 for event in events
154 if event.type.value == "assistant.message_delta"
155 ]
156 self.assertEqual(deltas, ["hello ", "traveler", "hello ", "traveler"])
157 messages = [
158 event.data.content
159 for event in events
160 if event.type.value == "assistant.message"
161 ]
162 self.assertEqual(messages, ["hello traveler", "hello traveler"])
163
164
165 if __name__ == "__main__":
166 unittest.main()