blob: b5753a130977a9f9078ff1d385c8c9c3aa1bb963 [file] [log] [blame]
philip.zeyliger26bc6592025-06-30 20:15:30 -07001import { http, HttpResponse } from "msw";
Pokey Rulee7c9a442025-04-25 20:02:22 +01002import { initialState, initialMessages } from "../../../fixtures/dummy";
Pokey Rule8cac59a2025-04-24 12:21:19 +01003
Pokey Rulec575fd72025-05-06 15:46:38 +01004// Mock state updates for SSE simulation
Pokey Rulee923bb32025-04-24 18:48:01 +01005const EMPTY_CONVERSATION =
6 new URL(window.location.href).searchParams.get("emptyConversation") === "1";
Pokey Rule8cac59a2025-04-24 12:21:19 +01007const ADD_NEW_MESSAGES =
8 new URL(window.location.href).searchParams.get("addNewMessages") === "1";
9
Pokey Rulee923bb32025-04-24 18:48:01 +010010const messages = EMPTY_CONVERSATION ? [] : [...initialMessages];
11
banksean54777362025-06-19 16:38:30 +000012// Initialize state with correct message_count
13let currentState = {
14 ...initialState,
15 message_count: messages.length,
16};
17
Pokey Rulec575fd72025-05-06 15:46:38 +010018// Text encoder for SSE messages
19const encoder = new TextEncoder();
20
21// Helper function to create SSE formatted messages
22function formatSSE(event, data) {
23 return `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
24}
25
Pokey Rule8cac59a2025-04-24 12:21:19 +010026export const handlers = [
Pokey Rulec575fd72025-05-06 15:46:38 +010027 // SSE stream endpoint
28 http.get("*/stream", async ({ request }) => {
Pokey Rule8cac59a2025-04-24 12:21:19 +010029 const url = new URL(request.url);
Pokey Rulec575fd72025-05-06 15:46:38 +010030 const fromIndex = parseInt(url.searchParams.get("from") || "0");
Pokey Rule8cac59a2025-04-24 12:21:19 +010031
Pokey Rulec575fd72025-05-06 15:46:38 +010032 // Create a readable stream for SSE
33 const stream = new ReadableStream({
34 async start(controller) {
35 // Send initial state update
36 controller.enqueue(encoder.encode(formatSSE("state", currentState)));
Pokey Rule8cac59a2025-04-24 12:21:19 +010037
Pokey Rulec575fd72025-05-06 15:46:38 +010038 // Send any existing messages that are newer than the fromIndex
39 const newMessages = messages.filter((msg) => msg.idx >= fromIndex);
40 for (const message of newMessages) {
41 controller.enqueue(encoder.encode(formatSSE("message", message)));
42 }
Pokey Rule8cac59a2025-04-24 12:21:19 +010043
Pokey Rulec575fd72025-05-06 15:46:38 +010044 // Simulate heartbeats and new messages
Pokey Rulec575fd72025-05-06 15:46:38 +010045 let messageInterval;
Pokey Rule8cac59a2025-04-24 12:21:19 +010046
Pokey Rulec575fd72025-05-06 15:46:38 +010047 // Send heartbeats every 30 seconds
philip.zeyliger26bc6592025-06-30 20:15:30 -070048 const heartbeatInterval = setInterval(() => {
Pokey Rulec575fd72025-05-06 15:46:38 +010049 controller.enqueue(
50 encoder.encode(
51 formatSSE("heartbeat", { timestamp: new Date().toISOString() }),
52 ),
53 );
54 }, 30000);
Pokey Rule8cac59a2025-04-24 12:21:19 +010055
Pokey Rulec575fd72025-05-06 15:46:38 +010056 // Add new messages if enabled
57 if (ADD_NEW_MESSAGES) {
58 messageInterval = setInterval(() => {
59 const newMessage = {
60 type: "agent" as const,
61 end_of_turn: false,
62 content: "Here's a new message via SSE",
63 timestamp: new Date().toISOString(),
64 conversation_id: "37s-g6xg",
65 usage: {
66 input_tokens: 5,
67 cache_creation_input_tokens: 250,
68 cache_read_input_tokens: 4017,
69 output_tokens: 92,
70 cost_usd: 0.0035376,
71 },
72 start_time: new Date(Date.now() - 2000).toISOString(),
73 end_time: new Date().toISOString(),
74 elapsed: 2075193375,
75 turnDuration: 28393844125,
76 idx: messages.length,
77 };
78
79 // Add to our messages array
80 messages.push(newMessage);
81
82 // Update the state
83 currentState = {
84 ...currentState,
85 message_count: messages.length,
86 };
87
88 // Send the message and updated state through SSE
89 controller.enqueue(
90 encoder.encode(formatSSE("message", newMessage)),
91 );
92 controller.enqueue(
93 encoder.encode(formatSSE("state", currentState)),
94 );
95 }, 2000); // Add a new message every 2 seconds
96 }
97
98 // Clean up on connection close
99 request.signal.addEventListener("abort", () => {
100 clearInterval(heartbeatInterval);
101 if (messageInterval) clearInterval(messageInterval);
102 });
103 },
104 });
105
106 return new HttpResponse(stream, {
107 headers: {
108 "Content-Type": "text/event-stream",
109 "Cache-Control": "no-cache",
110 Connection: "keep-alive",
111 },
112 });
113 }),
114
115 // State endpoint (non-streaming version for initial state)
116 http.get("*/state", () => {
Pokey Rule8cac59a2025-04-24 12:21:19 +0100117 return HttpResponse.json(currentState);
118 }),
119
120 // Messages endpoint
121 http.get("*/messages", ({ request }) => {
122 const url = new URL(request.url);
123 const startIndex = parseInt(url.searchParams.get("start") || "0");
124
125 return HttpResponse.json(messages.slice(startIndex));
126 }),
Pokey Rulec575fd72025-05-06 15:46:38 +0100127
128 // Chat endpoint for sending messages
129 http.post("*/chat", async ({ request }) => {
130 const body = await request.json();
131
132 // Add a user message
133 messages.push({
134 type: "user" as const,
135 end_of_turn: true,
136 content:
137 typeof body === "object" && body !== null
138 ? String(body.message || "")
139 : "",
140 timestamp: new Date().toISOString(),
141 conversation_id: "37s-g6xg",
142 idx: messages.length,
143 });
144
145 // Update state
146 currentState = {
147 ...currentState,
148 message_count: messages.length,
149 };
150
151 return new HttpResponse(null, { status: 204 });
152 }),
153
154 // Cancel endpoint
155 http.post("*/cancel", () => {
156 return new HttpResponse(null, { status: 204 });
157 }),
Pokey Rule8cac59a2025-04-24 12:21:19 +0100158];