Codex/avatar integrated 20260819 #2

Merged
stefanfeng merged 18 commits from codex/avatar-integrated-20260819 into main 2026-08-21 09:31:44 +08:00
4 changed files with 15 additions and 4 deletions
Showing only changes of commit 4ab7732da9 - Show all commits
+7 -1
View File
@@ -38,8 +38,9 @@ def init_db():
("authorizations", "takeover_enabled", "BOOLEAN DEFAULT 0"),
("authorizations", "takeover_mode", "VARCHAR DEFAULT 'immediate'"),
("authorizations", "takeover_delay_seconds", "INTEGER DEFAULT 30"),
("avatars", "share_token", "VARCHAR DEFAULT ''"),
("avatars", "share_token", "VARCHAR DEFAULT NULL"),
)
_normalize_optional_unique_values()
def _try_add_columns(*cols):
@@ -51,3 +52,8 @@ def _try_add_columns(*cols):
except Exception:
# 列已存在(或全新库由 create_all 建好)则忽略
pass
def _normalize_optional_unique_values():
with engine.begin() as conn:
conn.exec_driver_sql("UPDATE avatars SET share_token = NULL WHERE share_token = ''")
+2 -2
View File
@@ -20,7 +20,7 @@ class Avatar(Base):
photo_url = Column(String, default="")
emoji = Column(String, default="🤖")
status = Column(String, default="active") # active | inactive | training
share_token = Column(String, default="", unique=True, index=True) # 对外分享使用的不可猜测令牌
share_token = Column(String, nullable=True, default=None, unique=True, index=True) # 对外分享使用的不可猜测令牌
token_balance = Column(Integer, default=0)
config = Column(JSON, default=dict)
created_at = Column(DateTime, server_default=func.now())
@@ -36,7 +36,7 @@ class Avatar(Base):
"photoUrl": self.photo_url,
"emoji": self.emoji,
"status": self.status,
"shareToken": self.share_token,
"shareToken": self.share_token or "",
"tokenBalance": self.token_balance,
"config": self.config or {},
"createdAt": _iso(self.created_at),
@@ -133,4 +133,3 @@ def delete_avatar(avatar_id: str, db: Session = Depends(get_db)):
db.delete(a)
db.commit()
return ok({"success": True})
@@ -121,6 +121,12 @@ class ChatOrchestrationTests(unittest.TestCase):
self.assertNotIn("config", payload)
self.assertNotIn("ownerId", payload)
def test_unshared_avatars_do_not_reuse_a_unique_share_token(self):
first = Avatar(name="first")
second = Avatar(name="second")
self.assertIsNone(first.share_token)
self.assertIsNone(second.share_token)
def test_standard_answer_can_be_emitted_as_sse_chunks(self):
self.assertEqual(list(_iter_text_chunks("标准答案内容", size=2)), ["标准", "答案", "内容"])