Mercurial
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() |