Files
Kokorone/apps/web/lib/api.ts
T
Jeffery d82b834e47 feat: 完成 I 群組 — 生活作息與離線生活
- ScheduleService:I-1 平日/週末/假期時段表(可為角色寫入高中生預設作息)、
  I-3 時間感知(Asia/Taipei 當地時間,不受伺服器時區影響)、剛醒/睡前偵測
- constants.ts:I-2 時段狀態 → 回應可用度對照(FREE/SEMI_BUSY/BUSY/SLEEP →
  NORMAL/BRIEF/DELAYED/NO_REPLY),沒有作息表的角色一律視為 NORMAL(相容既有角色)
- ScheduleExceptionService:I-4 三種破例(深夜還陪你聊/上課偷回訊息/為你調整行程),
  各自獨立冷卻期,觸發時記入關係帳本
- ScheduleEmotionBaselineService:I-5 作息驅動情緒基線(社團剛結束的疲憊、假日偏高心情),
  只在本輪沒有明確情緒訊號時介入,串接進 EmotionService.processInput 的新增選項
- OfflineEventService:I-6 離線事件生成,依「今天已發生的固定行程」挑選對應模板,
  一天只生成一次,寫入低權重情節記憶
- DialogueService 整合以上機制:離線事件生成 → 情緒基線標記 → 累積親密度 →
  作息可用度判定 → 不符合則嘗試破例 → 依可用度決定回覆內容
- 修正真實 bug:G-5 角色建立流程誤用 source=OFFLINE_GENERATED 標記背景故事,
  與 I-6「今天是否已生成離線事件」的判斷共用同一標記,已改為 SOURCE_EXTRACTION
- ChatView.tsx 補上 NO_REPLY/context 為 null 的防呆(作息可用度整合後的必要調整)
- scripts/smoke/I.mjs:以獨立測試角色(避免污染種子角色的排程狀態)驗證全部六項

npm run restart && npm run smoke -- I 皆通過(I-V),A~H 群組冒煙測試無回歸。
2026-08-13 14:08:24 +08:00

52 lines
1.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { EmotionTag, RelationshipStage } from "@kokorone/shared";
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:3001";
export interface ChatResponseStyle {
tone: string;
sentenceLength: "short" | "medium" | "long";
initiative: "low" | "medium" | "high";
}
export interface ChatContext {
emotion: { dominant: EmotionTag; style: ChatResponseStyle };
relationship: { intimacy: number; trust: number; stage: RelationshipStage };
}
export type ChatAvailability = "NORMAL" | "BRIEF" | "DELAYED" | "NO_REPLY";
export interface ChatResult {
text: string;
actions: string[];
isFastChannel: boolean;
availability: ChatAvailability;
exceptionType: string | null;
// I-2:作息為睡眠且未破例時不會組裝上下文,此時為 null。
context: ChatContext | null;
}
export async function sendChatMessage(
characterId: string,
userId: string,
sessionId: string,
text: string,
): Promise<ChatResult> {
const res = await fetch(`${API_BASE_URL}/chat/${characterId}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userId, sessionId, text }),
});
if (!res.ok) {
throw new Error(`對話請求失敗(${res.status})`);
}
return res.json();
}
export async function endChatSession(sessionId: string, characterId: string): Promise<void> {
await fetch(`${API_BASE_URL}/session/${sessionId}/end`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ characterId }),
});
}