Documentation
¶
Overview ¶
Package ihtml 提供"AI 动态前端 UI"功能库: 一个内嵌的空壳页面(微内核) 按用户加载服务端记录的一组 UI Item(HTML/JS/CSS 片段)并在浏览器里执行, 拼装出该用户的完整界面。Item 由 AI 助手通过对话生成并持久化, 页面通过 SSE 实时应用变更, 无需刷新。
库对外的三个扩展点:
- Store: 持久化接口, 内置 Memory/File 两个实现, 生产可换数据库;
- UserResolver: 从宿主已验证的会话或令牌解析当前用户;
- ChatBackend: AI 对话后端, 子包 chat 提供基于 eino 的实现。
Index ¶
- Constants
- Variables
- func DefaultUserResolver(*http.Request) (string, error)
- func DevelopmentUserResolver(r *http.Request) (string, error)
- func ValidatePageName(name string) error
- type APISpec
- type Access
- type AuditEvent
- type Breakpoint
- type Broadcaster
- type ChatAgentStage
- type ChatBackend
- type ChatClientContext
- type ChatClientEvent
- type ChatClientInspection
- type ChatClientInspectionItem
- type ChatClientPage
- type ChatClientViewport
- type ChatMessage
- type ChatModels
- type ChatPlanItem
- type ChatSession
- type ChatSessionContext
- type ChatStreamEvent
- type ChatTurnBudget
- type ChatUsage
- type CurrentTemplate
- type ErrorService
- type Event
- type FileStore
- func (s *FileStore) AddRevision(ctx context.Context, user string, rev Revision, keep int) error
- func (s *FileStore) EraseUser(ctx context.Context, user string) error
- func (s *FileStore) Items(ctx context.Context, user string) ([]Item, error)
- func (s *FileStore) KVDelete(ctx context.Context, user, key string) error
- func (s *FileStore) KVGet(ctx context.Context, user, key string) (json.RawMessage, error)
- func (s *FileStore) KVKeys(ctx context.Context, user string) ([]string, error)
- func (s *FileStore) KVSet(ctx context.Context, user, key string, value json.RawMessage) error
- func (s *FileStore) ReplaceItems(ctx context.Context, user string, items []Item) error
- func (s *FileStore) ReplaceUserState(ctx context.Context, user string, state UserState) error
- func (s *FileStore) ReplaceUserStateVersioned(ctx context.Context, user string, state UserState, expected uint64) error
- func (s *FileStore) Revision(ctx context.Context, user, id string) (Revision, error)
- func (s *FileStore) RevisionItems(ctx context.Context, user, id string) ([]Item, error)
- func (s *FileStore) Revisions(ctx context.Context, user string) ([]RevisionInfo, error)
- func (s *FileStore) UserState(ctx context.Context, user string) (UserState, error)
- type Handler
- func (h *Handler) Close() error
- func (h *Handler) DeleteItems(ctx context.Context, user string, ids []string, by, note string) ([]string, error)
- func (h *Handler) DeletePage(ctx context.Context, user, name, by string) ([]string, error)
- func (h *Handler) DeleteTemplate(ctx context.Context, user, id string, expectedRevision uint64, by string) error
- func (h *Handler) DeleteTemplateSlot(ctx context.Context, user, id, slotID string, expectedRevision uint64, ...) (Template, error)
- func (h *Handler) ExportUser(ctx context.Context, user string) (*UserExport, error)
- func (h *Handler) GetCurrentTemplate(ctx context.Context, user, page string) (CurrentTemplate, error)
- func (h *Handler) GetTemplate(ctx context.Context, user, id string) (Template, error)
- func (h *Handler) ImportUser(ctx context.Context, user string, export *UserExport) error
- func (h *Handler) KVCompareAndSet(ctx context.Context, user, key string, value json.RawMessage, ...) (string, error)
- func (h *Handler) KVDelete(ctx context.Context, user, key string) error
- func (h *Handler) KVGet(ctx context.Context, user, key string) (json.RawMessage, error)
- func (h *Handler) KVGetWithRevision(ctx context.Context, user, key string) (json.RawMessage, string, error)
- func (h *Handler) KVSet(ctx context.Context, user, key string, value json.RawMessage) error
- func (h *Handler) ListItems(ctx context.Context, user string) ([]Item, error)
- func (h *Handler) ListPages(ctx context.Context, user string) ([]Page, error)
- func (h *Handler) ListRevisions(ctx context.Context, user string) ([]RevisionInfo, error)
- func (h *Handler) ListTemplates(ctx context.Context, user string) ([]Template, error)
- func (h *Handler) PageErrors(ctx context.Context, user string) ([]PageError, error)
- func (h *Handler) PublishTemplate(ctx context.Context, user, id string, expectedRevision uint64, by string) (Template, error)
- func (h *Handler) PurgeUser(ctx context.Context, user string) error
- func (h *Handler) PutCurrentTemplate(ctx context.Context, user, page string, input Template, ...) (Template, error)
- func (h *Handler) PutItems(ctx context.Context, user string, items []Item, by, note string) error
- func (h *Handler) PutTemplate(ctx context.Context, user string, input Template, expectedRevision uint64, ...) (Template, error)
- func (h *Handler) ReplaceItems(ctx context.Context, user string, items []Item, by, note string) error
- func (h *Handler) ReportErrors(ctx context.Context, user string, errs []PageError) error
- func (h *Handler) RevisionItems(ctx context.Context, user, id string) ([]Item, error)
- func (h *Handler) Rollback(ctx context.Context, user, revisionID, by string) error
- func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ServiceForRequest(r *http.Request) (ScopedService, error)
- func (h *Handler) SetPage(ctx context.Context, user string, page Page) error
- func (h *Handler) Stats() map[string]int64
- type HandlerConfigError
- type Item
- type ItemService
- type ItemType
- type KVConflictError
- type KVService
- type LayoutMode
- type Limits
- type MemoryStore
- func (s *MemoryStore) AddRevision(_ context.Context, user string, rev Revision, keep int) error
- func (s *MemoryStore) EraseUser(_ context.Context, user string) error
- func (s *MemoryStore) Items(_ context.Context, user string) ([]Item, error)
- func (s *MemoryStore) KVDelete(_ context.Context, user, key string) error
- func (s *MemoryStore) KVGet(_ context.Context, user, key string) (json.RawMessage, error)
- func (s *MemoryStore) KVKeys(_ context.Context, user string) ([]string, error)
- func (s *MemoryStore) KVSet(_ context.Context, user, key string, value json.RawMessage) error
- func (s *MemoryStore) ReplaceItems(_ context.Context, user string, items []Item) error
- func (s *MemoryStore) ReplaceUserState(_ context.Context, user string, state UserState) error
- func (s *MemoryStore) ReplaceUserStateVersioned(_ context.Context, user string, state UserState, expected uint64) error
- func (s *MemoryStore) Revision(_ context.Context, user, id string) (Revision, error)
- func (s *MemoryStore) RevisionItems(_ context.Context, user, id string) ([]Item, error)
- func (s *MemoryStore) Revisions(_ context.Context, user string) ([]RevisionInfo, error)
- func (s *MemoryStore) UserState(_ context.Context, user string) (UserState, error)
- type Metrics
- type ModelLister
- type Option
- func WithAPIs(apis ...APISpec) Option
- func WithAccessPolicy(fn func(r *http.Request, user string) Access) Option
- func WithAccessPolicyE(fn func(r *http.Request, user string) (Access, error)) Option
- func WithAudit(fn func(ctx context.Context, e AuditEvent)) Option
- func WithBroadcaster(b Broadcaster) Option
- func WithCORS(origins ...string) Option
- func WithChatBackend(b ChatBackend) Option
- func WithLimits(l Limits) Option
- func WithLogger(l *slog.Logger) Option
- func WithMaxRevisions(n int) Option
- func WithMessageOrigins(origins ...string) Option
- func WithMetrics(m Metrics) Option
- func WithOrderedBroadcaster(b Broadcaster) Option
- func WithPageConfig(fn func(r *http.Request, user string) map[string]any) Option
- func WithPageTitle(title string) Option
- func WithUserResolver(fn UserResolver) Option
- func WithWSOriginCheck(fn func(*http.Request) bool) Option
- type Page
- type PageError
- type PageService
- type Placement
- type ProposedToolCall
- type Revision
- type RevisionInfo
- type RevisionService
- type RevisionStore
- type ScopedErrorService
- type ScopedItemService
- type ScopedKVService
- type ScopedPageService
- type ScopedRevisionService
- type ScopedService
- type ScopedTemplateService
- type ScopedVersionedKVService
- type Service
- type Slot
- type Store
- type Template
- type TemplateConflictError
- type TemplateNode
- type TemplateNodeKind
- type TemplateService
- type TemplateVersion
- type UserExport
- type UserResolver
- type UserState
- type UserStateStore
- type VersionedBroadcaster
- type VersionedKVService
- type VersionedUserStateStore
Examples ¶
Constants ¶
const ( // ChatProtocolVersion identifies the additive WebSocket/capabilities contract. ChatProtocolVersion = 1 // MaxChatOutboundFrameBytes is the public wire ceiling for one server-to-client // chat event. Backends that pre-build signed or otherwise atomic payloads // should reserve envelope headroom below this value. MaxChatOutboundFrameBytes = (1 << 20) - 1 ChatClientUserMessage = "user_message" ChatClientConfirmTool = "confirm_tool" // ChatClientCancelTool revokes one signed confirmation card without running // it. The backend verifies the card and atomically consumes its nonce, so a // later confirm_tool replay is rejected. ChatClientCancelTool = "cancel_tool" ChatClientCancel = "cancel" // ChatClientInspectResult 是内核对 inspect_request 的应答帧, // 携带 inspect_id 与实时页面渲染状态(不可信数据)。 ChatClientInspectResult = "inspect_result" ChatStreamTurnStart = "turn_start" ChatStreamAssistantStatus = "assistant_status" ChatStreamAssistantThinking = "assistant_thinking" ChatStreamAssistantDelta = "assistant_delta" // ChatStreamAssistantReset discards assistant deltas already projected for // the current turn. The built-in backend uses it before a streaming retry; // legacy clients may ignore it because assistant_done remains authoritative. ChatStreamAssistantReset = "assistant_reset" ChatStreamAssistantPlan = "assistant_plan" ChatStreamAssistantAgent = "assistant_agent" ChatStreamAssistantBudget = "assistant_budget" ChatStreamToolStart = "tool_start" ChatStreamConfirmationRequired = "confirmation_required" // ChatStreamCancelToolResult is the authoritative acknowledgement for one // cancel_tool request. ToolID and ConfirmationNonce correlate the result to // the exact card without echoing its signed token or arguments. ChatStreamCancelToolResult = "cancel_tool_result" ChatStreamToolResult = "tool_result" ChatStreamAssistantDone = "assistant_done" ChatStreamTurnError = "turn_error" // ChatStreamInspectRequest 由服务端(ui_page_inspect 工具)发起, 请求内核 // 回传当前页面的实时渲染状态; 内核以 inspect_result 帧应答同一 inspect_id。 ChatStreamInspectRequest = "inspect_request" ChatToolPending = "pending" ChatToolRunning = "running" ChatToolSuccess = "success" ChatToolError = "error" ChatToolCancelled = "cancelled" // ChatToolBudget* 是 assistant_budget 快照和内置语义工具 // 返回的稳定机器可读值;客户端不应匹配本地化错误文案。 ChatToolBudgetExhaustedCode = "tool_call_budget_exhausted" ChatToolBudgetStatusExhausted = "budget_exhausted" ChatToolBudgetLimitTotal = "total_tool_calls" ChatToolBudgetLimitPerTool = "per_tool_calls" ChatToolBudgetLimitIdentical = "identical_arguments" ChatRiskLow = "low" ChatRiskMedium = "medium" ChatRiskHigh = "high" ChatRiskBlocked = "blocked" // ChatContentFormat* describe assistant answer bodies. Status, reasoning, // errors and tool summaries remain plain text regardless of this field. ChatContentFormatText = "text" ChatContentFormatMarkdown = "markdown" ChatContentFormatHTML = "html" )
const ( // EventItems 表示 Item 集合变化。 EventItems = "items" // EventTemplates 表示布局模板变化。 EventTemplates = "templates" // EventKV 表示某个用户级 KV 键变化(Event.Key 为键名)。 EventKV = "kv" // EventPages 表示页面注册表变化。 EventPages = "pages" // EventResync 要求客户端放弃增量、全量刷新(Event.Reason 说明原因)。 EventResync = "resync" // EventOpPut 是 items/templates 事件的"写入/更新"操作。 EventOpPut = "put" // EventOpPublish 是模板 Draft 发布为 Published 的操作。 EventOpPublish = "publish" // EventOpDelete 是删除操作(Event.IDs 为目标)。 EventOpDelete = "delete" // EventOpReplace 是 Item 全集整体替换操作。 EventOpReplace = "replace" // EventOpRollback 是快照回滚操作。 EventOpRollback = "rollback" // EventOpImport 是全量导入操作。 EventOpImport = "import" // EventOpPurge 是用户数据清除操作。 EventOpPurge = "purge" // ResyncSubscribe: 订阅建立时的基线 resync。 ResyncSubscribe = "subscribe" // ResyncOverflow: 订阅积压溢出、增量队列已被清空。 ResyncOverflow = "overflow" // ResyncPeriodic: 周期性校准(兜底不能报告丢包的外部 Broadcaster)。 ResyncPeriodic = "periodic" // ResyncExternal: 外部/跨副本 Broadcaster 收到变更通知后要求全量刷新。 // 持久化提交与消息发布不在同一事务内,因此外部通道不携带可能 // 因发布反序而过期的增量载荷,客户端改为重读已提交的权威状态。 ResyncExternal = "external" )
SSE 事件协议常量。这些字符串是公共线格式契约(见 COMPATIBILITY.md): 同一协议版本内只做增量扩展, 消费者可安全地按常量比较。
const ( // MaxItemContentBytes 是单个 Item 内容的上限。 MaxItemContentBytes = 512 << 10 // MaxUserItemBytes bounds the aggregate serialized source carried by all // Items for one user. Without an aggregate bound, repeated valid 512 KiB // writes could make every list, snapshot and AI inventory operation copy // hundreds of MiB at once. MaxUserItemBytes = 8 << 20 // MaxUserRevisionBytes bounds the aggregate Item/Template source retained in // automatic snapshots for one user. Count-only retention is insufficient // when a few otherwise-valid snapshots are several MiB each. MaxUserRevisionBytes = 64 << 20 // MaxRevisionsPerUser is the hard safety ceiling even when count-based // retention is disabled with WithMaxRevisions(0). The byte ceiling remains // the primary limit for content-heavy snapshots; this one bounds empty or // nearly-empty revision metadata and third-party Store implementations. MaxRevisionsPerUser = 4096 // MaxItemsPerUser 是单个用户 Item 数量上限。 MaxItemsPerUser = 512 // Item metadata is intended for compact runtime hints such as slot/module. // Bound it separately so small/empty content cannot hide an unbounded map. MaxItemMetaEntries = 64 MaxItemMetaKeyBytes = 128 MaxItemMetaValueBytes = 1024 // DefaultMaxRevisions 是默认保留的历史快照条数。 DefaultMaxRevisions = 50 // MaxPageErrors 是每个用户保留的页面错误条数。 MaxPageErrors = 50 // CurrentDataVersion 是 Revision 与 UserExport 当前使用的数据契约版本。 // v1/零值快照只包含 Items;v2 增加页面注册表;v3 增加模板目录。 CurrentDataVersion = 3 )
const ( // MaxKVEntriesPerUser is a hard structural ceiling across public and // kernel-owned keys. Limits.MaxKVKeysPerUser remains the configurable public // quota; this ceiling keeps an explicitly unlimited or malformed Store from // creating an unbounded map/list operation. MaxKVEntriesPerUser = 4096 // MaxUserKVBytes bounds aggregate key/value source bytes copied in a complete // UserState. It is deliberately above the default 256 x 64 KiB public quota // while remaining finite for third-party Store implementations. MaxUserKVBytes = 32 << 20 )
const ( // MaxTemplateVersionBytes bounds one Draft or Published structural document. // The estimate counts every source string/RawMessage plus conservative JSON // framing, preventing individually valid properties from multiplying into a // hundreds-of-MiB template. MaxTemplateVersionBytes = 1 << 20 // MaxTemplateCatalogBytes bounds all Draft/Published structures retained for // one user. Per-document limits alone would still allow 128 maximum-sized // templates to multiply every catalog load and snapshot. MaxTemplateCatalogBytes = 8 << 20 // MaxTemplateCatalogStorageBytes bounds the encoded internal catalog record. // It is intentionally larger than the logical structural budget to allow for // JSON field framing and escaping, while remaining below the whole-user KV // ceiling. Writers in this package disable unnecessary HTML escaping. MaxTemplateCatalogStorageBytes = 16 << 20 )
const ItemLayoutKVPrefix = "item-layout."
ItemLayoutKVPrefix identifies editor-owned per-Item layout records. These records remain ordinary KV so the browser can persist them, but lifecycle operations such as deleting a page must keep them in sync with that page.
const MaxKVValueBytes = 64 << 10
MaxKVValueBytes 是单个 KV 值的上限。
const MissingKVRevision = "missing"
MissingKVRevision 是 KVGetWithRevision 读取一个不存在的键时,调用方在首次 KVCompareAndSet 中使用的版本前置条件。它与已有值的 SHA-256 版本空间分离。
const ReservedKVPrefix = "__ihtml"
ReservedKVPrefix identifies kernel-owned keys that are intentionally hidden from the public KVService. Use the dedicated Page, Template, Error, and layout APIs instead of reading or mutating these records directly.
Variables ¶
var ( // ErrServiceScope 表示会话后端尝试访问非当前用户数据。 ErrServiceScope = errors.New("service user scope mismatch") // ErrAccessDenied 表示连接建立后鉴权/访问策略已撤销。 ErrAccessDenied = errors.New("access denied") // ErrWriteRateLimited 表示 HTTP/AI 共享的用户写频率额度已用尽。 ErrWriteRateLimited = errors.New("write rate limit exceeded") )
var ( // ErrConflict indicates a failed optimistic-concurrency (CAS) check. ErrConflict = errors.New("ihtml: revision conflict") )
var ErrInvalidHandlerConfig = errors.New("ihtml: invalid handler configuration")
ErrInvalidHandlerConfig matches constructor failures reported by NewHandler.
var ErrInvalidServiceScope = errors.New("ihtml: invalid service scope")
ErrInvalidServiceScope is returned when a user-scoped service cannot be constructed safely.
var ErrKVConflict = errors.New("ihtml: kv revision conflict")
ErrKVConflict 表示一次带版本前置条件的 KV 写入所依据的值已经变化。 调用方必须重新读取该键,并决定是重放本地修改还是让用户处理冲突。
var ErrNotFound = errors.New("ihtml: not found")
ErrNotFound 表示请求的对象(KV 键/快照)不存在。
var ErrReservedKVKey = errors.New("ihtml: reserved kv key")
ErrReservedKVKey is returned by the public KVService when a caller attempts to read, write, or delete a kernel-owned key.
var ErrVersionConflict = errors.New("ihtml: user state version conflict")
ErrVersionConflict 由 VersionedUserStateStore.ReplaceUserStateVersioned 返回, 表示存储中的用户状态版本已不等于调用方读取时的版本(通常是另一个副本先写入)。 调用方应重读状态、重放变更后重试。
Functions ¶
func DefaultUserResolver ¶
DefaultUserResolver 是安全默认: 在宿主未显式接入认证时拒绝请求。 ihtml 无法自行判断哪个 cookie/header 代表已验证会话,因此生产 服务必须通过 WithUserResolver 提供宿主的验证逻辑。
func DevelopmentUserResolver ¶
DevelopmentUserResolver 从 uid query、X-Ihtml-User header 或 ihtml_uid cookie 直接取用户名。这些值都由客户端控制,只能用于本地演示/测试, 绝不应用于生产环境。
func ValidatePageName ¶
ValidatePageName 校验页面名(小写字母数字开头, 允许 -_)。
Types ¶
type APISpec ¶
type APISpec struct {
Name string `json:"name"` // 标识, 如 player_search
Title string `json:"title,omitempty"` // 一句话名称
Method string `json:"method"` // 目前探测工具只放行 GET
Path string `json:"path"` // 页面同源绝对路径, 如 /capi/players
Description string `json:"description"` // 参数与返回结构说明(给 AI 看)
}
APISpec 描述宿主提供的一个业务 API, 用于告知 AI "有哪些接口可用"。
type Access ¶
type Access struct {
// ReadOnly 拒绝一切写操作(HTTP 写接口与 AI 工具写), 用于
// 客服代查/演示账号/免费套餐只读等场景。
ReadOnly bool
// ChatDisabled 对该用户禁用 AI 对话(入口隐藏, WS 拒绝)。
ChatDisabled bool
}
Access 是一次请求的访问策略结果。
type AuditEvent ¶
type AuditEvent struct {
Time time.Time `json:"time"`
User string `json:"user"`
Action string `json:"action"` // items.put/items.delete/items.replace/items.rollback/kv.set/kv.delete/page.set/page.delete/user.import/user.purge
By string `json:"by"` // api / chat / import ...
Note string `json:"note,omitempty"`
Detail json.RawMessage `json:"detail,omitempty"` // ids/key/page 等结构化明细
}
AuditEvent 是一次数据变更的审计记录, 经 WithAudit 同步交给宿主留痕。 与滚动淘汰的快照不同, 审计流不丢失历史。
type Breakpoint ¶
type Breakpoint struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
MinWidth int `json:"min_width,omitempty"`
MaxWidth int `json:"max_width,omitempty"`
Columns int `json:"columns,omitempty"`
Gap float64 `json:"gap,omitempty"`
}
Breakpoint defines a responsive editing surface. A zero MinWidth/MaxWidth is open-ended; Columns and Gap apply to flow layout.
type Broadcaster ¶
type Broadcaster interface {
// Publish 向该用户的所有订阅者投递事件。实现应保证不阻塞调用方。
// 若实现会丢弃积压事件,必须改为投递 Type="resync" 以显式
// 告知订阅者,不得静默丢弃。
Publish(user string, ev Event)
// Subscribe 订阅该用户的事件流; cancel 幂等, 调用后通道会被关闭。
Subscribe(user string) (ch <-chan Event, cancel func())
}
Broadcaster 把变更事件广播给某用户的全部页面订阅者。 默认实现是进程内 hub(单副本); 多副本部署时用 WithBroadcaster 接入 Redis Pub/Sub / NATS 等跨节点通道, 否则副本 A 的写入无法推到 连接在副本 B 上的页面。
Handler 只在内置进程内 hub 上发送低延迟增量。通过 WithBroadcaster 注入的外部 Broadcaster 收到 reason=external 的 resync 通知:Store 提交顺序与外部消息发布顺序可能相反, 全量重读才能保证客户端不应用过期载荷。只有宿主把同一用户在所有 副本上的完整写调用(含提交到 Publish)全局串行化时,才可显式使用 WithOrderedBroadcaster 保留增量载荷;Broadcaster 内部排序本身不够。
type ChatAgentStage ¶
type ChatAgentStage struct {
Mode string `json:"mode"` // fast / deep
Agent string `json:"agent"` // root / ui_inspector / ui_reviewer
TaskID string `json:"task_id,omitempty"`
ParentTaskID string `json:"parent_task_id,omitempty"`
Phase string `json:"phase,omitempty"` // turn / plan / inspect / review / approval
Status string `json:"status"` // running / completed / paused / error
Summary string `json:"summary,omitempty"`
// ApprovalResume 明示确认后的恢复能力。当前 deep 使用 manual_tool_only:
// 只执行确认卡,不会静默恢复或宣称整个计划完成。
ApprovalResume string `json:"approval_resume,omitempty"`
}
ChatAgentStage 描述主 Agent 或只读子 Agent 的阶段状态。它独立于工具卡, 前端可据此渲染层级任务,而无需解析模型文本。
type ChatBackend ¶
type ChatBackend interface {
NewSession(ctx context.Context, sctx *ChatSessionContext) (ChatSession, error)
}
ChatBackend 为每条 WS 连接创建一个会话。子包 chat 提供基于 eino 的实现。
type ChatClientContext ¶
type ChatClientContext struct {
Viewport ChatClientViewport `json:"viewport"`
Page ChatClientPage `json:"page"`
Locale string `json:"locale,omitempty"`
TimeZone string `json:"time_zone,omitempty"`
UserAgent string `json:"user_agent,omitempty"`
}
ChatClientContext 是随 user_message 上送的页面上下文。
type ChatClientEvent ¶
type ChatClientEvent struct {
Type string `json:"type"`
Messages []ChatMessage `json:"messages,omitempty"`
Model string `json:"model,omitempty"`
// AgentMode 为空或 fast 时走低延迟单 Agent;deep 仅在服务端显式开启后可用。
AgentMode string `json:"agent_mode,omitempty"`
ToolsEnabled *bool `json:"tools_enabled,omitempty"`
ClientContext ChatClientContext `json:"client_context"`
ToolCall *ProposedToolCall `json:"tool_call,omitempty"`
// InspectID/Inspect 仅用于 inspect_result: 应答服务端的同 ID inspect_request。
InspectID string `json:"inspect_id,omitempty"`
Inspect *ChatClientInspection `json:"inspect,omitempty"`
}
ChatClientEvent 是客户端发来的 WS 事件: type = user_message | confirm_tool | cancel_tool | cancel | inspect_result。
type ChatClientInspection ¶
type ChatClientInspection struct {
Page string `json:"page,omitempty"` // 当前页面名, 空 = 单页/根
Viewport ChatClientViewport `json:"viewport"`
ThemeMode string `json:"theme_mode,omitempty"` // light / dark
Total int `json:"total,omitempty"` // 过滤后的 Item 总数
Offset int `json:"offset,omitempty"`
Limit int `json:"limit,omitempty"`
NextOffset int `json:"next_offset,omitempty"`
Truncated bool `json:"truncated,omitempty"` // 仍有后续分页
Items []ChatClientInspectionItem `json:"items,omitempty"`
Errors []string `json:"errors,omitempty"` // 最近页面错误摘要
VisibleText string `json:"visible_text,omitempty"` // 全页可见文本(截断)
}
ChatClientInspection 是内核对 inspect_request 的完整应答载荷(不可信数据)。
type ChatClientInspectionItem ¶
type ChatClientInspectionItem struct {
ID string `json:"id"`
Type string `json:"type,omitempty"`
Page string `json:"page,omitempty"`
Slot string `json:"slot,omitempty"`
Present bool `json:"present"` // 是否有已连接的 DOM 节点
Visible bool `json:"visible,omitempty"` // 是否实际占据可见空间
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
Text string `json:"text,omitempty"` // 渲染文本摘要(截断)
Error string `json:"error,omitempty"` // 该 Item 最近一条运行错误
}
ChatClientInspectionItem 是内核回传的单个 Item 实时渲染状态(不可信数据)。
type ChatClientPage ¶
type ChatClientPage struct {
Path string `json:"path,omitempty"`
Title string `json:"title,omitempty"`
VisibleText string `json:"visible_text,omitempty"`
}
ChatClientPage 描述当前页面状态(路径/标题/可见文本), 属不可信数据。
type ChatClientViewport ¶
type ChatClientViewport struct {
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
DevicePixelRatio any `json:"device_pixel_ratio,omitempty"`
Mode string `json:"mode,omitempty"` // pc / mobile
}
ChatClientViewport 描述前端视口, 供 AI 做屏幕自适应。
type ChatMessage ¶
ChatMessage 是一条对话消息。
type ChatModels ¶
type ChatModels struct {
ProtocolVersion int `json:"protocol_version"`
Provider string `json:"provider,omitempty"`
ModelReady bool `json:"model_ready"`
// PreviewOnly 表示后端只提供确定性的界面预览,不代表真实模型已连接。
// 客户端可继续开放输入以演示交互,但必须向用户说明回答并非来自 AI。
PreviewOnly bool `json:"preview_only,omitempty"`
CurrentModel string `json:"current_model"`
Models []string `json:"models"`
AgentModes []string `json:"agent_modes,omitempty"`
// DeepApprovalResume 当前为 manual_tool_only;未来安全接入 ADK checkpoint
// 后可升级为 checkpoint,而无需客户端猜测。
DeepApprovalResume string `json:"deep_approval_resume,omitempty"`
RequestTimeoutMS int64 `json:"request_timeout_ms"`
// ModelSelect 为 true 时前端展示模型选择器(默认隐藏——
// 模型是运维概念, 不该暴露给终端用户)。
ModelSelect bool `json:"model_select,omitempty"`
}
ChatModels 是 GET /api/chat/models 的响应。
type ChatPlanItem ¶
type ChatPlanItem struct {
ID string `json:"id"`
Content string `json:"content"`
Status string `json:"status"` // pending / in_progress / completed
}
ChatPlanItem 是 Deep 模式向前端投影的一条结构化任务。
type ChatSession ¶
type ChatSession interface {
HandleEvent(ctx context.Context, ev *ChatClientEvent)
Close()
}
ChatSession 处理一条连接上的客户端事件。实现应自行做好并发控制: HandleEvent 会在读循环 goroutine 里被顺序调用, 不应长时间阻塞。
type ChatSessionContext ¶
type ChatSessionContext struct {
// User 是 UserResolver 解析出的用户。
User string
// Service 用于读改 UI 数据。为兼容已有 ChatBackend 保留;新实现应优先
// 使用 ScopedService,避免意外传入其他用户标识。
Service Service
// ScopedService 已绑定并持续校验当前用户,不暴露跨租户 user 参数。
ScopedService ScopedService
// Emit 向前端推流式事件, 并发安全; 连接断开后返回错误。
Emit func(ChatStreamEvent) error
// Request 是发起 WS 的原始请求, 供后端取额外参数/推导同源地址。
Request *http.Request
// APIs 是 WithAPIs 声明的业务 API 目录。
APIs []APISpec
// Access 是建立会话时的访问策略快照(只读时后端应隐藏/
// 拒绝写工具)。Service 还会在每次调用时动态重验,防止撤权空窗。
Access Access
}
ChatSessionContext 是为一条 WS 连接建立会话时交给后端的上下文。
type ChatStreamEvent ¶
type ChatStreamEvent struct {
Type string `json:"type"`
RunID string `json:"run_id,omitempty"`
TurnID string `json:"turn_id,omitempty"`
Seq int64 `json:"seq,omitempty"`
Delta string `json:"delta,omitempty"`
// ContentFormat applies only to assistant_delta.delta and
// assistant_done.message. Empty preserves the legacy auto-detected format;
// clients must render unknown values as plain text. HTML is always untrusted
// model output: third-party clients must parse it inertly and rebuild it
// through a strict element/attribute/URL allowlist before attaching to DOM.
ContentFormat string `json:"content_format,omitempty"`
Thinking string `json:"thinking,omitempty"`
// ThinkingID groups the incremental reasoning events produced by one model
// output. Legacy servers and clients may omit or ignore it.
ThinkingID string `json:"thinking_id,omitempty"`
// ThinkingDone marks the last event in a reasoning segment. A non-streamed
// reasoning response can carry both Thinking and ThinkingDone in one event.
ThinkingDone bool `json:"thinking_done,omitempty"`
Status string `json:"status,omitempty"`
Message string `json:"message,omitempty"`
ModelReady bool `json:"model_ready,omitempty"`
ToolCalls []ProposedToolCall `json:"tool_calls,omitempty"`
ToolCall *ProposedToolCall `json:"tool_call,omitempty"`
// ToolID/ConfirmationNonce are used by cancel_tool_result. They deliberately
// expose only the identifiers the client already sent, never the signed card.
ToolID string `json:"tool_id,omitempty"`
ConfirmationNonce string `json:"confirmation_nonce,omitempty"`
ToolResult json.RawMessage `json:"tool_result,omitempty"`
ToolName string `json:"tool_name,omitempty"`
ToolTitle string `json:"tool_title,omitempty"`
ToolStatus string `json:"tool_status,omitempty"`
ToolInput string `json:"tool_input,omitempty"`
ToolSummary string `json:"tool_summary,omitempty"`
ToolDebug json.RawMessage `json:"tool_debug,omitempty"`
Plan []ChatPlanItem `json:"plan,omitempty"`
Agent *ChatAgentStage `json:"agent,omitempty"`
Budget *ChatTurnBudget `json:"budget,omitempty"`
// FinishReason and Usage are populated on assistant_done when the provider
// exposes them. FinishReason is provider-defined; clients should display it
// only as diagnostic metadata and must tolerate unknown values.
FinishReason string `json:"finish_reason,omitempty"`
Usage *ChatUsage `json:"usage,omitempty"`
// Repaired reports that the visible answer was safely regenerated from
// already-completed tool evidence after the original final answer was empty
// or truncated. No tool is re-executed during this repair.
Repaired bool `json:"repaired,omitempty"`
Error string `json:"error,omitempty"`
// InspectID 仅用于 inspect_request: 内核应答 inspect_result 时原样带回。
InspectID string `json:"inspect_id,omitempty"`
InspectOffset int `json:"inspect_offset,omitempty"`
InspectLimit int `json:"inspect_limit,omitempty"`
InspectIDs []string `json:"inspect_ids,omitempty"`
}
ChatStreamEvent 是服务端推给前端的流式事件, type 取值: turn_start / assistant_status / assistant_thinking / assistant_delta / assistant_reset / assistant_plan / assistant_agent / assistant_budget / tool_start / confirmation_required / cancel_tool_result / tool_result / assistant_done / turn_error。
type ChatTurnBudget ¶
type ChatTurnBudget struct {
ModelCalls int64 `json:"model_calls"`
MaxModelCalls int64 `json:"max_model_calls,omitempty"`
Subtasks int64 `json:"subtasks"`
MaxSubtasks int64 `json:"max_subtasks,omitempty"`
ToolCalls int64 `json:"tool_calls"`
MaxToolCalls int64 `json:"max_tool_calls,omitempty"`
MaxToolCallsPerTool int64 `json:"max_tool_calls_per_tool,omitempty"`
MaxRepeatedToolCalls int64 `json:"max_repeated_tool_calls,omitempty"`
// ToolBudgetCode/Limit/Tool 仅在语义工具预算已锁存耗尽时设置。
ToolBudgetCode string `json:"tool_budget_code,omitempty"`
ToolBudgetLimit string `json:"tool_budget_limit,omitempty"`
ToolBudgetTool string `json:"tool_budget_tool,omitempty"`
DeadlineUnixMS int64 `json:"deadline_unix_ms,omitempty"`
}
ChatTurnBudget 是一轮共享预算的快照;Deep 根 Agent 与所有子 Agent 共用。
type ChatUsage ¶
type ChatUsage struct {
InputTokens int64 `json:"input_tokens,omitempty"`
OutputTokens int64 `json:"output_tokens,omitempty"`
TotalTokens int64 `json:"total_tokens,omitempty"`
CachedInputTokens int64 `json:"cached_input_tokens,omitempty"`
ReasoningTokens int64 `json:"reasoning_tokens,omitempty"`
}
ChatUsage is the normalized token accounting reported by the model provider. Providers may omit individual counters; zero therefore means either zero usage or unavailable data. Usage is observational and must never be used as an authorization or billing source without reconciliation at the provider.
type CurrentTemplate ¶
type CurrentTemplate struct {
Template Template `json:"template"`
Persisted bool `json:"persisted"`
Migrated bool `json:"migrated,omitempty"`
}
CurrentTemplate is returned by the page-scoped convenience API. Persisted is false for a deterministic empty/legacy-derived draft that has not been saved.
type ErrorService ¶
type ErrorService interface {
// PageErrors 返回内核上报的最近页面错误(新的在前)。
PageErrors(ctx context.Context, user string) ([]PageError, error)
// ReportErrors 追加页面错误记录。
ReportErrors(ctx context.Context, user string, errs []PageError) error
}
ErrorService is the browser runtime error capability.
type Event ¶
type Event struct {
// Type: "items" 表示 Item 集合变化; "templates" 表示模板变化;
// "kv" 表示某个 KV 键变化; "pages" 表示页面注册表变化;
// "resync" 表示客户端必须全量刷新。
Type string `json:"type"`
// Op 是 items/templates 事件的操作: put / publish / delete / replace /
// rollback / import / purge。
Op string `json:"op,omitempty"`
// Items 携带 put 的新内容, 内核原地应用。
Items []Item `json:"items,omitempty"`
// Templates 携带模板 put/publish 的最新完整值。
Templates []Template `json:"templates,omitempty"`
// IDs 携带 delete 的目标。
IDs []string `json:"ids,omitempty"`
// Key 是 kv 事件变化的键名。
Key string `json:"key,omitempty"`
// By 是变更来源(api / chat / import 等), 供宿主联动与前端展示。
By string `json:"by,omitempty"`
// Version 是某用户事件流的单调版本。默认 hub 总会填充;
// 外部 Broadcaster 若能提供全局单调版本也应填充。
Version uint64 `json:"version,omitempty"`
// Reason 说明 resync 原因: subscribe / overflow / periodic / external。
Reason string `json:"reason,omitempty"`
}
Event 是推送给页面内核的实时事件(经 SSE 下发)。
type FileStore ¶
type FileStore struct {
// contains filtered or unexported fields
}
FileStore 把每个用户的数据存成一个 JSON 文件(<dir>/u<sha256>.json), 写入走临时文件+fsync+rename+目录同步并保持原子替换;在平台/文件系统 支持文件与目录同步语义时同时提供崩溃/掉电持久性。不支持目录同步的 平台只能保证原子可见性,不能承诺掉电后目录项已落盘。 适合单机小规模部署与本地开发; 大规模生产建议使用 sqlstore 或自行实现 Store。
func NewFileStore ¶
NewFileStore 创建文件存储, dir 不存在时自动创建。
func (*FileStore) AddRevision ¶
AddRevision 实现 Store。
func (*FileStore) ReplaceItems ¶
ReplaceItems 实现 Store。
func (*FileStore) ReplaceUserState ¶
ReplaceUserState 原子替换用户的完整状态。
func (*FileStore) ReplaceUserStateVersioned ¶
func (s *FileStore) ReplaceUserStateVersioned(ctx context.Context, user string, state UserState, expected uint64) error
ReplaceUserStateVersioned 实现 VersionedUserStateStore。
func (*FileStore) RevisionItems ¶
RevisionItems 实现 Store。
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler 是库的 HTTP 入口: 服务空壳页面、内核资源与全部 /api/* 接口。 可挂在根路径, 也可经 http.StripPrefix 挂在任意子路径(内核用相对路径请求)。
func New ¶
New creates a Handler and preserves the original panic-on-programmer-error behavior for source compatibility. NewHandler is recommended for SDK users that want startup errors returned to their application.
func NewHandler ¶
NewHandler validates dependencies and options, then creates a Handler.
Example ¶
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"github.com/zdypro888/ihtml"
)
func main() {
handler, err := ihtml.NewHandler(
ihtml.NewMemoryStore(),
ihtml.WithUserResolver(func(*http.Request) (string, error) { return "tenant-user", nil }),
ihtml.WithPageTitle("Embedded workspace"),
)
if err != nil {
panic(err)
}
defer handler.Close()
request := httptest.NewRequest(http.MethodGet, "/api/config", nil)
response := httptest.NewRecorder()
handler.ServeHTTP(response, request)
fmt.Println(response.Code)
}
Output: 200
func (*Handler) Close ¶
Close 优雅停机: 拒绝新连接、断开全部 SSE/WS。应在 http.Server.Shutdown 前调用(WS 是被劫持的连接, Server.Shutdown 不会等待它们)。
func (*Handler) DeleteItems ¶
func (h *Handler) DeleteItems(ctx context.Context, user string, ids []string, by, note string) ([]string, error)
DeleteItems 实现 Service。
func (*Handler) DeletePage ¶
DeletePage 实现 Service。
func (*Handler) DeleteTemplate ¶
func (h *Handler) DeleteTemplate(ctx context.Context, user, id string, expectedRevision uint64, by string) error
DeleteTemplate deletes only the template and clears its page/root bindings. Items and their source code remain untouched.
func (*Handler) DeleteTemplateSlot ¶
func (h *Handler) DeleteTemplateSlot(ctx context.Context, user, id, slotID string, expectedRevision uint64, by string) (Template, error)
DeleteTemplateSlot removes a slot from the draft and moves all its Item placements to the unassigned tray. It never mutates the Item collection.
func (*Handler) ExportUser ¶
ExportUser 导出该用户全量数据(UserExport 即备份契约)。
func (*Handler) GetCurrentTemplate ¶
func (h *Handler) GetCurrentTemplate(ctx context.Context, user, page string) (CurrentTemplate, error)
GetCurrentTemplate resolves the root or a registered page's stable binding.
func (*Handler) GetTemplate ¶
GetTemplate returns one persisted template.
func (*Handler) ImportUser ¶
ImportUser 用 UserExport 整体替换该用户数据(先清场再写入)。 用途: 备份恢复、环境迁移、新用户套模板。
func (*Handler) KVCompareAndSet ¶
func (h *Handler) KVCompareAndSet(ctx context.Context, user, key string, value json.RawMessage, expectedRevision string) (string, error)
KVCompareAndSet 仅当 key 当前值仍等于 expectedRevision 所代表的快照时写入。 VersionedUserStateStore 路径把比较和写入放在同一次状态 CAS 中,因此跨 Handler / 跨副本也不会静默覆盖;旧 Store 仍在当前 Handler 的用户锁内提供原子语义。
func (*Handler) KVGetWithRevision ¶
func (h *Handler) KVGetWithRevision(ctx context.Context, user, key string) (json.RawMessage, string, error)
KVGetWithRevision 原子读取语义由底层 KVGet 提供,并为逐字节值生成一个 不透明版本令牌。令牌只用于 KVCompareAndSet,调用方不应解析或自行构造。
func (*Handler) ListRevisions ¶
ListRevisions 实现 Service。
func (*Handler) ListTemplates ¶
ListTemplates returns all persisted templates sorted by stable ID.
func (*Handler) PageErrors ¶
PageErrors 实现 Service。
func (*Handler) PublishTemplate ¶
func (h *Handler) PublishTemplate(ctx context.Context, user, id string, expectedRevision uint64, by string) (Template, error)
PublishTemplate copies the current draft into Published with CAS.
func (*Handler) PurgeUser ¶
PurgeUser 清空该用户的全部业务载荷。VersionedUserStateStore 会保留一个 递增版本墓碑以阻止旧 CAS 复活数据;SQL 实现因此仍保留版本表中的用户键。 需要物理抹除身份标识时,宿主必须先停止该用户的全部 writer,再按自己的 保留策略清理墓碑;不能把本方法单独当作完整的 GDPR 擦除流程。
func (*Handler) PutCurrentTemplate ¶
func (h *Handler) PutCurrentTemplate(ctx context.Context, user, page string, input Template, expectedRevision uint64, by string) (Template, error)
PutCurrentTemplate saves and binds the root/page-scoped template atomically.
func (*Handler) PutTemplate ¶
func (h *Handler) PutTemplate(ctx context.Context, user string, input Template, expectedRevision uint64, by string) (Template, error)
PutTemplate creates at expectedRevision=0 or updates a persisted template with CAS.
func (*Handler) ReplaceItems ¶
func (h *Handler) ReplaceItems(ctx context.Context, user string, items []Item, by, note string) error
ReplaceItems 实现 Service。
func (*Handler) ReportErrors ¶
ReportErrors 实现 Service。
func (*Handler) RevisionItems ¶
RevisionItems 实现 Service。
func (*Handler) ServeHTTP ¶
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP 实现 http.Handler。
func (*Handler) ServiceForRequest ¶
func (h *Handler) ServiceForRequest(r *http.Request) (ScopedService, error)
ServiceForRequest resolves and binds the authenticated user, then returns a service that rechecks identity, AccessPolicy, Handler.Close state, and shared write limits on every call. This is the recommended API for host HTTP routes.
type HandlerConfigError ¶
HandlerConfigError identifies the option or dependency that failed startup validation. Callers should use errors.As instead of matching Error strings.
func (*HandlerConfigError) Error ¶
func (e *HandlerConfigError) Error() string
func (*HandlerConfigError) Unwrap ¶
func (e *HandlerConfigError) Unwrap() error
type Item ¶
type Item struct {
ID string `json:"id"`
Type ItemType `json:"type"`
Title string `json:"title,omitempty"` // 人类/AI 可读的用途说明
Order int `json:"order"` // 应用顺序, 小的先执行
// Page 是所属页面名。空 = 全局 Item(菜单/顶栏/全局样式, 始终加载);
// 非空 = 仅在内核路由切到该页时加载执行, 离开时 teardown。
Page string `json:"page,omitempty"`
// Content 是片段源码。html 类型可内嵌 <script>/<style>。
Content string `json:"content"`
// Meta 是附加元数据。约定:
// - js 类型 meta["module"]=="true" 时以 <script type="module"> 执行;
// - html 类型 meta["slot"] 是 Item 对当前 Template Slot.id 的规范绑定;
// sidebar/header/footer/main 是旧内核兼容名称。绑定缺失或 Slot 已删除时,
// 模板运行时必须把 Item 放到 Root 末尾的未分配区,而不是丢弃内容。
Meta map[string]string `json:"meta,omitempty"`
UpdatedAt time.Time `json:"updated_at"`
}
Item 是一段由 AI 生成(或手工写入)的前端代码片段。 同一用户名下 ID 唯一, 以相同 ID 写入即覆盖(内核会原地替换并触发 teardown)。
type ItemService ¶
type ItemService interface {
// ListItems 返回按 Order 升序(同序按 ID)排好的 Item 全集。
ListItems(ctx context.Context, user string) ([]Item, error)
// PutItems 按 ID 增改 Item(变更前自动快照), note/by 记入快照说明。
PutItems(ctx context.Context, user string, items []Item, by, note string) error
// DeleteItems 删除指定 Item, 返回实际删除的 ID。
DeleteItems(ctx context.Context, user string, ids []string, by, note string) ([]string, error)
// ReplaceItems 整体替换 Item 全集。
ReplaceItems(ctx context.Context, user string, items []Item, by, note string) error
}
ItemService is the independently implementable Item capability. New public capabilities are added as separate interfaces so SDK consumers can depend on the smallest stable contract they need.
type KVConflictError ¶
type KVConflictError struct {
Key string
Expected string
Actual string
Current json.RawMessage
Exists bool
}
KVConflictError reports both KV revisions and, when the key currently exists, includes a defensive copy of its latest value so editors can offer a reload/merge flow without an extra round trip.
func (*KVConflictError) Error ¶
func (e *KVConflictError) Error() string
func (*KVConflictError) Unwrap ¶
func (e *KVConflictError) Unwrap() error
type KVService ¶
type KVService interface {
// KVGet 读取用户级 KV, 不存在返回 ErrNotFound。ReservedKVPrefix 下的
// 内核键不属于公共 KV 能力,返回 ErrReservedKVKey。
KVGet(ctx context.Context, user, key string) (json.RawMessage, error)
// KVSet 写入用户级 KV 并广播变更事件;保留键返回 ErrReservedKVKey。
KVSet(ctx context.Context, user, key string, value json.RawMessage) error
// KVDelete 删除用户级 KV 并广播变更事件;保留键返回 ErrReservedKVKey。
KVDelete(ctx context.Context, user, key string) error
}
KVService is the per-user JSON key/value capability.
type LayoutMode ¶
type LayoutMode string
LayoutMode describes how a placement participates in layout. Flow placements are responsive grid children; absolute placements use canvas coordinates.
const ( LayoutFlow LayoutMode = "flow" LayoutAbsolute LayoutMode = "absolute" )
type Limits ¶
type Limits struct {
MaxSSEPerUser int // 每用户 SSE 连接数, 默认 8
MaxWSPerUser int // 每用户对话 WebSocket 连接数, 默认 4
ChatTurnsPerMinute int // 每用户每分钟对话轮次(模型调用是真金白银), 默认 20
WritesPerMinute int // 每用户每分钟写操作次数, 默认 240
MaxKVKeysPerUser int // 每用户 KV 键数量(排除内核保留键), 默认 256
}
Limits 是连接与频率限制。字段为 0 时使用默认值, 为负时不限制。
type MemoryStore ¶
type MemoryStore struct {
// contains filtered or unexported fields
}
MemoryStore 是纯内存 Store 实现, 适合测试与临时体验, 进程退出即丢失。
func (*MemoryStore) AddRevision ¶
AddRevision 实现 Store。
func (*MemoryStore) EraseUser ¶
func (s *MemoryStore) EraseUser(_ context.Context, user string) error
EraseUser 实现 Store。
func (*MemoryStore) KVDelete ¶
func (s *MemoryStore) KVDelete(_ context.Context, user, key string) error
KVDelete 实现 Store。
func (*MemoryStore) KVGet ¶
func (s *MemoryStore) KVGet(_ context.Context, user, key string) (json.RawMessage, error)
KVGet 实现 Store。
func (*MemoryStore) KVSet ¶
func (s *MemoryStore) KVSet(_ context.Context, user, key string, value json.RawMessage) error
KVSet 实现 Store。
func (*MemoryStore) ReplaceItems ¶
ReplaceItems 实现 Store。
func (*MemoryStore) ReplaceUserState ¶
ReplaceUserState 原子替换用户的完整状态。
func (*MemoryStore) ReplaceUserStateVersioned ¶
func (s *MemoryStore) ReplaceUserStateVersioned(_ context.Context, user string, state UserState, expected uint64) error
ReplaceUserStateVersioned 实现 VersionedUserStateStore。
func (*MemoryStore) RevisionItems ¶
RevisionItems 实现 Store。
func (*MemoryStore) Revisions ¶
func (s *MemoryStore) Revisions(_ context.Context, user string) ([]RevisionInfo, error)
Revisions 实现 Store。
type Metrics ¶
Metrics 是可选的指标接收器, 宿主用它接入自己的监控(Prometheus 等)。 指标名: sse_connections(±1)/ws_sessions(±1)/events_published/chat_turns/ writes/errors_reported/rate_limited/auth_rejected。
type ModelLister ¶
type ModelLister interface {
Models(ctx context.Context) (ChatModels, error)
}
ModelLister 是 ChatBackend 的可选扩展: 提供可用模型列表。
type Option ¶
type Option func(*options)
Option 配置 Handler。
func WithAPIs ¶
WithAPIs 声明宿主提供的业务 API 目录: 写进 AI 的系统提示, AI 生成的 Item 会直接 fetch 这些接口取数; 同时 AI 可用只读探测工具实调它们 以了解真实返回结构。Path 是相对页面同源的绝对路径(如 /capi/players)。
func WithAccessPolicy ¶
WithAccessPolicy 设置按请求/用户的访问策略。长连接会重复且可能并发 调用 fn 以感知权限撤销,宿主实现必须并发安全。
func WithAccessPolicyE ¶
WithAccessPolicyE is the fail-closed form of WithAccessPolicy. It is intended for policies backed by databases or authorization services that can fail. An error rejects the request instead of silently granting the zero Access.
func WithAudit ¶
func WithAudit(fn func(ctx context.Context, e AuditEvent)) Option
WithAudit 注册审计回调。回调同步调用(需要异步的话由宿主自理), 覆盖全部写操作(含 AI 工具发起的)。
func WithBroadcaster ¶
func WithBroadcaster(b Broadcaster) Option
WithBroadcaster 替换事件广播通道(多副本部署必须, 见 Broadcaster 文档)。 为了避免数据库提交与消息发布反序,该安全默认只向外部通道 发送不携带状态载荷的 resync。Handler.Close 不接管外部 Broadcaster 的生命周期;宿主仍须关闭它及其客户端。
func WithCORS ¶
WithCORS 允许跨域访问(宿主页面把内核 mount 到自己域名下时必须开启)。 origins 为允许的 Origin 列表, "*" 表示全部。同时放行对应 Origin 的 对话 WebSocket。跨域认证应使用 UserResolver 验证的 Authorization 等非 cookie 凭据;本库不会下发 Access-Control-Allow-Credentials。 TLS 在反向代理终止时,还应把外部 https Origin 显式列入, 因为库不会信任客户端可伪造的 X-Forwarded-Proto。
func WithChatBackend ¶
func WithChatBackend(b ChatBackend) Option
WithChatBackend 挂载 AI 对话后端(见子包 chat)。不设置时页面隐藏对话入口。
func WithMaxRevisions ¶
WithMaxRevisions 设置每个用户保留的历史快照条数, 默认 DefaultMaxRevisions。
func WithMessageOrigins ¶
WithMessageOrigins 显式允许哪些父页面 Origin 通过 postMessage 与 iframe 中的 ihtml 通信,并同步用于 CSP frame-ancestors。 默认不配置时只允许同源。"*" 会允许任意父页面,仅应在明确 接受任意站点嵌入和消息交互风险时使用。非法 Origin 会被安全地忽略。
func WithMetrics ¶
WithMetrics 注册指标接收器。无论是否注册, Handler.Stats() 都可读内置计数。
func WithOrderedBroadcaster ¶
func WithOrderedBroadcaster(b Broadcaster) Option
WithOrderedBroadcaster 显式声明宿主能保证同一用户的所有 Store 写入 (跨全部 Handler/副本)从提交前到 Publish 返回都处于一个全局串行 边界内,因而允许 Handler 保留 items/templates/kv/pages 增量载荷。
这是一个需要宿主主动选择的危险能力:Broadcaster 自己按到达顺序 Publish 并不能修复两个副本“先提交、后发布”的反序。普通 Redis Pub/Sub、NATS 或“数据库提交后再 publish”的实现都不满足条件,应继续 使用 WithBroadcaster。典型安全场景是每个用户只有一个全局 writer,或宿主 的分布式锁覆盖完整 Handler 写调用。Handler 无法验证该前提。
func WithPageConfig ¶
WithPageConfig 注入"宿主动态参数": fn 的返回值会出现在 GET /api/config 的 params 字段里, 页面内核暴露为 ihtml.config, Item 代码可直接使用 (如业务昵称/权限/上下文)。fn 按请求调用, 返回 nil 表示无参数。
func WithPageTitle ¶
WithPageTitle 设置空壳页面的 <title>, 默认 "ihtml"。
type Page ¶
type Page struct {
Name string `json:"name"` // 路由名, 如 bills; 出现在 #/bills
Title string `json:"title"` // 菜单显示名
Icon string `json:"icon,omitempty"` // 菜单图标(emoji 或短文本)
Order int `json:"order,omitempty"` // 菜单排序
TemplateID string `json:"template_id,omitempty"` // 稳定引用的布局模板
}
Page 是页面注册表条目: 应用的一个"页", 也是侧边菜单的数据源。 页面下的 Item 通过 Item.Page 归属。
type PageError ¶
type PageError struct {
ItemID string `json:"item_id,omitempty"` // 归属的 Item, 无法归属时为空
Message string `json:"message"`
Stack string `json:"stack,omitempty"`
URL string `json:"url,omitempty"`
Time time.Time `json:"time"`
}
PageError 是内核上报的一条前端运行错误, 保留下来供 AI 修复参考。
type PageService ¶
type PageService interface {
// ListPages 返回页面注册表(按 Order 排序), 即侧边菜单的数据源。
ListPages(ctx context.Context, user string) ([]Page, error)
// SetPage 新建或更新页面(按 Name 合并)。
SetPage(ctx context.Context, user string, page Page) error
// DeletePage 删除页面及其名下全部 Item, 返回连带删除的 Item ID。
DeletePage(ctx context.Context, user, name, by string) ([]string, error)
}
PageService is the page registry capability.
type Placement ¶
type Placement struct {
ItemID string `json:"item_id"`
SlotID string `json:"slot_id,omitempty"`
Unassigned bool `json:"unassigned,omitempty"`
Breakpoint string `json:"breakpoint,omitempty"`
Mode LayoutMode `json:"mode"`
Order int `json:"order,omitempty"`
Column int `json:"column,omitempty"`
ColumnSpan int `json:"column_span,omitempty"`
Row int `json:"row,omitempty"`
RowSpan int `json:"row_span,omitempty"`
X float64 `json:"x,omitempty"`
Y float64 `json:"y,omitempty"`
Width float64 `json:"width,omitempty"`
Height float64 `json:"height,omitempty"`
ZIndex int `json:"z_index,omitempty"`
Locked bool `json:"locked,omitempty"`
Hidden bool `json:"hidden,omitempty"`
}
Placement is an optional page-instance/legacy override that binds one concrete Item at one breakpoint. Because ItemID makes a document non-portable, new reusable templates should normally leave Placements empty and bind Items with Item.Meta["slot"] == Slot.ID. An empty SlotID with Unassigned=true represents the compatibility/editor tray; runtimes append it to Root rather than hiding it.
type ProposedToolCall ¶
type ProposedToolCall struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Title string `json:"title,omitempty"`
Method string `json:"method"`
Path string `json:"path"`
Query map[string]any `json:"query,omitempty"`
Body any `json:"body,omitempty"`
Risk string `json:"risk"` // low / medium / high / blocked
RequiresConfirmation bool `json:"requires_confirmation"`
Reason string `json:"reason"`
AgentMode string `json:"agent_mode,omitempty"`
ApprovalResume string `json:"approval_resume,omitempty"`
ConfirmationToken string `json:"confirmation_token,omitempty"`
ConfirmationNonce string `json:"confirmation_nonce,omitempty"`
ConfirmationExpires int64 `json:"confirmation_expires,omitempty"`
}
ProposedToolCall 是一次工具调用的描述; 需人工确认的调用带 HMAC 签名的 confirmation_token, 前端确认后原样带回执行。
type Revision ¶
type Revision struct {
Version int `json:"version,omitempty"`
ID string `json:"id"`
Note string `json:"note,omitempty"` // 变更说明, 如 "put 2 items"
By string `json:"by,omitempty"` // 变更来源: api / chat
CreatedAt time.Time `json:"created_at"`
Items []Item `json:"items"`
Pages []Page `json:"pages,omitempty"`
Templates []Template `json:"templates,omitempty"`
RootTemplateID string `json:"root_template_id,omitempty"`
TemplateRevisionClock uint64 `json:"template_revision_clock,omitempty"`
}
Revision 是某用户 UI 状态的一次历史快照, 每次变更前自动保存, 用于回滚。 Version 为 0/1 的旧快照只有 Items;Version >= 2 同时恢复 Pages; Version >= 3 同时恢复模板目录与 root 模板绑定。
type RevisionInfo ¶
type RevisionInfo struct {
ID string `json:"id"`
Note string `json:"note,omitempty"`
By string `json:"by,omitempty"`
CreatedAt time.Time `json:"created_at"`
ItemCount int `json:"item_count"`
TemplateCount int `json:"template_count,omitempty"`
}
RevisionInfo 是快照的元信息(不含 Item 内容), 用于列表展示。
type RevisionService ¶
type RevisionService interface {
// ListRevisions 返回快照列表(新的在前)。
ListRevisions(ctx context.Context, user string) ([]RevisionInfo, error)
// RevisionItems 返回某快照的 Item 全集。
RevisionItems(ctx context.Context, user, id string) ([]Item, error)
// Rollback 把 Item 全集回滚到指定快照(回滚前同样自动快照)。
Rollback(ctx context.Context, user, revisionID, by string) error
}
RevisionService is the revision history capability.
type RevisionStore ¶
RevisionStore 是 Store 的可选增强接口,用于读取完整快照(包括较新契约中的 页面注册表)。旧 Store 只有 RevisionItems 时仍可读取旧版仅含 Item 的快照。
type ScopedErrorService ¶
type ScopedErrorService interface {
PageErrors(context.Context) ([]PageError, error)
ReportErrors(context.Context, []PageError) error
}
ScopedErrorService is ErrorService with the user already bound.
type ScopedItemService ¶
type ScopedItemService interface {
ListItems(context.Context) ([]Item, error)
PutItems(context.Context, []Item, string, string) error
DeleteItems(context.Context, []string, string, string) ([]string, error)
ReplaceItems(context.Context, []Item, string, string) error
}
ScopedItemService is ItemService with the authenticated user already bound.
type ScopedKVService ¶
type ScopedKVService interface {
KVGet(context.Context, string) (json.RawMessage, error)
KVSet(context.Context, string, json.RawMessage) error
KVDelete(context.Context, string) error
}
ScopedKVService is KVService with the user already bound.
type ScopedPageService ¶
type ScopedPageService interface {
ListPages(context.Context) ([]Page, error)
SetPage(context.Context, Page) error
DeletePage(context.Context, string, string) ([]string, error)
}
ScopedPageService is PageService with the user already bound.
type ScopedRevisionService ¶
type ScopedRevisionService interface {
ListRevisions(context.Context) ([]RevisionInfo, error)
RevisionItems(context.Context, string) ([]Item, error)
Rollback(context.Context, string, string) error
}
ScopedRevisionService is RevisionService with the user already bound.
type ScopedService ¶
type ScopedService interface {
ScopedItemService
ScopedRevisionService
ScopedKVService
ScopedErrorService
ScopedPageService
ScopedTemplateService
}
ScopedService is the cross-tenant-safe form of Service. It removes every user argument, preventing accidental reads or writes to a different tenant.
func ScopeService ¶
func ScopeService(service Service, user string) (ScopedService, error)
ScopeService binds a trusted Service to one user. It does not add an access policy by itself; use Handler.ServiceForRequest when handling an HTTP request.
type ScopedTemplateService ¶
type ScopedTemplateService interface {
ListTemplates(context.Context) ([]Template, error)
GetTemplate(context.Context, string) (Template, error)
GetCurrentTemplate(context.Context, string) (CurrentTemplate, error)
PutTemplate(context.Context, Template, uint64, string) (Template, error)
PutCurrentTemplate(context.Context, string, Template, uint64, string) (Template, error)
PublishTemplate(context.Context, string, uint64, string) (Template, error)
DeleteTemplateSlot(context.Context, string, string, uint64, string) (Template, error)
DeleteTemplate(context.Context, string, uint64, string) error
}
ScopedTemplateService is TemplateService with the user already bound.
type ScopedVersionedKVService ¶
type ScopedVersionedKVService interface {
KVGetWithRevision(context.Context, string) (json.RawMessage, string, error)
KVCompareAndSet(context.Context, string, json.RawMessage, string) (string, error)
}
ScopedVersionedKVService 是绑定用户后的可选条件 KV 能力。底层 Service 同时 实现 VersionedKVService 时,ScopeService 返回值才可通过类型断言取得它, 因而无需扩展既有 ScopedService 或破坏第三方旧实现。
type Service ¶
type Service interface {
ItemService
RevisionService
KVService
ErrorService
PageService
TemplateService
}
Service is the complete trusted administration API implemented by *Handler. Direct calls intentionally bypass HTTP authentication and AccessPolicy; use Handler.ServiceForRequest for request-scoped, policy-enforced access.
The interface embeds smaller capability interfaces so downstream code can avoid implementing or mocking unrelated future features.
type Slot ¶
type Slot struct {
ID string `json:"id"`
NodeID string `json:"node_id"`
Name string `json:"name,omitempty"`
Accepts []ItemType `json:"accepts,omitempty"`
Locked bool `json:"locked,omitempty"`
}
Slot is a named drop target owned by a node. Removing a slot never removes an Item; DeleteTemplateSlot moves its placements to the unassigned tray.
type Store ¶
type Store interface {
// Items 返回该用户的全部 Item(顺序不限, 上层会按 Order 排序)。
// 用户不存在时返回空切片而不是错误。
Items(ctx context.Context, user string) ([]Item, error)
// ReplaceItems 用 items 整体替换该用户的 Item 全集。
ReplaceItems(ctx context.Context, user string, items []Item) error
// KVGet 读取用户级键值, 键不存在时返回 ErrNotFound。KVSet/KVGet
// 必须逐字节保留有效 JSON(包括无意义空白与 HTML 字符),不得规范化、
// compact 或重新编码 RawMessage。
KVGet(ctx context.Context, user, key string) (json.RawMessage, error)
KVSet(ctx context.Context, user, key string, value json.RawMessage) error
// KVDelete 删除键, 键不存在时不报错。
KVDelete(ctx context.Context, user, key string) error
// KVKeys 返回该用户的全部 KV 键(含保留键), 顺序不限。
KVKeys(ctx context.Context, user string) ([]string, error)
// Revisions 返回该用户的快照元信息, 新的在前。
Revisions(ctx context.Context, user string) ([]RevisionInfo, error)
// RevisionItems 返回指定快照的 Item 全集, 不存在时返回 ErrNotFound。
RevisionItems(ctx context.Context, user, id string) ([]Item, error)
// AddRevision 追加一条快照。keep 是保留上限, 实现应删除最旧的超额快照。
AddRevision(ctx context.Context, user string, rev Revision, keep int) error
// EraseUser 清空该用户的业务数据(Item/KV/快照), 用于注销与导入前清场。
// VersionedUserStateStore 必须额外保留一个不可见的递增版本墓碑。
EraseUser(ctx context.Context, user string) error
}
Store 是持久化接口。实现只需要保证单个方法的原子性; "读-改-写"的并发合并在单副本内由上层(Handler)按用户加锁串行化。 多副本部署要求 Store 同时实现 VersionedUserStateStore(内置 Store 均已实现), 上层会用它做跨进程的条件写与冲突重试。
user 是 UserResolver 解析出的用户标识, 实现应按它隔离数据。所有返回的 slice/map/RawMessage 必须是调用方可修改的值快照;实现不得在返回后继续修改 它们。所有传入的 slice/map/RawMessage 也必须在方法返回前复制或持久化,不能 保留并随后修改调用方内存。
type Template ¶
type Template struct {
ID string `json:"id"`
Name string `json:"name"`
Revision uint64 `json:"revision"`
Draft TemplateVersion `json:"draft"`
Published *TemplateVersion `json:"published,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
PublishedAt *time.Time `json:"published_at,omitempty"`
}
Template is a reusable layout document. Revision is the CAS token for every mutation, independent from Draft.Version and Published.Version.
type TemplateConflictError ¶
TemplateConflictError reports both revisions and includes the latest value so an editor can offer reload/merge without another round trip.
func (*TemplateConflictError) Error ¶
func (e *TemplateConflictError) Error() string
func (*TemplateConflictError) Unwrap ¶
func (e *TemplateConflictError) Unwrap() error
type TemplateNode ¶
type TemplateNode struct {
ID string `json:"id"`
ParentID string `json:"parent_id,omitempty"`
Kind TemplateNodeKind `json:"kind"`
Name string `json:"name,omitempty"`
Order int `json:"order,omitempty"`
Props map[string]json.RawMessage `json:"props,omitempty"`
}
TemplateNode forms a tree through ParentID. Props is reserved for renderer-specific JSON hints; the built-in layouts/frames shapes receive the same finite/range validation as typed Placement and Breakpoint geometry.
type TemplateNodeKind ¶
type TemplateNodeKind string
TemplateNodeKind is the structural role of a template node. Nodes contain no page content: reusable Item bindings remain on Item.Meta, with Placement only as an optional instance override. A completely empty template is valid.
const ( NodeCanvas TemplateNodeKind = "canvas" NodeGrid TemplateNodeKind = "grid" NodeStack TemplateNodeKind = "stack" NodeGroup TemplateNodeKind = "group" )
type TemplateService ¶
type TemplateService interface {
// ListTemplates 返回一等布局模板;模板内容与动态页面 Item 分离。
ListTemplates(ctx context.Context, user string) ([]Template, error)
GetTemplate(ctx context.Context, user, id string) (Template, error)
GetCurrentTemplate(ctx context.Context, user, page string) (CurrentTemplate, error)
PutTemplate(ctx context.Context, user string, template Template, expectedRevision uint64, by string) (Template, error)
PutCurrentTemplate(ctx context.Context, user, page string, template Template, expectedRevision uint64, by string) (Template, error)
PublishTemplate(ctx context.Context, user, id string, expectedRevision uint64, by string) (Template, error)
DeleteTemplateSlot(ctx context.Context, user, id, slotID string, expectedRevision uint64, by string) (Template, error)
DeleteTemplate(ctx context.Context, user, id string, expectedRevision uint64, by string) error
}
TemplateService is the reusable layout template capability.
type TemplateVersion ¶
type TemplateVersion struct {
Version uint64 `json:"version"`
RootID string `json:"root_id,omitempty"`
Nodes []TemplateNode `json:"nodes"`
Slots []Slot `json:"slots"`
Placements []Placement `json:"placements"`
Breakpoints []Breakpoint `json:"breakpoints"`
}
TemplateVersion is a complete immutable-by-value template document. Draft is edited in place by creating a new Version; Published is a copy of a draft.
func (*TemplateVersion) Validate ¶
func (v *TemplateVersion) Validate() error
Validate checks a template version. Version zero is accepted only so a virtual, not-yet-persisted current template can be round-tripped by the API.
type UserExport ¶
type UserExport struct {
Version int `json:"version,omitempty"`
Items []Item `json:"items"`
Pages []Page `json:"pages,omitempty"`
Templates []Template `json:"templates,omitempty"`
RootTemplateID string `json:"root_template_id,omitempty"`
TemplateRevisionClock uint64 `json:"template_revision_clock,omitempty"`
KV map[string]json.RawMessage `json:"kv,omitempty"`
Revisions []Revision `json:"revisions,omitempty"`
}
UserExport 是单用户全量数据包(备份/迁移/模板预置的 JSON 契约)。 Version 为 0/1 的包按旧版契约读取;当前导出写入 CurrentDataVersion。
type UserResolver ¶
UserResolver 从宿主已验证的会话/令牌解析当前用户标识。 返回空串或错误都会让请求以 401 结束。为了让 SSE/WS 能感知会话撤销,同一请求的 resolver 可能被重复调用;实现应并发安全。
type UserState ¶
type UserState struct {
Items []Item
KV map[string]json.RawMessage
Revisions []Revision // 新的在前
// Version 是整个用户状态的乐观并发令牌,由实现 VersionedUserStateStore 的
// Store 在 UserState() 时填充;不支持版本的 Store 保持为 0。调用方应视为
// 不透明值:读到什么,条件写时就传什么。
Version uint64
}
UserState 是 Store 中一个用户的完整持久化状态。它主要供需要跨 Items、KV、 Revisions 原子更新的操作使用(例如导入、页面删除和带页面注册表的回滚)。 调用方和实现都必须把它当作值快照,不能保留或修改传入切片/RawMessage。
type UserStateStore ¶
type UserStateStore interface {
UserState(ctx context.Context, user string) (UserState, error)
ReplaceUserState(ctx context.Context, user string, state UserState) error
}
UserStateStore 是 Store 的可选增强接口。内置 Store 均实现它;第三方旧 Store 无需立即修改,Service 会使用备份+补偿恢复,但不能提供与该接口同等的强原子性。
ReplaceUserState 必须在单个原子操作中用 state 整体替换指定用户的数据。
type VersionedBroadcaster ¶
type VersionedBroadcaster interface {
Broadcaster
CurrentVersion(user string) uint64
}
VersionedBroadcaster 是 Broadcaster 的可选扩展。多副本实现可用 Redis INCR/流序号等返回用户级全局版本,使 SSE resync 事件 能建立准确基线。Publish 投递的 Event.Version 应与此版本一致。
type VersionedKVService ¶
type VersionedKVService interface {
// KVGetWithRevision 返回值及其不透明版本令牌;键不存在仍返回 ErrNotFound。
KVGetWithRevision(ctx context.Context, user, key string) (json.RawMessage, string, error)
// KVCompareAndSet 仅在当前版本等于 expectedRevision 时原子写入,并返回
// 新版本。读取时不存在的键使用 MissingKVRevision 作为 expectedRevision。
KVCompareAndSet(ctx context.Context, user, key string, value json.RawMessage, expectedRevision string) (string, error)
}
VersionedKVService 是 KVService 的可选增强能力。它把单个键的读取版本与 条件写暴露给 SDK 消费者,适合编辑器、多标签页等不能接受静默覆盖的场景。 这是独立接口,避免给已有第三方 KVService 实现增加破坏性方法要求。
type VersionedUserStateStore ¶
type VersionedUserStateStore interface {
UserStateStore
// ReplaceUserStateVersioned 仅当该用户当前版本等于 expected 时原子替换完整
// 状态并推进版本;否则返回 ErrVersionConflict 且不产生任何修改。
ReplaceUserStateVersioned(ctx context.Context, user string, state UserState, expected uint64) error
}
VersionedUserStateStore 是 UserStateStore 的可选增强接口,为多副本部署提供 跨进程的乐观并发控制。内置 Store 均实现它。
注意: 上层在两个替换方法中优先调用 ReplaceUserStateVersioned。若你通过嵌入 内置 Store 做包装(审计/加密等),Go 的方法提升会把版本化方法一并暴露—— 必须同时覆写 ReplaceUserState 与 ReplaceUserStateVersioned,否则包装逻辑会被绕过。
实现要求:
- 用户状态版本从 0 开始(0 仅表示该用户从未被任何写操作触及);
- 每个成功的写方法(ReplaceItems/KVSet/KVDelete/AddRevision/ReplaceUserState 等)都必须单调推进该用户的版本,使并发的整状态条件写能察觉任何交错写入;
- EraseUser 必须清空 Items/KV/Revisions,但仍单调推进并持久化版本。 不得把版本重置为 0 或复用旧值,否则 purge 前的过期 CAS 可通过 ABA 覆盖 purge 后重建的新状态。
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package chat 是 ihtml 的 AI 对话后端: 基于 CloudWeGo eino ADK, 通过语义工具直接增删改用户的 UI Item / 主题 / KV, 并把过程以流式事件 推回页面内核的对话抽屉。
|
Package chat 是 ihtml 的 AI 对话后端: 基于 CloudWeGo eino ADK, 通过语义工具直接增删改用户的 UI Item / 主题 / KV, 并把过程以流式事件 推回页面内核的对话抽屉。 |
|
config
Package chatconfig loads the data-only portion of an ihtml chat backend configuration.
|
Package chatconfig loads the data-only portion of an ihtml chat backend configuration. |
|
演示程序: 起一个带演示数据的多页 ihtml 服务。
|
演示程序: 起一个带演示数据的多页 ihtml 服务。 |
|
internal
|
|
|
nilcheck
Package nilcheck contains the SDK's interface-boundary nil guard.
|
Package nilcheck contains the SDK's interface-boundary nil guard. |
|
Package redisbroadcast 提供基于 Redis Pub/Sub 的 ihtml.Broadcaster 实现, 用于多副本部署: 任一副本的写事件会推送到连接在其他副本上的页面, 并以 Redis INCR 维护用户级全局单调版本(实现 ihtml.VersionedBroadcaster)。
|
Package redisbroadcast 提供基于 Redis Pub/Sub 的 ihtml.Broadcaster 实现, 用于多副本部署: 任一副本的写事件会推送到连接在其他副本上的页面, 并以 Redis INCR 维护用户级全局单调版本(实现 ihtml.VersionedBroadcaster)。 |
|
Package sqlstore 提供基于 database/sql 的 ihtml.Store 实现, 用于生产部署。
|
Package sqlstore 提供基于 database/sql 的 ihtml.Store 实现, 用于生产部署。 |
|
Package storetest 是 ihtml.Store 实现的官方一致性验收套件。
|
Package storetest 是 ihtml.Store 实现的官方一致性验收套件。 |