UNPKG

magnitude-core

Version:
80 lines (79 loc) 3.37 kB
/** * Observations and declarations for what kind of data is "observable" - i.e. can be processed into BAML context or JSON. */ import { fnv1a32Hex } from '@/util'; import { observableDataToJson } from './serde'; import { renderContentParts } from './rendering'; export class Observation { source; // where this observation came from role; timestamp; // time this observation was made content; // the arbitrarily-structured multimedia content of this observation retention; constructor(source, role, content, retention, timestamp) { this.source = source; this.role = role; this.content = content; this.timestamp = timestamp ?? Date.now(); this.retention = retention; } static fromConnector(connectorId, content, options) { return new Observation(`connector:${connectorId}`, 'user', content, options); } static fromActionTaken(actionId, content, options) { // see notes on fromThought return new Observation(`action:taken:${actionId}`, 'user', content, options); } static fromActionResult(actionId, content, options) { return new Observation(`action:result:${actionId}`, 'user', content, options); } static fromThought(content, options) { // may want to reconsider this structure - probably want to show something that actually matches what the assistant sent else it might pattern match. // or just do these as user messages. // changed to user for now return new Observation(`thought`, 'user', content, options); } toString() { // will be pretty ugly usually return JSON.stringify(this.content); } async toJson() { return await observableDataToJson(this.content); } async render(options) { return { role: this.role, cacheControl: options?.cacheControl ?? false, content: [...(options?.prefix ?? []), ...(await renderContentParts(this.content, { mode: 'json', indent: 2 })), ...(options?.postfix ?? [])] }; } // async renderContentParts(): Promise<MultiMediaContentPart[]> { // return await renderParts(this.content); // } // async toContext(): Promise<BamlRenderable[]> { // return await observableDataToContext(this.data); // } async hash() { // build str of all text+img content const stringifiedContent = JSON.stringify(await this.toJson()); return fnv1a32Hex(stringifiedContent); } async equals(obs) { // TODO: deep eq for both text and img content return (await this.hash()) == (await obs.hash()); } } // export async function renderObservations(observations: Observation[]): Promise<BamlRenderable[]> { // /** // * Refines observations (according to observation culling options) and converts to BAML-renderable content // */ // const filteredObservations = await filterObservations(observations); // let content: BamlRenderable[] = []; // for (const obs of filteredObservations) { // if (obs.source.startsWith('action:taken') || obs.source.startsWith('thought')) { // content.push(`[${new Date(obs.timestamp).toTimeString().split(' ')[0]}]: `); // } // content = [...content, ...await obs.toContext(), "\n"]; // } // return content; // }