From 9cbd3347f69ba4d368abea7021615e94351da9f1 Mon Sep 17 00:00:00 2001 From: lusixing Date: Mon, 2 Feb 2026 22:22:11 -0800 Subject: [PATCH] update_260202-3 --- app/__pycache__/database.cpython-311.pyc | Bin 5953 -> 7821 bytes app/__pycache__/main.cpython-311.pyc | Bin 21398 -> 23945 bytes app/__pycache__/models.cpython-311.pyc | Bin 4972 -> 5509 bytes app/__pycache__/schemas.cpython-311.pyc | Bin 6654 -> 7615 bytes app/database.py | 49 ++++++++++++++++++++++- app/main.py | 48 +++++++++++++++++++++- app/models.py | 12 +++++- app/schemas.py | 17 ++++++++ test/test_ai_proxy.py | 5 ++- 9 files changed, 125 insertions(+), 6 deletions(-) diff --git a/app/__pycache__/database.cpython-311.pyc b/app/__pycache__/database.cpython-311.pyc index 8946a9b1e41b9b213fa5261029bb7f7599cdcfa9..47badc8262979b0b025f8c3a0ef06bcce655c36f 100644 GIT binary patch delta 2040 zcma)7Uu+ab7~j3SUfZ*Gw{1#kOXQ}6(v}{JfKb3fJ&J`UVoQZYG=xld=I*xa?XEL> zl-pc$F@XeQRFdVvm?jWo6r&HCKKS5^HJbQli%mT5#l$z>&d5XKi+;1$YqgcYWcQny zZ@&5d{J!7bEvoOHh}*1L^u|-_a)b=6?T?aU@doA8_ zV|!CTDu#;kOv1a{(BWNb=-QqTIfyZX{0T%+At$`I8{XNrenv)g(0(kxC(pHd6HQ$` zt)Iw}^f|n>AxlnQeJx4kWKr?7@N3?g&@OLhU{HQ>;pfPKV99NtqG^L_j(&}z6z4j3 zu<5u{XQ^RxRIu`hh!aAojdatogo+_2YvnUpha+qfl%~|s%?t-E6Ka&B*aSs7Zy>hj z+%i@7^-JIlGm0?Ef_HH|Qvz=$HW02yR*{Gdt|-cL%IXe~WeT1Cn9!;lpQ(A;XXPEp zhE|%!EmPMllMjR|0iCdJz=9zVl@?y$4$Vz6D`%ISN=R5$B0`SgoNg4{*eIje2jo>7 zv#aiz6oT0?A)U0v&@{uk$sLR3pvWZB>c+E7cYKWig5a@0vDmn+qT6VKPL5E7F4&eQQq3Q_*{GjY(z~%i%g=TmmxO_m09cjrhOccFg427 z@B@rQW@u~JSzW;Ipn8_78_h!D>&3F8P;5R5;`jNRC^e3zEa*(%XVO5A*A)9ZUJ0kh zh03=ZX!DxmH!fD9SiizR$*&OX;7YSt*OpDd+uBhZxI|k9Q$Ss5)J@&FLZ+DpyLNc_ z@y3O3;t{$3T%aLQm7XjY9w|QsOA%>%XSq`?CwLhZ%bf$|o&D8lV>DKkglO|m`^$-J zH6Zn51Le*=k1@yygNer(>@6pBVQ?7?`u^?Vm00yqPgmqtv^pkB-F*0MEJe^CY>D1-OMzBsp%9R9ZX$KBt@A0#K1mB|%l(yO+P zZ$B-Ucl3X7Wd6j`j>GWWKUSN)^KD(-r|#)rhFA6sEp?o#J~;!k)bpND@n4d>vu#h@oy7Ifl2kS}?pAKj2*kx#CvTPAu9nKk2#N&UR8b62 FF#snRfA#2DN5!p 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 diff --git a/app/__pycache__/models.cpython-311.pyc b/app/__pycache__/models.cpython-311.pyc index 40b68e9fc391058d499e86bd675422a1f35e2b17..7f25e0ce02fd97d2b76d417e23e604e4ccef24c2 100644 GIT binary patch delta 432 zcmaE()~e07oR^o20SK;CHD>_-ms2T_q+4cZL*!7KRkTRGwwb3=FG*7y_b% zQ-p#UG=(>FF?tA0R^k<7TEH+ln=^6pHckOwZYG9QmQ?m-j6fYg4AsGdRg^b{6=)9I zEjCBbp!}TFTO5g*@kKyJaY&I6P-Bt7WOhEixFQu0n;k@G0f}2Y#g)Y+sks3~`MCup zw^%Zh^YdD2qYTd@P>%oWLX}`$qp>+fh-re zRWEX@Ug1`4aJeBQF+pvH$V8nfIt|`8ge51K%}|+WGsULC=LWZ6huH+RPP-mEpfW-6 z31TzYCdy5bYw!X=h_xUa{WL`;hYFT6CQUveD9e=v6afWB@!ZYt1#=lKL^v1~J}_Vs P9U&hoR^o20SGRytIK@LJ&{j>@!mxByKE@}!3>&$o7EUS1epCag(o`-mrvd+ dEXkDuRLTg%#ig4c2V diff --git a/app/__pycache__/schemas.cpython-311.pyc b/app/__pycache__/schemas.cpython-311.pyc index 7c42b8b5cd2f1c18b56ffecf570a69398093585c..9be2fff1280449d81af2af8159e9fb732ae4b3ac 100644 GIT binary patch delta 1352 zcmZXTO=uKJ6o9*Cdj5K*yXQAfCz&JWRips7E*^P-~enw-EFlscWOKqIVNcSj2 z7VN+x9z2Y^Wfcz|-Gicp;Ns0ai09ZIvKL6ZZw9YAyv@CnN)PKl#k3IKG(hHQe%B>foAl0 zw;3oYJjDIRMoyB%WixyXS7rcMD77O(bDzXxrt&iSoMHYbt*4}#0o*GFW3jV2FLZ-j z_GQcT6fxnIS*tu{ccN)tga6d+@IID~|AI?FpBdG2Ezsz>S%wi z*ByBK((ca2w@*$iASIj69G3JS(=C@FUgk9}Tqg1X{3o68M$@$-c@W6!6ZFP+ zup)Y0H6}?xI71Twk<#QwN+o!eh_ES<_`E+1|Hf0eqO-;lccW9}6epD6Q6i^I)08j` zZxcgoS{QaD7g-6OB#X*LQWGvhGkJj_!^ZNpRD>}LZl$yO9O((yP#g|3{w%yspJ#I- z&h|`IzeyH^Dp~9nW&A7C#cq-X8xw8krt;$f?69Edun=}n*i!}zlhrb(MzymPgYJ%P zA-tHg;M92Kj9snVs(G?CSDp71;_z(6DlcsObGl=rPWF#v tz9})5Tqmpv1Yte<<*E{7={0F9>PC~ku5YU1j5gegxuY&gKtpCK8FAR delta 636 zcmZWm&ubG=5Z)K*c6XEA?5{SviB_RPNfkZVi&pettSt-Ml!hu65(2wwN^5n4Nc1m~p92rk*Kh`4#unXzIj5|E&aZF^M2yUvI4#W`ZsNkA~m zk2va9M4bhRdpplXn-mN9=I(>->S7+YR}&4^T=njX1@ygH;{u5Y&SS=3M8hlh-uSvu zQ{q(~w?!#I8{dbzqQwrkhF6WtR3KPFfB5(yWpTBPLfEYHN_kH_B~QbTYETbf%C>Cb z5XZ~&WVWT9Tv<(SET^ZkCHYBzw|lMkKHL=JgJPnu{15lneh~ZXZ^^~$$|J1p{c0&W J>XE<~`~gqam4*NS diff --git a/app/database.py b/app/database.py index 1123d47..c9698b7 100644 --- a/app/database.py +++ b/app/database.py @@ -118,4 +118,51 @@ async def init_db(): ) session.add(gemini_config) await session.commit() - print("✅ Default Gemini AI configuration created") \ No newline at end of file + print("✅ Default Gemini AI configuration created") + + # 4. 检查并初始化 AI Roles + ai_roles_data = [ + { + "id": 0, + "name": 'Reflective Assistant', + "description": 'Helps you dive deep into your thoughts and feelings through meaningful reflection.', + "systemPrompt": 'You are a helpful journal assistant. Help the user reflect on their thoughts and feelings.', + "icon": 'journal-outline', + "iconFamily": 'Ionicons', + }, + { + "id": 1, + "name": 'Creative Spark', + "description": 'A partner for brainstorming, creative writing, and exploring new ideas.', + "systemPrompt": 'You are a creative brainstorming partner. Help the user explore new ideas, write stories, or look at things from a fresh perspective.', + "icon": 'bulb-outline', + "iconFamily": 'Ionicons', + }, + { + "id": 2, + "name": 'Action Planner', + "description": 'Focused on turning thoughts into actionable plans and organized goals.', + "systemPrompt": 'You are a productivity coach. Help the user break down their thoughts into actionable steps and clear goals.', + "icon": 'list-outline', + "iconFamily": 'Ionicons', + }, + { + "id": 3, + "name": 'Empathetic Guide', + "description": 'Provides a safe, non-judgmental space for emotional support and empathy.', + "systemPrompt": 'You are a supportive and empathetic friend. Listen to the user\'s concerns and provide emotional support without judgment.', + "icon": 'heart-outline', + "iconFamily": 'Ionicons', + }, + ] + + for role_data in ai_roles_data: + result = await session.execute( + select(models.AIRole).where(models.AIRole.id == role_data["id"]) + ) + if not result.scalars().first(): + new_role = models.AIRole(**role_data) + session.add(new_role) + print(f"✅ AI Role '{role_data['name']}' created") + + await session.commit() \ No newline at end of file diff --git a/app/main.py b/app/main.py index eb3d156..650532b 100644 --- a/app/main.py +++ b/app/main.py @@ -99,9 +99,18 @@ async def get_my_assets( db: AsyncSession = Depends(database.get_db) ): result = await db.execute( - select(models.Asset).where(models.Asset.author_id == current_user.id) + select(models.Asset) + .options(selectinload(models.Asset.heir)) + .where(models.Asset.author_id == current_user.id) ) - return result.scalars().all() + assets = result.scalars().all() + # Populate heir_email for the schema + for asset in assets: + if asset.heir: + asset.heir_email = asset.heir.email + else: + asset.heir_email = None + return assets @app.post("/assets/create", response_model=schemas.AssetOut) @@ -235,6 +244,30 @@ async def get_designated_assets( ) return result.scalars().all() +@app.post("/assets/delete") +async def delete_asset( + asset_del: schemas.AssetDelete, + current_user: models.User = Depends(auth.get_current_user), + db: AsyncSession = Depends(database.get_db) +): + """ + Delete an asset owned by the current user. + """ + result = await db.execute( + select(models.Asset).where(models.Asset.id == asset_del.asset_id) + ) + asset = result.scalars().first() + + if not asset: + raise HTTPException(status_code=404, detail="Asset not found") + + if asset.author_id != current_user.id: + raise HTTPException(status_code=403, detail="Not authorized to delete this asset") + + await db.delete(asset) + await db.commit() + return {"message": "Asset deleted successfully"} + @app.get("/users/search", response_model=List[schemas.UserOut]) async def search_users( query: str, @@ -416,6 +449,17 @@ async def ai_proxy( detail=f"An error occurred while requesting AI provider: {str(e)}" ) +@app.get("/get_ai_roles", response_model=List[schemas.AIRoleOut]) +async def get_ai_roles( + current_user: models.User = Depends(auth.get_current_user), + db: AsyncSession = Depends(database.get_db) +): + """ + Get all available AI roles for the logged-in user. + """ + result = await db.execute(select(models.AIRole).order_by(models.AIRole.id)) + return result.scalars().all() + # 用于测试热加载 @app.post("/post1") async def test1(): diff --git a/app/models.py b/app/models.py index 50e26ec..67f0524 100644 --- a/app/models.py +++ b/app/models.py @@ -84,4 +84,14 @@ class UserTokenUsage(Base): tokens_used = Column(Integer, default=0) last_reset_at = Column(DateTime) - user = relationship("User", backref="token_usage", uselist=False) \ No newline at end of file + user = relationship("User", backref="token_usage", uselist=False) + +class AIRole(Base): + __tablename__ = "ai_roles" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String, index=True) + description = Column(Text) + systemPrompt = Column(Text) + icon = Column(String) + iconFamily = Column(String) \ No newline at end of file diff --git a/app/schemas.py b/app/schemas.py index 6061758..a1c6c19 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -58,6 +58,8 @@ class AssetOut(AssetBase): content_outer_encrypted: str created_at: Optional[datetime] = None updated_at: Optional[datetime] = None + heir_id: Optional[int] = None + heir_email: Optional[str] = None model_config = ConfigDict(from_attributes=True) class AssetClaim(BaseModel): @@ -72,6 +74,9 @@ class AssetAssign(BaseModel): asset_id: int heir_email: str +class AssetDelete(BaseModel): + asset_id: int + class DeclareGuale(BaseModel): username: str @@ -105,4 +110,16 @@ class SubscriptionPlansBase(BaseModel): class SubscriptionPlansOut(SubscriptionPlansBase): id: int + model_config = ConfigDict(from_attributes=True) + +# AI Role Schemas +class AIRoleBase(BaseModel): + id: int + name: str + description: str + systemPrompt: str + icon: str + iconFamily: str + +class AIRoleOut(AIRoleBase): model_config = ConfigDict(from_attributes=True) \ No newline at end of file diff --git a/test/test_ai_proxy.py b/test/test_ai_proxy.py index d728ddc..de20347 100644 --- a/test/test_ai_proxy.py +++ b/test/test_ai_proxy.py @@ -14,7 +14,8 @@ async def test_ai_proxy_integration(): print(f"1. Registering user: {username}") reg_res = await client.post("/register", json={ "username": username, - "password": "testpassword" + "password": "testpassword", + "email": f"user_{int(time.time())}@example.com" }) if reg_res.status_code != 200: print(f"Registration failed: {reg_res.text}") @@ -22,7 +23,7 @@ async def test_ai_proxy_integration(): # 2. Login to get token print("2. Logging in...") - login_res = await client.post("/token", json={ + login_res = await client.post("/login", json={ "username": username, "password": "testpassword" })