topos-node 1.0.2__tar.gz → 1.0.6__tar.gz
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.
- {topos_node-1.0.2 → topos_node-1.0.6}/PKG-INFO +11 -1
- {topos_node-1.0.2 → topos_node-1.0.6}/README.md +10 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/pyproject.toml +1 -1
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/__version__.py +2 -2
- topos_node-1.0.6/topos/api/engine_run.py +43 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/app.py +2 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/config/settings.py +15 -3
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/disclosure/privacy_layer.py +7 -7
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/__init__.py +9 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/backends/huggingface.py +133 -76
- topos_node-1.0.6/topos/engine/client.py +85 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/engine.py +28 -3
- topos_node-1.0.6/topos/engine/memory_utils.py +57 -0
- topos_node-1.0.6/topos/engine/model_cache.py +211 -0
- topos_node-1.0.6/topos/engine/pipeline_memory.py +26 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/queue_manager.py +22 -1
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/registration.py +15 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/canonical/_engine_runner.py +6 -3
- topos_node-1.0.6/topos/enrichment/jobs/canonical/emo_27_job.py +111 -0
- topos_node-1.0.6/topos/enrichment/models/manager.py +37 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/orchestrator.py +25 -2
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/dimension_registry.py +1 -1
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/canonical_pipeline.py +6 -1
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/observability/metrics.py +9 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/query/inference.py +4 -4
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/query/manifest_validation.py +51 -6
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/sanitization/nsfw_classifier.py +7 -12
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/sanitization/privacy_filter.py +10 -13
- {topos_node-1.0.2 → topos_node-1.0.6}/topos_node.egg-info/PKG-INFO +11 -1
- {topos_node-1.0.2 → topos_node-1.0.6}/topos_node.egg-info/SOURCES.txt +5 -0
- topos_node-1.0.2/topos/enrichment/jobs/canonical/emo_27_job.py +0 -99
- topos_node-1.0.2/topos/enrichment/models/manager.py +0 -8
- {topos_node-1.0.2 → topos_node-1.0.6}/LICENSE +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/setup.cfg +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/shared/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/shared/filtering.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/shared/schema_registry.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/tests/test_llm_integrations_storage.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/analytics/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/analytics/duckdb_adapter.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/analytics/messenger_communities.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/analytics/messenger_graph.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/analytics/messenger_labels.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/analytics/profiles.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/analytics/query_engine.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/analytics/raw_queries.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/analytics.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/app_registry.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/backup.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/compute_remote.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/data_commit.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/data_explorer_table_prefs.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/database_explorer.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/db.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/device.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/enrichment.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/filter_lab.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/health.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/home_chat.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/ingestion_api.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/ingestion_compat.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/ingestion_sources.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/llm.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/local_mcp.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/messenger_analytics.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/privacy_disclose.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/query_api.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/runtime_bootstrap.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/sanitization_ollama_config.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/signal.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/signal_extraction_config.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/source_install.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/source_scrub.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/sources.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/sources_overview.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/sync.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/ui_config.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/uma_data.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/usage.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/api/user_identity.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/auth.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/canonicalization/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/canonicalization/mappers/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/canonicalization/mappers/base.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/canonicalization/mappers/browser_activity_mapper.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/canonicalization/mappers/chatgpt_mapper.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/canonicalization/mappers/demo_mappers.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/canonicalization/mappers/grok_mapper.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/canonicalization/mappers/messenger_mapper.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/canonicalization/models.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/canonicalization/resolver.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/cli/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/cli/__main__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/cli/commands.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/config/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/config/sanitization_ollama.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/config/signal_extraction.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/contacts/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/contacts/identity.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/control_plane_client.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/core/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/core/api_models.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/core/connection_resilience.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/core/device_helpers.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/core/errors.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/core/events.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/core/handlers.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/core/logging.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/core/metrics.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/core/routine_access.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/core/startup_banner.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/core/state.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/core/table_layers.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/core/types.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/data_explorer_row_delete.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/data_explorer_table_prefs.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/data_explorer_tables.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/disclosure/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/disclosure/canonical_writer.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/disclosure/content_policy.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/disclosure/field_registry.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/disclosure/tier.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/backends/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/backends/base.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/backends/generative_prompts.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/backends/generative_response.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/backends/ollama.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/backends/openai.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/backends/openai_compatible.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/backends/redpill.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/backends/stub.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/intake.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/result_formatter.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/router.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/scoped_token.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/tasks.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/transport.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/usage_guard.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/usage_observation.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/engine/validator.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/derived_tables.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/job_writer.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/base.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/canonical/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/canonical/_batch_limits.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/canonical/availability_scores_job.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/canonical/brief_fallback.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/canonical/dimension_summary_job.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/canonical/embeddings_job.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/canonical/entities_job.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/canonical/goal_extraction_job.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/canonical/pii_redaction_job.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/canonical/relationship_edges_job.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/canonical/sentiment_job.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/canonical/topic_clusters_job.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/canonical/topics_job.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/canonical/url_classification_core.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/canonical/url_classification_enrichment_job.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/canonical/url_classification_signal_job.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/raw/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/raw/attachments_job.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/raw/language_job.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/raw/time_normalization_job.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/jobs/raw/tool_calls_job.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/models/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/models/mvp_defaults.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/models/registry.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/models/versioning.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/processor.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/progress_bar.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/registry_contract.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/enrichment/website_classifier.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/fit/evaluator.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/brief_canonical_loader.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/brief_ontology.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/brief_renderer.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/brief_schemas.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/chunking.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/data_health.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/dimension_briefs.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/dimension_definition_loader.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/dimension_profiles.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/extraction/artifact_router.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/extraction/artifact_store.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/extraction/rule_extractors.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/graph_sanitize.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/hybrid_search.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/query_embed_cache.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/schemas.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/service.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/signal_object_store.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/source_hydration.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/structured_signal.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/topic_clustering.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/typed_stores/aggregates.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/typed_stores/scope_materializer.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/vector_codec.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/vector_math.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/features/signal/vector_settings.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/filter_lab/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/filter_lab/bundles.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/filter_lab/schema.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/filter_lab/service.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/filter_lab/store.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/filter_lab/worker.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/home_chat/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/home_chat/schema.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/home_chat/store.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/hosted_pool_lease.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/checkpoints/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/checkpoints/checkpoint_store.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/checkpoints/sqlite_checkpoint_store.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/inbox_crypto.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/inbox_drain.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/ingest_helpers.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/jobs.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/journal_location_fanout.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/journal_time_log_normalize.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/local_sync.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/log_preview.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/manager.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/parser.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/parsers/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/parsers/base.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/parsers/browser_parser.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/parsers/calendar_parser.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/parsers/chatgpt_conversation_flattener.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/parsers/chatgpt_parser.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/parsers/demo_file_parsers.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/parsers/grok_parser.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/parsers/messenger_parser.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/parsers/voxterm_parser.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/progress.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/reprocess.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/sources/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/sources/base.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/sources/calendar.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/sources/chatgpt.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/sources/contact_importers.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/sources/grok.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/sources/imessage_reader.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/sources/signal_export_parser.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/sources/signal_reader.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/state_machine.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/triggers/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/triggers/file_trigger.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/triggers/sqlite_trigger.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/usage_inbox_dedupe.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/validation/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/validation/base.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/validation/schema_registry.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/ingestion/validation/schema_validator.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/lineage/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/lineage/provenance.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/lineage/tracker.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/llm_integrations_storage.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/mcp_stdio_proxy.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/observability/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/observability/alerts.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/observability/tracing.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/openai_client.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/pipeline/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/pipeline/audit.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/pipeline/envelope.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/pipeline/stages.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/pipeline/stub_enqueue.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/projections/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/projections/vector_index/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/projections/vector_index/base.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/projections/vector_index/builders.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/projections/vector_index/health_checks.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/query/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/query/audit.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/query/disclosure.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/query/fingerprint.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/query/game_layer.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/query/intent.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/query/manifest.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/query/pipeline.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/query/retrieval.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/query/runtime.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/query/scope_registry_loader.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/query/session.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/query/session_utils.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/query/source_generation.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/query/turn_classifier.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/query/types.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/rate_limit.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/routines/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/routines/schema.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/routines/store.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/runtime_update.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/sanitization/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/sanitization/ollama_transforms.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/scope_resolution.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/services/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/services/container.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/services/embeddings/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/services/embeddings/base.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/services/embeddings/local.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/services/embeddings/remote.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/services/interfaces.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/services/llm/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/services/llm/base.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/services/llm/openai.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/services/local.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/services/postgres.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/sources/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/sources/bundled_canonical_triples.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/sources/canonical_signal_defaults.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/sources/definitions.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/sources/install_service.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/sources/registry.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/sources/runtime_install.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/sources/scrub_attribution.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/sources/scrub_service.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/startup_banner.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/adapters/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/adapters/factory.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/adapters/fakes.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/adapters/postgres/stores.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/adapters/protocols.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/adapters/sqlite/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/adapters/sqlite/stores.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/adapters/sqlite/vector_search.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/canonical/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/canonical/activity_tables.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/canonical/ai_chat/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/canonical/ai_chat/canonicalizer.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/canonical/ai_chat/mapper.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/canonical/ai_chat/model.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/canonical/ai_chat/tables.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/canonical/calendar_contacts_tables.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/canonical/canonical_store.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/canonical/conversations_tables.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/canonical/mapping_store.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/canonical/postgres.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/client.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/canonical_disclosure_v1.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/canonical_nsfw_v1.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/extraction_artifacts.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/journal_entries_duration_v1.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/journal_entries_ends_at_v1.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/journal_entries_people_v1.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/journal_entries_place_name_v1.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/journal_entries_starts_at_v1.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/remediation_person_model.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/scope_source_generation.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/signal_dimension_harness.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/signal_objects.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/signal_objects_updated_by_v1.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/stage9_column_renames.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/vector_storage_v1.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/vector_storage_v2.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/vector_storage_v3.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/wiki_mvp_phase0.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/wiki_mvp_phase1.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/wiki_mvp_phase4_messages_cutover.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/wiki_mvp_phase5_topic_clusters.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/wiki_mvp_phase6_dimension_briefs.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/wiki_mvp_query_quality.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/migrations/wiki_mvp_topic_clusters_coordination.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/paths.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/postgres.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/schema.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/db/storage_breakdown.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/enrichment/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/enrichment/canonical_enrichment_store.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/enrichment/raw_enrichment_store.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/normalized/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/normalized/normalized_store.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/oplog/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/oplog/decision.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/oplog/oplog_store.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/oplog/postgres.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/person/identity_resolver.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/projections/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/projections/index_ops_store.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/projections/vector_index_store.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/raw/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/raw/browser_flat_tables.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/raw/file_store.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/raw/raw_store.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/raw/raw_tables_manager.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/raw/sqlite_raw_store.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/security/encryption.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/signal_identity.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/source_settings.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/storage/user_identity.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/sync/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/sync/client.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/sync_handlers.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/testing/__init__.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/testing/lifespan.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/uma_contact_enrichment.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/uma_filters.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/uma_resource_id.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/uma_rpt.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/utils/base_object.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos/websocket_client.py +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos_node.egg-info/dependency_links.txt +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos_node.egg-info/entry_points.txt +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos_node.egg-info/requires.txt +0 -0
- {topos_node-1.0.2 → topos_node-1.0.6}/topos_node.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: topos-node
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.6
|
|
4
4
|
Summary: Topos personal AI engine (FastAPI): local data, sync, control plane WebSocket client
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Project-URL: Homepage, https://topos.dialogues.ai
|
|
@@ -195,6 +195,16 @@ uv sync --extra engine
|
|
|
195
195
|
just run
|
|
196
196
|
```
|
|
197
197
|
|
|
198
|
+
### Engine memory (local dev)
|
|
199
|
+
|
|
200
|
+
ML models are cached inside the Engine with LRU eviction. For lighter local runs (especially inside Cursor's integrated terminal), see [Engine memory management](topos/docs/ml-manager-v2/MEMORY_MANAGEMENT.md).
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# Default: ENGINE_MAX_RESIDENT_MODELS=3; pipeline flush is automatic
|
|
204
|
+
# Override only if needed, e.g. lower RAM: export ENGINE_MAX_RESIDENT_MODELS=2
|
|
205
|
+
export PRIVACY_FILTER_DEVICE=cpu
|
|
206
|
+
```
|
|
207
|
+
|
|
198
208
|
Run tests:
|
|
199
209
|
|
|
200
210
|
```bash
|
|
@@ -139,6 +139,16 @@ uv sync --extra engine
|
|
|
139
139
|
just run
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
+
### Engine memory (local dev)
|
|
143
|
+
|
|
144
|
+
ML models are cached inside the Engine with LRU eviction. For lighter local runs (especially inside Cursor's integrated terminal), see [Engine memory management](topos/docs/ml-manager-v2/MEMORY_MANAGEMENT.md).
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
# Default: ENGINE_MAX_RESIDENT_MODELS=3; pipeline flush is automatic
|
|
148
|
+
# Override only if needed, e.g. lower RAM: export ENGINE_MAX_RESIDENT_MODELS=2
|
|
149
|
+
export PRIVACY_FILTER_DEVICE=cpu
|
|
150
|
+
```
|
|
151
|
+
|
|
142
152
|
Run tests:
|
|
143
153
|
|
|
144
154
|
```bash
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "topos-node"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.6"
|
|
8
8
|
description = "Topos personal AI engine (FastAPI): local data, sync, control plane WebSocket client"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Engine task execution HTTP endpoint."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict
|
|
6
|
+
|
|
7
|
+
from fastapi import APIRouter, Depends, HTTPException, status
|
|
8
|
+
|
|
9
|
+
from ..auth import require_api_key
|
|
10
|
+
from ..engine.engine import Engine
|
|
11
|
+
from ..engine.tasks import ProcessingResult, ProcessingTask
|
|
12
|
+
|
|
13
|
+
router = APIRouter(tags=["engine-run"])
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@router.post("/v1/engine/run", dependencies=[Depends(require_api_key)])
|
|
17
|
+
async def engine_run(body: Dict[str, Any]) -> Dict[str, Any]:
|
|
18
|
+
"""Execute a ProcessingTask synchronously and return ProcessingResult JSON."""
|
|
19
|
+
try:
|
|
20
|
+
task = ProcessingTask.model_validate(body)
|
|
21
|
+
except Exception as exc:
|
|
22
|
+
raise HTTPException(
|
|
23
|
+
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
24
|
+
detail=f"Invalid ProcessingTask: {exc}",
|
|
25
|
+
) from exc
|
|
26
|
+
result: ProcessingResult = Engine().run(task)
|
|
27
|
+
return {"result": result.model_dump(mode="json")}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@router.get("/v1/engine/memory", dependencies=[Depends(require_api_key)])
|
|
31
|
+
async def engine_memory() -> Dict[str, Any]:
|
|
32
|
+
from ..engine.memory_utils import get_process_rss_mb, mps_available, torch_available
|
|
33
|
+
from ..engine.model_cache import get_model_cache
|
|
34
|
+
|
|
35
|
+
cache = get_model_cache()
|
|
36
|
+
return {
|
|
37
|
+
"rss_mb": get_process_rss_mb(),
|
|
38
|
+
"resident_slots": cache.resident_slots(),
|
|
39
|
+
"max_resident": cache.max_resident,
|
|
40
|
+
"evictions_total": cache.evictions_total,
|
|
41
|
+
"torch_available": torch_available(),
|
|
42
|
+
"mps_available": mps_available(),
|
|
43
|
+
}
|
|
@@ -15,6 +15,7 @@ from .api import (
|
|
|
15
15
|
app_registry as app_registry_routes,
|
|
16
16
|
backup as backup_routes,
|
|
17
17
|
compute_remote as compute_remote_routes,
|
|
18
|
+
engine_run as engine_run_routes,
|
|
18
19
|
data_commit as data_commit_routes,
|
|
19
20
|
db as db_routes,
|
|
20
21
|
device as device_routes,
|
|
@@ -119,6 +120,7 @@ app.include_router(filter_lab_routes.router)
|
|
|
119
120
|
app.include_router(home_chat_routes.router)
|
|
120
121
|
app.include_router(privacy_disclose_routes.router)
|
|
121
122
|
app.include_router(compute_remote_routes.router)
|
|
123
|
+
app.include_router(engine_run_routes.router)
|
|
122
124
|
app.include_router(data_commit_routes.router)
|
|
123
125
|
|
|
124
126
|
|
|
@@ -5,7 +5,7 @@ import os
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from typing import List, Optional
|
|
7
7
|
|
|
8
|
-
from pydantic import Field, model_validator
|
|
8
|
+
from pydantic import AliasChoices, Field, model_validator
|
|
9
9
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
10
10
|
|
|
11
11
|
# Set before any huggingface_hub import during app/route loading.
|
|
@@ -34,8 +34,14 @@ class Settings(BaseSettings):
|
|
|
34
34
|
|
|
35
35
|
openai_base_url: str = Field("https://api.openai.com/v1", env="OPENAI_BASE_URL")
|
|
36
36
|
openai_model: str = Field("gpt-4o-mini", env="OPENAI_MODEL")
|
|
37
|
-
red_pill_api_key: Optional[str] = Field(
|
|
38
|
-
|
|
37
|
+
red_pill_api_key: Optional[str] = Field(
|
|
38
|
+
None,
|
|
39
|
+
validation_alias=AliasChoices("RED_PILL_API_KEY", "REDPILL_API_KEY"),
|
|
40
|
+
)
|
|
41
|
+
red_pill_api_base: str = Field(
|
|
42
|
+
"https://api.redpill.ai/v1",
|
|
43
|
+
validation_alias=AliasChoices("RED_PILL_API_BASE", "REDPILL_API_BASE"),
|
|
44
|
+
)
|
|
39
45
|
|
|
40
46
|
gt_cloud_api_key: Optional[str] = Field(None, env="GT_CLOUD_API_KEY")
|
|
41
47
|
griptape_nodes_api_base_url: str = Field(
|
|
@@ -95,6 +101,12 @@ class Settings(BaseSettings):
|
|
|
95
101
|
ollama_query_model: str = Field("llama3.2:latest", env="TOPOS_OLLAMA_QUERY_MODEL")
|
|
96
102
|
engine_default_provider: str = Field("huggingface", env="ENGINE_DEFAULT_PROVIDER")
|
|
97
103
|
|
|
104
|
+
engine_max_resident_models: int = Field(3, env="ENGINE_MAX_RESIDENT_MODELS")
|
|
105
|
+
engine_model_idle_ttl_sec: int = Field(0, env="ENGINE_MODEL_IDLE_TTL_SEC")
|
|
106
|
+
engine_memory_rss_soft_limit_mb: int = Field(0, env="ENGINE_MEMORY_RSS_SOFT_LIMIT_MB")
|
|
107
|
+
engine_flush_after_task: bool = Field(False, env="ENGINE_FLUSH_AFTER_TASK")
|
|
108
|
+
engine_ml_device: Optional[str] = Field(None, env="ENGINE_ML_DEVICE")
|
|
109
|
+
|
|
98
110
|
# Sanitization field-transforms via Ollama (see topos.config.sanitization_ollama + engine_config overrides)
|
|
99
111
|
sanitization_ollama_enabled: bool = Field(False, env="SANITIZATION_OLLAMA_ENABLED")
|
|
100
112
|
sanitization_ollama_host: Optional[str] = Field(None, env="SANITIZATION_OLLAMA_HOST")
|
|
@@ -69,13 +69,13 @@ class PrivacyLayerClient:
|
|
|
69
69
|
*,
|
|
70
70
|
transform_id: str,
|
|
71
71
|
) -> Dict[str, Any]:
|
|
72
|
-
from ..engine import
|
|
72
|
+
from ..engine.client import get_engine_client_or_local
|
|
73
73
|
from ..enrichment.jobs.canonical._engine_runner import run_engine_task
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
client = get_engine_client_or_local(self._engine)
|
|
76
76
|
record_ids = [str(i.get("id") or "") for i in items]
|
|
77
77
|
result = await run_engine_task(
|
|
78
|
-
|
|
78
|
+
client,
|
|
79
79
|
task_id=f"privacy_{record_ids[0] or 'batch'}",
|
|
80
80
|
subtype="privacy_disclosure",
|
|
81
81
|
source_id=None,
|
|
@@ -127,15 +127,15 @@ class PrivacyLayerClient:
|
|
|
127
127
|
return await self._classify_nsfw_via_engine(items)
|
|
128
128
|
|
|
129
129
|
async def _classify_nsfw_via_engine(self, items: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
130
|
-
from ..engine import Engine
|
|
131
|
-
from ..enrichment.jobs.canonical._engine_runner import run_engine_task
|
|
132
130
|
from ..config.settings import settings
|
|
131
|
+
from ..engine.client import get_engine_client_or_local
|
|
132
|
+
from ..enrichment.jobs.canonical._engine_runner import run_engine_task
|
|
133
133
|
|
|
134
|
-
|
|
134
|
+
client = get_engine_client_or_local(self._engine)
|
|
135
135
|
model = getattr(settings, "nsfw_classifier_model", "michellejieli/NSFW_text_classifier")
|
|
136
136
|
record_ids = [str(i.get("id") or "") for i in items]
|
|
137
137
|
result = await run_engine_task(
|
|
138
|
-
|
|
138
|
+
client,
|
|
139
139
|
task_id=f"nsfw_{record_ids[0] or 'batch'}",
|
|
140
140
|
subtype="content_nsfw_classification",
|
|
141
141
|
source_id=None,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""Topos Engine: unified runtime for ML/LLM processing (enrichments, transformations, queries)."""
|
|
2
2
|
|
|
3
3
|
from .engine import Engine
|
|
4
|
+
from .client import EngineClient, LocalEngineClient, RemoteEngineClient, get_engine_client, get_engine_client_or_local
|
|
5
|
+
from .model_cache import ModelSlot, get_model_cache
|
|
4
6
|
from .tasks import (
|
|
5
7
|
ExecutionMeta,
|
|
6
8
|
ExecutionSpec,
|
|
@@ -16,6 +18,13 @@ from .tasks import (
|
|
|
16
18
|
|
|
17
19
|
__all__ = [
|
|
18
20
|
"Engine",
|
|
21
|
+
"EngineClient",
|
|
22
|
+
"LocalEngineClient",
|
|
23
|
+
"RemoteEngineClient",
|
|
24
|
+
"get_engine_client",
|
|
25
|
+
"get_engine_client_or_local",
|
|
26
|
+
"ModelSlot",
|
|
27
|
+
"get_model_cache",
|
|
19
28
|
"ProcessingTask",
|
|
20
29
|
"ProcessingResult",
|
|
21
30
|
"ModelRequest",
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
|
-
import
|
|
7
|
-
|
|
6
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
7
|
+
|
|
8
|
+
from ..model_cache import ModelSlot, get_model_cache
|
|
8
9
|
|
|
9
10
|
logger = logging.getLogger("topos.engine.huggingface")
|
|
10
11
|
|
|
@@ -15,24 +16,38 @@ DEFAULT_NER_MODEL = "dslim/bert-base-NER"
|
|
|
15
16
|
DEFAULT_EMBEDDING_MODEL = "sentence-transformers/all-MiniLM-L6-v2"
|
|
16
17
|
DEFAULT_SENTIMENT_MODEL = "cardiffnlp/twitter-roberta-base-sentiment-latest"
|
|
17
18
|
|
|
19
|
+
_MODEL_NAME_TO_SLOT: Dict[str, ModelSlot] = {
|
|
20
|
+
DEFAULT_URL_CLASSIFICATION_MODEL: ModelSlot.URL_PIPELINE,
|
|
21
|
+
DEFAULT_EMOTION_MODEL: ModelSlot.EMOTION,
|
|
22
|
+
DEFAULT_NER_MODEL: ModelSlot.NER,
|
|
23
|
+
DEFAULT_EMBEDDING_MODEL: ModelSlot.EMBEDDING,
|
|
24
|
+
DEFAULT_SENTIMENT_MODEL: ModelSlot.SENTIMENT,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _match_slot(model_name: str) -> Optional[ModelSlot]:
|
|
29
|
+
name = (model_name or "").strip().lower()
|
|
30
|
+
if not name:
|
|
31
|
+
return None
|
|
32
|
+
for key, slot in _MODEL_NAME_TO_SLOT.items():
|
|
33
|
+
if key.lower() == name or key.split("/")[-1].lower() in name:
|
|
34
|
+
return slot
|
|
35
|
+
if "website-classifier" in name:
|
|
36
|
+
return ModelSlot.URL_PIPELINE
|
|
37
|
+
if "go_emotions" in name or "emotion" in name:
|
|
38
|
+
return ModelSlot.EMOTION
|
|
39
|
+
if "ner" in name:
|
|
40
|
+
return ModelSlot.NER
|
|
41
|
+
if "minilm" in name or "embedding" in name or "sentence-transformers" in name:
|
|
42
|
+
return ModelSlot.EMBEDDING
|
|
43
|
+
if "sentiment" in name:
|
|
44
|
+
return ModelSlot.SENTIMENT
|
|
45
|
+
return None
|
|
46
|
+
|
|
18
47
|
|
|
19
48
|
class HuggingFaceAdapter:
|
|
20
49
|
"""BackendAdapter for HuggingFace: text-classification pipeline and go_emotions model."""
|
|
21
50
|
|
|
22
|
-
def __init__(self) -> None:
|
|
23
|
-
self._url_pipeline: Any = None
|
|
24
|
-
self._url_lock = threading.Lock()
|
|
25
|
-
self._emotion_model: Any = None
|
|
26
|
-
self._emotion_tokenizer: Any = None
|
|
27
|
-
self._emotion_loaded = False
|
|
28
|
-
self._emotion_lock = threading.Lock()
|
|
29
|
-
self._ner_pipeline: Any = None
|
|
30
|
-
self._ner_lock = threading.Lock()
|
|
31
|
-
self._embedding_model: Any = None
|
|
32
|
-
self._embedding_lock = threading.Lock()
|
|
33
|
-
self._sentiment_pipeline: Any = None
|
|
34
|
-
self._sentiment_lock = threading.Lock()
|
|
35
|
-
|
|
36
51
|
def load_model(self, model_name: str, config: Optional[Dict[str, Any]] = None) -> None:
|
|
37
52
|
"""Load model by name; we load on first run_inference per subtype instead."""
|
|
38
53
|
pass
|
|
@@ -41,7 +56,6 @@ class HuggingFaceAdapter:
|
|
|
41
56
|
"""
|
|
42
57
|
Ensure the model is downloaded (e.g. from HuggingFace Hub). Downloads if not present.
|
|
43
58
|
Returns True if a download was triggered (caller may clean up cache later), False if already in cache.
|
|
44
|
-
Logs when download starts; Hub may show progress via tqdm if enabled.
|
|
45
59
|
"""
|
|
46
60
|
try:
|
|
47
61
|
from huggingface_hub import snapshot_download
|
|
@@ -49,8 +63,7 @@ class HuggingFaceAdapter:
|
|
|
49
63
|
return False
|
|
50
64
|
logger.info("Downloading model %s (huggingface)...", model_name)
|
|
51
65
|
try:
|
|
52
|
-
|
|
53
|
-
snapshot_download(repo_id=model_name, tqdm_enabled=True)
|
|
66
|
+
snapshot_download(repo_id=model_name, tqdm_enabled=False)
|
|
54
67
|
except Exception:
|
|
55
68
|
logger.exception("Failed to download model %s", model_name)
|
|
56
69
|
return False
|
|
@@ -58,27 +71,29 @@ class HuggingFaceAdapter:
|
|
|
58
71
|
return True
|
|
59
72
|
|
|
60
73
|
def _get_url_pipeline(self, model_name: str):
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
74
|
+
model = model_name or DEFAULT_URL_CLASSIFICATION_MODEL
|
|
75
|
+
|
|
76
|
+
def _load():
|
|
64
77
|
from transformers import pipeline
|
|
65
|
-
self._url_pipeline = pipeline(
|
|
66
|
-
task="text-classification",
|
|
67
|
-
model=model_name,
|
|
68
|
-
)
|
|
69
|
-
return self._url_pipeline
|
|
70
78
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
79
|
+
return pipeline(task="text-classification", model=model)
|
|
80
|
+
|
|
81
|
+
handle, _ = get_model_cache().acquire(ModelSlot.URL_PIPELINE, model, _load)
|
|
82
|
+
return handle
|
|
83
|
+
|
|
84
|
+
def _get_emotion_model(self, model_name: str) -> Tuple[Any, Any]:
|
|
85
|
+
model = model_name or DEFAULT_EMOTION_MODEL
|
|
86
|
+
|
|
87
|
+
def _load():
|
|
75
88
|
from transformers import AutoModelForSequenceClassification, AutoTokenizer
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
|
|
90
|
+
tokenizer = AutoTokenizer.from_pretrained(model)
|
|
91
|
+
emo_model = AutoModelForSequenceClassification.from_pretrained(model)
|
|
92
|
+
emo_model.eval()
|
|
93
|
+
return emo_model, tokenizer
|
|
94
|
+
|
|
95
|
+
handle, _ = get_model_cache().acquire(ModelSlot.EMOTION, model, _load)
|
|
96
|
+
return handle
|
|
82
97
|
|
|
83
98
|
def run_inference(self, payload: Dict[str, Any], config: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
84
99
|
"""Dispatch by config.subtype to url_classification or emotion_classification."""
|
|
@@ -92,6 +107,8 @@ class HuggingFaceAdapter:
|
|
|
92
107
|
return self._run_url_classification_batch(payload, model)
|
|
93
108
|
if subtype in ("emotion_classification", "emo_27"):
|
|
94
109
|
return self._run_emotion_classification(payload, model)
|
|
110
|
+
if subtype == "emotion_classification_batch":
|
|
111
|
+
return self._run_emotion_classification_batch(payload, model)
|
|
95
112
|
if subtype == "entity_extraction":
|
|
96
113
|
return self._run_entity_extraction(payload, model)
|
|
97
114
|
if subtype == "embedding":
|
|
@@ -105,17 +122,21 @@ class HuggingFaceAdapter:
|
|
|
105
122
|
return {"error": f"Unknown subtype: {subtype}", "status": "unsupported"}
|
|
106
123
|
|
|
107
124
|
def _run_url_classification(self, payload: Dict[str, Any], model_name: str) -> Dict[str, Any]:
|
|
108
|
-
"""Same behavior as WebsiteUrlClassifier."""
|
|
109
125
|
url = payload.get("url") or ""
|
|
110
126
|
title = payload.get("title") or ""
|
|
111
127
|
if not isinstance(url, str) or not url.strip():
|
|
112
|
-
return {
|
|
128
|
+
return {
|
|
129
|
+
"error": "url must be a non-empty string",
|
|
130
|
+
"category": "unknown",
|
|
131
|
+
"confidence": 0.0,
|
|
132
|
+
"model": model_name or DEFAULT_URL_CLASSIFICATION_MODEL,
|
|
133
|
+
}
|
|
113
134
|
model = model_name or DEFAULT_URL_CLASSIFICATION_MODEL
|
|
114
|
-
|
|
135
|
+
pipe = self._get_url_pipeline(model)
|
|
115
136
|
clean_url = url.strip()
|
|
116
137
|
clean_title = (title or "").strip()
|
|
117
138
|
text = f"{clean_url} [SEP] {clean_title}" if clean_title else clean_url
|
|
118
|
-
result =
|
|
139
|
+
result = pipe(text, truncation=True, top_k=1)
|
|
119
140
|
top_result = result[0] if isinstance(result, list) and result else {}
|
|
120
141
|
return {
|
|
121
142
|
"category": top_result.get("label", "unknown"),
|
|
@@ -128,7 +149,7 @@ class HuggingFaceAdapter:
|
|
|
128
149
|
if not isinstance(items, list) or not items:
|
|
129
150
|
return {"error": "items required", "items": [], "model": model_name or DEFAULT_URL_CLASSIFICATION_MODEL}
|
|
130
151
|
model = model_name or DEFAULT_URL_CLASSIFICATION_MODEL
|
|
131
|
-
|
|
152
|
+
pipe = self._get_url_pipeline(model)
|
|
132
153
|
texts: List[str] = []
|
|
133
154
|
for item in items:
|
|
134
155
|
url = str(item.get("url") or "").strip()
|
|
@@ -142,7 +163,7 @@ class HuggingFaceAdapter:
|
|
|
142
163
|
classified: List[Any] = []
|
|
143
164
|
if non_empty_idx:
|
|
144
165
|
batch_texts = [texts[i] for i in non_empty_idx]
|
|
145
|
-
classified =
|
|
166
|
+
classified = pipe(batch_texts, truncation=True, top_k=1)
|
|
146
167
|
if not isinstance(classified, list):
|
|
147
168
|
classified = [classified]
|
|
148
169
|
|
|
@@ -165,13 +186,10 @@ class HuggingFaceAdapter:
|
|
|
165
186
|
)
|
|
166
187
|
return {"items": out_items, "model": model}
|
|
167
188
|
|
|
168
|
-
def
|
|
169
|
-
"""Same behavior as Emo27Job._classify_emotion."""
|
|
170
|
-
text = payload.get("text") or payload.get("content") or ""
|
|
171
|
-
if not text or not isinstance(text, str):
|
|
172
|
-
return {"error": "text or content required", "emotion_label": None, "confidence": None, "all_emotions": [], "model": model_name or DEFAULT_EMOTION_MODEL}
|
|
173
|
-
model = model_name or DEFAULT_EMOTION_MODEL
|
|
189
|
+
def _classify_emotion_text(self, text: str, model_name: str) -> Dict[str, Any]:
|
|
174
190
|
import torch
|
|
191
|
+
|
|
192
|
+
model = model_name or DEFAULT_EMOTION_MODEL
|
|
175
193
|
emo_model, tokenizer = self._get_emotion_model(model)
|
|
176
194
|
inputs = tokenizer(
|
|
177
195
|
text,
|
|
@@ -201,35 +219,74 @@ class HuggingFaceAdapter:
|
|
|
201
219
|
"model": model,
|
|
202
220
|
}
|
|
203
221
|
|
|
222
|
+
def _run_emotion_classification(self, payload: Dict[str, Any], model_name: str) -> Dict[str, Any]:
|
|
223
|
+
text = payload.get("text") or payload.get("content") or ""
|
|
224
|
+
if not text or not isinstance(text, str):
|
|
225
|
+
return {
|
|
226
|
+
"error": "text or content required",
|
|
227
|
+
"emotion_label": None,
|
|
228
|
+
"confidence": None,
|
|
229
|
+
"all_emotions": [],
|
|
230
|
+
"model": model_name or DEFAULT_EMOTION_MODEL,
|
|
231
|
+
}
|
|
232
|
+
return self._classify_emotion_text(text, model_name)
|
|
233
|
+
|
|
234
|
+
def _run_emotion_classification_batch(self, payload: Dict[str, Any], model_name: str) -> Dict[str, Any]:
|
|
235
|
+
items = payload.get("items") or []
|
|
236
|
+
if not isinstance(items, list) or not items:
|
|
237
|
+
return {"error": "items required", "items": [], "model": model_name or DEFAULT_EMOTION_MODEL}
|
|
238
|
+
model = model_name or DEFAULT_EMOTION_MODEL
|
|
239
|
+
out_items: List[Dict[str, Any]] = []
|
|
240
|
+
for item in items:
|
|
241
|
+
text = str(item.get("text") or item.get("content") or "")
|
|
242
|
+
item_id = item.get("id") or item.get("message_id")
|
|
243
|
+
if not text:
|
|
244
|
+
out_items.append(
|
|
245
|
+
{
|
|
246
|
+
"id": item_id,
|
|
247
|
+
"emotion_label": None,
|
|
248
|
+
"confidence": None,
|
|
249
|
+
"all_emotions": [],
|
|
250
|
+
"model": model,
|
|
251
|
+
}
|
|
252
|
+
)
|
|
253
|
+
continue
|
|
254
|
+
result = self._classify_emotion_text(text, model)
|
|
255
|
+
out_items.append({"id": item_id, **result})
|
|
256
|
+
return {"items": out_items, "model": model}
|
|
257
|
+
|
|
204
258
|
def _get_ner_pipeline(self, model_name: str):
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
259
|
+
model = model_name or DEFAULT_NER_MODEL
|
|
260
|
+
|
|
261
|
+
def _load():
|
|
208
262
|
from transformers import pipeline
|
|
209
263
|
|
|
210
|
-
model =
|
|
211
|
-
|
|
212
|
-
|
|
264
|
+
return pipeline("ner", model=model, aggregation_strategy="simple")
|
|
265
|
+
|
|
266
|
+
handle, _ = get_model_cache().acquire(ModelSlot.NER, model, _load)
|
|
267
|
+
return handle
|
|
213
268
|
|
|
214
269
|
def _get_embedding_model(self, model_name: str):
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
270
|
+
model = model_name or DEFAULT_EMBEDDING_MODEL
|
|
271
|
+
|
|
272
|
+
def _load():
|
|
218
273
|
from sentence_transformers import SentenceTransformer
|
|
219
274
|
|
|
220
|
-
model
|
|
221
|
-
|
|
222
|
-
|
|
275
|
+
return SentenceTransformer(model)
|
|
276
|
+
|
|
277
|
+
handle, _ = get_model_cache().acquire(ModelSlot.EMBEDDING, model, _load)
|
|
278
|
+
return handle
|
|
223
279
|
|
|
224
280
|
def _get_sentiment_pipeline(self, model_name: str):
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
281
|
+
model = model_name or DEFAULT_SENTIMENT_MODEL
|
|
282
|
+
|
|
283
|
+
def _load():
|
|
228
284
|
from transformers import pipeline
|
|
229
285
|
|
|
230
|
-
model
|
|
231
|
-
|
|
232
|
-
|
|
286
|
+
return pipeline("sentiment-analysis", model=model)
|
|
287
|
+
|
|
288
|
+
handle, _ = get_model_cache().acquire(ModelSlot.SENTIMENT, model, _load)
|
|
289
|
+
return handle
|
|
233
290
|
|
|
234
291
|
def _run_entity_extraction(self, payload: Dict[str, Any], model_name: str) -> Dict[str, Any]:
|
|
235
292
|
text = payload.get("text") or payload.get("content") or ""
|
|
@@ -342,12 +399,12 @@ class HuggingFaceAdapter:
|
|
|
342
399
|
return self._run_emotion_classification(payload, DEFAULT_EMOTION_MODEL)
|
|
343
400
|
|
|
344
401
|
def unload_model(self, model_name: str) -> None:
|
|
345
|
-
"""
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
402
|
+
"""Evict cached pipeline/model for the given model name."""
|
|
403
|
+
slot = _match_slot(model_name)
|
|
404
|
+
if slot is not None:
|
|
405
|
+
get_model_cache().evict_slot(slot)
|
|
406
|
+
|
|
407
|
+
def unload_all(self) -> None:
|
|
408
|
+
for slot in ModelSlot:
|
|
409
|
+
if slot not in (ModelSlot.NSFW, ModelSlot.PRIVACY_FILTER):
|
|
410
|
+
get_model_cache().evict_slot(slot)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Engine client: in-process or remote HTTP execution."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib.util
|
|
6
|
+
from typing import Optional, Protocol
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
|
|
10
|
+
from .engine import Engine
|
|
11
|
+
from .tasks import ProcessingResult, ProcessingTask
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EngineClient(Protocol):
|
|
15
|
+
def run(self, task: ProcessingTask) -> ProcessingResult: ...
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class LocalEngineClient:
|
|
19
|
+
"""Runs tasks in-process via Engine.run()."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, engine: Optional[Engine] = None) -> None:
|
|
22
|
+
self._engine = engine or Engine()
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def engine(self) -> Engine:
|
|
26
|
+
return self._engine
|
|
27
|
+
|
|
28
|
+
def run(self, task: ProcessingTask) -> ProcessingResult:
|
|
29
|
+
return self._engine.run(task)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class RemoteEngineClient:
|
|
33
|
+
"""POST ProcessingTask JSON to a remote Topos Engine service."""
|
|
34
|
+
|
|
35
|
+
def __init__(self, base_url: str, *, api_key: Optional[str] = None, timeout_sec: float = 120.0) -> None:
|
|
36
|
+
self._base_url = base_url.rstrip("/")
|
|
37
|
+
self._api_key = (api_key or "").strip() or None
|
|
38
|
+
self._timeout_sec = timeout_sec
|
|
39
|
+
|
|
40
|
+
def run(self, task: ProcessingTask) -> ProcessingResult:
|
|
41
|
+
headers = {"Content-Type": "application/json"}
|
|
42
|
+
if self._api_key:
|
|
43
|
+
headers["Authorization"] = f"Bearer {self._api_key}"
|
|
44
|
+
body = task.model_dump(mode="json")
|
|
45
|
+
with httpx.Client(timeout=self._timeout_sec) as client:
|
|
46
|
+
resp = client.post(f"{self._base_url}/v1/engine/run", json=body, headers=headers)
|
|
47
|
+
resp.raise_for_status()
|
|
48
|
+
data = resp.json()
|
|
49
|
+
if isinstance(data, dict) and "result" in data:
|
|
50
|
+
data = data["result"]
|
|
51
|
+
return ProcessingResult.model_validate(data)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def ml_deps_available() -> bool:
|
|
55
|
+
return importlib.util.find_spec("torch") is not None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_engine_client(engine: Optional[Engine] = None) -> EngineClient:
|
|
59
|
+
"""Return remote client when configured or ML deps missing; else local."""
|
|
60
|
+
from ..config.settings import settings
|
|
61
|
+
|
|
62
|
+
remote_url = (getattr(settings, "topos_engine_service_url", None) or "").strip()
|
|
63
|
+
if remote_url:
|
|
64
|
+
return RemoteEngineClient(
|
|
65
|
+
remote_url,
|
|
66
|
+
api_key=getattr(settings, "topos_key", None),
|
|
67
|
+
)
|
|
68
|
+
if not ml_deps_available():
|
|
69
|
+
raise RuntimeError(
|
|
70
|
+
"ML dependencies unavailable and TOPOS_ENGINE_SERVICE_URL is not configured"
|
|
71
|
+
)
|
|
72
|
+
return LocalEngineClient(engine)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_engine_client_or_local(engine: Optional[Engine] = None) -> EngineClient:
|
|
76
|
+
"""Like get_engine_client but always falls back to local when torch is present."""
|
|
77
|
+
from ..config.settings import settings
|
|
78
|
+
|
|
79
|
+
remote_url = (getattr(settings, "topos_engine_service_url", None) or "").strip()
|
|
80
|
+
if remote_url:
|
|
81
|
+
return RemoteEngineClient(
|
|
82
|
+
remote_url,
|
|
83
|
+
api_key=getattr(settings, "topos_key", None),
|
|
84
|
+
)
|
|
85
|
+
return LocalEngineClient(engine)
|