Support mocking chats (#138)

This commit is contained in:
Brian Hackett 2025-06-08 17:10:27 -07:00 committed by GitHub
parent b550d15930
commit 448660bf58
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 68 additions and 0 deletions

View File

@ -1,5 +1,11 @@
// FIXME ping telemetry server directly instead of going through the backend.
let gDisableTelemetry = false;
export function disableTelemetry() {
gDisableTelemetry = true;
}
// We do this to work around CORS insanity.
export async function pingTelemetry(event: string, data: any) {
const requestBody: any = {
@ -26,6 +32,10 @@ export class ChatMessageTelemetry {
}
private _ping(event: string, data: any = {}) {
if (gDisableTelemetry) {
return;
}
pingTelemetry(event, {
...data,
messageId: this.id,

View File

@ -9,8 +9,10 @@ interface MessageBase {
id: string;
role: MessageRole;
repositoryId?: string;
repositoryURL?: string;
peanuts?: number;
category?: string;
createTime?: string;
// Not part of the protocol, indicates whether the user has explicitly approved
// the message. Once approved, the approve/reject UI is not shown again for the message.

View File

@ -12,6 +12,7 @@ import { chatStore } from '~/lib/stores/chat';
import { debounce } from '~/utils/debounce';
import { getSupabase } from '~/lib/supabase/client';
import { pingTelemetry } from '~/lib/hooks/pingTelemetry';
import { sendChatMessageMocked, usingMockChat } from './MockChat';
// We report to telemetry if we start a message and don't get any response
// before this timeout.
@ -367,6 +368,11 @@ export async function sendChatMessage(
references: ChatReference[],
callbacks: ChatMessageCallbacks,
) {
if (usingMockChat()) {
await sendChatMessageMocked(callbacks);
return;
}
if (gMessageChatManager) {
gMessageChatManager.destroy();
}

View File

@ -0,0 +1,46 @@
/*
* Mock chats generate a hardcoded series of responses to a chat message.
* This avoids non-deterministic behavior in the chat backend and is helpful for
* development, testing, demos etc.
*/
import { assert, waitForTime } from '~/lib/replay/ReplayProtocolClient';
import type { Message } from '~/lib/persistence/message';
import type { ChatMessageCallbacks } from './ChatManager';
import { disableTelemetry } from '~/lib/hooks/pingTelemetry';
// Add your mock chat messages here!
const gMockChat: Message[] | undefined = undefined;
if (gMockChat) {
disableTelemetry();
}
export function usingMockChat() {
return !!gMockChat;
}
export async function sendChatMessageMocked(callbacks: ChatMessageCallbacks) {
assert(gMockChat, 'Mock chat is not defined');
console.log('Using mock chat', gMockChat);
assert(gMockChat[0].createTime, 'Mock chat first message must have a create time');
let currentTime = Date.parse(gMockChat[0].createTime);
for (const message of gMockChat) {
if (message.role === 'user') {
continue;
}
if (message.createTime) {
const messageTime = Date.parse(message.createTime);
if (messageTime > currentTime) {
await waitForTime(messageTime - currentTime);
currentTime = messageTime;
}
}
callbacks.onResponsePart(message);
}
}

View File

@ -26,6 +26,10 @@ export function defer<T>(): { promise: Promise<T>; resolve: (value: T) => void;
return { promise, resolve: resolve!, reject: reject! };
}
export function waitForTime(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export function uint8ArrayToBase64(data: Uint8Array) {
let str = '';