b2ac0e7a00
Allows resetting the LLM stats aggregation window without deleting historical data. Stores a reset timestamp in metadata and filters the aggregation query to only include calls after that point. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
398 lines
11 KiB
TypeScript
398 lines
11 KiB
TypeScript
import type { AccessorySlot, PortraitSlot, BerryPhase } from './constants.js';
|
|
import type { NarrationEvent } from './narration.js';
|
|
export type EntityId = number;
|
|
export type MemoryEventType = 'social_positive' | 'social_negative' | 'proposal_accepted' | 'proposal_rejected' | 'tier_change' | 'need_crisis' | 'need_recovery' | 'goal_change' | 'bond_formed' | 'bond_dissolved' | 'spawned' | 'invention' | 'desire_fulfilled' | 'desire_added';
|
|
export interface MemoryEvent {
|
|
id: number;
|
|
type: MemoryEventType;
|
|
tick: number;
|
|
otherEntityId?: EntityId;
|
|
otherName?: string;
|
|
detail: string;
|
|
oldTier?: string;
|
|
newTier?: string;
|
|
need?: 'hunger' | 'thirst' | 'energy' | 'productivity';
|
|
oldGoal?: string;
|
|
newGoal?: string;
|
|
}
|
|
export interface Position {
|
|
x: number;
|
|
y: number;
|
|
}
|
|
export interface Velocity {
|
|
dx: number;
|
|
dy: number;
|
|
}
|
|
export interface Appearance {
|
|
skinId: string;
|
|
accessories: Partial<Record<AccessorySlot, string>>;
|
|
portraitFeatures?: Partial<Record<PortraitSlot, string>>;
|
|
}
|
|
export interface Needs {
|
|
hunger: number;
|
|
thirst: number;
|
|
energy: number;
|
|
productivity: number;
|
|
}
|
|
export interface Stats {
|
|
strength: number;
|
|
dexterity: number;
|
|
constitution: number;
|
|
intelligence: number;
|
|
perception: number;
|
|
sociability: number;
|
|
courage: number;
|
|
curiosity: number;
|
|
empathy: number;
|
|
temperament: number;
|
|
}
|
|
export type StatName = keyof Stats;
|
|
export interface StatModifier {
|
|
stat: StatName;
|
|
value: number;
|
|
remaining: number;
|
|
}
|
|
export interface StatModifiers {
|
|
modifiers: StatModifier[];
|
|
}
|
|
export type MovementState = 'idle' | 'walking';
|
|
export type GoalType = 'wander' | 'eat' | 'drink' | 'forage' | 'sleep' | 'gather' | 'craft' | 'build' | 'dropoff' | 'pickup';
|
|
export interface Movement {
|
|
state: MovementState;
|
|
target: Position | null;
|
|
path: Position[];
|
|
direction: number;
|
|
moveProgress: number;
|
|
}
|
|
export interface PlayerControlled {
|
|
playerId: string;
|
|
mode: 'avatar' | 'camera';
|
|
}
|
|
export interface NPCBrain {
|
|
currentGoal: GoalType | null;
|
|
goalQueue: GoalType[];
|
|
gatherTarget?: {
|
|
x: number;
|
|
y: number;
|
|
resourceType: string;
|
|
} | null;
|
|
}
|
|
export type InteractionPhase = 'none' | 'facing' | 'pausing' | 'emoting' | 'proposing';
|
|
export type InteractionOutcome = 'positive' | 'negative';
|
|
export interface SocialState {
|
|
phase: InteractionPhase;
|
|
partnerId: EntityId | null;
|
|
phaseTimer: number;
|
|
outcome: InteractionOutcome | null;
|
|
globalCooldown: number;
|
|
pairCooldowns: Map<EntityId, number>;
|
|
lastOutcome: LastOutcome | null;
|
|
proposalCooldown: number;
|
|
pendingProposal: {
|
|
targetId: EntityId;
|
|
type: string;
|
|
} | null;
|
|
isProposalInteraction: boolean;
|
|
}
|
|
export interface RelationshipData {
|
|
value: number;
|
|
interactions: number;
|
|
lastInteractionTick: number;
|
|
status: 'active' | 'memory';
|
|
}
|
|
export type Relationships = Map<EntityId, RelationshipData>;
|
|
export interface LastOutcome {
|
|
partnerId: EntityId;
|
|
outcome: InteractionOutcome;
|
|
tick: number;
|
|
}
|
|
export interface EntityState {
|
|
id: EntityId;
|
|
position: Position;
|
|
movement: Movement;
|
|
appearance: Appearance;
|
|
needs?: Needs;
|
|
stats?: Stats;
|
|
npcBrain?: NPCBrain;
|
|
playerControlled?: PlayerControlled;
|
|
name?: string;
|
|
backstory?: string;
|
|
inventory?: Record<string, number>;
|
|
desires?: {
|
|
description: string;
|
|
category: DesireCategory;
|
|
}[];
|
|
socialState?: {
|
|
phase: InteractionPhase;
|
|
partnerId: EntityId | null;
|
|
outcome: InteractionOutcome | null;
|
|
};
|
|
relationships?: Array<{
|
|
entityId: EntityId;
|
|
name: string;
|
|
value: number;
|
|
classification: string;
|
|
status: 'active' | 'memory';
|
|
bond: string | null;
|
|
}>;
|
|
}
|
|
export interface WorldState {
|
|
entities: EntityState[];
|
|
worldWidth: number;
|
|
worldHeight: number;
|
|
tileSize: number;
|
|
obstacles: Position[];
|
|
terrain: number[];
|
|
decorations: number[];
|
|
trunkDecorations: number[];
|
|
resourceTiles: Array<{
|
|
x: number;
|
|
y: number;
|
|
resourceType: string;
|
|
}>;
|
|
berryBushStates: Array<{
|
|
x: number;
|
|
y: number;
|
|
phase: BerryPhase;
|
|
}>;
|
|
}
|
|
export interface StateUpdate {
|
|
entities: EntityState[];
|
|
tick: number;
|
|
gameTime: number;
|
|
dayNumber: number;
|
|
berryBushStates: Array<{
|
|
x: number;
|
|
y: number;
|
|
phase: BerryPhase;
|
|
}>;
|
|
}
|
|
export interface PlayerJoined {
|
|
playerId: string;
|
|
entityId: EntityId;
|
|
}
|
|
export interface PlayerLeft {
|
|
playerId: string;
|
|
}
|
|
export interface PlayerInput {
|
|
type: 'move' | 'toggle-mode' | 'follow' | 'interact';
|
|
direction?: {
|
|
dx: number;
|
|
dy: number;
|
|
};
|
|
targetPlayerId?: string;
|
|
targetEntityId?: EntityId;
|
|
}
|
|
export interface SuperlativeEntry {
|
|
entityId: EntityId;
|
|
name: string;
|
|
value: number;
|
|
}
|
|
export interface SuperlativesData {
|
|
mostLoved: SuperlativeEntry | null;
|
|
mostPopular: SuperlativeEntry | null;
|
|
mostReviled: SuperlativeEntry | null;
|
|
mostAnnoying: SuperlativeEntry | null;
|
|
shyest: SuperlativeEntry | null;
|
|
mostOutgoing: SuperlativeEntry | null;
|
|
biggestHeartbreaker: SuperlativeEntry | null;
|
|
mostDevoted: SuperlativeEntry | null;
|
|
loneliest: SuperlativeEntry | null;
|
|
mostPolarizing: SuperlativeEntry | null;
|
|
socialButterfly: SuperlativeEntry | null;
|
|
mostInventive: SuperlativeEntry | null;
|
|
}
|
|
export interface InventionSummary {
|
|
itemId: string;
|
|
name: string;
|
|
category: 'resource' | 'tool' | 'material' | 'structure';
|
|
inputs: {
|
|
itemId: string;
|
|
quantity: number;
|
|
}[];
|
|
workshopType: string | null;
|
|
toolRequired: string | null;
|
|
inventorName: string;
|
|
day: number;
|
|
}
|
|
export interface StockpileLogEntry {
|
|
npcName: string;
|
|
action: 'dropoff' | 'pickup';
|
|
itemId: string;
|
|
quantity: number;
|
|
tick: number;
|
|
}
|
|
export type DesireCategory = 'material' | 'social' | 'shelter' | 'comfort' | 'community' | 'creative';
|
|
export type FulfillmentCriteria = {
|
|
type: 'own_item';
|
|
itemId: string;
|
|
quantity: number;
|
|
} | {
|
|
type: 'own_item_category';
|
|
category: string;
|
|
quantity: number;
|
|
} | {
|
|
type: 'structure_exists';
|
|
structureType: string;
|
|
} | {
|
|
type: 'building_exists';
|
|
buildingType: string;
|
|
} | {
|
|
type: 'relationship_tier';
|
|
tier: string;
|
|
count: number;
|
|
} | {
|
|
type: 'recipe_exists';
|
|
tag: string;
|
|
} | {
|
|
type: 'custom';
|
|
check: string;
|
|
};
|
|
export interface Desire {
|
|
id: string;
|
|
description: string;
|
|
category: DesireCategory;
|
|
fulfillment: FulfillmentCriteria;
|
|
priority: number;
|
|
source: 'spawn' | 'event' | 'periodic';
|
|
sourceDetail?: string;
|
|
createdAtTick: number;
|
|
cooldownTicks?: number;
|
|
}
|
|
export interface TunableConstants {
|
|
TICK_RATE: number;
|
|
BROADCAST_EVERY_N_TICKS: number;
|
|
HUNGER_DECAY_PER_TICK: number;
|
|
ENERGY_DECAY_PER_TICK: number;
|
|
HUNGER_THRESHOLD: number;
|
|
THIRST_DECAY_PER_TICK: number;
|
|
THIRST_THRESHOLD: number;
|
|
ENERGY_THRESHOLD: number;
|
|
NEED_RECOVERY_RATE: number;
|
|
SLEEP_ENERGY_RECOVERY_PER_TICK: number;
|
|
SLEEP_HUNGER_DECAY_MULTIPLIER: number;
|
|
SLEEP_WAKE_THRESHOLD: number;
|
|
SLEEP_VOLUNTARY_ENERGY_THRESHOLD: number;
|
|
NAP_BUFFER: number;
|
|
PRODUCTIVITY_DECAY_PER_TICK: number;
|
|
PRODUCTIVITY_THRESHOLD: number;
|
|
PRODUCTIVITY_RECOVERY_RATE: number;
|
|
DAY_NIGHT_RATIO: number;
|
|
DAY_HOURS: number;
|
|
SUNSET_DURATION_HOURS: number;
|
|
SUNRISE_DURATION_HOURS: number;
|
|
NIGHT_DARKNESS: number;
|
|
AWARENESS_RADIUS: number;
|
|
FACING_DURATION: number;
|
|
PAUSING_DURATION: number;
|
|
EMOTING_DURATION: number;
|
|
SOCIAL_GLOBAL_COOLDOWN: number;
|
|
SOCIAL_PAIR_COOLDOWN: number;
|
|
PROPOSAL_EMOTING_DURATION: number;
|
|
MAX_NPC_COUNT: number;
|
|
MOVE_SPEED: number;
|
|
}
|
|
export type TunableKey = keyof TunableConstants;
|
|
export type LogSeverity = 'error' | 'warning' | 'info';
|
|
export type LogCategory = 'LLM' | 'Save' | 'Network' | 'Performance' | 'Game';
|
|
export interface LogEntry {
|
|
timestamp: number;
|
|
severity: LogSeverity;
|
|
category: LogCategory;
|
|
message: string;
|
|
}
|
|
export interface LlmCallTypeStats {
|
|
templateName: string;
|
|
label: string;
|
|
count: number;
|
|
totalCost: number;
|
|
minCost: number;
|
|
maxCost: number;
|
|
avgCost: number;
|
|
retryCount: number;
|
|
failCount: number;
|
|
retryPct: number;
|
|
failPct: number;
|
|
}
|
|
export interface LlmStatsData {
|
|
types: LlmCallTypeStats[];
|
|
totalCount: number;
|
|
totalCost: number;
|
|
totalRetries: number;
|
|
totalFailures: number;
|
|
trackingEnabled: boolean;
|
|
}
|
|
export interface ServerEvents {
|
|
'world-state': (data: WorldState) => void;
|
|
'state-update': (data: StateUpdate) => void;
|
|
'player-joined': (data: PlayerJoined) => void;
|
|
'player-left': (data: PlayerLeft) => void;
|
|
'npc-recomposed': (data: {
|
|
entityId: EntityId;
|
|
appearance: Appearance;
|
|
}) => void;
|
|
'superlatives-update': (data: SuperlativesData) => void;
|
|
'narration-event': (data: NarrationEvent) => void;
|
|
'narration-update': (data: {
|
|
id: number;
|
|
narration: string;
|
|
}) => void;
|
|
'narration-history': (data: NarrationEvent[]) => void;
|
|
'npc-thought': (data: {
|
|
entityId: EntityId;
|
|
text: string;
|
|
emoji: string;
|
|
}) => void;
|
|
'memory-event': (data: {
|
|
entityId: EntityId;
|
|
event: MemoryEvent;
|
|
}) => void;
|
|
'memory-history': (data: {
|
|
entityId: EntityId;
|
|
events: MemoryEvent[];
|
|
}) => void;
|
|
'invention-event': (data: InventionSummary) => void;
|
|
'invention-history': (data: InventionSummary[]) => void;
|
|
'stockpile-event': (data: StockpileLogEntry) => void;
|
|
'stockpile-history': (data: StockpileLogEntry[]) => void;
|
|
'stockpile-summary': (data: Record<string, number>) => void;
|
|
'admin-auth-result': (data: {
|
|
success: boolean;
|
|
constants?: TunableConstants;
|
|
}) => void;
|
|
'admin-constants-updated': (data: TunableConstants) => void;
|
|
'log-history': (data: LogEntry[]) => void;
|
|
'log-entry': (data: LogEntry) => void;
|
|
'admin-llm-stats-result': (data: LlmStatsData) => void;
|
|
'admin-llm-tracking-toggled': (data: {
|
|
enabled: boolean;
|
|
}) => void;
|
|
}
|
|
export interface ClientEvents {
|
|
'player-input': (data: PlayerInput) => void;
|
|
'spawn-npc': (data: {
|
|
x: number;
|
|
y: number;
|
|
}) => void;
|
|
'superlatives-subscribe': () => void;
|
|
'superlatives-unsubscribe': () => void;
|
|
'follow-npc': (data: {
|
|
entityId: EntityId | null;
|
|
}) => void;
|
|
'admin-auth': (data: {
|
|
password: string;
|
|
}) => void;
|
|
'admin-update-constant': (data: {
|
|
key: TunableKey;
|
|
value: number;
|
|
}) => void;
|
|
'admin-reset-defaults': () => void;
|
|
'log-subscribe': () => void;
|
|
'log-unsubscribe': () => void;
|
|
'admin-llm-stats': () => void;
|
|
'admin-toggle-llm-tracking': (data: {
|
|
enabled: boolean;
|
|
}) => void;
|
|
'admin-reset-llm-stats': () => void;
|
|
}
|