topos-node 0.1.0__py3-none-any.whl
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.
- shared/__init__.py +59 -0
- shared/filtering.py +640 -0
- shared/schema_registry.py +229 -0
- topos/__init__.py +5 -0
- topos/__version__.py +6 -0
- topos/analytics/__init__.py +15 -0
- topos/analytics/duckdb_adapter.py +48 -0
- topos/analytics/messenger_communities.py +349 -0
- topos/analytics/messenger_graph.py +522 -0
- topos/analytics/messenger_labels.py +321 -0
- topos/analytics/profiles.py +22 -0
- topos/analytics/query_engine.py +64 -0
- topos/analytics/raw_queries.py +174 -0
- topos/api/__init__.py +1 -0
- topos/api/analytics.py +52 -0
- topos/api/app_registry.py +31 -0
- topos/api/backup.py +15 -0
- topos/api/compute_remote.py +175 -0
- topos/api/data_commit.py +158 -0
- topos/api/data_explorer_table_prefs.py +81 -0
- topos/api/db.py +10 -0
- topos/api/device.py +25 -0
- topos/api/enrichment.py +959 -0
- topos/api/filter_lab.py +195 -0
- topos/api/health.py +61 -0
- topos/api/ingestion_api.py +37 -0
- topos/api/ingestion_compat.py +21 -0
- topos/api/ingestion_sources.py +600 -0
- topos/api/llm.py +76 -0
- topos/api/local_mcp.py +46 -0
- topos/api/messenger_analytics.py +385 -0
- topos/api/query_api.py +13 -0
- topos/api/sanitization_ollama_config.py +64 -0
- topos/api/source_install.py +324 -0
- topos/api/sources.py +13 -0
- topos/api/sync.py +10 -0
- topos/api/ui_config.py +83 -0
- topos/api/uma_data.py +311 -0
- topos/api/usage.py +49 -0
- topos/api/user_identity.py +46 -0
- topos/app.py +239 -0
- topos/auth.py +17 -0
- topos/canonicalization/__init__.py +1 -0
- topos/canonicalization/mappers/__init__.py +22 -0
- topos/canonicalization/mappers/base.py +26 -0
- topos/canonicalization/mappers/chatgpt_mapper.py +40 -0
- topos/canonicalization/mappers/grok_mapper.py +17 -0
- topos/canonicalization/mappers/messenger_mapper.py +58 -0
- topos/canonicalization/models.py +31 -0
- topos/canonicalization/resolver.py +23 -0
- topos/cli/__init__.py +1 -0
- topos/cli/__main__.py +6 -0
- topos/cli/commands.py +132 -0
- topos/config/__init__.py +1 -0
- topos/config/sanitization_ollama.py +189 -0
- topos/config/settings.py +310 -0
- topos/contacts/__init__.py +5 -0
- topos/contacts/identity.py +24 -0
- topos/control_plane_client.py +300 -0
- topos/core/__init__.py +1 -0
- topos/core/api_models.py +128 -0
- topos/core/connection_resilience.py +99 -0
- topos/core/device_helpers.py +8 -0
- topos/core/errors.py +13 -0
- topos/core/events.py +12 -0
- topos/core/handlers.py +5625 -0
- topos/core/logging.py +175 -0
- topos/core/metrics.py +21 -0
- topos/core/startup_banner.py +62 -0
- topos/core/state.py +682 -0
- topos/core/table_layers.py +45 -0
- topos/core/types.py +13 -0
- topos/data_explorer_table_prefs.py +150 -0
- topos/engine/__init__.py +29 -0
- topos/engine/backends/__init__.py +50 -0
- topos/engine/backends/base.py +21 -0
- topos/engine/backends/huggingface.py +151 -0
- topos/engine/backends/ollama.py +181 -0
- topos/engine/backends/stub.py +22 -0
- topos/engine/engine.py +165 -0
- topos/engine/intake.py +32 -0
- topos/engine/queue_manager.py +112 -0
- topos/engine/registration.py +126 -0
- topos/engine/result_formatter.py +38 -0
- topos/engine/router.py +19 -0
- topos/engine/scoped_token.py +82 -0
- topos/engine/tasks.py +154 -0
- topos/engine/transport.py +44 -0
- topos/engine/usage_guard.py +100 -0
- topos/engine/usage_observation.py +129 -0
- topos/engine/validator.py +23 -0
- topos/enrichment/__init__.py +1 -0
- topos/enrichment/derived_tables.py +214 -0
- topos/enrichment/jobs/__init__.py +30 -0
- topos/enrichment/jobs/base.py +54 -0
- topos/enrichment/jobs/canonical/__init__.py +1 -0
- topos/enrichment/jobs/canonical/embeddings_job.py +27 -0
- topos/enrichment/jobs/canonical/emo_27_job.py +97 -0
- topos/enrichment/jobs/canonical/entities_job.py +27 -0
- topos/enrichment/jobs/canonical/sentiment_job.py +27 -0
- topos/enrichment/jobs/canonical/topics_job.py +27 -0
- topos/enrichment/jobs/raw/__init__.py +1 -0
- topos/enrichment/jobs/raw/attachments_job.py +12 -0
- topos/enrichment/jobs/raw/language_job.py +12 -0
- topos/enrichment/jobs/raw/time_normalization_job.py +12 -0
- topos/enrichment/jobs/raw/tool_calls_job.py +12 -0
- topos/enrichment/models/__init__.py +1 -0
- topos/enrichment/models/manager.py +8 -0
- topos/enrichment/models/registry.py +71 -0
- topos/enrichment/models/versioning.py +8 -0
- topos/enrichment/orchestrator.py +177 -0
- topos/enrichment/processor.py +17 -0
- topos/enrichment/progress_bar.py +122 -0
- topos/enrichment/website_classifier.py +31 -0
- topos/filter_lab/__init__.py +1 -0
- topos/filter_lab/bundles.py +300 -0
- topos/filter_lab/schema.py +86 -0
- topos/filter_lab/service.py +167 -0
- topos/filter_lab/store.py +374 -0
- topos/filter_lab/worker.py +250 -0
- topos/hosted_pool_lease.py +153 -0
- topos/ingestion/__init__.py +1 -0
- topos/ingestion/checkpoints/__init__.py +6 -0
- topos/ingestion/checkpoints/checkpoint_store.py +24 -0
- topos/ingestion/checkpoints/sqlite_checkpoint_store.py +82 -0
- topos/ingestion/ingest_helpers.py +504 -0
- topos/ingestion/jobs.py +91 -0
- topos/ingestion/local_sync.py +823 -0
- topos/ingestion/log_preview.py +21 -0
- topos/ingestion/manager.py +1100 -0
- topos/ingestion/parser.py +174 -0
- topos/ingestion/parsers/__init__.py +32 -0
- topos/ingestion/parsers/base.py +24 -0
- topos/ingestion/parsers/browser_parser.py +171 -0
- topos/ingestion/parsers/calendar_parser.py +21 -0
- topos/ingestion/parsers/chatgpt_conversation_flattener.py +266 -0
- topos/ingestion/parsers/chatgpt_parser.py +67 -0
- topos/ingestion/parsers/grok_parser.py +21 -0
- topos/ingestion/parsers/messenger_parser.py +97 -0
- topos/ingestion/progress.py +54 -0
- topos/ingestion/sources/__init__.py +20 -0
- topos/ingestion/sources/base.py +39 -0
- topos/ingestion/sources/calendar.py +29 -0
- topos/ingestion/sources/chatgpt.py +29 -0
- topos/ingestion/sources/contact_importers.py +274 -0
- topos/ingestion/sources/grok.py +29 -0
- topos/ingestion/sources/imessage_reader.py +479 -0
- topos/ingestion/sources/signal_export_parser.py +132 -0
- topos/ingestion/sources/signal_reader.py +491 -0
- topos/ingestion/state_machine.py +70 -0
- topos/ingestion/triggers/__init__.py +1 -0
- topos/ingestion/triggers/file_trigger.py +36 -0
- topos/ingestion/triggers/sqlite_trigger.py +18 -0
- topos/ingestion/validation/__init__.py +1 -0
- topos/ingestion/validation/base.py +27 -0
- topos/ingestion/validation/schema_registry.py +111 -0
- topos/ingestion/validation/schema_validator.py +13 -0
- topos/lineage/__init__.py +1 -0
- topos/lineage/provenance.py +9 -0
- topos/lineage/tracker.py +9 -0
- topos/mcp_stdio_proxy.py +83 -0
- topos/observability/__init__.py +1 -0
- topos/observability/alerts.py +7 -0
- topos/observability/metrics.py +25 -0
- topos/observability/tracing.py +18 -0
- topos/openai_client.py +69 -0
- topos/projections/__init__.py +1 -0
- topos/projections/vector_index/__init__.py +1 -0
- topos/projections/vector_index/base.py +21 -0
- topos/projections/vector_index/builders.py +11 -0
- topos/projections/vector_index/health_checks.py +5 -0
- topos/rate_limit.py +43 -0
- topos/sanitization/__init__.py +16 -0
- topos/sanitization/ollama_transforms.py +276 -0
- topos/scope_resolution.py +89 -0
- topos/services/__init__.py +1 -0
- topos/services/container.py +46 -0
- topos/services/embeddings/__init__.py +1 -0
- topos/services/embeddings/base.py +7 -0
- topos/services/embeddings/local.py +9 -0
- topos/services/embeddings/remote.py +9 -0
- topos/services/interfaces.py +40 -0
- topos/services/llm/__init__.py +1 -0
- topos/services/llm/base.py +7 -0
- topos/services/llm/openai.py +126 -0
- topos/services/local.py +123 -0
- topos/services/postgres.py +385 -0
- topos/sources/__init__.py +6 -0
- topos/sources/definitions.py +114 -0
- topos/sources/install_service.py +836 -0
- topos/sources/registry.py +263 -0
- topos/sources/runtime_install.py +427 -0
- topos/storage/__init__.py +1 -0
- topos/storage/canonical/__init__.py +18 -0
- topos/storage/canonical/ai_chat/__init__.py +22 -0
- topos/storage/canonical/ai_chat/canonicalizer.py +147 -0
- topos/storage/canonical/ai_chat/mapper.py +168 -0
- topos/storage/canonical/ai_chat/model.py +87 -0
- topos/storage/canonical/ai_chat/tables.py +179 -0
- topos/storage/canonical/canonical_store.py +24 -0
- topos/storage/canonical/conversations_tables.py +1020 -0
- topos/storage/canonical/mapping_store.py +30 -0
- topos/storage/canonical/postgres.py +10 -0
- topos/storage/db/__init__.py +1 -0
- topos/storage/db/client.py +8 -0
- topos/storage/db/migrations/__init__.py +1 -0
- topos/storage/db/migrations/stage9_column_renames.py +78 -0
- topos/storage/db/paths.py +122 -0
- topos/storage/db/postgres.py +240 -0
- topos/storage/db/schema.py +6 -0
- topos/storage/enrichment/__init__.py +1 -0
- topos/storage/enrichment/canonical_enrichment_store.py +7 -0
- topos/storage/enrichment/raw_enrichment_store.py +18 -0
- topos/storage/normalized/__init__.py +1 -0
- topos/storage/normalized/normalized_store.py +24 -0
- topos/storage/oplog/__init__.py +1 -0
- topos/storage/oplog/decision.py +6 -0
- topos/storage/oplog/oplog_store.py +17 -0
- topos/storage/oplog/postgres.py +10 -0
- topos/storage/projections/__init__.py +1 -0
- topos/storage/projections/index_ops_store.py +6 -0
- topos/storage/projections/vector_index_store.py +6 -0
- topos/storage/raw/__init__.py +1 -0
- topos/storage/raw/browser_flat_tables.py +303 -0
- topos/storage/raw/file_store.py +100 -0
- topos/storage/raw/raw_store.py +29 -0
- topos/storage/raw/raw_tables_manager.py +295 -0
- topos/storage/raw/sqlite_raw_store.py +17 -0
- topos/storage/security/encryption.py +21 -0
- topos/storage/signal_identity.py +71 -0
- topos/storage/source_settings.py +116 -0
- topos/storage/user_identity.py +69 -0
- topos/sync/__init__.py +5 -0
- topos/sync/client.py +272 -0
- topos/sync_handlers.py +70 -0
- topos/testing/__init__.py +1 -0
- topos/testing/lifespan.py +7 -0
- topos/uma_contact_enrichment.py +1032 -0
- topos/uma_filters.py +669 -0
- topos/uma_resource_id.py +24 -0
- topos/uma_rpt.py +69 -0
- topos/utils/base_object.py +61 -0
- topos/websocket_client.py +21 -0
- topos_node-0.1.0.dist-info/METADATA +199 -0
- topos_node-0.1.0.dist-info/RECORD +249 -0
- topos_node-0.1.0.dist-info/WHEEL +5 -0
- topos_node-0.1.0.dist-info/entry_points.txt +2 -0
- topos_node-0.1.0.dist-info/licenses/LICENSE +201 -0
- topos_node-0.1.0.dist-info/top_level.txt +2 -0
topos/api/filter_lab.py
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""Filter Lab REST API (engine HTTP surface)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
from typing import Any, Dict, List, Optional
|
|
8
|
+
|
|
9
|
+
from fastapi import APIRouter, Body, Depends, HTTPException, Query
|
|
10
|
+
|
|
11
|
+
from ..auth import require_api_key
|
|
12
|
+
from ..filter_lab import bundles as bundles_mod
|
|
13
|
+
from ..filter_lab import store
|
|
14
|
+
from ..filter_lab import service as lab_service
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger("topos.api.filter_lab")
|
|
17
|
+
|
|
18
|
+
router = APIRouter(tags=["filter-lab"])
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@router.get("/v1/filter-lab/bundles", dependencies=[Depends(require_api_key)])
|
|
22
|
+
async def filter_lab_list_bundles() -> List[Dict[str, Any]]:
|
|
23
|
+
return bundles_mod.list_bundle_metadata()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@router.get("/v1/filter-lab/bundles/{bundle_id}", dependencies=[Depends(require_api_key)])
|
|
27
|
+
async def filter_lab_get_bundle(bundle_id: str) -> Dict[str, Any]:
|
|
28
|
+
data = bundles_mod.get_bundle_preview(bundle_id)
|
|
29
|
+
if not data:
|
|
30
|
+
raise HTTPException(status_code=404, detail="Bundle not found")
|
|
31
|
+
return data
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@router.post("/v1/filter-lab/job-groups", dependencies=[Depends(require_api_key)])
|
|
35
|
+
async def filter_lab_create_job_group(body: Dict[str, Any] = Body(default_factory=dict)) -> Dict[str, Any]:
|
|
36
|
+
from topos.core.state import get_db_connection
|
|
37
|
+
|
|
38
|
+
conn = get_db_connection()
|
|
39
|
+
if not conn:
|
|
40
|
+
raise HTTPException(status_code=503, detail="Database not available")
|
|
41
|
+
try:
|
|
42
|
+
gid = lab_service.create_job_group(
|
|
43
|
+
filter_id=str(body.get("filter_id") or "").strip(),
|
|
44
|
+
bundle_id=str(body.get("bundle_id") or "").strip(),
|
|
45
|
+
models=list(body.get("models") or []),
|
|
46
|
+
options=body.get("options") if isinstance(body.get("options"), dict) else None,
|
|
47
|
+
)
|
|
48
|
+
return lab_service.serialize_job_group(conn, gid)
|
|
49
|
+
except ValueError as exc:
|
|
50
|
+
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
51
|
+
except RuntimeError as exc:
|
|
52
|
+
raise HTTPException(status_code=503, detail=str(exc)) from exc
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@router.get("/v1/filter-lab/job-groups", dependencies=[Depends(require_api_key)])
|
|
56
|
+
async def filter_lab_list_job_groups(
|
|
57
|
+
filter_id: Optional[str] = Query(
|
|
58
|
+
None,
|
|
59
|
+
description="Omit to list all transforms; set to restrict history to one filter id.",
|
|
60
|
+
),
|
|
61
|
+
limit: int = Query(20, ge=1, le=100),
|
|
62
|
+
offset: int = Query(0, ge=0),
|
|
63
|
+
) -> Dict[str, Any]:
|
|
64
|
+
from topos.core.state import get_db_connection
|
|
65
|
+
|
|
66
|
+
conn = get_db_connection()
|
|
67
|
+
if not conn:
|
|
68
|
+
raise HTTPException(status_code=503, detail="Database not available")
|
|
69
|
+
store.prune_old_groups(conn, max_age_days=30)
|
|
70
|
+
fid = (filter_id or "").strip()
|
|
71
|
+
if fid:
|
|
72
|
+
rows = store.list_groups_for_filter(conn, fid, limit=limit, offset=offset)
|
|
73
|
+
else:
|
|
74
|
+
rows = store.list_all_job_groups(conn, limit=limit, offset=offset)
|
|
75
|
+
groups = []
|
|
76
|
+
for row in rows:
|
|
77
|
+
g = dict(row)
|
|
78
|
+
g["baseline_models"] = json.loads(g.pop("baseline_models_json") or "[]")
|
|
79
|
+
g["pulled_models"] = json.loads(g.pop("pulled_models_json") or "[]")
|
|
80
|
+
opt_raw = g.pop("options_json", "{}")
|
|
81
|
+
try:
|
|
82
|
+
g["options"] = json.loads(opt_raw) if isinstance(opt_raw, str) else {}
|
|
83
|
+
except json.JSONDecodeError:
|
|
84
|
+
g["options"] = {}
|
|
85
|
+
groups.append(g)
|
|
86
|
+
lab_service.enrich_job_groups_list_with_run_summaries(conn, groups)
|
|
87
|
+
return {"groups": groups, "limit": limit, "offset": offset}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@router.get("/v1/filter-lab/job-groups/{group_id}", dependencies=[Depends(require_api_key)])
|
|
91
|
+
async def filter_lab_get_job_group(group_id: str) -> Dict[str, Any]:
|
|
92
|
+
from topos.core.state import get_db_connection
|
|
93
|
+
|
|
94
|
+
conn = get_db_connection()
|
|
95
|
+
if not conn:
|
|
96
|
+
raise HTTPException(status_code=503, detail="Database not available")
|
|
97
|
+
try:
|
|
98
|
+
return lab_service.serialize_job_group(conn, group_id)
|
|
99
|
+
except KeyError:
|
|
100
|
+
raise HTTPException(status_code=404, detail="Job group not found") from None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@router.patch("/v1/filter-lab/job-groups/{group_id}", dependencies=[Depends(require_api_key)])
|
|
104
|
+
async def filter_lab_patch_job_group(group_id: str, body: Dict[str, Any] = Body(default_factory=dict)) -> Dict[str, Any]:
|
|
105
|
+
from topos.core.state import get_db_connection
|
|
106
|
+
|
|
107
|
+
conn = get_db_connection()
|
|
108
|
+
if not conn:
|
|
109
|
+
raise HTTPException(status_code=503, detail="Database not available")
|
|
110
|
+
if not store.get_group(conn, group_id):
|
|
111
|
+
raise HTTPException(status_code=404, detail="Job group not found")
|
|
112
|
+
if "preferred_model_tag" in body:
|
|
113
|
+
p = body.get("preferred_model_tag")
|
|
114
|
+
store.patch_group(conn, group_id, preferred_model_tag=p if p is None else str(p).strip() or None)
|
|
115
|
+
if "group_notes" in body:
|
|
116
|
+
store.patch_group(conn, group_id, group_notes=body.get("group_notes"))
|
|
117
|
+
if "notes" in body:
|
|
118
|
+
store.patch_group(conn, group_id, notes=body.get("notes"))
|
|
119
|
+
return lab_service.serialize_job_group(conn, group_id)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@router.patch("/v1/filter-lab/job-groups/{group_id}/runs/{run_id}", dependencies=[Depends(require_api_key)])
|
|
123
|
+
async def filter_lab_patch_run(group_id: str, run_id: str, body: Dict[str, Any] = Body(default_factory=dict)) -> Dict[str, Any]:
|
|
124
|
+
from topos.core.state import get_db_connection
|
|
125
|
+
|
|
126
|
+
conn = get_db_connection()
|
|
127
|
+
if not conn:
|
|
128
|
+
raise HTTPException(status_code=503, detail="Database not available")
|
|
129
|
+
runs = store.list_runs(conn, group_id)
|
|
130
|
+
if not any(dict(r)["id"] == run_id for r in runs):
|
|
131
|
+
raise HTTPException(status_code=404, detail="Run not found")
|
|
132
|
+
rated = False
|
|
133
|
+
if "user_quality_score_0_10" in body:
|
|
134
|
+
v = body.get("user_quality_score_0_10")
|
|
135
|
+
if v is not None and (not isinstance(v, int) or v < 0 or v > 10):
|
|
136
|
+
raise HTTPException(status_code=400, detail="user_quality_score_0_10 must be 0–10 or null")
|
|
137
|
+
store.patch_run(conn, run_id, user_quality_score_0_10=v)
|
|
138
|
+
rated = True
|
|
139
|
+
if "user_liked" in body:
|
|
140
|
+
v = body.get("user_liked")
|
|
141
|
+
if v is None:
|
|
142
|
+
conn.execute("UPDATE filter_lab_run SET user_liked = NULL WHERE id = ?", (run_id,))
|
|
143
|
+
conn.commit()
|
|
144
|
+
else:
|
|
145
|
+
store.patch_run(conn, run_id, user_liked=bool(v))
|
|
146
|
+
rated = True
|
|
147
|
+
if "user_note" in body:
|
|
148
|
+
note = body.get("user_note")
|
|
149
|
+
store.patch_run(conn, run_id, user_note=None if note is None else str(note)[:2000])
|
|
150
|
+
rated = True
|
|
151
|
+
if rated:
|
|
152
|
+
conn.execute(
|
|
153
|
+
"UPDATE filter_lab_run SET rated_at = ? WHERE id = ?",
|
|
154
|
+
(store.utc_now_iso(), run_id),
|
|
155
|
+
)
|
|
156
|
+
conn.commit()
|
|
157
|
+
return lab_service.serialize_job_group(conn, group_id)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@router.post("/v1/filter-lab/job-groups/{group_id}/apply-preferred", dependencies=[Depends(require_api_key)])
|
|
161
|
+
async def filter_lab_apply_preferred(group_id: str) -> Dict[str, Any]:
|
|
162
|
+
try:
|
|
163
|
+
return lab_service.apply_preferred_model(group_id)
|
|
164
|
+
except ValueError as exc:
|
|
165
|
+
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
166
|
+
except RuntimeError as exc:
|
|
167
|
+
raise HTTPException(status_code=503, detail=str(exc)) from exc
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@router.delete("/v1/filter-lab/job-groups/{group_id}", dependencies=[Depends(require_api_key)])
|
|
171
|
+
async def filter_lab_delete_job_group(group_id: str) -> Dict[str, Any]:
|
|
172
|
+
from topos.core.state import get_db_connection
|
|
173
|
+
|
|
174
|
+
conn = get_db_connection()
|
|
175
|
+
if not conn:
|
|
176
|
+
raise HTTPException(status_code=503, detail="Database not available")
|
|
177
|
+
if not store.get_group(conn, group_id):
|
|
178
|
+
raise HTTPException(status_code=404, detail="Job group not found")
|
|
179
|
+
store.delete_group(conn, group_id)
|
|
180
|
+
return {"status": "ok", "group_id": group_id, "deleted": True}
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@router.delete("/v1/filter-lab/data", dependencies=[Depends(require_api_key)])
|
|
184
|
+
async def filter_lab_clear_all() -> Dict[str, Any]:
|
|
185
|
+
from topos.core.state import get_db_connection
|
|
186
|
+
|
|
187
|
+
conn = get_db_connection()
|
|
188
|
+
if not conn:
|
|
189
|
+
raise HTTPException(status_code=503, detail="Database not available")
|
|
190
|
+
store.ensure_schema(conn)
|
|
191
|
+
conn.execute("DELETE FROM filter_lab_model_event")
|
|
192
|
+
conn.execute("DELETE FROM filter_lab_run")
|
|
193
|
+
conn.execute("DELETE FROM filter_lab_job_group")
|
|
194
|
+
conn.commit()
|
|
195
|
+
return {"status": "ok", "cleared": True}
|
topos/api/health.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from time import time
|
|
4
|
+
|
|
5
|
+
from fastapi import Depends, APIRouter
|
|
6
|
+
from fastapi.security import HTTPAuthorizationCredentials
|
|
7
|
+
|
|
8
|
+
from ..__version__ import __version__
|
|
9
|
+
from ..auth import bearer_scheme, require_api_key
|
|
10
|
+
from ..core.api_models import HealthResponse
|
|
11
|
+
from ..core import state
|
|
12
|
+
|
|
13
|
+
router = APIRouter()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@router.get("/healthcheck", response_model=HealthResponse)
|
|
17
|
+
async def healthcheck(
|
|
18
|
+
credentials: HTTPAuthorizationCredentials | None = Depends(bearer_scheme),
|
|
19
|
+
) -> HealthResponse:
|
|
20
|
+
# Evaluate health auth dynamically instead of at import time so tests and
|
|
21
|
+
# runtime env changes do not get locked to stale settings.
|
|
22
|
+
from ..config.settings import settings as runtime_settings
|
|
23
|
+
|
|
24
|
+
if runtime_settings.enable_health_auth:
|
|
25
|
+
require_api_key(credentials)
|
|
26
|
+
cp_status = (
|
|
27
|
+
state.control_plane_client.get_connection_status()
|
|
28
|
+
if state.control_plane_client and hasattr(state.control_plane_client, "get_connection_status")
|
|
29
|
+
else None
|
|
30
|
+
)
|
|
31
|
+
sync_status = (
|
|
32
|
+
state.sync_client.get_connection_status()
|
|
33
|
+
if state.sync_client and hasattr(state.sync_client, "get_connection_status")
|
|
34
|
+
else None
|
|
35
|
+
)
|
|
36
|
+
cloud_connected = None
|
|
37
|
+
if cp_status is not None or sync_status is not None:
|
|
38
|
+
cloud_connected = bool(
|
|
39
|
+
(cp_status or {}).get("connected")
|
|
40
|
+
or (sync_status or {}).get("connected")
|
|
41
|
+
)
|
|
42
|
+
return HealthResponse(
|
|
43
|
+
status="ok",
|
|
44
|
+
time=time(),
|
|
45
|
+
cloud_connected=cloud_connected,
|
|
46
|
+
control_plane_connection=cp_status,
|
|
47
|
+
sync_connection=sync_status,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@router.get("/version")
|
|
52
|
+
async def get_version() -> dict:
|
|
53
|
+
return {
|
|
54
|
+
"version": __version__,
|
|
55
|
+
"name": "Topos Control Plane",
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@router.get("/")
|
|
60
|
+
async def root() -> dict:
|
|
61
|
+
return {"status": "ok"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, Body, Depends, Request
|
|
4
|
+
|
|
5
|
+
from ..auth import require_api_key
|
|
6
|
+
from .ingestion_sources import ingest_source
|
|
7
|
+
|
|
8
|
+
router = APIRouter()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@router.post("/ingestion/start")
|
|
12
|
+
async def start_ingestion() -> dict:
|
|
13
|
+
return {"status": "stub"}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@router.post("/ingestion/progress")
|
|
17
|
+
async def report_progress() -> dict:
|
|
18
|
+
return {"status": "stub"}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@router.post("/ingestion/upload", dependencies=[Depends(require_api_key)])
|
|
22
|
+
async def upload_ingestion_file(request: Request) -> dict:
|
|
23
|
+
return await ingest_source("chatgpt_file_ingestion", request)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@router.post("/ingestion/upload-local-path", dependencies=[Depends(require_api_key)])
|
|
27
|
+
async def upload_ingestion_local_path(
|
|
28
|
+
request: Request, payload: dict = Body(default_factory=dict)
|
|
29
|
+
) -> dict:
|
|
30
|
+
dataset_id = payload.get("dataset_id")
|
|
31
|
+
file_path = payload.get("file_path")
|
|
32
|
+
return await ingest_source(
|
|
33
|
+
"chatgpt_file_ingestion",
|
|
34
|
+
request,
|
|
35
|
+
dataset_id=dataset_id,
|
|
36
|
+
file_path=file_path,
|
|
37
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, Body, Depends, Request
|
|
4
|
+
|
|
5
|
+
from ..auth import require_api_key
|
|
6
|
+
from .ingestion_sources import ingest_source
|
|
7
|
+
|
|
8
|
+
router = APIRouter()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@router.post("/store_message", dependencies=[Depends(require_api_key)])
|
|
12
|
+
async def store_message(
|
|
13
|
+
request: Request, payload: dict = Body(default_factory=dict)
|
|
14
|
+
) -> dict:
|
|
15
|
+
dataset_id = payload.get("dataset_id")
|
|
16
|
+
return await ingest_source(
|
|
17
|
+
"chatgpt_ui_conversation",
|
|
18
|
+
request,
|
|
19
|
+
dataset_id=dataset_id,
|
|
20
|
+
payload=payload,
|
|
21
|
+
)
|