Skip to content

Commit f45ec8c

Browse files
nishenghao.nshclaude
andcommitted
fix(memory_case): remove unused tenant_id/team_id from scope system
OpenDerisk is not a SaaS multi-tenant platform. tenant_id and team_id were designed for multi-tenant isolation but have never been used in practice—the ContextVar always injects "default", DB data shows zero cases with meaningful tenant/team values, and LLM agents naturally use tags/region/application_name in case_context instead. Removing them simplifies scope to only app_code and environment (both default=wildcard), eliminates dead SQL/vector filter code, and makes room for future business-semantic scope dimensions (region, tags). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 67d0840 commit f45ec8c

9 files changed

Lines changed: 8 additions & 95 deletions

File tree

packages/derisk-ext/src/derisk_ext/plugin/memory_case/__init__.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@
1111
KEY_REGION,
1212
KEY_RELATED_SERVICES,
1313
KEY_TAGS,
14-
KEY_TEAM_ID,
1514
KEY_TELEMETRY_CHANNELS,
16-
KEY_TENANT_ID,
1715
case_context_from_metadata,
1816
is_memory_search_scope_app_wildcard,
1917
is_memory_search_scope_env_wildcard,
@@ -69,9 +67,7 @@
6967
"KEY_REGION",
7068
"KEY_RELATED_SERVICES",
7169
"KEY_TAGS",
72-
"KEY_TEAM_ID",
7370
"KEY_TELEMETRY_CHANNELS",
74-
"KEY_TENANT_ID",
7571
"MEMORY_CASE_VECTOR_NAME",
7672
"MemoryCaseDao",
7773
"MemoryCaseEntity",

packages/derisk-ext/src/derisk_ext/plugin/memory_case/case_context.py

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
"""Case routing and descriptive context stored only in ``metadata_json``.
22
33
**Table model:** ``derisk_plugin_memory_case`` has **no** ``app_code`` / ``environment``
4-
columns (nor tenant/team). Those values exist only under
4+
columns. Those values exist only under
55
``metadata["case_context"]`` (JSON inside ``metadata_json``).
66
77
**Search narrowing (``memory_case_search``):**
8-
- Optional ``scope`` keys ``app_code``, ``environment``, ``tenant_id``, ``team_id`` filter
8+
- Optional ``scope`` keys ``app_code``, ``environment`` filter
99
via ``JSON_EXTRACT(metadata_json, '$.case_context.*')`` only.
1010
- For ``app_code`` / ``environment``: value missing, empty, or literal ``default`` → **no**
1111
filter on that key (wildcard). Non-default → equality on stored ``case_context``.
12-
- For ``tenant_id`` / ``team_id``: only filter when present and non-empty in ``scope``.
1312
1413
**Lexical ``query``:** FULLTEXT / LIKE over ``FULLTEXT_LEXICAL_COLUMNS`` (must match DB index).
1514
@@ -37,9 +36,6 @@
3736
# Routing hints (optional; defaults align with tool_pack / MemoryRequestContext)
3837
KEY_APP_CODE = "app_code"
3938
KEY_ENVIRONMENT = "environment"
40-
KEY_TENANT_ID = "tenant_id"
41-
KEY_TEAM_ID = "team_id"
42-
4339
# Descriptive / operational context (extend as needed)
4440
KEY_APPLICATION_NAME = "application_name"
4541
KEY_REGION = "region"
@@ -86,12 +82,6 @@ def scope_filters_match(metadata: Optional[Dict[str, Any]], scope: Dict[str, Any
8682
got_env = str(ctx.get(KEY_ENVIRONMENT) or "default").strip().lower()
8783
if got_env != want_env:
8884
return False
89-
if scope.get(KEY_TENANT_ID):
90-
if str(ctx.get(KEY_TENANT_ID) or "") != str(scope[KEY_TENANT_ID]):
91-
return False
92-
if scope.get(KEY_TEAM_ID):
93-
if str(ctx.get(KEY_TEAM_ID) or "") != str(scope[KEY_TEAM_ID]):
94-
return False
9585
return True
9686

9787

@@ -128,15 +118,8 @@ def is_memory_search_scope_env_wildcard(scope: Optional[Dict[str, Any]]) -> bool
128118
def vector_metadata_from_case(case_id: str, metadata: Optional[Dict[str, Any]]) -> Dict[str, Any]:
129119
"""Flatten ``case_context`` for Chroma metadata filters."""
130120
ctx = case_context_from_metadata(metadata)
131-
out: Dict[str, Any] = {
121+
return {
132122
"case_id": case_id,
133123
KEY_APP_CODE: ctx.get(KEY_APP_CODE) or "default",
134124
KEY_ENVIRONMENT: ctx.get(KEY_ENVIRONMENT) or "default",
135125
}
136-
tid = ctx.get(KEY_TENANT_ID)
137-
if tid is not None:
138-
out[KEY_TENANT_ID] = tid
139-
tm = ctx.get(KEY_TEAM_ID)
140-
if tm is not None:
141-
out[KEY_TEAM_ID] = tm
142-
return out

packages/derisk-ext/src/derisk_ext/plugin/memory_case/markdown.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ def _format_case_context(case: CandidateCase) -> str:
1717
"app_code",
1818
"environment",
1919
"region",
20-
"tenant_id",
21-
"team_id",
2220
"data_sources",
2321
"telemetry_channels",
2422
"related_services",

packages/derisk-ext/src/derisk_ext/plugin/memory_case/models.py

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ def default_case_fingerprint(data: dict) -> str:
2727
ctx = ctx if isinstance(ctx, dict) else {}
2828
env = str(ctx.get("environment") or "default").strip().lower()
2929
app = str(ctx.get("app_code") or "default").strip().lower()
30-
tenant = str(ctx.get("tenant_id") or "").strip().lower()
31-
team = str(ctx.get("team_id") or "").strip().lower()
32-
blob = "|".join([tenant, team, app, env, case_id, symptom[:4000]])
30+
blob = "|".join([app, env, case_id, symptom[:4000]])
3331
return hashlib.sha256(blob.encode("utf-8")).hexdigest()
3432

3533

@@ -52,7 +50,7 @@ def _normalize_metadata_scope_and_fingerprint(cls, data: Any) -> Any:
5250
meta = dict(d.get("metadata") or {})
5351
raw_ctx = meta.get(CASE_CONTEXT_KEY)
5452
ctx = dict(raw_ctx) if isinstance(raw_ctx, dict) else {}
55-
for key in ("tenant_id", "team_id", "app_code", "environment"):
53+
for key in ("app_code", "environment"):
5654
if key in d and d[key] is not None:
5755
ctx.setdefault(key, d[key])
5856
del d[key]
@@ -113,7 +111,7 @@ def _coerce_handling_path(cls, v: Any) -> str:
113111
default_factory=dict,
114112
description=(
115113
"Case meta: routing hints and context live under metadata['case_context'] "
116-
"(app_code, environment, tenant_id, team_id, application_name, data_sources, …)."
114+
"(app_code, environment, application_name, data_sources, …)."
117115
),
118116
)
119117
created_at: Optional[datetime] = None
@@ -128,12 +126,6 @@ class MemoryRequestContext(BaseModel):
128126

129127
model_config = ConfigDict(arbitrary_types_allowed=True)
130128

131-
tenant_id: Optional[str] = Field(
132-
None, description="Optional; if set, rows must match case_context.tenant_id"
133-
)
134-
team_id: Optional[str] = Field(
135-
None, description="Optional; if set, rows must match case_context.team_id"
136-
)
137129
app_code: str = Field(
138130
"default",
139131
description=(
@@ -156,8 +148,6 @@ class MemoryRequestContext(BaseModel):
156148

157149
def scope_dict(self) -> Dict[str, Any]:
158150
return {
159-
"tenant_id": self.tenant_id,
160-
"team_id": self.team_id,
161151
"app_code": self.app_code,
162152
"environment": self.environment,
163153
}

packages/derisk-ext/src/derisk_ext/plugin/memory_case/service.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ def list_tools(self) -> List[MemoryToolSpec]:
7575
"scope": {
7676
"type": "object",
7777
"description": (
78-
"Routing isolation ONLY (app_code/tenant_id/team_id for "
79-
"multi-tenant; environment for deploy env prod/staging). "
78+
"Routing isolation ONLY (app_code for app scope; "
79+
"environment for deploy env prod/staging). "
8080
"Cloud-vendor or region info belongs in case metadata "
8181
"(region/tags), NOT in scope. "
8282
"Omit or set to 'default' for wildcard (recommended)."
@@ -376,8 +376,6 @@ async def _find_similar_cases(
376376
scope = {
377377
"app_code": ctx.get("app_code") or "default",
378378
"environment": ctx.get("environment") or "default",
379-
"tenant_id": ctx.get("tenant_id"),
380-
"team_id": ctx.get("team_id"),
381379
}
382380
query_text = case.markdown_summary or case.symptom_summary
383381
if not query_text:

packages/derisk-ext/src/derisk_ext/plugin/memory_case/sqlalchemy_dao.py

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -94,20 +94,6 @@ def _apply_scope_sql_filters(q, scope: Dict[str, Any], dialect: str):
9494
"'$.case_context.environment')),'default')) = LOWER(:__mem_env)"
9595
).bindparams(__mem_env=env),
9696
)
97-
if scope.get("tenant_id"):
98-
q = q.filter(
99-
text(
100-
"JSON_UNQUOTE(JSON_EXTRACT(COALESCE(metadata_json, '{}'), "
101-
"'$.case_context.tenant_id')) = :__mem_tenant"
102-
).bindparams(__mem_tenant=scope["tenant_id"])
103-
)
104-
if scope.get("team_id"):
105-
q = q.filter(
106-
text(
107-
"JSON_UNQUOTE(JSON_EXTRACT(COALESCE(metadata_json, '{}'), "
108-
"'$.case_context.team_id')) = :__mem_team"
109-
).bindparams(__mem_team=scope["team_id"])
110-
)
11197
return q, False
11298
if dialect == "sqlite":
11399
if not wild_app:
@@ -124,16 +110,6 @@ def _apply_scope_sql_filters(q, scope: Dict[str, Any], dialect: str):
124110
)
125111
== str(env).lower(),
126112
)
127-
if scope.get("tenant_id"):
128-
q = q.filter(
129-
func.coalesce(func.json_extract(m, "$.case_context.tenant_id"), "")
130-
== str(scope["tenant_id"])
131-
)
132-
if scope.get("team_id"):
133-
q = q.filter(
134-
func.coalesce(func.json_extract(m, "$.case_context.team_id"), "")
135-
== str(scope["team_id"])
136-
)
137113
return q, False
138114
logger.warning(
139115
"memory_case DAO: dialect %r has no JSON scope SQL; applying in-Python filter",

packages/derisk-ext/src/derisk_ext/plugin/memory_case/tests/test_vector_search.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,6 @@ def _case(
131131
summary: str,
132132
env: str = "default",
133133
app: str = "default",
134-
tenant: str = "",
135-
team: str = "",
136134
) -> CandidateCase:
137135
return CandidateCase(
138136
case_id=cid,
@@ -142,8 +140,6 @@ def _case(
142140
"case_context": {
143141
"app_code": app,
144142
"environment": env,
145-
"tenant_id": tenant,
146-
"team_id": team,
147143
}
148144
},
149145
)

packages/derisk-ext/src/derisk_ext/plugin/memory_case/tool_pack.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
from .case_context import (
1313
KEY_APP_CODE,
1414
KEY_ENVIRONMENT,
15-
KEY_TEAM_ID,
16-
KEY_TENANT_ID,
1715
merge_case_context,
1816
)
1917
from .plugin_resolver import resolve_memory_case_plugin
@@ -75,10 +73,6 @@ def inject_memory_scope(
7573
KEY_APP_CODE: scope.get(KEY_APP_CODE, "default"),
7674
KEY_ENVIRONMENT: scope.get(KEY_ENVIRONMENT, "default"),
7775
}
78-
if scope.get(KEY_TENANT_ID):
79-
patch[KEY_TENANT_ID] = scope[KEY_TENANT_ID]
80-
if scope.get(KEY_TEAM_ID):
81-
patch[KEY_TEAM_ID] = scope[KEY_TEAM_ID]
8276
case_data["metadata"] = merge_case_context(
8377
case_data.get("metadata"), patch
8478
)

packages/derisk-ext/src/derisk_ext/plugin/memory_case/vector_index.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
from .case_context import (
1212
KEY_APP_CODE,
1313
KEY_ENVIRONMENT,
14-
KEY_TEAM_ID,
15-
KEY_TENANT_ID,
1614
is_memory_search_scope_app_wildcard,
1715
is_memory_search_scope_env_wildcard,
1816
vector_metadata_from_case,
@@ -129,14 +127,6 @@ async def search(self, query: str, case_scope: dict, top_k: int) -> List[str]:
129127
key=KEY_ENVIRONMENT, value=case_scope.get(KEY_ENVIRONMENT),
130128
),
131129
)
132-
if case_scope.get(KEY_TENANT_ID):
133-
fl.append(
134-
MetadataFilter(key=KEY_TENANT_ID, value=case_scope.get(KEY_TENANT_ID))
135-
)
136-
if case_scope.get(KEY_TEAM_ID):
137-
fl.append(
138-
MetadataFilter(key=KEY_TEAM_ID, value=case_scope.get(KEY_TEAM_ID))
139-
)
140130
filters = MetadataFilters(filters=fl) if fl else None
141131
chunks = self._vector_store.similar_search_with_scores(
142132
text=query, topk=top_k, score_threshold=0.0, filters=filters
@@ -159,14 +149,6 @@ async def search_with_scores(
159149
key=KEY_ENVIRONMENT, value=case_scope.get(KEY_ENVIRONMENT),
160150
),
161151
)
162-
if case_scope.get(KEY_TENANT_ID):
163-
fl.append(
164-
MetadataFilter(key=KEY_TENANT_ID, value=case_scope.get(KEY_TENANT_ID))
165-
)
166-
if case_scope.get(KEY_TEAM_ID):
167-
fl.append(
168-
MetadataFilter(key=KEY_TEAM_ID, value=case_scope.get(KEY_TEAM_ID))
169-
)
170152
filters = MetadataFilters(filters=fl) if fl else None
171153
chunks = self._vector_store.similar_search_with_scores(
172154
text=query, topk=top_k, score_threshold=0.0, filters=filters

0 commit comments

Comments
 (0)