claude-smart 0.2.46 → 0.2.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/README.md +19 -11
- package/bin/claude-smart.js +290 -68
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/README.md +11 -10
- package/plugin/dashboard/app/layout.tsx +20 -0
- package/plugin/dashboard/app/sessions/[sessionId]/page.tsx +36 -2
- package/plugin/dashboard/package-lock.json +61 -390
- package/plugin/dashboard/package.json +2 -2
- package/plugin/opencode/dist/server.mjs +76 -2
- package/plugin/opencode/server.mts +79 -2
- package/plugin/pyproject.toml +6 -2
- package/plugin/scripts/smart-install.sh +7 -1
- package/plugin/src/claude_smart/cli.py +210 -22
- package/plugin/src/claude_smart/context_format.py +9 -9
- package/plugin/src/claude_smart/cs_cite.py +66 -19
- package/plugin/uv.lock +5 -5
- package/plugin/vendor/reflexio/.env.example +7 -0
- package/plugin/vendor/reflexio/README.md +3 -3
- package/plugin/vendor/reflexio/pyproject.toml +2 -1
- package/plugin/vendor/reflexio/reflexio/README.md +11 -6
- package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +1 -1
- package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +2 -2
- package/plugin/vendor/reflexio/reflexio/cli/utils.py +44 -1
- package/plugin/vendor/reflexio/reflexio/client/client.py +97 -0
- package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +8 -0
- package/plugin/vendor/reflexio/reflexio/lib/_base.py +15 -0
- package/plugin/vendor/reflexio/reflexio/lib/_config.py +23 -18
- package/plugin/vendor/reflexio/reflexio/lib/_generation.py +9 -8
- package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +16 -1
- package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +27 -16
- package/plugin/vendor/reflexio/reflexio/lib/_search.py +27 -5
- package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +9 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +1 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/governance.py +117 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +45 -3
- package/plugin/vendor/reflexio/reflexio/models/config_schema.py +37 -0
- package/plugin/vendor/reflexio/reflexio/server/README.md +38 -9
- package/plugin/vendor/reflexio/reflexio/server/__init__.py +21 -2
- package/plugin/vendor/reflexio/reflexio/server/api.py +274 -3267
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/README.md +4 -3
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/publisher_api.py +16 -1
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +1 -1
- package/plugin/vendor/reflexio/reflexio/server/{_auth.py → auth.py} +2 -0
- package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +62 -36
- package/plugin/vendor/reflexio/reflexio/server/deployment_profile.py +69 -0
- package/plugin/vendor/reflexio/reflexio/server/extensions.py +213 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_embedding.py +424 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_json_extraction.py +249 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_structured_output.py +195 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_subprocess.py +152 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_text_generation.py +980 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_types.py +110 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +73 -1819
- package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +4 -4
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +57 -5
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +12 -1
- package/plugin/vendor/reflexio/reflexio/server/middleware.py +244 -0
- package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +9 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.4.0.prompt.md +14 -2
- package/plugin/vendor/reflexio/reflexio/server/rate_limit.py +79 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/__init__.py +6 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/_common.py +25 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/_metering.py +98 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/braintrust.py +129 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/config.py +210 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/evaluation.py +549 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/interactions.py +259 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/playbooks.py +578 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/profiles.py +423 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/provenance.py +345 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/search.py +349 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/system.py +261 -0
- package/plugin/vendor/reflexio/reflexio/server/scheduling.py +132 -0
- package/plugin/vendor/reflexio/reflexio/server/services/README.md +5 -2
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/__init__.py +38 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_batch_progress.py +298 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_config_filter.py +152 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_extraction_lifecycle.py +243 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_should_run.py +299 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_status_change.py +273 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_usage_billing.py +258 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +17 -1183
- package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +8 -1
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +26 -41
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +8 -0
- package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +232 -123
- package/plugin/vendor/reflexio/reflexio/server/services/governance/config.py +52 -0
- package/plugin/vendor/reflexio/reflexio/server/services/governance/service.py +378 -0
- package/plugin/vendor/reflexio/reflexio/server/services/governance/subject_refs.py +34 -0
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +385 -78
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +9 -1
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/aggregation_prompt_processing.py +100 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator.py +121 -525
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_clustering.py +184 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_postprocessing.py +130 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_prompt_formatting.py +212 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/consolidator.py +258 -69
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/service.py +9 -9
- package/plugin/vendor/reflexio/reflexio/server/services/profile/components/extractor.py +3 -2
- package/plugin/vendor/reflexio/reflexio/server/services/publish_learning_worker.py +288 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/recency.py +211 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +29 -13
- package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +4 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/governance_validation.py +681 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +43 -6
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +10 -1167
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +58 -351
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +49 -19
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +452 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +11 -4
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1 -2133
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +7 -1126
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +73 -33
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +30 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/__init__.py +9 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_agent_run_store.py +506 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_pending_tool_call_store.py +704 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_run_tool_dependency_store.py +123 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/__init__.py +6 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_deletion.py +263 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_fts_vec.py +132 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/__init__.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_audit.py +122 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_erase_execution.py +465 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_purge.py +387 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_rebuild_hide.py +332 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_subject_barrier.py +511 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/__init__.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +955 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_eval_results.py +189 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_optimization.py +247 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_source_linkage.py +145 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +844 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/__init__.py +9 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_interaction_store.py +263 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_profile_store.py +896 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_search.py +270 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +50 -9
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +64 -370
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +4 -4
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +0 -909
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +2 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +20 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/__init__.py +9 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_agent_run_store.py +86 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_models.py +195 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_pending_tool_call_store.py +148 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_run_tool_dependency_store.py +38 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/__init__.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_audit.py +29 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_erase_execution.py +30 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_purge.py +74 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_rebuild_hide.py +32 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_subject_barrier.py +49 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/__init__.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_agent.py +365 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_eval_results.py +124 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_optimization.py +85 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_source_linkage.py +47 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_user.py +333 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/__init__.py +9 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_interaction_store.py +73 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/{_profiles.py → profiles/_profile_store.py} +57 -86
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_search.py +32 -0
- package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +153 -12
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/user_detail_stripping.py +0 -84
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
"""Profile route handlers (extracted from api.py, Tier3 A2)."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
from fastapi import (
|
|
10
|
+
APIRouter,
|
|
11
|
+
BackgroundTasks,
|
|
12
|
+
Depends,
|
|
13
|
+
Request,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from reflexio.models.api_schema.retriever_schema import (
|
|
17
|
+
GetProfileStatisticsResponse,
|
|
18
|
+
GetProfilesViewResponse,
|
|
19
|
+
GetUserProfilesRequest,
|
|
20
|
+
ProfileChangeLogViewResponse,
|
|
21
|
+
UpdateUserProfileRequest,
|
|
22
|
+
UpdateUserProfileResponse,
|
|
23
|
+
)
|
|
24
|
+
from reflexio.models.api_schema.service_schemas import (
|
|
25
|
+
AddUserProfileRequest,
|
|
26
|
+
AddUserProfileResponse,
|
|
27
|
+
BulkDeleteResponse,
|
|
28
|
+
DeleteProfilesByIdsRequest,
|
|
29
|
+
DeleteUserProfileRequest,
|
|
30
|
+
DeleteUserProfileResponse,
|
|
31
|
+
DowngradeProfilesRequest,
|
|
32
|
+
DowngradeProfilesResponse,
|
|
33
|
+
ManualProfileGenerationRequest,
|
|
34
|
+
ManualProfileGenerationResponse,
|
|
35
|
+
RerunProfileGenerationRequest,
|
|
36
|
+
RerunProfileGenerationResponse,
|
|
37
|
+
Status,
|
|
38
|
+
UpgradeProfilesRequest,
|
|
39
|
+
UpgradeProfilesResponse,
|
|
40
|
+
)
|
|
41
|
+
from reflexio.models.api_schema.ui.converters import (
|
|
42
|
+
to_profile_change_log_view,
|
|
43
|
+
to_profile_view,
|
|
44
|
+
)
|
|
45
|
+
from reflexio.server.api_endpoints import (
|
|
46
|
+
publisher_api,
|
|
47
|
+
)
|
|
48
|
+
from reflexio.server.auth import (
|
|
49
|
+
default_get_org_id,
|
|
50
|
+
)
|
|
51
|
+
from reflexio.server.cache import reflexio_cache
|
|
52
|
+
from reflexio.server.rate_limit import limiter
|
|
53
|
+
|
|
54
|
+
logger = logging.getLogger(__name__)
|
|
55
|
+
router = APIRouter()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@router.post(
|
|
59
|
+
"/api/add_user_profile",
|
|
60
|
+
response_model=AddUserProfileResponse,
|
|
61
|
+
response_model_exclude_none=True,
|
|
62
|
+
)
|
|
63
|
+
@limiter.limit("60/minute") # Rate limit for write operations
|
|
64
|
+
def add_user_profile_endpoint(
|
|
65
|
+
request: Request,
|
|
66
|
+
payload: AddUserProfileRequest,
|
|
67
|
+
org_id: str = Depends(default_get_org_id),
|
|
68
|
+
) -> AddUserProfileResponse:
|
|
69
|
+
"""Add user profile directly to storage, bypassing inference.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
request (Request): The HTTP request object (for rate limiting)
|
|
73
|
+
payload (AddUserProfileRequest): The request containing user profiles
|
|
74
|
+
org_id (str): Organization ID
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
AddUserProfileResponse: Response containing success status, message, and added count
|
|
78
|
+
"""
|
|
79
|
+
return publisher_api.add_user_profile(org_id=org_id, request=payload)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@router.get("/api/profile_change_log", response_model=ProfileChangeLogViewResponse)
|
|
83
|
+
def get_profile_change_log(
|
|
84
|
+
org_id: str = Depends(default_get_org_id),
|
|
85
|
+
) -> ProfileChangeLogViewResponse:
|
|
86
|
+
# Serves the reconstructed profile change log (rebuilt from lineage events). The
|
|
87
|
+
# legacy `profile_change_logs` table is no longer written; see
|
|
88
|
+
# reconstruct_profile_change_log in lib/_profiles.py.
|
|
89
|
+
response = reflexio_cache.get_reflexio(org_id=org_id).get_profile_change_logs()
|
|
90
|
+
return ProfileChangeLogViewResponse(
|
|
91
|
+
success=response.success,
|
|
92
|
+
profile_change_logs=[
|
|
93
|
+
to_profile_change_log_view(log) for log in response.profile_change_logs
|
|
94
|
+
],
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@router.delete(
|
|
99
|
+
"/api/delete_profile",
|
|
100
|
+
response_model=DeleteUserProfileResponse,
|
|
101
|
+
response_model_exclude_none=True,
|
|
102
|
+
)
|
|
103
|
+
def delete_profile(
|
|
104
|
+
request: DeleteUserProfileRequest,
|
|
105
|
+
org_id: str = Depends(default_get_org_id),
|
|
106
|
+
) -> DeleteUserProfileResponse:
|
|
107
|
+
return publisher_api.delete_user_profile(org_id=org_id, request=request)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@router.delete(
|
|
111
|
+
"/api/delete_profiles_by_ids",
|
|
112
|
+
response_model=BulkDeleteResponse,
|
|
113
|
+
response_model_exclude_none=True,
|
|
114
|
+
)
|
|
115
|
+
def delete_profiles_by_ids(
|
|
116
|
+
request: DeleteProfilesByIdsRequest,
|
|
117
|
+
org_id: str = Depends(default_get_org_id),
|
|
118
|
+
) -> BulkDeleteResponse:
|
|
119
|
+
"""Delete multiple profiles by their IDs.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
request (DeleteProfilesByIdsRequest): Request containing list of profile IDs to delete
|
|
123
|
+
org_id (str): Organization ID
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
BulkDeleteResponse: Response containing success status and deleted count
|
|
127
|
+
"""
|
|
128
|
+
return publisher_api.delete_profiles_by_ids(org_id=org_id, request=request)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@router.delete(
|
|
132
|
+
"/api/delete_all_profiles",
|
|
133
|
+
response_model=BulkDeleteResponse,
|
|
134
|
+
response_model_exclude_none=True,
|
|
135
|
+
)
|
|
136
|
+
@limiter.limit("10/minute")
|
|
137
|
+
def delete_all_profiles(
|
|
138
|
+
request: Request,
|
|
139
|
+
org_id: str = Depends(default_get_org_id),
|
|
140
|
+
) -> BulkDeleteResponse:
|
|
141
|
+
"""Delete all profiles.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
org_id (str): Organization ID
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
BulkDeleteResponse: Response containing success status and deleted count
|
|
148
|
+
"""
|
|
149
|
+
return publisher_api.delete_all_profiles_bulk(org_id=org_id)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@router.post(
|
|
153
|
+
"/api/get_profiles",
|
|
154
|
+
response_model=GetProfilesViewResponse,
|
|
155
|
+
response_model_exclude_none=True,
|
|
156
|
+
)
|
|
157
|
+
def get_profiles(
|
|
158
|
+
request: GetUserProfilesRequest,
|
|
159
|
+
org_id: str = Depends(default_get_org_id),
|
|
160
|
+
) -> GetProfilesViewResponse:
|
|
161
|
+
response = reflexio_cache.get_reflexio(org_id=org_id).get_profiles(request)
|
|
162
|
+
return GetProfilesViewResponse(
|
|
163
|
+
success=response.success,
|
|
164
|
+
user_profiles=[to_profile_view(p) for p in response.user_profiles],
|
|
165
|
+
msg=response.msg,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@router.get(
|
|
170
|
+
"/api/get_all_profiles",
|
|
171
|
+
response_model=GetProfilesViewResponse,
|
|
172
|
+
response_model_exclude_none=True,
|
|
173
|
+
)
|
|
174
|
+
@limiter.limit("30/minute")
|
|
175
|
+
def get_all_profiles(
|
|
176
|
+
request: Request,
|
|
177
|
+
limit: int = 100,
|
|
178
|
+
status_filter: str | None = None,
|
|
179
|
+
profile_id: str | None = None,
|
|
180
|
+
user_id: str | None = None,
|
|
181
|
+
query: str | None = None,
|
|
182
|
+
source: str | None = None,
|
|
183
|
+
profile_time_to_live: str | None = None,
|
|
184
|
+
start_time: int | None = None,
|
|
185
|
+
end_time: int | None = None,
|
|
186
|
+
include_tombstones: bool = False,
|
|
187
|
+
org_id: str = Depends(default_get_org_id),
|
|
188
|
+
) -> GetProfilesViewResponse:
|
|
189
|
+
"""Get all user profiles across all users.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
limit (int, optional): Maximum number of profiles to return. Defaults to 100.
|
|
193
|
+
status_filter (str, optional): Filter by profile status. Can be "current", "pending", or "archived".
|
|
194
|
+
profile_id (str, optional): Exact profile ID to retrieve.
|
|
195
|
+
user_id (str, optional): Exact user ID to filter by.
|
|
196
|
+
query (str, optional): Case-insensitive text filter across visible fields.
|
|
197
|
+
source (str, optional): Exact profile source to filter by.
|
|
198
|
+
profile_time_to_live (str, optional): Exact TTL value to filter by.
|
|
199
|
+
start_time (int, optional): Minimum last-modified epoch seconds.
|
|
200
|
+
end_time (int, optional): Maximum last-modified epoch seconds.
|
|
201
|
+
include_tombstones (bool, optional): Include merged/superseded rows when
|
|
202
|
+
looking up a specific profile_id.
|
|
203
|
+
org_id (str): Organization ID
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
GetProfilesViewResponse: Response containing all user profiles
|
|
207
|
+
"""
|
|
208
|
+
reflexio = reflexio_cache.get_reflexio(org_id=org_id)
|
|
209
|
+
|
|
210
|
+
if profile_id and include_tombstones:
|
|
211
|
+
storage = reflexio.request_context.storage
|
|
212
|
+
if storage is None:
|
|
213
|
+
return GetProfilesViewResponse(
|
|
214
|
+
success=True,
|
|
215
|
+
user_profiles=[],
|
|
216
|
+
msg="Storage is not configured",
|
|
217
|
+
)
|
|
218
|
+
profile = storage.get_profile_by_id(
|
|
219
|
+
profile_id, include_tombstones=include_tombstones
|
|
220
|
+
)
|
|
221
|
+
profiles = [profile] if profile else []
|
|
222
|
+
return GetProfilesViewResponse(
|
|
223
|
+
success=True,
|
|
224
|
+
user_profiles=[to_profile_view(p) for p in profiles],
|
|
225
|
+
msg=f"Found {len(profiles)} profile(s)",
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Map status_filter string to Status list
|
|
229
|
+
status_filter_list = None
|
|
230
|
+
if status_filter == "current":
|
|
231
|
+
status_filter_list = [None]
|
|
232
|
+
elif status_filter == "pending":
|
|
233
|
+
status_filter_list = [Status.PENDING]
|
|
234
|
+
elif status_filter == "archived":
|
|
235
|
+
status_filter_list = [Status.ARCHIVED]
|
|
236
|
+
|
|
237
|
+
response = reflexio.get_all_profiles(
|
|
238
|
+
limit=limit,
|
|
239
|
+
status_filter=status_filter_list, # type: ignore[reportArgumentType]
|
|
240
|
+
user_id=user_id,
|
|
241
|
+
profile_id=profile_id,
|
|
242
|
+
query=query,
|
|
243
|
+
source=source,
|
|
244
|
+
profile_time_to_live=profile_time_to_live,
|
|
245
|
+
start_time=start_time,
|
|
246
|
+
end_time=end_time,
|
|
247
|
+
)
|
|
248
|
+
return GetProfilesViewResponse(
|
|
249
|
+
success=response.success,
|
|
250
|
+
user_profiles=[to_profile_view(p) for p in response.user_profiles],
|
|
251
|
+
msg=response.msg,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
@router.get(
|
|
256
|
+
"/api/get_profile_statistics",
|
|
257
|
+
response_model=GetProfileStatisticsResponse,
|
|
258
|
+
response_model_exclude_none=True,
|
|
259
|
+
)
|
|
260
|
+
def get_profile_statistics(
|
|
261
|
+
org_id: str = Depends(default_get_org_id),
|
|
262
|
+
) -> GetProfileStatisticsResponse:
|
|
263
|
+
"""Get efficient profile statistics using storage layer queries.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
org_id (str): Organization ID
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
GetProfileStatisticsResponse: Response containing profile counts by status
|
|
270
|
+
"""
|
|
271
|
+
# Create Reflexio instance
|
|
272
|
+
reflexio = reflexio_cache.get_reflexio(org_id=org_id)
|
|
273
|
+
|
|
274
|
+
# Get profile statistics using Reflexio's method
|
|
275
|
+
return reflexio.get_profile_statistics()
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
@router.put(
|
|
279
|
+
"/api/update_user_profile",
|
|
280
|
+
response_model=UpdateUserProfileResponse,
|
|
281
|
+
response_model_exclude_none=True,
|
|
282
|
+
)
|
|
283
|
+
def update_user_profile_endpoint(
|
|
284
|
+
request: UpdateUserProfileRequest,
|
|
285
|
+
org_id: str = Depends(default_get_org_id),
|
|
286
|
+
) -> UpdateUserProfileResponse:
|
|
287
|
+
"""Apply a partial update to an existing user profile.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
request (UpdateUserProfileRequest): The update request
|
|
291
|
+
org_id (str): Organization ID
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
UpdateUserProfileResponse: Response containing success status and message
|
|
295
|
+
"""
|
|
296
|
+
return publisher_api.update_user_profile(org_id=org_id, request=request)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
@router.post(
|
|
300
|
+
"/api/rerun_profile_generation",
|
|
301
|
+
response_model=RerunProfileGenerationResponse,
|
|
302
|
+
response_model_exclude_none=True,
|
|
303
|
+
)
|
|
304
|
+
@limiter.limit("5/minute") # Strict limit for expensive operations
|
|
305
|
+
def rerun_profile_generation_endpoint(
|
|
306
|
+
request: Request,
|
|
307
|
+
payload: RerunProfileGenerationRequest,
|
|
308
|
+
background_tasks: BackgroundTasks,
|
|
309
|
+
org_id: str = Depends(default_get_org_id),
|
|
310
|
+
) -> RerunProfileGenerationResponse:
|
|
311
|
+
"""Rerun profile generation for a user with filtered interactions.
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
request (Request): The HTTP request object (for rate limiting)
|
|
315
|
+
payload (RerunProfileGenerationRequest): Request containing user_id, time filters, and source
|
|
316
|
+
background_tasks (BackgroundTasks): Background task runner
|
|
317
|
+
org_id (str): Organization ID
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
RerunProfileGenerationResponse: Response containing success status and profiles generated count
|
|
321
|
+
"""
|
|
322
|
+
# Create Reflexio instance
|
|
323
|
+
reflexio = reflexio_cache.get_reflexio(org_id=org_id)
|
|
324
|
+
|
|
325
|
+
# Run the long-running task in the background to avoid proxy timeout
|
|
326
|
+
# Client polls get_operation_status for progress
|
|
327
|
+
background_tasks.add_task(reflexio.rerun_profile_generation, payload)
|
|
328
|
+
|
|
329
|
+
return RerunProfileGenerationResponse(
|
|
330
|
+
success=True, msg="Profile generation started"
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
@router.post(
|
|
335
|
+
"/api/manual_profile_generation",
|
|
336
|
+
response_model=ManualProfileGenerationResponse,
|
|
337
|
+
response_model_exclude_none=True,
|
|
338
|
+
)
|
|
339
|
+
@limiter.limit("5/minute") # Strict limit for expensive operations
|
|
340
|
+
def manual_profile_generation_endpoint(
|
|
341
|
+
request: Request,
|
|
342
|
+
payload: ManualProfileGenerationRequest,
|
|
343
|
+
org_id: str = Depends(default_get_org_id),
|
|
344
|
+
) -> ManualProfileGenerationResponse:
|
|
345
|
+
"""Manually trigger profile generation with window-sized interactions and CURRENT output.
|
|
346
|
+
|
|
347
|
+
Runs with auto_run=False, which bypasses the regular stride/should_run
|
|
348
|
+
gates. Only profile extraction is triggered. Each extractor uses its own
|
|
349
|
+
window_size_override when present, falling back to the global window_size.
|
|
350
|
+
Output is CURRENT profiles only.
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
request (Request): The HTTP request object (for rate limiting)
|
|
354
|
+
payload (ManualProfileGenerationRequest): Request containing user_id, source, and extractor_names
|
|
355
|
+
org_id (str): Organization ID
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
ManualProfileGenerationResponse: Response containing success status and profiles generated count
|
|
359
|
+
"""
|
|
360
|
+
# Create Reflexio instance
|
|
361
|
+
reflexio = reflexio_cache.get_reflexio(org_id=org_id)
|
|
362
|
+
|
|
363
|
+
# Call manual_profile_generation
|
|
364
|
+
return reflexio.manual_profile_generation(payload)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
@router.post(
|
|
368
|
+
"/api/upgrade_all_profiles",
|
|
369
|
+
response_model=UpgradeProfilesResponse,
|
|
370
|
+
response_model_exclude_none=True,
|
|
371
|
+
)
|
|
372
|
+
def upgrade_all_profiles_endpoint(
|
|
373
|
+
request: UpgradeProfilesRequest,
|
|
374
|
+
org_id: str = Depends(default_get_org_id),
|
|
375
|
+
) -> UpgradeProfilesResponse:
|
|
376
|
+
"""Upgrade all profiles by deleting old ARCHIVED, archiving CURRENT, and promoting PENDING.
|
|
377
|
+
|
|
378
|
+
This operation performs three atomic steps:
|
|
379
|
+
1. Delete all ARCHIVED profiles (old archived profiles from previous upgrades)
|
|
380
|
+
2. Archive all CURRENT profiles → ARCHIVED (save current state for potential rollback)
|
|
381
|
+
3. Promote all PENDING profiles → CURRENT (activate new profiles)
|
|
382
|
+
|
|
383
|
+
Args:
|
|
384
|
+
request (UpgradeProfilesRequest): The upgrade request with only_affected_users parameter
|
|
385
|
+
org_id (str): Organization ID
|
|
386
|
+
|
|
387
|
+
Returns:
|
|
388
|
+
UpgradeProfilesResponse: Response containing success status and counts
|
|
389
|
+
"""
|
|
390
|
+
# Create Reflexio instance
|
|
391
|
+
reflexio = reflexio_cache.get_reflexio(org_id=org_id)
|
|
392
|
+
|
|
393
|
+
# Call upgrade_all_profiles with request
|
|
394
|
+
return reflexio.upgrade_all_profiles(request=request)
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
@router.post(
|
|
398
|
+
"/api/downgrade_all_profiles",
|
|
399
|
+
response_model=DowngradeProfilesResponse,
|
|
400
|
+
response_model_exclude_none=True,
|
|
401
|
+
)
|
|
402
|
+
def downgrade_all_profiles_endpoint(
|
|
403
|
+
request: DowngradeProfilesRequest,
|
|
404
|
+
org_id: str = Depends(default_get_org_id),
|
|
405
|
+
) -> DowngradeProfilesResponse:
|
|
406
|
+
"""Downgrade all profiles by demoting CURRENT to PENDING and restoring ARCHIVED.
|
|
407
|
+
|
|
408
|
+
This operation performs two atomic steps:
|
|
409
|
+
1. Demote all CURRENT profiles → PENDING
|
|
410
|
+
2. Restore all ARCHIVED profiles → CURRENT
|
|
411
|
+
|
|
412
|
+
Args:
|
|
413
|
+
request (DowngradeProfilesRequest): The downgrade request with only_affected_users parameter
|
|
414
|
+
org_id (str): Organization ID
|
|
415
|
+
|
|
416
|
+
Returns:
|
|
417
|
+
DowngradeProfilesResponse: Response containing success status and counts
|
|
418
|
+
"""
|
|
419
|
+
# Create Reflexio instance
|
|
420
|
+
reflexio = reflexio_cache.get_reflexio(org_id=org_id)
|
|
421
|
+
|
|
422
|
+
# Call downgrade_all_profiles with request
|
|
423
|
+
return reflexio.downgrade_all_profiles(request=request)
|