256 lines
5.9 KiB
TypeScript
256 lines
5.9 KiB
TypeScript
/**
|
||
* 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 }
|