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