Files
social-app/.opencode/skills/ag-ui/scripts/state_sync_example.ts
T

256 lines
5.9 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.
/**
* AG-UI 状态同步示例
*
* 展示 Agent 与前端的 Snapshot-Delta 状态同步模式
*
* 参考文档: modules/events.md (行 1067-1155)
*
* 状态管理模式:
* 1. StateSnapshot - 完整状态快照(初始同步/周期性刷新)
* 2. StateDelta - 增量更新(JSON Patch RFC 6902
* 3. MessagesSnapshot - 消息历史快照
*/
import {
AbstractAgent,
RunAgent,
RunAgentInput,
EventType,
BaseEvent,
} from "@ag-ui/client"
import { Observable } from "rxjs"
/**
* Agent 状态定义示例
*/
interface AgentState {
user: {
name: string
preferences: {
theme: "light" | "dark"
language: string
}
}
session: {
currentPage: number
itemsPerPage: number
totalItems: number
}
formData?: {
[key: string]: any
}
}
class StateSyncAgent extends AbstractAgent {
private state: AgentState = {
user: {
name: "Alice",
preferences: {
theme: "light",
language: "en",
},
},
session: {
currentPage: 1,
itemsPerPage: 10,
totalItems: 100,
},
}
run(input: RunAgentInput): RunAgent {
const { threadId, runId } = input
return () =>
new Observable<BaseEvent>((observer) => {
observer.next({
type: EventType.RUN_STARTED,
threadId,
runId,
})
// 1. 发送初始状态快照
observer.next({
type: EventType.STATE_SNAPSHOT,
snapshot: this.state,
})
// 2. 发送消息历史快照
observer.next({
type: EventType.MESSAGES_SNAPSHOT,
messages: [
{
id: "msg_1",
role: "user",
content: "Show me page 2",
},
{
id: "msg_2",
role: "assistant",
content: "Loading page 2...",
},
],
})
// 3. 模拟状态变化 - 分页更新
setTimeout(() => {
// 发送 Delta 更新(JSON Patch 格式)
observer.next({
type: EventType.STATE_DELTA,
delta: [
{ op: "replace", path: "/session/currentPage", value: 2 },
],
})
}, 500)
// 4. 模拟用户偏好更新
setTimeout(() => {
observer.next({
type: EventType.STATE_DELTA,
delta: [
{ op: "replace", path: "/user/preferences/theme", value: "dark" },
],
})
}, 1000)
// 5. 添加新字段(表单数据)
setTimeout(() => {
observer.next({
type: EventType.STATE_DELTA,
delta: [
{
op: "add",
path: "/formData",
value: {
searchQuery: "AG-UI tutorial",
filters: ["beginner", "typescript"],
},
},
],
})
}, 1500)
// 6. 周期性完整快照(确保状态一致性)
setTimeout(() => {
const updatedState: AgentState = {
...this.state,
session: {
...this.state.session,
currentPage: 2,
},
user: {
...this.state.user,
preferences: {
...this.state.user.preferences,
theme: "dark",
},
},
formData: {
searchQuery: "AG-UI tutorial",
filters: ["beginner", "typescript"],
},
}
observer.next({
type: EventType.STATE_SNAPSHOT,
snapshot: updatedState,
})
observer.next({
type: EventType.RUN_FINISHED,
threadId,
runId,
})
observer.complete()
}, 2000)
})
}
}
/**
* 前端状态管理示例(接收端)
*/
class StateManager {
private state: AgentState | null = null
handleEvent(event: BaseEvent) {
switch (event.type) {
case EventType.STATE_SNAPSHOT:
// 完整替换状态
this.state = (event as any).snapshot as AgentState
console.log("[State] Snapshot received:", this.state)
break
case EventType.STATE_DELTA:
// 应用 JSON Patch 增量更新
if (this.state) {
const patches = (event as any).delta
this.state = this.applyPatches(this.state, patches)
console.log("[State] Delta applied:", patches)
console.log("[State] Current state:", this.state)
}
break
case EventType.MESSAGES_SNAPSHOT:
console.log("[Messages] Snapshot:", (event as any).messages)
break
}
}
/**
* 应用 JSON Patch 操作(简化实现)
* 生产环境应使用 json-patch 库
*/
private applyPatches(state: AgentState, patches: any[]): AgentState {
const newState = JSON.parse(JSON.stringify(state))
for (const patch of patches) {
const { op, path, value } = patch
const pathParts = path.split("/").filter(Boolean)
let target: any = newState
// 导航到目标对象的父级
for (let i = 0; i < pathParts.length - 1; i++) {
target = target[pathParts[i]]
}
const lastKey = pathParts[pathParts.length - 1]
switch (op) {
case "replace":
target[lastKey] = value
break
case "add":
target[lastKey] = value
break
case "remove":
delete target[lastKey]
break
}
}
return newState
}
}
// 使用示例
const agent = new StateSyncAgent()
const stateManager = new StateManager()
agent
.runAgent({
runId: "run_state_sync",
threadId: "thread_123",
messages: [],
tools: [],
context: [],
})
.subscribe({
next: (event) => {
stateManager.handleEvent(event)
},
complete: () => console.log("\n[Complete] State sync demo finished"),
})
export { StateSyncAgent, StateManager, AgentState }