e80a82bef4
- 更新 http-error-codes, user-points-chat-data-protocol - 更新 divination-run-protocol, profile-protocol - 删除废弃的后端和前端设计计划文档
5.2 KiB
5.2 KiB
User Points & Chat Data Protocol
This protocol defines the canonical data contract for user profile, points account, points ledger, chat session, and chat messages.
Protocol verification status:
- Last audited migration:
backend/alembic/versions/20260403_0004_remove_points_reason_code.py - Last audited models:
backend/src/models/profile.py,backend/src/models/user_points.py,backend/src/models/points_ledger.py,backend/src/models/agent_chat_session.py,backend/src/models/agent_chat_message.py - Current status: aligned
Scope
profilesuser_pointspoints_ledgersessionsmessages
Compatibility strategy
- Current strategy: additive evolution.
- Breaking changes (drop/rename/type change on core fields) require explicit migration + rollback notes.
points_ledger.metadata.schema_versionis mandatory and current value is1.
Runtime charging policy (chat)
- Charge unit:
20points per successful run. - Charge timing: deduct after worker run succeeds (
RUN_FINISHEDpath). - Failure behavior: failed/canceled runs do not deduct points.
- Precheck: before accepting a run, backend must verify
available = balance - frozen_balance >= 20. - Session follow-up cap: one session allows at most 2 user runs total (initial divination + 1 follow-up).
- Billing idempotency key for per-run consume:
chat.run.success:{session_id}:{run_id}.
Table contract
profiles
- PK:
id(auth.users.id,on delete cascade) - Core fields:
username,avatar_url,bio,settings,created_at,updated_at,deleted_at - Constraints:
usernamenot empty
- Indexes:
ix_profiles_usernameix_profiles_settings_gin
user_points
- PK:
user_id(auth.users.id,on delete cascade) - Core fields:
balance,frozen_balance,lifetime_earned,lifetime_spent,version,created_at,updated_at - Constraints:
- all numeric totals must be non-negative
frozen_balance <= balance
points_ledger
- PK:
id - FK:
user_id -> auth.users.id(on delete cascade)biz_id -> sessions.id(on delete restrict, nullable)operator_id -> auth.users.id(on delete set null)
- Core fields:
direction,amount,balance_after,change_type,biz_type,biz_id,event_id,operator_id,metadata,created_at,updated_at - Constraints:
amount > 0direction in (1, -1)balance_after >= 0change_type in ('register', 'consume', 'grant', 'adjust')biz_type is null or biz_type='chat'- biz binding:
register => biz_type is null and biz_id is nullconsume/grant/adjust => biz_type='chat' and biz_id not null
- direction and change_type coupling:
register/grant => direction = 1consume => direction = -1adjust => direction in (1, -1)
- idempotency:
unique (user_id, event_id)
points_ledger.metadata (schema_version=1)
Canonical shape:
{
"schema_version": 1,
"operator_type": "user|system|admin",
"run_id": "string",
"request_id": "string|null",
"charge": {
"message_id": "uuid",
"message_seq": 1,
"model_code": "string",
"input_tokens": 0,
"output_tokens": 0,
"cost": "0.000000"
},
"ext": {}
}
JSON constraints:
- Common:
- must be object
schema_version = 1operator_type in (user, system, admin)run_idnon-empty- if present,
extmust be object
- Per
change_type:register: nocharge, and no chat binding (biz_type/biz_idboth null)consume: requireschargeobject with required fieldsgrant: no extra metadata shape requirementadjust: requiresext.ticket_idnon-empty
Signup initialization contract
- Trigger:
auth.usersafter insert - Function:
public.initialize_profile_and_points_on_signup() - Side effects:
- create
profilesrow with default settings - username format:
user_xxxxxx(x= 6 chars from[a-z0-9]) - create
user_pointsrow with initialbalance=100,lifetime_earned=100 - create
points_ledgerregister row:change_type='register'biz_type=null,biz_id=nullamount=100,direction=1,balance_after=100
- create
sessions
- PK:
id - FK:
user_id -> auth.users.id - Core fields:
session_type,job_id,title,status,last_activity_at,message_count,total_tokens,total_cost,state_snapshot,created_at,updated_at,deleted_at - Constraints:
session_type in ('chat', 'automation')status in ('pending', 'running', 'completed', 'failed')message_count/total_tokens/total_costnon-negative
messages
- PK:
id - FK:
session_id -> sessions.id(on delete cascade) - Core fields:
seq,role,content,model_code,tool_name,input_tokens,output_tokens,cost,latency_ms,visibility_mask,metadata,created_at,updated_at,deleted_at - Constraints:
unique (session_id, seq)seq > 0role in ('user', 'assistant', 'system', 'tool')- token/cost non-negative
latency_msnull or non-negative
Security and ownership
- Backend service must derive owner identity from verified auth context.
- Client must not be trusted for
user_id/operator_idownership semantics. metadataandsettingsmust not include secrets.