104 lines
3.2 KiB
TypeScript
104 lines
3.2 KiB
TypeScript
export type Theme = 'moon' | 'fog';
|
|
export type Relation = '朋友' | '恋人';
|
|
export interface Identity {
|
|
id: string;
|
|
name: string;
|
|
birth: string;
|
|
hour: string;
|
|
relation: Relation;
|
|
nickname: string;
|
|
boundary: string;
|
|
}
|
|
export interface Settings {
|
|
city: string;
|
|
appearance: string;
|
|
reduced: boolean;
|
|
services: Record<string, string>;
|
|
}
|
|
export interface LifeEvent {
|
|
id: string;
|
|
title: string;
|
|
text: string;
|
|
time: string;
|
|
place: string;
|
|
kind: 'done' | 'now' | 'plan';
|
|
shared: boolean;
|
|
// When and how she told you ("19:34 告诉你", "你们一起经历的"); only ever present on records she has shared.
|
|
told?: string;
|
|
memoryIds: string[];
|
|
// Real records only: the (already server-trimmed) companion messages in which she told it.
|
|
messageIds?: string[];
|
|
}
|
|
export interface Memory {
|
|
id: string;
|
|
title: string;
|
|
text: string;
|
|
group: string;
|
|
source: string;
|
|
lifeIds: string[];
|
|
}
|
|
export interface Message {
|
|
id: string;
|
|
role: 'user' | 'companion';
|
|
text: string;
|
|
lifeIds: string[];
|
|
turnId?: string;
|
|
status?: 'pending' | 'failed' | 'sent' | 'cancelled';
|
|
proactive?: boolean;
|
|
}
|
|
export interface DemoState {
|
|
version: 1;
|
|
identity: Identity;
|
|
settings: Settings;
|
|
events: LifeEvent[];
|
|
memories: Memory[];
|
|
messages: Message[];
|
|
lastVisit: number;
|
|
}
|
|
export interface SceneDesc {
|
|
mode: 'daily' | 'talk';
|
|
activity: string;
|
|
time_band: string;
|
|
weather: string;
|
|
outfit: string;
|
|
}
|
|
export type ServerEvent = {
|
|
t: 'chat.error'; epoch: number; turn_id: string; phi: string; retryable: boolean;
|
|
} | {
|
|
t: 'chat.delta';
|
|
epoch: number;
|
|
turn_id: string;
|
|
delta: string;
|
|
} | {
|
|
t: 'chat.final';
|
|
epoch: number;
|
|
turn_id: string;
|
|
utterance: {
|
|
text: string;
|
|
speech_tags: string[];
|
|
};
|
|
// Only on a successful real final while the R2 backend is enabled; demo and older servers omit it.
|
|
receipt?: Receipt | null;
|
|
};
|
|
export interface Receipt {
|
|
message_id: number;
|
|
turn_id: string;
|
|
// The epoch of the turn that produced the message; on a replay it can differ from the frame's current epoch.
|
|
epoch: number;
|
|
token: string;
|
|
text_hash: string;
|
|
expires_at: number;
|
|
}
|
|
export const defaultSettings: Settings = { city: '杭州', appearance: 'reference-v1', reduced: false, services: { '对话模型': '', 'ASR': '', 'TTS': '', '视觉观察': '' } };
|
|
export function validateCreation(name: string, birth: string, relation: string, now = new Date()): string {
|
|
if (!name.trim() || name.trim().length > 20 || (/[<>]/u.test(name) || [...name].some(c => c.charCodeAt(0) < 32)))
|
|
return '请为她起一个 1–20 字的名字,不含控制字符或尖括号。';
|
|
const d = new Date(birth + 'T00:00:00');
|
|
const limit = new Date(now.getFullYear() - 18, now.getMonth(), now.getDate());
|
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(birth) || !Number.isFinite(d.getTime()) || d > limit || d.getFullYear() < 1900)
|
|
return '请填写有效的出生日期,且已满 18 岁。';
|
|
if (!['朋友', '恋人'].includes(relation))
|
|
return '请亲自选择你们的关系。';
|
|
return '';
|
|
}
|