Skip to content

Peep — Chinese Metaphysics Workbench

Showcase #2

Peep is an all-in-one Chinese metaphysics workbench — integrating person profiles, documents, Ziwei Doushu, Da Liu Ren, Liu Yao, and a knowledge base. We embed rtc-agent as a Web Component into the React app, letting the AI call business APIs directly:

  • 🧑 “Create a profile for Zhang San” → calls PersonCreate, jumps to the detail page
  • 🔮 “Read Zhang San’s Ziwei chart, check this year’s fortune” → calls ZiWei / GetScopeData, renders the chart
  • 🎲 “Cast a hexagram — should I switch jobs soon?” → calls LiuYaoCreate, displays the hexagram
  • 📚 “Write a study note about the Life Palace” → calls WikiCreate, jumps to the editor
  • 📅 “What’s today’s date in the lunar calendar?” → calls SolarToLunar, returns the lunar date

The AI doesn’t just chat — it reads data, invokes functions, and drives the UI.

The video above (4x speed) shows the full workflow: user interacts with the AI via natural language to create profiles, cast charts, divine hexagrams, and manage documents.

Peep metaphysics workbench interface screenshot

The screenshot shows the Peep main interface: left 5 parts for the Ziwei Doushu main content area, right 3 parts for the embedded <rtc-agent> AI assistant panel.

If Mermaid Live Editor demonstrates the simplest floating-window integration, Peep showcases production-grade embedded integration:

FeatureMermaid Live EditorPeep
FrameworkSvelteReact 19
Window ModeFloating window (minimized at bottom-left)Embedded (5:3 split layout)
AuthDefault (rtc-agent built-in)Custom AuthProvider
Business ComplexitySingle editor6 business domains, 40+ functions
Safety MechanismNoneWrite operation confirmation
Theme SyncSvelte $effectPolling + API sync
Language SyncNonezh-CN / en-US switching

💡 rtc-agent’s integration is extremely flexible — from the simplest floating window to production-grade embedded integration, all achievable with the same component.

┌─────────────────────────────────────────────────────────────────┐
│ Peep (React 19 + Vite 8) │
│ │
│ ┌────────────────────────────┐ ┌──────────────────────────┐ │
│ │ │ │ │ │
│ │ Main Content (5 parts) │ │ AI Assistant (3 parts) │ │
│ │ │ │ ┌────────────────────┐ │ │
│ │ 🧭 Ziwei / Da Liu Ren / │ │ │ <rtc-agent> │ │ │
│ │ 🎲 Liu Yao / 📚 Wiki │ │ │ Web Component │ │ │
│ │ │ │ │ Embedded Max │ │ │
│ │ │ │ │ No close/min │ │ │
│ │ │ │ └────────────────────┘ │ │
│ │ │ │ │ │
│ └────────────────────────────┘ └──────────────────────────┘ │
│ ▲ │ │
│ │ peepApi() internal channel│ │
│ └──────────────────────────────┘ │
│ (AI invokes business APIs) │
└─────────────────────────────────────────────────────────────────┘

Key design decisions:

DecisionReason
Embedded, not floatingAI assistant is a core part of the app, not an optional plugin
Fixed 5:3 ratioEnsures enough space for charts/documents while keeping the AI panel spacious
Disable drag/resize/minimizeNot needed in embedded mode — AI is always visible
Custom AuthProviderFully delegates to peep’s auth system, shares login state
Write confirmationPrevents AI mistakes from causing irreversible data changes

The integration uses npm to install @rtc-agent/component, with changes concentrated in 3 files:

FileChangeNotes
src/core/rtcAgent.tsCreate rtc-agent instanceConfigure embedded mode, AuthProvider, function groups
src/core/rtcAgent/*.tsDefine 40+ functions by business domainZod Schema driven, auto-generates OpenAPI docs
src/App.tsxMount into 5:3 layout right sideslot.replaceChildren(agent)

The AI panel is a native part of the app, not a floating overlay:

// App.tsx — global layout
<div className="rtc-layout">
<div className="rtc-layout-main">
{/* Main content: Ziwei / Da Liu Ren / Liu Yao / Wiki */}
<BrowserRouter basename="/peep">
<Layout>
<Routes>...</Routes>
</Layout>
</BrowserRouter>
</div>
<aside className="rtc-layout-side" aria-label="AI Assistant">
<div ref={rtcSlotRef} id="rtc-slot" />
</aside>
</div>
/* 5:3 split layout */
.rtc-layout {
display: grid;
grid-template-columns: 5fr 3fr;
height: 100vh;
}

Use the createRtcAgent factory function, configuring embedded maximized mode and custom AuthProvider:

src/core/rtcAgent.ts
import { createRtcAgent } from '@rtc-agent/component';
import type { RtcAgentWithLifecycle } from '@rtc-agent/component';
export function createPeepRtcAgent(): RtcAgentWithLifecycle {
return createRtcAgent({
appLabel: 'Peep · Destiny AI Assistant',
agentName: 'PeepAstro',
agentDescription: 'Ziwei Doushu · Da Liu Ren · Liu Yao · Wiki — Destiny Analysis AI',
persona: PERSONA_EN, // or PERSONA_ZH, switches with language
// Embedded mode configuration
window: {
embedded: true,
defaultMode: 'maximized',
draggable: false,
resizable: false,
showMinimize: false,
showMaximize: false,
showClose: false,
},
// Custom AuthProvider (Mode 3)
auth: {
getToken: async () => {
const tokens = getTokens();
if (!tokens) throw new Error('Not authenticated');
return tokens.access_token;
},
refreshToken: async () => {
const tokens = getTokens();
const newTokens = await refreshAccessToken(tokens.refresh_token);
return {
accessToken: newTokens.access_token,
refreshToken: newTokens.refresh_token,
expiresIn: Math.floor((newTokens.expires_at - Date.now()) / 1000),
};
},
isLoggedIn: () => {
const tokens = getTokens();
if (!tokens) return false;
return !isTokenExpired(tokens, 5 * 60 * 1000);
},
logout: async () => {
clearTokens();
},
getUserId: () => {
const tokens = getTokens();
if (!tokens) throw new Error('Not authenticated');
return tokens.user_id;
},
deviceId: getOrCreateDeviceIdSync(),
},
// Business function groups (6 groups, 40+ functions)
groups: FUNCTION_GROUPS,
scenariosURL: `${import.meta.env.BASE_URL}scenarios/`,
workerURL: `${import.meta.env.BASE_URL}rtc-agent/shared-worker.js`,
server: {
url: 'https://rtc-agent.cherish.chat',
redirectUri: `${import.meta.env.BASE_URL}auth/callback.html`,
},
theme: getTheme(),
lang: document.documentElement.lang as 'zh-CN' | 'en-US',
databaseName: 'peep-rtc',
});
}

rtc-agent’s core design is “Agent never touches your source — it operates state through declarative APIs”. Peep drives function definitions with Zod Schema and auto-generates OpenAPI docs the AI can understand:

src/core/rtcAgent/personFunctions.ts
import { z, withMeta } from '@rtc-agent/component';
export const personCreateFunction = {
name: 'PersonCreate',
description:
'Create a new person profile and auto-switch to that person. ' +
'Example: PersonCreate({ name: "Zhang San", date: "1990-05-15", timeIndex: 4, gender: "男" })',
zodSchema: z.object({
name: withMeta(z.string(), { example: 'Zhang San' }).describe('Person name'),
date: withMeta(z.string(), { example: '1990-05-15' })
.describe('Birth date in solar calendar, format YYYY-MM-DD'),
timeIndex: withMeta(z.number().int().min(0).max(12), { example: 4 })
.describe('Time index 0-12, from early Zi hour (0) to late Zi hour (12)'),
gender: withMeta(z.enum(['男', '女']), { example: '男' }).describe('Gender'),
calendar: withMeta(z.enum(['solar', 'lunar']), { example: 'solar' })
.optional().describe('Calendar type, defaults to solar'),
confirmed: CONFIRM_FIELD, // Write operation confirmation flag
}),
handler: createConfirmHandler(schema, (p) => peepApi().PersonCreate(p)),
returns: {
zodSchema: z.object({
id: z.number().describe('Person ID'),
name: z.string().describe('Name'),
// ... other fields
}),
},
};

6 business function groups:

GroupFunctionsCapabilities
person6Person profile CRUD (List / Get / Create / Update / Delete / SetDefault)
ziwei3Ziwei Doushu charting (ZiWei / GetScopeData / SetHoroscopeTime)
daliuren7Da Liu Ren divination (Create / List / View / Delete / BatchView / UpdateTags / UpdateNote)
liuyao7Liu Yao hexagram (Create / List / View / Delete / BatchView / UpdateTags / UpdateNote)
wiki7Knowledge base documents (List / View / Create / Update / BatchView / ReplaceContent / InsertContent)
lunar10Calendar calculations (SolarToLunar / LunarToSolar / GetEightCharacters / GetSolarTerms, etc.)

For destructive operations like Create / Update / Delete, Peep implements a two-step confirmation mechanism to prevent AI mistakes:

src/core/rtcAgent/shared.ts
export const CONFIRM_FIELD = withMeta(z.boolean(), { example: true })
.optional()
.describe(
'⚠️ Safety confirmation flag: on the first call, don\'t pass this field; ' +
'the function returns an operation summary for user confirmation. ' +
'After the user confirms, the AI calls again with confirmed: true to execute.'
);
export function createConfirmHandler(schema, handler) {
return async (params) => {
// First call: confirmed not passed, return operation summary
if (!params.confirmed) {
return {
needsConfirm: true,
summary: `About to create person "${params.name}", birth date ${params.date}`,
};
}
// Second call: confirmed, actually execute
return handler(params);
};
}

Workflow:

  1. AI first calls PersonCreate({ name: 'Zhang San', ... })
  2. Handler detects confirmed not passed, returns { needsConfirm: true, summary: '...' }
  3. AI shows the operation summary to the user, waits for confirmation
  4. After user confirms, AI calls again PersonCreate({ ..., confirmed: true })
  5. Handler detects confirmed: true, actually executes the creation

💡 This way, even if the AI hallucinates or there’s a prompt injection, irreversible operations won’t execute directly.

Peep supports light/dark themes and Chinese/English bilingual, which need to stay in sync with rtc-agent:

// Theme sync: polling check (500ms interval)
export function startThemeSync(): () => void {
let lastTheme = getTheme();
const timer = setInterval(() => {
const current = getTheme();
if (current !== lastTheme) {
lastTheme = current;
syncTheme(); // Sets agent.theme
}
}, 500);
return () => clearInterval(timer);
}
// Language sync: switch via API
export async function syncLocale(locale: Locale): Promise<void> {
if (!_agent) return;
await switchLocale(locale); // Triggers @lit/localize re-render
}
// App.tsx — start sync
useEffect(() => {
const agent = createPeepRtcAgent();
slot.replaceChildren(agent);
const cleanupThemeSync = startThemeSync();
return () => {
cleanupThemeSync();
agent.destroy();
};
}, []);
// Sync on language switch
const { locale } = useI18n();
useEffect(() => {
syncLocale(locale);
}, [locale]);
LayerTechnology
FrameworkReact 19 · TypeScript 6 · Vite 8
StylingTailwind CSS 4 · Light/dark themes
ComponentsRadix UI · shadcn/ui · Lucide Icons
DataDexie (IndexedDB) · Zustand
Divination Enginesiztro · lunar-lite · lunar-typescript
AI Integrationrtc-agent (npm) · Zod Schema
EditorByteMD (Markdown + GFM)
TestingPlaywright E2E
DeploymentGitHub Pages

Beyond the core files above, the integration also touches:

FilePurpose
src/core/rtcAgent/*.tsFunction definitions split by business domain (person / ziwei / daliuren / liuyao / wiki / lunar)
src/core/auth/AuthContext.tsxCustom AuthProvider, fully delegated by rtc-agent
public/scenarios/Preset scenario templates + manifest
public/auth/callback.htmlOAuth callback page

The integration doesn’t modify rtc-agent source at all. @rtc-agent/component is installed via npm and bundled into the host app.

PineappleBond/peep

Live demo: pineapplebond.github.io/peep