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/core/logging.py
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import sys
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from time import time
|
|
6
|
+
|
|
7
|
+
from ..config.settings import settings
|
|
8
|
+
|
|
9
|
+
_LOG_FORMAT: str | None = None
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def configure_logging() -> None:
|
|
13
|
+
"""Configure logging to stdout based on environment/log settings."""
|
|
14
|
+
handler = logging.StreamHandler(sys.stdout)
|
|
15
|
+
|
|
16
|
+
def resolve_format() -> str:
|
|
17
|
+
if settings.log_format:
|
|
18
|
+
return settings.log_format.lower()
|
|
19
|
+
if settings.environment.lower() in {"prod", "production"}:
|
|
20
|
+
return "json"
|
|
21
|
+
return "color"
|
|
22
|
+
|
|
23
|
+
log_format = resolve_format()
|
|
24
|
+
if log_format == "json":
|
|
25
|
+
handler.setFormatter(JsonFormatter())
|
|
26
|
+
elif log_format == "color" and sys.stdout.isatty():
|
|
27
|
+
handler.setFormatter(ColorFormatter())
|
|
28
|
+
else:
|
|
29
|
+
handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
|
|
30
|
+
|
|
31
|
+
root = logging.getLogger()
|
|
32
|
+
# Set log level from settings
|
|
33
|
+
log_level_str = settings.log_level.upper()
|
|
34
|
+
log_level = getattr(logging, log_level_str, logging.INFO)
|
|
35
|
+
root.setLevel(log_level)
|
|
36
|
+
root.handlers = [handler]
|
|
37
|
+
|
|
38
|
+
# Suppress WebSocket ping/pong keepalive messages
|
|
39
|
+
# These are noisy debug logs from the websockets library
|
|
40
|
+
websockets_logger = logging.getLogger("websockets")
|
|
41
|
+
websockets_logger.setLevel(logging.INFO) # Only show INFO and above, suppress DEBUG
|
|
42
|
+
|
|
43
|
+
# transformers / huggingface_hub use urllib3; at DEBUG root these lines break tqdm/progress output
|
|
44
|
+
logging.getLogger("urllib3").setLevel(logging.WARNING)
|
|
45
|
+
logging.getLogger("urllib3.connectionpool").setLevel(logging.WARNING)
|
|
46
|
+
# httpx/httpcore DEBUG emits per-request socket chatter; keep UMA transform logs readable.
|
|
47
|
+
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
48
|
+
logging.getLogger("httpcore").setLevel(logging.WARNING)
|
|
49
|
+
|
|
50
|
+
global _LOG_FORMAT
|
|
51
|
+
_LOG_FORMAT = log_format
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def log_request_timing(logger: logging.Logger, path: str, status_code: int, started_at: float) -> None:
|
|
55
|
+
elapsed_ms = int((time() - started_at) * 1000)
|
|
56
|
+
if _LOG_FORMAT == "json":
|
|
57
|
+
logger.info(
|
|
58
|
+
"request.complete",
|
|
59
|
+
extra={"extra": {"path": path, "status": status_code, "latency_ms": elapsed_ms}},
|
|
60
|
+
)
|
|
61
|
+
else:
|
|
62
|
+
logger.info(
|
|
63
|
+
"request.complete path=%s status=%s latency_ms=%s",
|
|
64
|
+
path,
|
|
65
|
+
status_code,
|
|
66
|
+
elapsed_ms,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class JsonFormatter(logging.Formatter):
|
|
71
|
+
def format(self, record: logging.LogRecord) -> str: # noqa: D401
|
|
72
|
+
payload = {
|
|
73
|
+
"level": record.levelname,
|
|
74
|
+
"logger": record.name,
|
|
75
|
+
"message": record.getMessage(),
|
|
76
|
+
"timestamp": record.created,
|
|
77
|
+
}
|
|
78
|
+
if record.exc_info:
|
|
79
|
+
payload["exc_info"] = self.formatException(record.exc_info)
|
|
80
|
+
if hasattr(record, "extra"):
|
|
81
|
+
payload.update(record.extra)
|
|
82
|
+
return json.dumps(payload)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ColorFormatter(logging.Formatter):
|
|
86
|
+
"""Color formatter matching Pipecat-style logs with text colors (no backgrounds)."""
|
|
87
|
+
|
|
88
|
+
_RESET = "\x1b[0m"
|
|
89
|
+
|
|
90
|
+
# Text colors for log levels (no backgrounds)
|
|
91
|
+
_LEVEL_COLORS = {
|
|
92
|
+
logging.DEBUG: "\x1b[38;5;26m", # Light blue (matching DEBUG in image)
|
|
93
|
+
logging.INFO: "\x1b[38;5;220m", # Light yellow/gold (matching INFO in image)
|
|
94
|
+
logging.WARNING: "\x1b[38;5;208m", # Orange (matching WARNING in image)
|
|
95
|
+
logging.ERROR: "\x1b[38;5;196m", # Red
|
|
96
|
+
logging.CRITICAL: "\x1b[38;5;196m", # Red
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# Timestamp color: Forest green
|
|
100
|
+
_TIMESTAMP_COLOR = "\x1b[38;5;28m" # Forest green
|
|
101
|
+
|
|
102
|
+
# Separator color: Light gray
|
|
103
|
+
_SEPARATOR_COLOR = "\x1b[38;5;244m" # Light gray
|
|
104
|
+
|
|
105
|
+
# Class instance names: Teal/cyan-like blue-green (slightly more green/blue than debug blue)
|
|
106
|
+
_CLASS_NAME_COLOR = "\x1b[38;5;26m" # Teal/cyan blue-green
|
|
107
|
+
|
|
108
|
+
# Pipeline stage tags: Blue color
|
|
109
|
+
_PIPELINE_TAG_COLOR = "\x1b[38;5;75m" # Blue (matching user request)
|
|
110
|
+
|
|
111
|
+
# Message text: Same blue as DEBUG
|
|
112
|
+
_MESSAGE_COLOR = "\x1b[38;5;26m" # Light blue (same as DEBUG)
|
|
113
|
+
|
|
114
|
+
# Logger name (module path): Same teal/cyan as class names
|
|
115
|
+
_LOGGER_NAME_COLOR = "\x1b[38;5;75m" # Teal/cyan blue-green
|
|
116
|
+
|
|
117
|
+
_ARROW_COLOR = "\x1b[38;5;244m" # Gray for arrows
|
|
118
|
+
|
|
119
|
+
def format(self, record: logging.LogRecord) -> str: # noqa: D401
|
|
120
|
+
# Format timestamp: YYYY-MM-DD HH:MM:SS.ms (green)
|
|
121
|
+
timestamp_str = datetime.fromtimestamp(record.created).strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
|
122
|
+
timestamp = f"{self._TIMESTAMP_COLOR}{timestamp_str}{self._RESET}"
|
|
123
|
+
|
|
124
|
+
# Get text color for log level (no background)
|
|
125
|
+
level_color = self._LEVEL_COLORS.get(record.levelno, "")
|
|
126
|
+
level = record.levelname
|
|
127
|
+
if level_color:
|
|
128
|
+
level = f"{level_color}{level}{self._RESET}"
|
|
129
|
+
|
|
130
|
+
# Color logger name (module path) in teal/cyan
|
|
131
|
+
logger_name = record.name
|
|
132
|
+
logger_name_colored = f"{self._LOGGER_NAME_COLOR}{logger_name}{self._RESET}"
|
|
133
|
+
|
|
134
|
+
# Get message and extract pipeline tag if present
|
|
135
|
+
message = record.getMessage()
|
|
136
|
+
import re
|
|
137
|
+
|
|
138
|
+
# Extract pipeline stage tag from message if it exists
|
|
139
|
+
pipeline_tag_pattern = r'\[PIPELINE:[A-Z_]+\]'
|
|
140
|
+
pipeline_tag_match = re.search(pipeline_tag_pattern, message)
|
|
141
|
+
pipeline_tag_display = ""
|
|
142
|
+
if pipeline_tag_match:
|
|
143
|
+
# Extract the tag and remove it from the message
|
|
144
|
+
pipeline_tag = pipeline_tag_match.group(0)
|
|
145
|
+
message = re.sub(pipeline_tag_pattern + r'\s*', '', message, count=1) # Remove tag and any following space
|
|
146
|
+
# Color the pipeline tag
|
|
147
|
+
pipeline_tag_display = f" {self._PIPELINE_TAG_COLOR}{pipeline_tag}{self._RESET}"
|
|
148
|
+
|
|
149
|
+
# Apply colors to message
|
|
150
|
+
# First, wrap the entire message in blue (default message color)
|
|
151
|
+
message_colored = f"{self._MESSAGE_COLOR}{message}{self._RESET}"
|
|
152
|
+
|
|
153
|
+
# Then apply special colors to specific parts (these will override the blue)
|
|
154
|
+
# Color class instance names (pattern: ClassName#N)
|
|
155
|
+
# Match class names like IngestionManager#1, Emo27Job#1, etc.
|
|
156
|
+
class_name_pattern = r'\b([A-Z][a-zA-Z0-9_]*#\d+)\b'
|
|
157
|
+
message_colored = re.sub(
|
|
158
|
+
class_name_pattern,
|
|
159
|
+
f"{self._CLASS_NAME_COLOR}\\1{self._RESET}{self._MESSAGE_COLOR}",
|
|
160
|
+
message_colored
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Color arrows (→ and ←) for pipeline flow
|
|
164
|
+
message_colored = re.sub(
|
|
165
|
+
r'([→←])',
|
|
166
|
+
f"{self._ARROW_COLOR}\\1{self._RESET}{self._MESSAGE_COLOR}",
|
|
167
|
+
message_colored
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Format: TIMESTAMP | LEVEL | [PIPELINE:TAG] | LOGGER: MESSAGE
|
|
171
|
+
separator = f"{self._SEPARATOR_COLOR}|{self._RESET}"
|
|
172
|
+
if pipeline_tag_display:
|
|
173
|
+
return f"{timestamp} {separator} {level} {separator}{pipeline_tag_display} {separator} {logger_name_colored}: {message_colored}"
|
|
174
|
+
else:
|
|
175
|
+
return f"{timestamp} {separator} {level} {separator} {logger_name_colored}: {message_colored}"
|
topos/core/metrics.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""No-op metrics interface for Topos."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MetricsClient:
|
|
9
|
+
"""No-op metrics client for early scaffolding."""
|
|
10
|
+
|
|
11
|
+
def increment(self, name: str, value: int = 1, tags: Optional[Dict[str, str]] = None) -> None:
|
|
12
|
+
_ = (name, value, tags)
|
|
13
|
+
|
|
14
|
+
def gauge(self, name: str, value: float, tags: Optional[Dict[str, str]] = None) -> None:
|
|
15
|
+
_ = (name, value, tags)
|
|
16
|
+
|
|
17
|
+
def observe(self, name: str, value: float, tags: Optional[Dict[str, str]] = None) -> None:
|
|
18
|
+
_ = (name, value, tags)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
metrics = MetricsClient()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Startup banner display for Topos Control Plane."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import sys
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
from ..__version__ import __version__
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger("topos.core")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def print_startup_banner(
|
|
15
|
+
engine_mode: Optional[str] = None,
|
|
16
|
+
database_mode: Optional[str] = None,
|
|
17
|
+
sync_enabled: bool = False,
|
|
18
|
+
) -> None:
|
|
19
|
+
"""
|
|
20
|
+
Display a startup banner for Topos.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
engine_mode: Engine mode (full, sync, etc.)
|
|
24
|
+
database_mode: Database mode (local, postgres, etc.)
|
|
25
|
+
sync_enabled: Whether sync is enabled
|
|
26
|
+
"""
|
|
27
|
+
banner = """
|
|
28
|
+
╔═══════════════════════════════════════════════════════════════╗
|
|
29
|
+
║ ║
|
|
30
|
+
║ ████████╗ ██████╗ ██████╗ ██████╗ ███████╗ ║
|
|
31
|
+
║ ╚══██╔══╝██╔═══██╗██╔══██╗██╔═══██╗██╔════╝ ║
|
|
32
|
+
║ ██║ ██║ ██║██████╔╝██║ ██║███████╗ ║
|
|
33
|
+
║ ██║ ██║ ██║██╔═══╝ ██║ ██║╚════██║ ║
|
|
34
|
+
║ ██║ ╚██████╔╝██║ ╚██████╔╝███████║ ║
|
|
35
|
+
║ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚══════╝ ║
|
|
36
|
+
║ ║
|
|
37
|
+
║ Control Plane v{version:8s} ║
|
|
38
|
+
║ ║
|
|
39
|
+
╚═══════════════════════════════════════════════════════════════╝
|
|
40
|
+
""".format(version=__version__)
|
|
41
|
+
|
|
42
|
+
config_lines = []
|
|
43
|
+
if engine_mode:
|
|
44
|
+
config_lines.append(f" Mode: {engine_mode}")
|
|
45
|
+
if database_mode:
|
|
46
|
+
config_lines.append(f" Database: {database_mode}")
|
|
47
|
+
config_lines.append(f" Sync: {'enabled' if sync_enabled else 'disabled'}")
|
|
48
|
+
|
|
49
|
+
config_info = "\n".join(config_lines) if config_lines else ""
|
|
50
|
+
|
|
51
|
+
print(banner, file=sys.stderr)
|
|
52
|
+
if config_info:
|
|
53
|
+
print(config_info, file=sys.stderr)
|
|
54
|
+
print("", file=sys.stderr)
|
|
55
|
+
|
|
56
|
+
logger.info("=" * 65)
|
|
57
|
+
logger.info("Topos Control Plane v%s", __version__)
|
|
58
|
+
logger.info("=" * 65)
|
|
59
|
+
if config_info:
|
|
60
|
+
for line in config_lines:
|
|
61
|
+
logger.info(line)
|
|
62
|
+
logger.info("")
|