Files
AskaEth 22e8398110 feat(contract,gateway): B4 枚举权威表 — GET /api/v1/meta/enums + 未知值回落约定
- 新增 internal/contract/enums 叶子包:platform/mood/mode 三表 + UnknownFallback="display-raw";每次调用返回新切片,无包级可变全局
- GET /api/v1/meta/enums(anyAuth 只读、纯投影、无 store/无错误路径)
- 反漂移 4 处:真构造内置适配器断言 Name() 与表双向相等、真工厂 buildAccountAdapter 逐项可建、mood 对齐 emotion 常量 + 默认转移表、mode 对齐前端 MODES(跨语言,文件缺失则 skip)
- Docs/api-client §1.1 枚举权威表 + 行15 + v0.38;api-reference §3.7 + 行12
2026-10-03 16:55:22 +08:00

82 lines
3.5 KiB
Go

// Package enums is the single Go-side source of truth for the shared client
// enumerations published at GET /api/v1/meta/enums (Android collaboration B4,
// design/07 §6). It carries three tables — platform (a message's source
// platform), mood (a persona's emotion) and mode (the presentation mode a
// message was produced in) — so every client maps ids to labels from the same
// place instead of hardcoding its own, which is what made source badges and mood
// labels drift per client.
//
// Adding a value is a non-breaking contract change. A value absent from a table
// must be rendered raw by the client — never dropped, never an error — and that
// policy is published alongside the tables (UnknownFallback). The anti-drift
// tests in this package pin each table to its real source of truth: the platform
// table to the built-in adapters' Name() values plus the Web main-session source
// (internal/runtime/history), the mood table to internal/persona/emotion's
// built-in states, and the mode table to the nine modes design/07 §1 defines
// (mirrored by frontend/web/src/lib/types.ts MODES).
package enums
// Item is one enumeration entry: the machine-readable ID a client compares
// against, plus the default Simplified-Chinese label it may display directly.
// The label is a default, not a contract: a client with i18n may override it.
type Item struct {
ID string `json:"id"`
Label string `json:"label"`
}
// UnknownFallback is the machine-readable fallback policy a client applies to a
// value that is not in the published tables: display the value exactly as
// received — no label substitution, no drop, no error. It is served in the same
// response as the tables so a client reads the policy and the data together.
const UnknownFallback = "display-raw"
// Platforms returns the authoritative message-source platforms: the Web main
// session plus every built-in IM adapter, keyed by the adapter's Name() value
// (kook, qq-obv11, weixin-ilink, telegram, webhook).
//
// Each call builds a fresh slice, so a caller may sort or otherwise mutate its
// copy without affecting another — the table itself is never package-level
// mutable state.
func Platforms() []Item {
return []Item{
{ID: "web", Label: "网页"},
{ID: "kook", Label: "KOOK"},
{ID: "qq-obv11", Label: "QQ"},
{ID: "weixin-ilink", Label: "微信"},
{ID: "telegram", Label: "Telegram"},
{ID: "webhook", Label: "Webhook"},
}
}
// Moods returns the authoritative persona-emotion values, mirroring
// internal/persona/emotion's built-in states (calm/happy/excited/sad/angry). The
// emotion state machine is extensible — a persona may register further states via
// AddTransition — so a client must treat an unlisted mood as display-raw rather
// than assuming this table is closed.
func Moods() []Item {
return []Item{
{ID: "calm", Label: "平静"},
{ID: "happy", Label: "开心"},
{ID: "excited", Label: "兴奋"},
{ID: "sad", Label: "低落"},
{ID: "angry", Label: "生气"},
}
}
// Modes returns the authoritative behavior/presentation modes (design/01 §3,
// design/07 §1). A mode drives how a message is rendered, never what the persona
// is able to do: it is a presentation dimension, not a capability boundary.
func Modes() []Item {
return []Item{
{ID: "chat", Label: "对话"},
{ID: "code", Label: "代码"},
{ID: "home", Label: "家居"},
{ID: "work", Label: "工作"},
{ID: "docs", Label: "文档"},
{ID: "voice", Label: "语音"},
{ID: "vision", Label: "视觉"},
{ID: "agents", Label: "智能体"},
{ID: "settings", Label: "设置"},
}
}