magnitude-core
Version:
Magnitude e2e testing agent
80 lines (79 loc) • 3.37 kB
JavaScript
/**
* 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;
// }