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.
Files changed (249) hide show
  1. shared/__init__.py +59 -0
  2. shared/filtering.py +640 -0
  3. shared/schema_registry.py +229 -0
  4. topos/__init__.py +5 -0
  5. topos/__version__.py +6 -0
  6. topos/analytics/__init__.py +15 -0
  7. topos/analytics/duckdb_adapter.py +48 -0
  8. topos/analytics/messenger_communities.py +349 -0
  9. topos/analytics/messenger_graph.py +522 -0
  10. topos/analytics/messenger_labels.py +321 -0
  11. topos/analytics/profiles.py +22 -0
  12. topos/analytics/query_engine.py +64 -0
  13. topos/analytics/raw_queries.py +174 -0
  14. topos/api/__init__.py +1 -0
  15. topos/api/analytics.py +52 -0
  16. topos/api/app_registry.py +31 -0
  17. topos/api/backup.py +15 -0
  18. topos/api/compute_remote.py +175 -0
  19. topos/api/data_commit.py +158 -0
  20. topos/api/data_explorer_table_prefs.py +81 -0
  21. topos/api/db.py +10 -0
  22. topos/api/device.py +25 -0
  23. topos/api/enrichment.py +959 -0
  24. topos/api/filter_lab.py +195 -0
  25. topos/api/health.py +61 -0
  26. topos/api/ingestion_api.py +37 -0
  27. topos/api/ingestion_compat.py +21 -0
  28. topos/api/ingestion_sources.py +600 -0
  29. topos/api/llm.py +76 -0
  30. topos/api/local_mcp.py +46 -0
  31. topos/api/messenger_analytics.py +385 -0
  32. topos/api/query_api.py +13 -0
  33. topos/api/sanitization_ollama_config.py +64 -0
  34. topos/api/source_install.py +324 -0
  35. topos/api/sources.py +13 -0
  36. topos/api/sync.py +10 -0
  37. topos/api/ui_config.py +83 -0
  38. topos/api/uma_data.py +311 -0
  39. topos/api/usage.py +49 -0
  40. topos/api/user_identity.py +46 -0
  41. topos/app.py +239 -0
  42. topos/auth.py +17 -0
  43. topos/canonicalization/__init__.py +1 -0
  44. topos/canonicalization/mappers/__init__.py +22 -0
  45. topos/canonicalization/mappers/base.py +26 -0
  46. topos/canonicalization/mappers/chatgpt_mapper.py +40 -0
  47. topos/canonicalization/mappers/grok_mapper.py +17 -0
  48. topos/canonicalization/mappers/messenger_mapper.py +58 -0
  49. topos/canonicalization/models.py +31 -0
  50. topos/canonicalization/resolver.py +23 -0
  51. topos/cli/__init__.py +1 -0
  52. topos/cli/__main__.py +6 -0
  53. topos/cli/commands.py +132 -0
  54. topos/config/__init__.py +1 -0
  55. topos/config/sanitization_ollama.py +189 -0
  56. topos/config/settings.py +310 -0
  57. topos/contacts/__init__.py +5 -0
  58. topos/contacts/identity.py +24 -0
  59. topos/control_plane_client.py +300 -0
  60. topos/core/__init__.py +1 -0
  61. topos/core/api_models.py +128 -0
  62. topos/core/connection_resilience.py +99 -0
  63. topos/core/device_helpers.py +8 -0
  64. topos/core/errors.py +13 -0
  65. topos/core/events.py +12 -0
  66. topos/core/handlers.py +5625 -0
  67. topos/core/logging.py +175 -0
  68. topos/core/metrics.py +21 -0
  69. topos/core/startup_banner.py +62 -0
  70. topos/core/state.py +682 -0
  71. topos/core/table_layers.py +45 -0
  72. topos/core/types.py +13 -0
  73. topos/data_explorer_table_prefs.py +150 -0
  74. topos/engine/__init__.py +29 -0
  75. topos/engine/backends/__init__.py +50 -0
  76. topos/engine/backends/base.py +21 -0
  77. topos/engine/backends/huggingface.py +151 -0
  78. topos/engine/backends/ollama.py +181 -0
  79. topos/engine/backends/stub.py +22 -0
  80. topos/engine/engine.py +165 -0
  81. topos/engine/intake.py +32 -0
  82. topos/engine/queue_manager.py +112 -0
  83. topos/engine/registration.py +126 -0
  84. topos/engine/result_formatter.py +38 -0
  85. topos/engine/router.py +19 -0
  86. topos/engine/scoped_token.py +82 -0
  87. topos/engine/tasks.py +154 -0
  88. topos/engine/transport.py +44 -0
  89. topos/engine/usage_guard.py +100 -0
  90. topos/engine/usage_observation.py +129 -0
  91. topos/engine/validator.py +23 -0
  92. topos/enrichment/__init__.py +1 -0
  93. topos/enrichment/derived_tables.py +214 -0
  94. topos/enrichment/jobs/__init__.py +30 -0
  95. topos/enrichment/jobs/base.py +54 -0
  96. topos/enrichment/jobs/canonical/__init__.py +1 -0
  97. topos/enrichment/jobs/canonical/embeddings_job.py +27 -0
  98. topos/enrichment/jobs/canonical/emo_27_job.py +97 -0
  99. topos/enrichment/jobs/canonical/entities_job.py +27 -0
  100. topos/enrichment/jobs/canonical/sentiment_job.py +27 -0
  101. topos/enrichment/jobs/canonical/topics_job.py +27 -0
  102. topos/enrichment/jobs/raw/__init__.py +1 -0
  103. topos/enrichment/jobs/raw/attachments_job.py +12 -0
  104. topos/enrichment/jobs/raw/language_job.py +12 -0
  105. topos/enrichment/jobs/raw/time_normalization_job.py +12 -0
  106. topos/enrichment/jobs/raw/tool_calls_job.py +12 -0
  107. topos/enrichment/models/__init__.py +1 -0
  108. topos/enrichment/models/manager.py +8 -0
  109. topos/enrichment/models/registry.py +71 -0
  110. topos/enrichment/models/versioning.py +8 -0
  111. topos/enrichment/orchestrator.py +177 -0
  112. topos/enrichment/processor.py +17 -0
  113. topos/enrichment/progress_bar.py +122 -0
  114. topos/enrichment/website_classifier.py +31 -0
  115. topos/filter_lab/__init__.py +1 -0
  116. topos/filter_lab/bundles.py +300 -0
  117. topos/filter_lab/schema.py +86 -0
  118. topos/filter_lab/service.py +167 -0
  119. topos/filter_lab/store.py +374 -0
  120. topos/filter_lab/worker.py +250 -0
  121. topos/hosted_pool_lease.py +153 -0
  122. topos/ingestion/__init__.py +1 -0
  123. topos/ingestion/checkpoints/__init__.py +6 -0
  124. topos/ingestion/checkpoints/checkpoint_store.py +24 -0
  125. topos/ingestion/checkpoints/sqlite_checkpoint_store.py +82 -0
  126. topos/ingestion/ingest_helpers.py +504 -0
  127. topos/ingestion/jobs.py +91 -0
  128. topos/ingestion/local_sync.py +823 -0
  129. topos/ingestion/log_preview.py +21 -0
  130. topos/ingestion/manager.py +1100 -0
  131. topos/ingestion/parser.py +174 -0
  132. topos/ingestion/parsers/__init__.py +32 -0
  133. topos/ingestion/parsers/base.py +24 -0
  134. topos/ingestion/parsers/browser_parser.py +171 -0
  135. topos/ingestion/parsers/calendar_parser.py +21 -0
  136. topos/ingestion/parsers/chatgpt_conversation_flattener.py +266 -0
  137. topos/ingestion/parsers/chatgpt_parser.py +67 -0
  138. topos/ingestion/parsers/grok_parser.py +21 -0
  139. topos/ingestion/parsers/messenger_parser.py +97 -0
  140. topos/ingestion/progress.py +54 -0
  141. topos/ingestion/sources/__init__.py +20 -0
  142. topos/ingestion/sources/base.py +39 -0
  143. topos/ingestion/sources/calendar.py +29 -0
  144. topos/ingestion/sources/chatgpt.py +29 -0
  145. topos/ingestion/sources/contact_importers.py +274 -0
  146. topos/ingestion/sources/grok.py +29 -0
  147. topos/ingestion/sources/imessage_reader.py +479 -0
  148. topos/ingestion/sources/signal_export_parser.py +132 -0
  149. topos/ingestion/sources/signal_reader.py +491 -0
  150. topos/ingestion/state_machine.py +70 -0
  151. topos/ingestion/triggers/__init__.py +1 -0
  152. topos/ingestion/triggers/file_trigger.py +36 -0
  153. topos/ingestion/triggers/sqlite_trigger.py +18 -0
  154. topos/ingestion/validation/__init__.py +1 -0
  155. topos/ingestion/validation/base.py +27 -0
  156. topos/ingestion/validation/schema_registry.py +111 -0
  157. topos/ingestion/validation/schema_validator.py +13 -0
  158. topos/lineage/__init__.py +1 -0
  159. topos/lineage/provenance.py +9 -0
  160. topos/lineage/tracker.py +9 -0
  161. topos/mcp_stdio_proxy.py +83 -0
  162. topos/observability/__init__.py +1 -0
  163. topos/observability/alerts.py +7 -0
  164. topos/observability/metrics.py +25 -0
  165. topos/observability/tracing.py +18 -0
  166. topos/openai_client.py +69 -0
  167. topos/projections/__init__.py +1 -0
  168. topos/projections/vector_index/__init__.py +1 -0
  169. topos/projections/vector_index/base.py +21 -0
  170. topos/projections/vector_index/builders.py +11 -0
  171. topos/projections/vector_index/health_checks.py +5 -0
  172. topos/rate_limit.py +43 -0
  173. topos/sanitization/__init__.py +16 -0
  174. topos/sanitization/ollama_transforms.py +276 -0
  175. topos/scope_resolution.py +89 -0
  176. topos/services/__init__.py +1 -0
  177. topos/services/container.py +46 -0
  178. topos/services/embeddings/__init__.py +1 -0
  179. topos/services/embeddings/base.py +7 -0
  180. topos/services/embeddings/local.py +9 -0
  181. topos/services/embeddings/remote.py +9 -0
  182. topos/services/interfaces.py +40 -0
  183. topos/services/llm/__init__.py +1 -0
  184. topos/services/llm/base.py +7 -0
  185. topos/services/llm/openai.py +126 -0
  186. topos/services/local.py +123 -0
  187. topos/services/postgres.py +385 -0
  188. topos/sources/__init__.py +6 -0
  189. topos/sources/definitions.py +114 -0
  190. topos/sources/install_service.py +836 -0
  191. topos/sources/registry.py +263 -0
  192. topos/sources/runtime_install.py +427 -0
  193. topos/storage/__init__.py +1 -0
  194. topos/storage/canonical/__init__.py +18 -0
  195. topos/storage/canonical/ai_chat/__init__.py +22 -0
  196. topos/storage/canonical/ai_chat/canonicalizer.py +147 -0
  197. topos/storage/canonical/ai_chat/mapper.py +168 -0
  198. topos/storage/canonical/ai_chat/model.py +87 -0
  199. topos/storage/canonical/ai_chat/tables.py +179 -0
  200. topos/storage/canonical/canonical_store.py +24 -0
  201. topos/storage/canonical/conversations_tables.py +1020 -0
  202. topos/storage/canonical/mapping_store.py +30 -0
  203. topos/storage/canonical/postgres.py +10 -0
  204. topos/storage/db/__init__.py +1 -0
  205. topos/storage/db/client.py +8 -0
  206. topos/storage/db/migrations/__init__.py +1 -0
  207. topos/storage/db/migrations/stage9_column_renames.py +78 -0
  208. topos/storage/db/paths.py +122 -0
  209. topos/storage/db/postgres.py +240 -0
  210. topos/storage/db/schema.py +6 -0
  211. topos/storage/enrichment/__init__.py +1 -0
  212. topos/storage/enrichment/canonical_enrichment_store.py +7 -0
  213. topos/storage/enrichment/raw_enrichment_store.py +18 -0
  214. topos/storage/normalized/__init__.py +1 -0
  215. topos/storage/normalized/normalized_store.py +24 -0
  216. topos/storage/oplog/__init__.py +1 -0
  217. topos/storage/oplog/decision.py +6 -0
  218. topos/storage/oplog/oplog_store.py +17 -0
  219. topos/storage/oplog/postgres.py +10 -0
  220. topos/storage/projections/__init__.py +1 -0
  221. topos/storage/projections/index_ops_store.py +6 -0
  222. topos/storage/projections/vector_index_store.py +6 -0
  223. topos/storage/raw/__init__.py +1 -0
  224. topos/storage/raw/browser_flat_tables.py +303 -0
  225. topos/storage/raw/file_store.py +100 -0
  226. topos/storage/raw/raw_store.py +29 -0
  227. topos/storage/raw/raw_tables_manager.py +295 -0
  228. topos/storage/raw/sqlite_raw_store.py +17 -0
  229. topos/storage/security/encryption.py +21 -0
  230. topos/storage/signal_identity.py +71 -0
  231. topos/storage/source_settings.py +116 -0
  232. topos/storage/user_identity.py +69 -0
  233. topos/sync/__init__.py +5 -0
  234. topos/sync/client.py +272 -0
  235. topos/sync_handlers.py +70 -0
  236. topos/testing/__init__.py +1 -0
  237. topos/testing/lifespan.py +7 -0
  238. topos/uma_contact_enrichment.py +1032 -0
  239. topos/uma_filters.py +669 -0
  240. topos/uma_resource_id.py +24 -0
  241. topos/uma_rpt.py +69 -0
  242. topos/utils/base_object.py +61 -0
  243. topos/websocket_client.py +21 -0
  244. topos_node-0.1.0.dist-info/METADATA +199 -0
  245. topos_node-0.1.0.dist-info/RECORD +249 -0
  246. topos_node-0.1.0.dist-info/WHEEL +5 -0
  247. topos_node-0.1.0.dist-info/entry_points.txt +2 -0
  248. topos_node-0.1.0.dist-info/licenses/LICENSE +201 -0
  249. topos_node-0.1.0.dist-info/top_level.txt +2 -0
@@ -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
+ )