UNPKG

@wearesage/schema

Version:

A flexible schema definition and validation system for TypeScript with multi-database support

141 lines (117 loc) 3.41 kB
import { Conversation } from './Conversation'; import { Embeddings } from '../adapters/embeddings'; import { Entity, Property, Id, ManyToOne, Index, Timestamp, Auth , Labels } from '../core/decorators'; import { RelationshipType } from '../adapters/neo4j'; import { PostgresMetadata } from '../adapters/metadata-layer'; @Entity() @Labels(['Message']) @Auth({ permissions: ['user'] }) @PostgresMetadata({ fields: ['id', 'role', 'messageIndex', 'model', 'tokenCount', 'responseTime', 'createdAt'], tableName: 'message_metadata' }) @Embeddings({ fields: ['content', 'thinking'], model: 'nomic-embed-text:latest', provider: 'ollama', dimensions: 768, // nomic-embed-text dimensions chunkSize: 512, overlap: 50, embeddingField: 'contentEmbedding', metadataFields: ['id', 'role', 'messageIndex', 'model', 'createdAt'] }) export class Message { @Id() id!: string; // The conversation this message belongs to @ManyToOne({ target: () => Conversation, inverse: 'messages', neo4j: { type: 'BELONGS_TO', direction: 'OUT' } }) @RelationshipType('BELONGS_TO') conversation!: Conversation; // Message content @Property({ required: true }) content!: string; @Property({ required: true }) @Index() role!: 'user' | 'assistant' | 'system'; // Sender information (who sent this message) @Property() sender?: { type: 'human' | 'ai'; id: string; // userId or model name displayName?: string; // For display purposes }; // AI-specific properties @Property() thinking?: string; // AI's thinking process (for assistant messages) @Property() model?: string; // Model used for this specific message @Property() temperature?: number; // Temperature used for this message @Property() responseTime?: number; // Time taken to generate response (in milliseconds) @Property() tokenCount?: number; // Number of tokens in this message // Message ordering @Property({ required: true }) @Index() messageIndex!: number; // Order of message in conversation (0, 1, 2, ...) // Message metadata @Property() isEdited?: boolean; // Whether this message was edited after creation @Property() editHistory?: Array<{ content: string; editedAt: Date; reason?: string; }>; @Timestamp({ onCreate: true }) createdAt!: Date; @Timestamp({ onUpdate: true }) updatedAt!: Date; // Semantic embedding for content similarity search @Property() contentEmbedding?: number[]; // Constructor constructor() { this.messageIndex = 0; this.isEdited = false; } // Helper methods editContent(newContent: string, reason?: string): void { if (!this.editHistory) { this.editHistory = []; } this.editHistory.push({ content: this.content, editedAt: new Date(), reason }); this.content = newContent; this.isEdited = true; } isAssistantMessage(): boolean { return this.role === 'assistant'; } isUserMessage(): boolean { return this.role === 'user'; } hasThinking(): boolean { return !!(this.thinking && this.thinking.trim()); } getAuthorDisplay(): string { switch (this.role) { case 'user': return this.sender?.displayName || 'You'; case 'assistant': return this.model || 'Assistant'; case 'system': return 'System'; default: return 'Unknown'; } } }