Files
open-gsio/packages/client/src/models/Message.ts
2025-06-24 17:32:59 -04:00

81 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* eslint-disable no-irregular-whitespace */
import { types, flow } from 'mobx-state-tree';
// Utility to pause execution inside a flow
const sleep = (ms: number) => new Promise<void>(res => setTimeout(res, ms));
// Simple function to generate a unique ID
export const generateId = () => {
return Date.now().toString(36) + Math.random().toString(36).substring(2);
};
// Utility for efficient batched content updates
let batchedContent = '';
let batchUpdateTimeout: NodeJS.Timeout | null = null;
const BATCH_UPDATE_DELAY = 50; // ms
export const batchContentUpdate = (message: any, content: string) => {
if (!content) return;
// Add the content to the batch
batchedContent += content;
// If we already have a timeout scheduled, do nothing
if (batchUpdateTimeout) return;
// Schedule a timeout to apply the batched content
batchUpdateTimeout = setTimeout(() => {
if (message && batchedContent) {
message.append(batchedContent);
batchedContent = '';
}
batchUpdateTimeout = null;
}, BATCH_UPDATE_DELAY);
};
const Message = types
.model('Message', {
id: types.optional(types.identifier, generateId),
content: types.string,
role: types.enumeration(['user', 'assistant']),
})
// Runtime-only flags that never persist or get serialized
.volatile(() => ({
/** Indicates that characters are still being streamed in */
isStreaming: false,
}))
.actions(self => {
// Basic mutators ---------------------------------------------------------
const setContent = (newContent: string) => {
self.content = newContent;
};
const append = (newContent: string) => {
self.content += newContent;
};
/**
* Stream content into the message for a smooth “typing” effect.
* @param newContent The full text to stream in.
* @param chunkSize How many characters to reveal per tick (default 3).
* @param delay Delay (ms) between ticks (default 20ms).
*/
const streamContent = flow(function* (newContent: string, chunkSize = 3, delay = 20) {
self.isStreaming = true;
let pointer = 0;
// Reveal the content chunkbychunk
while (pointer < newContent.length) {
append(newContent.slice(pointer, pointer + chunkSize));
pointer += chunkSize;
yield sleep(delay);
}
self.isStreaming = false; // finished
});
return { setContent, append, streamContent };
});
export default Message;