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