Skip to content

Commit 0275ff3

Browse files
committed
Merge remote-tracking branch 'origin/develop' into bwq/0904_re
# Conflicts: # backend/apps/data_process_app.py # backend/apps/model_managment_app.py # backend/consts/const.py # backend/services/user_management_service.py # backend/utils/auth_utils.py
2 parents 14660e9 + 9c7838f commit 0275ff3

70 files changed

Lines changed: 4284 additions & 2688 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

backend/apps/data_process_app.py

Lines changed: 84 additions & 186 deletions
Large diffs are not rendered by default.

backend/apps/me_model_managment_app.py

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logging
22
from http import HTTPStatus
33

4-
from fastapi import APIRouter, Query
4+
from fastapi import APIRouter, Query, HTTPException
55
from fastapi.responses import JSONResponse
66

77
from consts.exceptions import TimeoutException, NotFoundException, MEConnectionException
@@ -44,9 +44,9 @@ async def get_me_models(
4444
"data": []
4545
})
4646
except Exception as e:
47-
logging.error(f"Failed to get model list: {str(e)}")
47+
logging.error(f"Failed to get me model list: {str(e)}")
4848
return JSONResponse(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, content={
49-
"message": f"Failed to get model list: {str(e)}",
49+
"message": f"Failed to get me model list: {str(e)}",
5050
"data": []
5151
})
5252

@@ -61,23 +61,16 @@ async def check_me_connectivity(timeout: int = Query(default=2, description="Tim
6161
return JSONResponse(
6262
status_code=HTTPStatus.OK,
6363
content={
64-
"status": "Connected",
65-
"desc": "Connection successful.",
66-
"connect_status": ModelConnectStatusEnum.AVAILABLE.value
64+
"connectivity": True,
65+
"message": "ModelEngine model connect successfully.",
6766
}
6867
)
6968
except MEConnectionException as e:
70-
logging.error(f"Request me model connectivity failed: {str(e)}")
71-
return JSONResponse(status_code=HTTPStatus.SERVICE_UNAVAILABLE, content={"status": "Disconnected",
72-
"desc": f"Connection failed.",
73-
"connect_status": ModelConnectStatusEnum.UNAVAILABLE.value})
69+
logging.error(f"ModelEngine model healthcheck failed: {str(e)}")
70+
raise HTTPException(status_code=HTTPStatus.SERVICE_UNAVAILABLE, detail="ModelEngine model connect failed.")
7471
except TimeoutException as e:
75-
logging.error(f"Request me model connectivity timeout: {str(e)}")
76-
return JSONResponse(status_code=HTTPStatus.REQUEST_TIMEOUT, content={"status": "Disconnected",
77-
"desc": "Connection timeout.",
78-
"connect_status": ModelConnectStatusEnum.UNAVAILABLE.value})
72+
logging.error(f"ModelEngine model healthcheck timeout: {str(e)}")
73+
raise HTTPException(status_code=HTTPStatus.REQUEST_TIMEOUT, detail="ModelEngine model connect timeout.")
7974
except Exception as e:
80-
logging.error(f"Unknown error occurred: {str(e)}.")
81-
return JSONResponse(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, content={"status": "Disconnected",
82-
"desc": f"Unknown error occurred: {str(e)}",
83-
"connect_status": ModelConnectStatusEnum.UNAVAILABLE.value})
75+
logging.error(f"ModelEngine model healthcheck failed with unknown error: {str(e)}.")
76+
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="ModelEngine model connect failed.")

backend/apps/memory_config_app.py

Lines changed: 142 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,46 @@
1+
"""Memory configuration and CRUD API endpoints for the app layer.
2+
3+
This module exposes HTTP endpoints under the `/memory` prefix. It follows the
4+
app-layer responsibilities:
5+
- Parse and validate HTTP inputs
6+
- Delegate business logic to the service layer
7+
- Convert unexpected exceptions to error JSON responses
8+
9+
Routes:
10+
- GET `/memory/config/load`: Load memory-related configuration for current user
11+
- POST `/memory/config/set`: Set a single configuration entry
12+
- POST `/memory/config/disable_agent`: Add a disabled agent id
13+
- DELETE `/memory/config/disable_agent/{agent_id}`: Remove a disabled agent id
14+
- POST `/memory/config/disable_useragent`: Add a disabled user-agent id
15+
- DELETE `/memory/config/disable_useragent/{agent_id}`: Remove a disabled user-agent id
16+
- POST `/memory/add`: Add memory items (optionally with LLM inference)
17+
- POST `/memory/search`: Semantic search memory items
18+
- GET `/memory/list`: List memory items
19+
- DELETE `/memory/delete/{memory_id}`: Delete a single memory item
20+
- DELETE `/memory/clear`: Clear memory items by scope
21+
"""
122
import asyncio
223
import logging
324
from typing import Any, Dict, List, Optional
425

5-
from fastapi import APIRouter, Body, Header, Path, Query
26+
from http import HTTPStatus
27+
from fastapi import APIRouter, Body, Header, Path, Query, HTTPException
628
from fastapi.responses import JSONResponse
29+
730
from nexent.memory.memory_service import (
831
add_memory as svc_add_memory,
932
clear_memory as svc_clear_memory,
1033
delete_memory as svc_delete_memory,
1134
list_memory as svc_list_memory,
1235
search_memory as svc_search_memory,
1336
)
14-
1537
from consts.const import (
1638
MEMORY_AGENT_SHARE_KEY,
1739
MEMORY_SWITCH_KEY,
1840
BOOLEAN_TRUE_VALUES,
1941
)
2042
from consts.model import MemoryAgentShareMode
43+
from consts.exceptions import UnauthorizedError
2144
from services.memory_config_service import (
2245
add_disabled_agent_id,
2346
add_disabled_useragent_id,
@@ -35,35 +58,26 @@
3558
router = APIRouter(prefix="/memory")
3659

3760

38-
# ---------------------------------------------------------------------------
39-
# Generic helpers
40-
# ---------------------------------------------------------------------------
41-
def _success(message: str = "success", content: Optional[Any] = None):
42-
return JSONResponse(status_code=200, content={"message": message, "status": "success", "content": content})
43-
44-
45-
def _error(message: str = "error"):
46-
return JSONResponse(status_code=400, content={"message": message, "status": "error"})
47-
48-
49-
# ---------------------------------------------------------------------------
50-
# Helper function
51-
# ---------------------------------------------------------------------------
52-
53-
5461
# ---------------------------------------------------------------------------
5562
# Configuration Endpoints
5663
# ---------------------------------------------------------------------------
5764
@router.get("/config/load")
5865
def load_configs(authorization: Optional[str] = Header(None)):
59-
"""Load all memory-related configuration for current user."""
66+
"""Load all memory-related configuration for the current user.
67+
68+
Args:
69+
authorization: Optional authorization header used to identify the user.
70+
"""
6071
try:
6172
user_id, _ = get_current_user_id(authorization)
6273
configs = get_user_configs(user_id)
63-
return _success(content=configs)
74+
return JSONResponse(status_code=HTTPStatus.OK, content=configs)
75+
except UnauthorizedError as e:
76+
raise HTTPException(status_code=HTTPStatus.UNAUTHORIZED, detail=str(e))
6477
except Exception as e:
6578
logger.error("load_configs failed: %s", e)
66-
return _error("Failed to load configuration")
79+
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST,
80+
detail="Failed to load configuration")
6781

6882

6983
@router.post("/config/set")
@@ -72,7 +86,17 @@ def set_single_config(
7286
value: Any = Body(..., embed=True, description="Configuration value"),
7387
authorization: Optional[str] = Header(None),
7488
):
75-
"""Unified endpoint to set single-value configuration items."""
89+
"""Set a single-value configuration item for the current user.
90+
91+
Supported keys:
92+
- `MEMORY_SWITCH_KEY`: Toggle memory system on/off (boolean-like values accepted)
93+
- `MEMORY_AGENT_SHARE_KEY`: Set agent share mode (`always`/`ask`/`never`)
94+
95+
Args:
96+
key: Configuration key to update.
97+
value: New value for the configuration key.
98+
authorization: Optional authorization header used to identify the user.
99+
"""
76100
user_id, _ = get_current_user_id(authorization)
77101

78102
if key == MEMORY_SWITCH_KEY:
@@ -83,52 +107,93 @@ def set_single_config(
83107
try:
84108
mode = MemoryAgentShareMode(str(value))
85109
except ValueError:
86-
return _error("Invalid value for MEMORY_AGENT_SHARE (expected always/ask/never)")
110+
raise HTTPException(status_code=HTTPStatus.NOT_ACCEPTABLE,
111+
detail="Invalid value for MEMORY_AGENT_SHARE (expected always/ask/never)")
87112
ok = set_agent_share(user_id, mode)
88113
else:
89-
return _error("Unsupported configuration key")
114+
raise HTTPException(status_code=HTTPStatus.NOT_ACCEPTABLE,
115+
detail="Unsupported configuration key")
90116

91-
return _success() if ok else _error("Failed to update configuration")
117+
if ok:
118+
return JSONResponse(status_code=HTTPStatus.OK, content={"success": True})
119+
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST,
120+
detail="Failed to update configuration")
92121

93122

94123
@router.post("/config/disable_agent")
95124
def add_disable_agent(
96125
agent_id: str = Body(..., embed=True),
97126
authorization: Optional[str] = Header(None),
98127
):
128+
"""Add an agent id to the user's disabled agent list.
129+
130+
Args:
131+
agent_id: Identifier of the agent to disable.
132+
authorization: Optional authorization header used to identify the user.
133+
"""
99134
user_id, _ = get_current_user_id(authorization)
100135
ok = add_disabled_agent_id(user_id, agent_id)
101-
return _success() if ok else _error("Failed to add disable agent id")
136+
if ok:
137+
return JSONResponse(status_code=HTTPStatus.OK, content={"success": True})
138+
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST,
139+
detail="Failed to add disable agent id")
102140

103141

104142
@router.delete("/config/disable_agent/{agent_id}")
105143
def remove_disable_agent(
106144
agent_id: str = Path(...),
107145
authorization: Optional[str] = Header(None),
108146
):
147+
"""Remove an agent id from the user's disabled agent list.
148+
149+
Args:
150+
agent_id: Identifier of the agent to remove from the disabled list.
151+
authorization: Optional authorization header used to identify the user.
152+
"""
109153
user_id, _ = get_current_user_id(authorization)
110154
ok = remove_disabled_agent_id(user_id, agent_id)
111-
return _success() if ok else _error("Failed to remove disable agent id")
155+
if ok:
156+
return JSONResponse(status_code=HTTPStatus.OK, content={"success": True})
157+
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST,
158+
detail="Failed to remove disable agent id")
112159

113160

114161
@router.post("/config/disable_useragent")
115162
def add_disable_useragent(
116163
agent_id: str = Body(..., embed=True),
117164
authorization: Optional[str] = Header(None),
118165
):
166+
"""Add a user-agent id to the user's disabled user-agent list.
167+
168+
Args:
169+
agent_id: Identifier of the user-agent to disable.
170+
authorization: Optional authorization header used to identify the user.
171+
"""
119172
user_id, _ = get_current_user_id(authorization)
120173
ok = add_disabled_useragent_id(user_id, agent_id)
121-
return _success() if ok else _error("Failed to add disable user-agent id")
174+
if ok:
175+
return JSONResponse(status_code=HTTPStatus.OK, content={"success": True})
176+
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST,
177+
detail="Failed to add disable user-agent id")
122178

123179

124180
@router.delete("/config/disable_useragent/{agent_id}")
125181
def remove_disable_useragent(
126182
agent_id: str = Path(...),
127183
authorization: Optional[str] = Header(None),
128184
):
185+
"""Remove a user-agent id from the user's disabled user-agent list.
186+
187+
Args:
188+
agent_id: Identifier of the user-agent to remove from the disabled list.
189+
authorization: Optional authorization header used to identify the user.
190+
"""
129191
user_id, _ = get_current_user_id(authorization)
130192
ok = remove_disabled_useragent_id(user_id, agent_id)
131-
return _success() if ok else _error("Failed to remove disable user-agent id")
193+
if ok:
194+
return JSONResponse(status_code=HTTPStatus.OK, content={"success": True})
195+
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST,
196+
detail="Failed to remove disable user-agent id")
132197

133198

134199
# ---------------------------------------------------------------------------
@@ -145,6 +210,15 @@ def add_memory(
145210
True, embed=True, description="Whether to run LLM inference during add"),
146211
authorization: Optional[str] = Header(None),
147212
):
213+
"""Add memory records for the given scope.
214+
215+
Args:
216+
messages: List of chat messages as dictionaries.
217+
memory_level: Scope for the memory record (tenant/agent/user/user_agent).
218+
agent_id: Optional agent identifier when scope is agent-related.
219+
infer: Whether to run LLM inference during add.
220+
authorization: Optional authorization header used to identify the user.
221+
"""
148222
user_id, tenant_id = get_current_user_id(authorization)
149223
try:
150224
result = asyncio.run(svc_add_memory(
@@ -156,10 +230,10 @@ def add_memory(
156230
agent_id=agent_id,
157231
infer=infer,
158232
))
159-
return _success(content=result)
233+
return JSONResponse(status_code=HTTPStatus.OK, content=result)
160234
except Exception as e:
161235
logger.error("add_memory error: %s", e, exc_info=True)
162-
return _error(str(e))
236+
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
163237

164238

165239
@router.post("/search")
@@ -170,6 +244,15 @@ def search_memory(
170244
agent_id: Optional[str] = Body(None, embed=True),
171245
authorization: Optional[str] = Header(None),
172246
):
247+
"""Search memory semantically for the given scope.
248+
249+
Args:
250+
query_text: Natural language query to search memory.
251+
memory_level: Scope for search (tenant/agent/user/user_agent).
252+
top_k: Maximum number of results to return.
253+
agent_id: Optional agent identifier when scope is agent-related.
254+
authorization: Optional authorization header used to identify the user.
255+
"""
173256
user_id, tenant_id = get_current_user_id(authorization)
174257
try:
175258
results = asyncio.run(svc_search_memory(
@@ -181,10 +264,10 @@ def search_memory(
181264
top_k=top_k,
182265
agent_id=agent_id,
183266
))
184-
return _success(content=results)
267+
return JSONResponse(status_code=HTTPStatus.OK, content=results)
185268
except Exception as e:
186269
logger.error("search_memory error: %s", e, exc_info=True)
187-
return _error(str(e))
270+
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
188271

189272

190273
@router.get("/list")
@@ -195,6 +278,13 @@ def list_memory(
195278
None, description="Filter by agent id if applicable"),
196279
authorization: Optional[str] = Header(None),
197280
):
281+
"""List memory for the given scope.
282+
283+
Args:
284+
memory_level: Scope for listing (tenant/agent/user/user_agent).
285+
agent_id: Optional agent filter when scope is agent-related.
286+
authorization: Optional authorization header used to identify the user.
287+
"""
198288
user_id, tenant_id = get_current_user_id(authorization)
199289
try:
200290
payload = asyncio.run(svc_list_memory(
@@ -204,25 +294,31 @@ def list_memory(
204294
user_id=user_id,
205295
agent_id=agent_id,
206296
))
207-
return _success(content=payload)
297+
return JSONResponse(status_code=HTTPStatus.OK, content=payload)
208298
except Exception as e:
209299
logger.error("list_memory error: %s", e, exc_info=True)
210-
return _error(str(e))
300+
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
211301

212302

213303
@router.delete("/delete/{memory_id}")
214304
def delete_memory(
215305
memory_id: str = Path(..., description="ID of memory to delete"),
216306
authorization: Optional[str] = Header(None),
217307
):
308+
"""Delete a specific memory record by id.
309+
310+
Args:
311+
memory_id: Identifier of the memory record to delete.
312+
authorization: Optional authorization header used to identify the user.
313+
"""
218314
_user_id, tenant_id = get_current_user_id(authorization)
219315
try:
220316
result = asyncio.run(svc_delete_memory(
221317
memory_id=memory_id, memory_config=build_memory_config(tenant_id)))
222-
return _success(content=result)
318+
return JSONResponse(status_code=HTTPStatus.OK, content=result)
223319
except Exception as e:
224320
logger.error("delete_memory error: %s", e, exc_info=True)
225-
return _error(str(e))
321+
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
226322

227323

228324
@router.delete("/clear")
@@ -233,6 +329,13 @@ def clear_memory(
233329
None, description="Filter by agent id if applicable"),
234330
authorization: Optional[str] = Header(None),
235331
):
332+
"""Clear memory records for the given scope.
333+
334+
Args:
335+
memory_level: Scope for clearing (tenant/agent/user/user_agent).
336+
agent_id: Optional agent filter when scope is agent-related.
337+
authorization: Optional authorization header used to identify the user.
338+
"""
236339
user_id, tenant_id = get_current_user_id(authorization)
237340
try:
238341
result = asyncio.run(svc_clear_memory(
@@ -242,7 +345,7 @@ def clear_memory(
242345
user_id=user_id,
243346
agent_id=agent_id,
244347
))
245-
return _success(content=result)
348+
return JSONResponse(status_code=HTTPStatus.OK, content=result)
246349
except Exception as e:
247350
logger.error("clear_memory error: %s", e, exc_info=True)
248-
return _error(str(e))
351+
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))

0 commit comments

Comments
 (0)