typedef-data-intelligence 0.35.0__tar.gz → 0.36.0__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.
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/CHANGELOG.md +12 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/PKG-INFO +1 -1
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/pyproject.toml +1 -1
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/app.py +8 -4
- typedef_data_intelligence-0.36.0/src/lineage/observability/__init__.py +1 -0
- typedef_data_intelligence-0.36.0/src/lineage/observability/desktop_logfire.py +97 -0
- typedef_data_intelligence-0.36.0/src/lineage/observability/local_otlp_export.py +109 -0
- typedef_data_intelligence-0.36.0/src/lineage/observability/local_span_export.py +62 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/git.py +23 -5
- typedef_data_intelligence-0.36.0/tests/api/test_app_logfire.py +257 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_diagnostics.py +202 -3
- typedef_data_intelligence-0.36.0/tests/api/test_local_otlp_export.py +72 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/test_git_utils.py +17 -5
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/.dockerignore +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/.env.example +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/.gitignore +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/.justfile_help.txt +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/Dockerfile +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/Dockerfile.dev +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/Justfile +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/README.pypi.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/config.api.yml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/config.cli.yml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/config.desktop.yml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/config.ingest.unified.yml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/config.ingest.yml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docker-entrypoint.dev.sh +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/LINEAR_TICKETING_BACKEND.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/architecture/BUSINESS_CONCEPT_INDEX.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/architecture/CLUSTERING_ALGORITHM_DECISION.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/architecture/GENERIC_REPORT_SCHEMA.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/architecture/HYBRID_ANALYSIS_ARCHITECTURE.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/architecture/PHASE2_IR_STITCHING.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/architecture/PIPELINE_PRIMITIVES_USECASES.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/architecture/SALESFORCE_REPORT_IR.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/architecture/SEMANTIC_JOIN_RESOLUTION.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/architecture/SEMANTIC_SCHEMA_DESIGN.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/design/AGENT_DESIGN_NOTES.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/design/COLUMN_PROFILING.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/design/COLUMN_PROFILING_IMPLEMENTATION.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/design/EXTRACTION_DETECTION.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/design/PERFORMANCE_AND_QUALITY_SPECIALISTS.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/design/STREAMING_SUBAGENT_ARCHITECTURE.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/examples/memory_usage_examples.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/examples/pydantic_tool_customization_example.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/reference/AGENT_USER_GUIDE.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/reference/GETTING_STARTED.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/reference/MIGRATION_THREE_SQL.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/reference/SCOPE_LEVEL_LINEAGE_GUIDE.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/reference/SEMANTIC_DIFF_HARNESS.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/reference/SEMANTIC_DIMENSIONS_GUIDE.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/reference/TYPE_ENUMS.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/pytest.ini +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/create_salesforce_reports.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/data_generation/utils.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/databricks-smoke.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/delete_salesforce_reports.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/populate_salesforce_schema.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/reset_demo_git_workspace.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/reset_demo_linear.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/snowflake_server.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/surreal-engine-bench.sh +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/surreal-fts-b2-corpus.sh +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/surreal-fts-bench.sh +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/surreal-fts-quality.sh +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/snowflake-mcp-config.yaml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/auth_context.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/concept_cards.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/controlplane_identity.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_agents.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_context.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_logs.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_quickstart.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_runtime.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_semdiff_provision.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_setup.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_sync.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_wizard_sync.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_workspaces.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_worktree.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/graph_availability.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/posthog_analytics.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/semantic_runtime.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/serve.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/context_projects/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/context_projects/airflow_discovery.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/context_projects/dbt_plugin.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/backward_dryrun.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/clustering_metrics.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/drop_snowflake_tables.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/ephemeral_lineage_report.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/eval_clustering.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/list_ephemeral_models.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/refingerprint_cache.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/regen_semantic_goldens.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/reset_git_workspace.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/reset_linear_workspace.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/reset_project.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/run_dbt_workflow.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/seed_census.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/semantic_goldens.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/fts_bench.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/fts_cells.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/fts_corpus.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/fts_corpus_dump.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/fts_coverage.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/fts_coverage_report.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/fts_oracle.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/fts_quality.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/fts_quality_bench.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/log_parse.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/report.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/runner.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/README.md +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/cli.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/db_names.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/dbt_compile.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/diff_runner.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/expected_changes.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/column_rename_downstream/fixture.yaml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/column_rename_downstream/mutation.diff +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_coarsen/fixture.yaml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_coarsen/mutation.diff +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_deleted_leaf_model/fixture.yaml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_deleted_leaf_model/mutation.diff +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_fanout_compound_oli/fixture.yaml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_fanout_compound_oli/mutation.diff +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_gold_control/fixture.yaml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_gold_control/mutation.diff +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_v1_003_user_telemetry/fixture.yaml +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_v1_003_user_telemetry/mutation.diff +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures_loader.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/ingest.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/lifecycle.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/source_prep.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/surreal/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/surreal/desktop_mcp.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/unified_cli.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/mcp/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/mcp/runner.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/mcp/server.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/templates/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/templates/typedef_config.yaml.j2 +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/typedef_cli.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/databricks.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/databricks_oauth.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/dbt.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/dbt_profile.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/env.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/gitignore.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/logging.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/snowflake.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_app_lifespan.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_concept_cards.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_databricks_auth_wiring.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_agents_clone_backend.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_agents_runtime.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_agents_ticket_resolution.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_airflow_snapshot.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_clerk_auth.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_context_reader_cache.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_databases_delete.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_databases_edit.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_databricks_oauth.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_databricks_setup.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_delete_endpoints.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_logs.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_orphan_flags.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_posthog.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_quickstart.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_repo_projects_discovery.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_runtime.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_salesforce.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_save_env_credentials.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_search.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_secondary_roles_hook.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_semdiff_provision.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_setup.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_setup_clone.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_setup_env_vars.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_sync.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_sync_render_profile.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_telemetry.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_adopt.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_context_kinds.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_delete.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_gold_triggers.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_integrations.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_preview_name.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_projects_and_repos.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_r3_r10.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_summary.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_target_scope.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_wizard.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_worktree.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_worktree_clone.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_worktree_clone_token_scope.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_worktree_gold_queue.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_worktree_kind_target.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_worktree_resolution_scope.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_graph_availability_any_sentinel.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_no_legacy_surface.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_surreal_graph_availability.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_target_envelope_reads.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_validate_project_collisions.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_wizard_create_project_integration_persistence.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_workspace_clone_settings.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_worktree_registry_workspaces_shape.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/backends/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/backends/lineage/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/backends/lineage/test_search_enum_coverage.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/backends/lineage/test_surreal_search.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/conftest.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/devtools/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/devtools/surreal_bench/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/devtools/surreal_bench/test_fts_cells.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/devtools/surreal_bench/test_fts_coverage.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/devtools/surreal_bench/test_fts_quality.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/devtools/surreal_bench/test_log_parse.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/devtools/surreal_bench/test_report.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/devtools/test_typedef_home_provision.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/devtools/test_worktree_reclaim.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/docs/test_desktop_posthog_event_docs.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/integration/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/integration/test_semantic_diff_harness.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/mcp/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/mcp/test_desktop_mcp_retry.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/mcp/test_list_projects.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/mcp/test_server.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/test_gitignore_utils.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/test_helpers_semantic.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/test_model_tier_validation.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/test_template_yaml_escaping.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/test_typedef_cli_import_budget.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/test_unified_cli_airflow.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/test_unified_cli_fast.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/test_unified_cli_semantic.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/context_projects/test_airflow_discovery.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/context_projects/test_dbt_plugin.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/devtools/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/devtools/test_semdiff_expected_changes.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/devtools/test_semdiff_harness_units.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/devtools/test_semdiff_lifecycle.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/devtools/test_semdiff_lifecycle_io.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/devtools/test_semdiff_lifecycle_provisioning.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/formula/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/utils/__init__.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/utils/test_databricks_utils.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/utils/test_dbt_docs_generate.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/utils/test_dbt_profile.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/utils/test_logging.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/utils/test_run_dbt_command.py +0 -0
- {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/utils/test_snowflake_utils.py +0 -0
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.36.0](https://github.com/typedef-ai/typedef/compare/typedef-data-intelligence-v0.35.0...typedef-data-intelligence-v0.36.0) (2026-07-07)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **observability:** enable local desktop span export (TD-3805) ([#1114](https://github.com/typedef-ai/typedef/issues/1114)) ([32cf196](https://github.com/typedef-ai/typedef/commit/32cf196272f658a639ef3d6ae9ce3599c79078eb))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **observability:** classify + clean customer data in retained/exported logs (TD-3825) ([#1107](https://github.com/typedef-ai/typedef/issues/1107)) ([cac553d](https://github.com/typedef-ai/typedef/commit/cac553d50a84275a734d198baca25a7290c6ab70))
|
|
14
|
+
|
|
3
15
|
## [0.35.0](https://github.com/typedef-ai/typedef/compare/typedef-data-intelligence-v0.34.0...typedef-data-intelligence-v0.35.0) (2026-07-06)
|
|
4
16
|
|
|
5
17
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: typedef-data-intelligence
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.36.0
|
|
4
4
|
Summary: Unified lineage management and AI-powered analytics platform for dbt projects
|
|
5
5
|
Project-URL: Homepage, https://typedef.ai
|
|
6
6
|
Project-URL: Repository, https://github.com/typedef-ai/data-intelligence
|
{typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/app.py
RENAMED
|
@@ -8,7 +8,6 @@ from dataclasses import dataclass
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Any, Optional
|
|
10
10
|
|
|
11
|
-
import logfire
|
|
12
11
|
import yaml
|
|
13
12
|
from agent.api import shutdown_event
|
|
14
13
|
from agent.api.profiles import (
|
|
@@ -37,6 +36,10 @@ from core.backends.unified_graph.surreal.reader import SurrealUnifiedGraphReader
|
|
|
37
36
|
from fastapi import FastAPI, HTTPException, Request
|
|
38
37
|
from fastapi.middleware.cors import CORSMiddleware
|
|
39
38
|
from lineage.api.auth_context import resolve_user_context
|
|
39
|
+
from lineage.observability.desktop_logfire import (
|
|
40
|
+
configure_desktop_logfire,
|
|
41
|
+
shutdown_desktop_logfire,
|
|
42
|
+
)
|
|
40
43
|
from lineage.utils.env import load_env_file
|
|
41
44
|
from pydantic import ValidationError
|
|
42
45
|
|
|
@@ -396,9 +399,9 @@ async def lifespan(app: FastAPI):
|
|
|
396
399
|
# If user opts out via the consent modal or Settings, takes effect on next launch.
|
|
397
400
|
from lineage.api.desktop_setup import _read_telemetry_prefs
|
|
398
401
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
+
configure_desktop_logfire(
|
|
403
|
+
telemetry_opted_out=bool(_read_telemetry_prefs().get("telemetry_opted_out"))
|
|
404
|
+
)
|
|
402
405
|
|
|
403
406
|
# Startup: no maintained API route initializes the legacy state path.
|
|
404
407
|
if _NEEDS_LEGACY_STATE:
|
|
@@ -759,6 +762,7 @@ async def lifespan(app: FastAPI):
|
|
|
759
762
|
shutdown_shared_posthog_client()
|
|
760
763
|
except Exception:
|
|
761
764
|
logger.debug("Failed to shut down PostHog analytics client", exc_info=True)
|
|
765
|
+
shutdown_desktop_logfire()
|
|
762
766
|
|
|
763
767
|
# Close standalone data backend (e.g. DuckDB file handle)
|
|
764
768
|
standalone_db = getattr(app.state, "standalone_data_backend", None)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Local observability helpers for the desktop backend."""
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Desktop Logfire setup with local trace export processors."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
from typing import Final
|
|
8
|
+
|
|
9
|
+
import logfire
|
|
10
|
+
from opentelemetry.sdk.trace import SpanProcessor
|
|
11
|
+
|
|
12
|
+
from lineage.observability.local_otlp_export import make_local_otlp_span_processor
|
|
13
|
+
from lineage.observability.local_span_export import make_local_span_processor
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
_OTEL_EXPORTER_ENV_KEYS: Final = (
|
|
18
|
+
"OTEL_TRACES_EXPORTER",
|
|
19
|
+
"OTEL_METRICS_EXPORTER",
|
|
20
|
+
"OTEL_LOGS_EXPORTER",
|
|
21
|
+
)
|
|
22
|
+
_OTEL_REMOTE_EXPORT_ENV_KEYS: Final = (
|
|
23
|
+
*_OTEL_EXPORTER_ENV_KEYS,
|
|
24
|
+
"OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
25
|
+
"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",
|
|
26
|
+
"OTEL_EXPORTER_OTLP_METRICS_ENDPOINT",
|
|
27
|
+
"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT",
|
|
28
|
+
)
|
|
29
|
+
_local_only_previous_otel_exporters: dict[str, str | None] | None = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def configure_desktop_logfire(*, telemetry_opted_out: bool) -> None:
|
|
33
|
+
"""Configure cloud-gated Logfire and local desktop span processors."""
|
|
34
|
+
logfire_token = os.getenv("LOGFIRE_TOKEN") or None
|
|
35
|
+
span_processors = _desktop_span_processors()
|
|
36
|
+
send_to_logfire = logfire_token is not None and not telemetry_opted_out
|
|
37
|
+
if send_to_logfire:
|
|
38
|
+
logfire.configure(
|
|
39
|
+
token=logfire_token,
|
|
40
|
+
send_to_logfire=True,
|
|
41
|
+
console=False,
|
|
42
|
+
additional_span_processors=span_processors,
|
|
43
|
+
)
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
_disable_otel_exporters_for_local_only()
|
|
47
|
+
logfire.configure(
|
|
48
|
+
token=None,
|
|
49
|
+
send_to_logfire=False,
|
|
50
|
+
metrics=False,
|
|
51
|
+
console=False,
|
|
52
|
+
additional_span_processors=span_processors,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def shutdown_desktop_logfire() -> None:
|
|
57
|
+
"""Flush configured Logfire processors and restore local-only OTEL env changes."""
|
|
58
|
+
try:
|
|
59
|
+
logfire.shutdown()
|
|
60
|
+
except Exception:
|
|
61
|
+
logger.debug("Failed to shut down Logfire", exc_info=True)
|
|
62
|
+
finally:
|
|
63
|
+
_restore_local_only_otel_exporters()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _desktop_span_processors() -> list[SpanProcessor]:
|
|
67
|
+
processors: list[SpanProcessor] = [make_local_span_processor()]
|
|
68
|
+
local_otlp_processor = make_local_otlp_span_processor()
|
|
69
|
+
if local_otlp_processor is not None:
|
|
70
|
+
processors.append(local_otlp_processor)
|
|
71
|
+
return processors
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _disable_otel_exporters_for_local_only() -> None:
|
|
75
|
+
global _local_only_previous_otel_exporters
|
|
76
|
+
if _local_only_previous_otel_exporters is not None:
|
|
77
|
+
_restore_local_only_otel_exporters()
|
|
78
|
+
if not any(os.getenv(key) for key in _OTEL_REMOTE_EXPORT_ENV_KEYS):
|
|
79
|
+
return
|
|
80
|
+
_local_only_previous_otel_exporters = {
|
|
81
|
+
key: os.environ.get(key) for key in _OTEL_EXPORTER_ENV_KEYS
|
|
82
|
+
}
|
|
83
|
+
for key in _OTEL_EXPORTER_ENV_KEYS:
|
|
84
|
+
os.environ[key] = "none"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _restore_local_only_otel_exporters() -> None:
|
|
88
|
+
global _local_only_previous_otel_exporters
|
|
89
|
+
previous_exporters = _local_only_previous_otel_exporters
|
|
90
|
+
if previous_exporters is None:
|
|
91
|
+
return
|
|
92
|
+
for key, value in previous_exporters.items():
|
|
93
|
+
if value is None:
|
|
94
|
+
os.environ.pop(key, None)
|
|
95
|
+
else:
|
|
96
|
+
os.environ[key] = value
|
|
97
|
+
_local_only_previous_otel_exporters = None
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Local OTLP/HTTP export for explicitly enabled desktop development traces."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import ipaddress
|
|
6
|
+
import os
|
|
7
|
+
from typing import Final
|
|
8
|
+
from urllib.parse import SplitResult, urlsplit, urlunsplit
|
|
9
|
+
|
|
10
|
+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
11
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
12
|
+
|
|
13
|
+
_LOCAL_OTLP_EXPORT_ENV: Final = "TYPEDEF_ENABLE_LOCAL_OTLP_EXPORT"
|
|
14
|
+
_GENERIC_ENDPOINT_ENV: Final = "OTEL_EXPORTER_OTLP_ENDPOINT"
|
|
15
|
+
_TRACES_ENDPOINT_ENV: Final = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"
|
|
16
|
+
_GENERIC_PROTOCOL_ENV: Final = "OTEL_EXPORTER_OTLP_PROTOCOL"
|
|
17
|
+
_TRACES_PROTOCOL_ENV: Final = "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL"
|
|
18
|
+
_DEFAULT_TRACES_ENDPOINT: Final = "http://127.0.0.1:4318/v1/traces"
|
|
19
|
+
_TRACES_PATH: Final = "/v1/traces"
|
|
20
|
+
_HTTP_PROTOBUF_PROTOCOL: Final = "http/protobuf"
|
|
21
|
+
_ENABLED_VALUES: Final = frozenset({"1", "true", "yes", "on"})
|
|
22
|
+
_LOOPBACK_HOSTNAMES: Final = frozenset({"localhost"})
|
|
23
|
+
_EXPORT_TIMEOUT_SECONDS: Final = 2.0
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def resolve_local_otlp_traces_endpoint() -> str | None:
|
|
27
|
+
"""Resolve a loopback OTLP/HTTP traces endpoint from development env config."""
|
|
28
|
+
if not _local_otlp_export_enabled():
|
|
29
|
+
return None
|
|
30
|
+
if _resolved_protocol() != _HTTP_PROTOBUF_PROTOCOL:
|
|
31
|
+
return None
|
|
32
|
+
traces_endpoint = _clean_env(_TRACES_ENDPOINT_ENV)
|
|
33
|
+
if traces_endpoint is not None:
|
|
34
|
+
return _validated_loopback_endpoint(traces_endpoint)
|
|
35
|
+
generic_endpoint = _clean_env(_GENERIC_ENDPOINT_ENV)
|
|
36
|
+
if generic_endpoint is None:
|
|
37
|
+
return _DEFAULT_TRACES_ENDPOINT
|
|
38
|
+
return _validated_loopback_endpoint(_with_traces_path(generic_endpoint))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def make_local_otlp_span_processor() -> BatchSpanProcessor | None:
|
|
42
|
+
"""Create a local OTLP/HTTP span processor when development export is enabled."""
|
|
43
|
+
endpoint = resolve_local_otlp_traces_endpoint()
|
|
44
|
+
if endpoint is None:
|
|
45
|
+
return None
|
|
46
|
+
return BatchSpanProcessor(
|
|
47
|
+
OTLPSpanExporter(endpoint=endpoint, timeout=_EXPORT_TIMEOUT_SECONDS)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _local_otlp_export_enabled() -> bool:
|
|
52
|
+
value = _clean_env(_LOCAL_OTLP_EXPORT_ENV)
|
|
53
|
+
return value is not None and value.lower() in _ENABLED_VALUES
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _resolved_protocol() -> str:
|
|
57
|
+
traces_protocol = _clean_env(_TRACES_PROTOCOL_ENV)
|
|
58
|
+
if traces_protocol is not None:
|
|
59
|
+
return traces_protocol.lower()
|
|
60
|
+
generic_protocol = _clean_env(_GENERIC_PROTOCOL_ENV)
|
|
61
|
+
if generic_protocol is not None:
|
|
62
|
+
return generic_protocol.lower()
|
|
63
|
+
return _HTTP_PROTOBUF_PROTOCOL
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _clean_env(key: str) -> str | None:
|
|
67
|
+
value = os.getenv(key)
|
|
68
|
+
if value is None:
|
|
69
|
+
return None
|
|
70
|
+
clean_value = value.strip()
|
|
71
|
+
if clean_value:
|
|
72
|
+
return clean_value
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _with_traces_path(endpoint: str) -> str:
|
|
77
|
+
parsed = urlsplit(endpoint)
|
|
78
|
+
clean_path = parsed.path.rstrip("/")
|
|
79
|
+
if clean_path.endswith(_TRACES_PATH):
|
|
80
|
+
return endpoint
|
|
81
|
+
traces_path = f"{clean_path}{_TRACES_PATH}" if clean_path else _TRACES_PATH
|
|
82
|
+
return urlunsplit(
|
|
83
|
+
SplitResult(
|
|
84
|
+
scheme=parsed.scheme,
|
|
85
|
+
netloc=parsed.netloc,
|
|
86
|
+
path=traces_path,
|
|
87
|
+
query=parsed.query,
|
|
88
|
+
fragment=parsed.fragment,
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _validated_loopback_endpoint(endpoint: str) -> str | None:
|
|
94
|
+
parsed = urlsplit(endpoint)
|
|
95
|
+
if parsed.scheme not in {"http", "https"}:
|
|
96
|
+
return None
|
|
97
|
+
if parsed.username is not None or parsed.password is not None:
|
|
98
|
+
return None
|
|
99
|
+
if parsed.query or parsed.fragment:
|
|
100
|
+
return None
|
|
101
|
+
hostname = parsed.hostname
|
|
102
|
+
if hostname is None:
|
|
103
|
+
return None
|
|
104
|
+
if hostname.lower() in _LOOPBACK_HOSTNAMES:
|
|
105
|
+
return endpoint
|
|
106
|
+
try:
|
|
107
|
+
return endpoint if ipaddress.ip_address(hostname).is_loopback else None
|
|
108
|
+
except ValueError:
|
|
109
|
+
return None
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""OpenTelemetry span export for local desktop traces."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import threading
|
|
7
|
+
import time
|
|
8
|
+
from collections.abc import Sequence
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Final
|
|
11
|
+
|
|
12
|
+
from core.paths import logs_dir
|
|
13
|
+
from opentelemetry.sdk.trace.export import (
|
|
14
|
+
BatchSpanProcessor,
|
|
15
|
+
ReadableSpan,
|
|
16
|
+
SpanExporter,
|
|
17
|
+
SpanExportResult,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
_TRACE_FILE_PREFIX: Final = "spans"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class NdjsonFileSpanExporter(SpanExporter):
|
|
24
|
+
"""Write OpenTelemetry spans to a per-process NDJSON file."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, traces_dir: Path) -> None:
|
|
27
|
+
"""Create an exporter rooted at the configured traces directory."""
|
|
28
|
+
self._traces_dir = traces_dir
|
|
29
|
+
self._path = (
|
|
30
|
+
traces_dir / f"{_TRACE_FILE_PREFIX}-{os.getpid()}-{time.time_ns()}.ndjson"
|
|
31
|
+
)
|
|
32
|
+
self._lock = threading.Lock()
|
|
33
|
+
self._closed = False
|
|
34
|
+
|
|
35
|
+
def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
|
|
36
|
+
"""Append spans as one JSON object per line."""
|
|
37
|
+
with self._lock:
|
|
38
|
+
if self._closed:
|
|
39
|
+
return SpanExportResult.FAILURE
|
|
40
|
+
try:
|
|
41
|
+
self._traces_dir.mkdir(parents=True, exist_ok=True)
|
|
42
|
+
with self._path.open("a", encoding="utf-8") as trace_file:
|
|
43
|
+
for span in spans:
|
|
44
|
+
trace_file.write(span.to_json(indent=None))
|
|
45
|
+
trace_file.write("\n")
|
|
46
|
+
except (OSError, TypeError, ValueError):
|
|
47
|
+
return SpanExportResult.FAILURE
|
|
48
|
+
return SpanExportResult.SUCCESS
|
|
49
|
+
|
|
50
|
+
def shutdown(self) -> None:
|
|
51
|
+
"""Stop accepting new spans."""
|
|
52
|
+
with self._lock:
|
|
53
|
+
self._closed = True
|
|
54
|
+
|
|
55
|
+
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
56
|
+
"""Report that file writes are already flushed."""
|
|
57
|
+
return True
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def make_local_span_processor() -> BatchSpanProcessor:
|
|
61
|
+
"""Create the always-on local span processor for desktop startup."""
|
|
62
|
+
return BatchSpanProcessor(NdjsonFileSpanExporter(logs_dir() / "traces"))
|
{typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/git.py
RENAMED
|
@@ -10,6 +10,8 @@ from collections.abc import AsyncIterator
|
|
|
10
10
|
from dataclasses import dataclass
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
|
|
13
|
+
from core.models.observability import sanitize_diagnostic_text
|
|
14
|
+
|
|
13
15
|
logger = logging.getLogger(__name__)
|
|
14
16
|
|
|
15
17
|
|
|
@@ -69,7 +71,6 @@ class CloneProgress:
|
|
|
69
71
|
# Regex to parse git progress lines like:
|
|
70
72
|
# "Receiving objects: 67% (835/1247)"
|
|
71
73
|
_PROGRESS_RE = re.compile(r"(Counting|Compressing|Receiving|Resolving)[^:]*:\s*(\d+)%")
|
|
72
|
-
_URL_CREDENTIALS_RE = re.compile(r"(?P<scheme>https?://)(?:[^/\s@]+(?::[^/\s@]*)?@)")
|
|
73
74
|
|
|
74
75
|
|
|
75
76
|
def _handle_clone_stderr_line(
|
|
@@ -91,19 +92,36 @@ def _handle_clone_stderr_line(
|
|
|
91
92
|
|
|
92
93
|
|
|
93
94
|
def _build_clone_failure_message(stderr_lines: list[str], exit_code: int) -> str:
|
|
94
|
-
"""Return the
|
|
95
|
+
"""Return the clone failure text to surface to callers.
|
|
96
|
+
|
|
97
|
+
Credential shapes are stripped here (TD-3825) so every downstream consumer
|
|
98
|
+
inherits clean text: the raised ``RuntimeError``, the error ``CloneProgress``
|
|
99
|
+
event, the ``logger.debug`` full-stderr line, and anything that logs the
|
|
100
|
+
exception into ``sync.log`` / ``quickstart.log`` / ``backend-stdout.log``.
|
|
101
|
+
A token-bearing remote URL (``https://TOKEN@host/repo.git``) never reaches
|
|
102
|
+
disk from this path.
|
|
103
|
+
"""
|
|
95
104
|
stderr_detail = "\n".join(line.strip() for line in stderr_lines if line.strip())
|
|
96
|
-
|
|
105
|
+
if not stderr_detail:
|
|
106
|
+
return f"git clone failed (exit {exit_code})"
|
|
107
|
+
sanitized, _ = sanitize_diagnostic_text(stderr_detail)
|
|
108
|
+
return sanitized
|
|
97
109
|
|
|
98
110
|
|
|
99
111
|
def _build_clone_failure_log_summary(stderr_message: str, exit_code: int) -> str:
|
|
100
|
-
"""Return a
|
|
112
|
+
"""Return a one-line summary safe for error logs.
|
|
113
|
+
|
|
114
|
+
``stderr_message`` is already credential-stripped by
|
|
115
|
+
:func:`_build_clone_failure_message`; this re-runs the sanitizer defensively
|
|
116
|
+
in case a caller passes raw text.
|
|
117
|
+
"""
|
|
101
118
|
first_line = next(
|
|
102
119
|
(line.strip() for line in stderr_message.splitlines() if line.strip()), ""
|
|
103
120
|
)
|
|
104
121
|
if not first_line:
|
|
105
122
|
return f"git clone failed (exit {exit_code})"
|
|
106
|
-
|
|
123
|
+
sanitized, _ = sanitize_diagnostic_text(first_line)
|
|
124
|
+
return sanitized
|
|
107
125
|
|
|
108
126
|
|
|
109
127
|
async def clone_repo_with_progress(
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from collections.abc import Iterator, Sequence
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import NotRequired, TypedDict, Unpack
|
|
8
|
+
|
|
9
|
+
import core.paths as core_paths
|
|
10
|
+
import pytest
|
|
11
|
+
from opentelemetry.context import Context
|
|
12
|
+
from opentelemetry.sdk.trace import ReadableSpan, SpanProcessor, TracerProvider
|
|
13
|
+
from opentelemetry.sdk.trace.export import SpanExportResult
|
|
14
|
+
from opentelemetry.trace import Span
|
|
15
|
+
|
|
16
|
+
_OTEL_TEST_ENV_KEYS = (
|
|
17
|
+
"TYPEDEF_ENABLE_LOCAL_OTLP_EXPORT",
|
|
18
|
+
"OTEL_EXPORTER_OTLP_PROTOCOL",
|
|
19
|
+
"OTEL_EXPORTER_OTLP_TRACES_PROTOCOL",
|
|
20
|
+
"OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
21
|
+
"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",
|
|
22
|
+
"OTEL_TRACES_EXPORTER",
|
|
23
|
+
"OTEL_METRICS_EXPORTER",
|
|
24
|
+
"OTEL_LOGS_EXPORTER",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class LogfireConfigureCall(TypedDict):
|
|
29
|
+
additional_span_processors: Sequence[SpanProcessor]
|
|
30
|
+
console: bool
|
|
31
|
+
send_to_logfire: bool
|
|
32
|
+
token: NotRequired[str | None]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest.fixture(autouse=True)
|
|
36
|
+
def restore_desktop_logfire_env(
|
|
37
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
38
|
+
) -> Iterator[None]:
|
|
39
|
+
from lineage.observability import desktop_logfire
|
|
40
|
+
|
|
41
|
+
desktop_logfire._restore_local_only_otel_exporters()
|
|
42
|
+
for key in _OTEL_TEST_ENV_KEYS:
|
|
43
|
+
monkeypatch.delenv(key, raising=False)
|
|
44
|
+
yield
|
|
45
|
+
desktop_logfire._restore_local_only_otel_exporters()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _set_typedef_home(monkeypatch, tmp_path: Path) -> Path:
|
|
49
|
+
home = tmp_path / "typedef-home"
|
|
50
|
+
monkeypatch.setenv("TYPEDEF_DEV_MODE", "1")
|
|
51
|
+
monkeypatch.setenv("TYPEDEF_HOME", str(home))
|
|
52
|
+
core_paths.typedef_home.cache_clear()
|
|
53
|
+
return home
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _capture_logfire_configure(monkeypatch) -> list[LogfireConfigureCall]:
|
|
57
|
+
from lineage.observability import desktop_logfire
|
|
58
|
+
|
|
59
|
+
calls: list[LogfireConfigureCall] = []
|
|
60
|
+
|
|
61
|
+
def fake_configure(**kwargs: Unpack[LogfireConfigureCall]) -> None:
|
|
62
|
+
calls.append(kwargs)
|
|
63
|
+
|
|
64
|
+
monkeypatch.setattr(desktop_logfire.logfire, "configure", fake_configure)
|
|
65
|
+
return calls
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class RecordingSpanProcessor(SpanProcessor):
|
|
69
|
+
def on_start(self, span: Span, parent_context: Context | None = None) -> None:
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
def on_end(self, span: ReadableSpan) -> None:
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
def shutdown(self) -> None:
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
79
|
+
return True
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class CapturingSpanProcessor(SpanProcessor):
|
|
83
|
+
def __init__(self) -> None:
|
|
84
|
+
self.spans: list[ReadableSpan] = []
|
|
85
|
+
|
|
86
|
+
def on_start(self, span: Span, parent_context: Context | None = None) -> None:
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
def on_end(self, span: ReadableSpan) -> None:
|
|
90
|
+
self.spans.append(span)
|
|
91
|
+
|
|
92
|
+
def shutdown(self) -> None:
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
96
|
+
return True
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_ndjson_file_span_exporter_writes_sdk_span(tmp_path: Path) -> None:
|
|
100
|
+
from lineage.observability.local_span_export import NdjsonFileSpanExporter
|
|
101
|
+
|
|
102
|
+
processor = CapturingSpanProcessor()
|
|
103
|
+
provider = TracerProvider()
|
|
104
|
+
provider.add_span_processor(processor)
|
|
105
|
+
tracer = provider.get_tracer(__name__)
|
|
106
|
+
|
|
107
|
+
with tracer.start_as_current_span("typedef-test-span"):
|
|
108
|
+
pass
|
|
109
|
+
|
|
110
|
+
traces_dir = tmp_path / "traces"
|
|
111
|
+
exporter = NdjsonFileSpanExporter(traces_dir)
|
|
112
|
+
|
|
113
|
+
assert exporter.export(processor.spans) is SpanExportResult.SUCCESS
|
|
114
|
+
trace_files = list(traces_dir.glob("spans-*.ndjson"))
|
|
115
|
+
assert len(trace_files) == 1
|
|
116
|
+
lines = trace_files[0].read_text(encoding="utf-8").splitlines()
|
|
117
|
+
assert len(lines) == 1
|
|
118
|
+
assert json.loads(lines[0])["name"] == "typedef-test-span"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_configures_local_span_export_when_logfire_token_is_absent(
|
|
122
|
+
monkeypatch,
|
|
123
|
+
tmp_path: Path,
|
|
124
|
+
) -> None:
|
|
125
|
+
from lineage.observability import desktop_logfire
|
|
126
|
+
|
|
127
|
+
_set_typedef_home(monkeypatch, tmp_path)
|
|
128
|
+
monkeypatch.delenv("LOGFIRE_TOKEN", raising=False)
|
|
129
|
+
calls = _capture_logfire_configure(monkeypatch)
|
|
130
|
+
|
|
131
|
+
desktop_logfire.configure_desktop_logfire(telemetry_opted_out=False)
|
|
132
|
+
|
|
133
|
+
assert len(calls) == 1
|
|
134
|
+
assert calls[0]["send_to_logfire"] is False
|
|
135
|
+
assert calls[0]["token"] is None
|
|
136
|
+
assert calls[0]["console"] is False
|
|
137
|
+
assert len(calls[0]["additional_span_processors"]) == 1
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_local_only_configuration_suppresses_env_otlp_exporters(
|
|
141
|
+
monkeypatch,
|
|
142
|
+
tmp_path: Path,
|
|
143
|
+
) -> None:
|
|
144
|
+
from lineage.observability import desktop_logfire
|
|
145
|
+
|
|
146
|
+
_set_typedef_home(monkeypatch, tmp_path)
|
|
147
|
+
monkeypatch.delenv("LOGFIRE_TOKEN", raising=False)
|
|
148
|
+
monkeypatch.setenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", "https://otel.example")
|
|
149
|
+
monkeypatch.setenv("OTEL_TRACES_EXPORTER", "otlp")
|
|
150
|
+
monkeypatch.setenv("OTEL_METRICS_EXPORTER", "otlp")
|
|
151
|
+
monkeypatch.setenv("OTEL_LOGS_EXPORTER", "otlp")
|
|
152
|
+
observed_exporters: list[tuple[str | None, str | None, str | None]] = []
|
|
153
|
+
|
|
154
|
+
def fake_configure(**kwargs: Unpack[LogfireConfigureCall]) -> None:
|
|
155
|
+
observed_exporters.append(
|
|
156
|
+
(
|
|
157
|
+
os.getenv("OTEL_TRACES_EXPORTER"),
|
|
158
|
+
os.getenv("OTEL_METRICS_EXPORTER"),
|
|
159
|
+
os.getenv("OTEL_LOGS_EXPORTER"),
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
monkeypatch.setattr(desktop_logfire.logfire, "configure", fake_configure)
|
|
164
|
+
monkeypatch.setattr(desktop_logfire.logfire, "shutdown", lambda: None)
|
|
165
|
+
|
|
166
|
+
desktop_logfire.configure_desktop_logfire(telemetry_opted_out=False)
|
|
167
|
+
|
|
168
|
+
assert observed_exporters == [("none", "none", "none")]
|
|
169
|
+
assert os.getenv("OTEL_TRACES_EXPORTER") == "none"
|
|
170
|
+
assert os.getenv("OTEL_METRICS_EXPORTER") == "none"
|
|
171
|
+
assert os.getenv("OTEL_LOGS_EXPORTER") == "none"
|
|
172
|
+
|
|
173
|
+
desktop_logfire.shutdown_desktop_logfire()
|
|
174
|
+
|
|
175
|
+
assert os.getenv("OTEL_TRACES_EXPORTER") == "otlp"
|
|
176
|
+
assert os.getenv("OTEL_METRICS_EXPORTER") == "otlp"
|
|
177
|
+
assert os.getenv("OTEL_LOGS_EXPORTER") == "otlp"
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def test_local_only_configuration_adds_explicit_local_otlp_processor(
|
|
181
|
+
monkeypatch,
|
|
182
|
+
tmp_path: Path,
|
|
183
|
+
) -> None:
|
|
184
|
+
from lineage.observability import desktop_logfire
|
|
185
|
+
|
|
186
|
+
_set_typedef_home(monkeypatch, tmp_path)
|
|
187
|
+
local_otlp_processor = RecordingSpanProcessor()
|
|
188
|
+
monkeypatch.delenv("LOGFIRE_TOKEN", raising=False)
|
|
189
|
+
monkeypatch.setenv("TYPEDEF_ENABLE_LOCAL_OTLP_EXPORT", "1")
|
|
190
|
+
monkeypatch.setenv("OTEL_EXPORTER_OTLP_ENDPOINT", "http://127.0.0.1:4318")
|
|
191
|
+
monkeypatch.setenv("OTEL_TRACES_EXPORTER", "otlp")
|
|
192
|
+
monkeypatch.setattr(
|
|
193
|
+
desktop_logfire,
|
|
194
|
+
"make_local_otlp_span_processor",
|
|
195
|
+
lambda: local_otlp_processor,
|
|
196
|
+
)
|
|
197
|
+
calls = _capture_logfire_configure(monkeypatch)
|
|
198
|
+
|
|
199
|
+
desktop_logfire.configure_desktop_logfire(telemetry_opted_out=False)
|
|
200
|
+
|
|
201
|
+
assert len(calls) == 1
|
|
202
|
+
assert calls[0]["send_to_logfire"] is False
|
|
203
|
+
assert calls[0]["additional_span_processors"][1] is local_otlp_processor
|
|
204
|
+
assert os.getenv("OTEL_TRACES_EXPORTER") == "none"
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def test_configures_cloud_and_local_span_export_with_token_and_consent(
|
|
208
|
+
monkeypatch,
|
|
209
|
+
tmp_path: Path,
|
|
210
|
+
) -> None:
|
|
211
|
+
from lineage.observability import desktop_logfire
|
|
212
|
+
|
|
213
|
+
_set_typedef_home(monkeypatch, tmp_path)
|
|
214
|
+
monkeypatch.setenv("LOGFIRE_TOKEN", "test-token")
|
|
215
|
+
calls = _capture_logfire_configure(monkeypatch)
|
|
216
|
+
|
|
217
|
+
desktop_logfire.configure_desktop_logfire(telemetry_opted_out=False)
|
|
218
|
+
|
|
219
|
+
assert len(calls) == 1
|
|
220
|
+
assert calls[0]["send_to_logfire"] is True
|
|
221
|
+
assert calls[0]["token"] == "test-token"
|
|
222
|
+
assert calls[0]["console"] is False
|
|
223
|
+
assert len(calls[0]["additional_span_processors"]) == 1
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def test_configures_local_only_when_token_exists_but_user_opted_out(
|
|
227
|
+
monkeypatch,
|
|
228
|
+
tmp_path: Path,
|
|
229
|
+
) -> None:
|
|
230
|
+
from lineage.observability import desktop_logfire
|
|
231
|
+
|
|
232
|
+
_set_typedef_home(monkeypatch, tmp_path)
|
|
233
|
+
monkeypatch.setenv("LOGFIRE_TOKEN", "test-token")
|
|
234
|
+
calls = _capture_logfire_configure(monkeypatch)
|
|
235
|
+
|
|
236
|
+
desktop_logfire.configure_desktop_logfire(telemetry_opted_out=True)
|
|
237
|
+
|
|
238
|
+
assert len(calls) == 1
|
|
239
|
+
assert calls[0]["send_to_logfire"] is False
|
|
240
|
+
assert calls[0]["token"] is None
|
|
241
|
+
assert calls[0]["console"] is False
|
|
242
|
+
assert len(calls[0]["additional_span_processors"]) == 1
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def test_shutdown_flushes_desktop_logfire(monkeypatch) -> None:
|
|
246
|
+
from lineage.observability import desktop_logfire
|
|
247
|
+
|
|
248
|
+
shutdown_calls: list[bool] = []
|
|
249
|
+
|
|
250
|
+
def fake_shutdown() -> None:
|
|
251
|
+
shutdown_calls.append(True)
|
|
252
|
+
|
|
253
|
+
monkeypatch.setattr(desktop_logfire.logfire, "shutdown", fake_shutdown)
|
|
254
|
+
|
|
255
|
+
desktop_logfire.shutdown_desktop_logfire()
|
|
256
|
+
|
|
257
|
+
assert shutdown_calls == [True]
|