Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
4bc0ade
feat(gfql): add WHERE clause and df_executor (stacked PR)
lmeyerov Jan 9, 2026
f63f52a
fix(tests): skip oracle tests for multi-hop + WHERE limitations
lmeyerov Jan 9, 2026
e746ee4
docs(changelog): add WHERE feature and bugfix entries for PR 886
lmeyerov Jan 9, 2026
591f7ec
test(gfql): restore df_executor profiling scripts
lmeyerov Jan 9, 2026
30222e4
fix(enumerator): restore source/destination_node_match filter support
lmeyerov Jan 9, 2026
c9bd868
fix(enumerator): restore full pre-split functionality and remove test…
lmeyerov Jan 9, 2026
3750030
docs(changelog): restore missing cuDF same-path and test entries
lmeyerov Jan 9, 2026
efb07d1
docs(changelog): restore from_json where validation fix entry
lmeyerov Jan 9, 2026
20975a7
docs(changelog): move WHERE entries to Development section
lmeyerov Jan 9, 2026
bedd980
fix(df_executor): fix off-by-one in _bfs_reachability max_hops
lmeyerov Jan 10, 2026
c220576
test(gfql): add requires_gpu decorator for proper GPU test skipping
lmeyerov Jan 10, 2026
4714f65
refactor(gfql): extract ChainMeta for O(1) chain lookups
lmeyerov Jan 10, 2026
0da288e
refactor(gfql): extract EdgeSemantics for direction handling
lmeyerov Jan 10, 2026
0e2aa6b
refactor(gfql): extract df_utils for DataFrame operations
lmeyerov Jan 10, 2026
96d27d2
refactor(gfql): use EdgeSemantics in multihop methods, remove depreca…
lmeyerov Jan 10, 2026
dbdb722
refactor(gfql): use EdgeSemantics in _filter_edges_by_clauses
lmeyerov Jan 10, 2026
9ea3e2b
refactor(gfql): extract BFS functions to same_path/bfs.py
lmeyerov Jan 10, 2026
37fd858
refactor(gfql): remove redundant _is_single_hop, use EdgeSemantics.is…
lmeyerov Jan 10, 2026
b5c5ac6
refactor(gfql): extract post-prune methods to same_path/post_prune.py
lmeyerov Jan 10, 2026
9875a29
refactor(gfql): extract multihop methods to same_path/multihop.py
lmeyerov Jan 10, 2026
f04d11b
refactor(gfql): extract _re_propagate_backward to post_prune module
lmeyerov Jan 10, 2026
9bf2364
refactor(gfql): extract WHERE edge filtering to same_path/where_filte…
lmeyerov Jan 10, 2026
811009b
refactor(gfql): delete dead code and unused optimizations
lmeyerov Jan 10, 2026
4c71c68
fix(gfql): add forward WHERE pruning to df_executor
lmeyerov Jan 10, 2026
f5f8747
refactor(gfql): remove unused imports from df_executor
lmeyerov Jan 10, 2026
b5fc9f2
refactor(gfql): unify NULL semantics in evaluate_clause()
lmeyerov Jan 10, 2026
3b9750c
refactor(gfql): move re_propagate_backward to executor method
lmeyerov Jan 11, 2026
d6d8df2
fix(cudf): comprehensive cuDF compatibility fixes for GFQL executor
lmeyerov Jan 11, 2026
f5bc4c6
refactor(cudf): consolidate DataFrame construction helpers in df_utils
lmeyerov Jan 11, 2026
d92e433
fix(cudf): fix cuDF compatibility in chain backward pass and cross-en…
lmeyerov Jan 11, 2026
3c7633f
docs(changelog): add WHERE clause feature entries
lmeyerov Jan 11, 2026
47056cc
fix(tests): cuDF compatibility for tolist() calls in chain optimizati…
lmeyerov Jan 11, 2026
2a160a2
fix(cudf): use module string checks for cross-engine coercion
lmeyerov Jan 11, 2026
9841f9f
refactor(gfql): remove dead SamePathPlan code (~80 LOC)
lmeyerov Jan 12, 2026
6224912
refactor(gfql): remove unused same_path/__init__.py (~35 LOC)
lmeyerov Jan 12, 2026
e30b1e7
refactor(gfql): add immutable PathState type (Phase 1)
lmeyerov Jan 12, 2026
ec993cc
refactor(gfql): _backward_prune tracks pruned edges separately (Phase…
lmeyerov Jan 12, 2026
c414fe7
refactor(gfql): backward_propagate_constraints uses local state (Phas…
lmeyerov Jan 12, 2026
1f19d4b
refactor(gfql): post_prune.py uses local state copies (Phase 2c)
lmeyerov Jan 12, 2026
32aa350
refactor(gfql): add edges_df_for_step accessor (Phase 2d)
lmeyerov Jan 12, 2026
a18264b
test(gfql): add PathState immutability unit tests (Phase 3)
lmeyerov Jan 12, 2026
173cc3c
refactor(gfql): Phase 4 - convert to pure PathState API
lmeyerov Jan 12, 2026
af7bda4
refactor(gfql): Phase 5 - remove old _PathState class and update docs…
lmeyerov Jan 12, 2026
5da660c
test(gfql): Phase 6 - add PathState immutability contract tests
lmeyerov Jan 12, 2026
a3a044b
fix(cudf): use series_values helper instead of .tolist() in backward_…
lmeyerov Jan 12, 2026
d2ab7e5
chore: remove redundant comments from post_prune.py
lmeyerov Jan 12, 2026
df3c425
perf(gfql): replace Set with pd.Index for 7x faster ID operations
lmeyerov Jan 12, 2026
1b58198
perf(chain): use .isin() instead of merge for endpoint filtering
lmeyerov Jan 12, 2026
b9ca062
revert: remove cuDF dtype coercion from safe_merge
lmeyerov Jan 12, 2026
fb78ec9
perf(hop): use .isin() instead of merge for wavefront->edges join (8x…
lmeyerov Jan 12, 2026
d5440b1
refactor(hop): remove unused prepare_merge_dataframe function
lmeyerov Jan 12, 2026
abff94f
perf(hop): precompute node predicate domains
lmeyerov Jan 12, 2026
115d18a
perf(hop): unify direction pairs; modest CPU gains
lmeyerov Jan 12, 2026
64f80ba
perf(hop): mask target/dest filters with isin
lmeyerov Jan 12, 2026
22cc300
perf(hop): precompute target wavefront domains
lmeyerov Jan 12, 2026
671d4a6
perf(hop): use merge for EDGE_ID joins
lmeyerov Jan 12, 2026
804c559
perf(df_executor): DF-native cuDF forward prune
lmeyerov Jan 12, 2026
0a57bed
perf(hop): undirected single-pass expansion
lmeyerov Jan 12, 2026
819eec4
perf(hop): domain-based fast path traversal
lmeyerov Jan 13, 2026
0acddd1
fix(hop): undirected single-pass frontier
lmeyerov Jan 13, 2026
91fe759
fix(gfql): use domain helpers for same-path ids
lmeyerov Jan 16, 2026
63ebc62
fix(docs): include gfql same_path package in build
lmeyerov Jan 16, 2026
5c959a4
fix(lint): clean unused imports and f-string
lmeyerov Jan 16, 2026
fb6d8f9
fix(mypy): narrow optional frames in hop/gfql
lmeyerov Jan 16, 2026
2036761
fix(mypy): drop dataclass slots for py3.9
lmeyerov Jan 16, 2026
0a0ada0
fix(mypy): avoid optional node cols in hop/bfs
lmeyerov Jan 16, 2026
5adc175
Fix hop pair typing without asserts
lmeyerov Jan 16, 2026
b830aa7
Re-export col/compare in reference enumerator
lmeyerov Jan 16, 2026
10b127f
Expose col/compare without flake8 shadowing
lmeyerov Jan 17, 2026
0f0536f
DRY hop edge expansion helpers
lmeyerov Jan 17, 2026
283aaa6
Avoid re-deduping matches in hop
lmeyerov Jan 17, 2026
4869cfd
Revert hop undirected fast path and match anti-join
lmeyerov Jan 17, 2026
d57a5b8
Add tracked benchmark scripts and docs
lmeyerov Jan 17, 2026
f492135
Expand real-data benchmark coverage
lmeyerov Jan 17, 2026
67d47f9
Add benchmark results log
lmeyerov Jan 17, 2026
7080e35
Add real-data WHERE benchmark scenarios
lmeyerov Jan 17, 2026
48b8b6b
Log real-data WHERE benchmark run
lmeyerov Jan 17, 2026
2e2e7e1
Add scores to real-data benchmark output
lmeyerov Jan 17, 2026
6bec468
Log real-data benchmark scores
lmeyerov Jan 17, 2026
607a831
Log redteam benchmark rerun
lmeyerov Jan 17, 2026
8f618e5
Add redteam categorical benchmark option
lmeyerov Jan 17, 2026
8467374
Add optional df_executor OTel spans
lmeyerov Jan 18, 2026
4a72ab6
Add OTel detail stats for df_executor
lmeyerov Jan 18, 2026
c9a06f0
Add non-adjacent OTel detail stats
lmeyerov Jan 18, 2026
fad4aa2
benchmarks: add optional otel setup
lmeyerov Jan 18, 2026
f2357bc
otel: core helper, spans, trace headers
lmeyerov Jan 18, 2026
b7178af
tests: assert traceparent headers
lmeyerov Jan 18, 2026
20aab65
tests: behavior-level trace headers
lmeyerov Jan 18, 2026
36c3cef
benchmarks: log fast-path A/B; hop: clarify toggle
lmeyerov Jan 19, 2026
cdbf683
experiments: add non-adjacent WHERE modes
lmeyerov Jan 19, 2026
7e3da87
experiments: add non-adj ordering/bounds
lmeyerov Jan 19, 2026
ff3f5a9
benchmarks: log non-adj experiment
lmeyerov Jan 19, 2026
60a1b93
gfql: limit value-mode to equality; log phase-18 runs
lmeyerov Jan 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,31 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
## [Development]
<!-- Do Not Erase This Section - Used for tracking unreleased changes -->

### Added
- **GFQL / WHERE** (experimental): Added `Chain.where` field for same-path WHERE clause constraints. New modules: `same_path_types.py`, `same_path_plan.py`, `df_executor.py` implementing Yannakakis-style semijoin reduction for efficient WHERE filtering. Supports equality, inequality, and comparison operators on named alias columns.
- **GFQL / cuDF same-path**: Added execution-mode gate `GRAPHISTRY_CUDF_SAME_PATH_MODE` (auto/oracle/strict) for GFQL cuDF same-path executor. Auto falls back to oracle when GPU unavailable; strict requires cuDF or raises.
- **Compute / hop**: Added `GRAPHISTRY_HOP_FAST_PATH` (set to `0`/`false`/`off`) to disable fast-path traversal for benchmarking or compatibility checks.

### Performance
- **Compute / hop**: Refactored hop traversal to precompute node predicate domains and unify direction handling; synthetic CPU benchmarks show modest median improvements with some regressions on undirected/range scenarios.
- **GFQL / WHERE**: Use DF-native forward pruning for cuDF equality constraints to avoid host syncs (pandas path unchanged).
- **Compute / hop**: Undirected traversal skips oriented-pair expansion when no destination filters; modest CPU gains in undirected benchmarks.
- **Compute / hop**: Fast-path traversal uses domain-based visited/frontier tracking to avoid per-hop concat+dedupe overhead; modest CPU improvements in synthetic benchmarks.

### Fixed
- **GFQL / chain**: Fixed `from_json` to validate `where` field type before casting, preventing type errors on malformed input.
- **GFQL / WHERE**: Fixed undirected edge handling in WHERE clause filtering to check both src→dst and dst→src directions.
- **GFQL / WHERE**: Fixed multi-hop path edge retention to keep all edges in valid paths, not just terminal edges.
- **GFQL / WHERE**: Fixed unfiltered start node handling with multi-hop edges in native path executor.

### Infra
- **GFQL / same_path**: Modular architecture for WHERE execution: `same_path_types.py` (types), `same_path_plan.py` (planning), `df_executor.py` (execution), plus `same_path/` submodules for BFS, edge semantics, multihop, post-pruning, and WHERE filtering.
- **Benchmarks**: Added manual hop microbench + frontier sweep scripts under `benchmarks/` (not wired into CI).

### Tests
- **GFQL / df_executor**: Added comprehensive test suite (core, amplify, patterns, dimension) with 200+ tests covering Yannakakis semijoin, WHERE clause filtering, multi-hop paths, and pandas/cuDF parity.
- **GFQL / cuDF same-path**: Added strict/auto mode coverage for cuDF executor fallback behavior.

## [0.50.4 - 2026-01-15]

### Fixed
Expand Down
35 changes: 27 additions & 8 deletions ai/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,19 +184,38 @@ WITH_BUILD=0 WITH_TEST=0 ./test-cpu-local.sh

### GPU Testing - Fast (Reuse Base Image)

Docker containers include: **pytest, mypy, ruff** (preinstalled)
Docker containers include: **pytest, mypy, ruff, cudf** (preinstalled)

```bash
# Reuse existing graphistry image (no rebuild)
IMAGE="graphistry/graphistry-nvidia:${APP_BUILD_TAG:-latest}-${CUDA_SHORT_VERSION:-12.8}"

# Container with cuDF available (cudf 25.10)
IMAGE="graphistry/graphistry-nvidia:v2.50.0-13.0"

# Run compute + GFQL tests with cuDF fallback (491 tests)
# Uses CUDA_VISIBLE_DEVICES="" to avoid GPU driver issues
docker run --rm -v /home/lmeyerov/Work/pygraphistry:/app -w /app \
-e CUDA_VISIBLE_DEVICES="" \
$IMAGE \
python -m pytest graphistry/tests/test_compute*.py tests/gfql/ref/ -q \
--ignore=tests/gfql/ref/test_ref_enumerator.py \
-k "not cudf_gpu_path"

# Run GFQL ref tests only (372 tests)
docker run --rm -v /home/lmeyerov/Work/pygraphistry:/app -w /app \
-e CUDA_VISIBLE_DEVICES="" \
$IMAGE \
python -m pytest tests/gfql/ref/ -q \
--ignore=tests/gfql/ref/test_ref_enumerator.py

# With full GPU access (requires nvidia-container-toolkit)
docker run --rm --gpus all \
-v "$(pwd):/workspace:ro" \
-w /workspace -e PYTHONPATH=/workspace \
$IMAGE pytest graphistry/tests/test_file.py -v
-v /home/lmeyerov/Work/pygraphistry:/app -w /app \
$IMAGE python -m pytest graphistry/tests/compute/ -q
```

**Fast iteration**: Use this during development
**Note**: Tests in `graphistry/tests/compute/predicates/` require real GPU access.
Use `CUDA_VISIBLE_DEVICES=""` for cuDF import-path testing without GPU.

**Fast iteration**: Use cuDF container during development
**Full rebuild**: Use `./docker/test-gpu-local.sh` before merge

### Environment Control
Expand Down
97 changes: 97 additions & 0 deletions benchmarks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Benchmarks

Manual-only scripts for local performance checks. Not wired into CI.

Summary results go into `benchmarks/RESULTS.md` (raw outputs stay in `plans/`).

## Hop microbench

Run a small set of hop() scenarios across synthetic graphs.

```bash
uv run python benchmarks/run_hop_microbench.py --runs 5 --output /tmp/hop-microbench.md
```

## Frontier sweep

Sweep seed sizes on a fixed linear graph.

```bash
uv run python benchmarks/run_hop_frontier_sweep.py --runs 5 --nodes 100000 --edges 200000 --output /tmp/hop-frontier.md
```

Notes:
- Use `--engine cudf` for GPU runs when cuDF is available.
- Scripts print a table to stdout; `--output` writes Markdown results.

## Chain vs Yannakakis

Compare regular `chain()` against the Yannakakis same-path executor on synthetic graphs.

```bash
uv run python benchmarks/run_chain_vs_samepath.py --runs 7 --warmup 1 --output /tmp/chain-vs-samepath.md
```

To toggle non-adjacent WHERE experiments on synthetic scenarios:

```bash
uv run python benchmarks/run_chain_vs_samepath.py \
--non-adj-mode value_prefilter \
--non-adj-value-card-max 500 \
--non-adj-order selectivity \
--non-adj-bounds \
--runs 7 --warmup 1
```

## Real-data GFQL

Run GFQL chain scenarios on demo datasets plus WHERE scenarios (df_executor), with separate sections and a per-section score.

```bash
uv run python benchmarks/run_realdata_benchmarks.py --runs 7 --warmup 1 --output /tmp/realdata-gfql.md
```

To test categorical domains for redteam:

```bash
uv run python benchmarks/run_realdata_benchmarks.py --datasets redteam50k --redteam-domain-categorical --runs 9 --warmup 2
```

To experiment with non-adjacent WHERE modes:

```bash
uv run python benchmarks/run_realdata_benchmarks.py \
--datasets redteam50k \
--non-adj-mode value_prefilter \
--non-adj-value-card-max 500 \
--non-adj-order selectivity \
--non-adj-bounds \
--runs 7 --warmup 1
```

To enable OpenTelemetry spans for df_executor:

```bash
GRAPHISTRY_OTEL=1 \
GRAPHISTRY_OTEL_DETAIL=1 \
uv run --with opentelemetry-api --with opentelemetry-sdk \
python benchmarks/run_realdata_benchmarks.py --datasets redteam50k --runs 3 --warmup 1
```

To export spans to OTLP (optional):

```bash
GRAPHISTRY_OTEL=1 \
GRAPHISTRY_OTEL_EXPORTER=otlp \
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 \
uv run --with opentelemetry-api --with opentelemetry-sdk --with opentelemetry-exporter-otlp \
python benchmarks/run_realdata_benchmarks.py --datasets redteam50k --runs 3 --warmup 1
```

To limit datasets:

```bash
uv run python benchmarks/run_realdata_benchmarks.py --datasets redteam50k,transactions --runs 7 --warmup 1
```

Available datasets: `redteam50k`, `transactions`, `facebook_combined`, `honeypot`, `twitter_demo`, `lesmiserables`, `twitter_congress`, `all`.
16 changes: 16 additions & 0 deletions benchmarks/RESULTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Benchmark Results Log

Summary-only log for notable benchmark runs. Raw per-scenario outputs live in
`plans/` (gitignored) and should be referenced here.

| Date | Commit | Scripts | Summary | Notes |
|------|--------|---------|---------|-------|
| 2026-01-17 | f492135e (feat/where-clause-executor) | `run_chain_vs_samepath.py` (median-of-7, warmup-1); `run_realdata_benchmarks.py` (median-of-7, warmup-1) | Synthetic: yann/regular median ~0.51x (52/54 wins). Real data: expanded to 7 datasets, medians ~30–173ms. | Raw outputs: `plans/pr-886-where/benchmarks/phase-12-revert-8-11.md`, `plans/pr-886-where/benchmarks/phase-13-realdata.md` |
| 2026-01-17 | 7080e356 (feat/where-clause-executor) | `run_realdata_benchmarks.py` (median-of-7, warmup-1) | Real data now includes WHERE (df_executor): redteam ~14s, transactions ~11s, others ~14–282ms. Chain-only medians ~31–175ms. | Raw outputs: `plans/pr-886-where/benchmarks/phase-14-realdata.md` |
| 2026-01-17 | 2e2e7e18 (feat/where-clause-executor) | `run_realdata_benchmarks.py` (median-of-7, warmup-1) | Added per-section scores. Chain score (median of medians) 72.78ms; WHERE score 247.07ms. | Raw outputs: `plans/pr-886-where/benchmarks/phase-14-realdata.md` |
| 2026-01-17 | 6bec468b (feat/where-clause-executor) | `run_realdata_benchmarks.py --datasets redteam50k --runs 9 --warmup 2` | Redteam-only rerun: chain score 157.83ms; WHERE score 13.12s. Low selectivity (WHERE keeps ~83.6% nodes / 74.3% edges). | Raw outputs: `plans/pr-886-where/benchmarks/phase-14-redteam-highruns.md`, `plans/pr-886-where/benchmarks/phase-14-redteam-selectivity.md` |
| 2026-01-17 | 6bec468b (feat/where-clause-executor) | `run_realdata_benchmarks.py --datasets redteam50k --redteam-domain-categorical --runs 9 --warmup 2` | Redteam categorical domains: chain score 164.63ms; WHERE score 13.12s (no meaningful change). | Raw outputs: `plans/pr-886-where/benchmarks/phase-14-redteam-cat.md` |
| 2026-01-18 | 20aab655 (feat/where-clause-executor) | `run_realdata_benchmarks.py --datasets redteam50k` (median-of-7, warmup-1) with `GRAPHISTRY_HOP_FAST_PATH=0/1` | Fast path on is slower for chain (~6-13%, score 164.89ms vs 154.75ms); WHERE delta likely noise (12.07s vs 13.12s). | Raw outputs: `plans/pr-886-where/benchmarks/phase-17-redteam-fastpath-off.md`, `plans/pr-886-where/benchmarks/phase-17-redteam-fastpath-on.md` |
| 2026-01-18 | 7e3da877 (feat/where-clause-executor) | `run_realdata_benchmarks.py --datasets redteam50k` (median-of-7, warmup-1) with baseline vs `--non-adj-mode value_prefilter --non-adj-value-card-max 500 --non-adj-order selectivity --non-adj-bounds` | Non-adj value+prefilter dropped redteam WHERE from 12.96s → 0.35s; needs parity validation. Chain-only roughly unchanged. | Raw outputs: `plans/pr-886-where/benchmarks/phase-18-redteam-baseline.md`, `plans/pr-886-where/benchmarks/phase-18-redteam-value_prefilter.md` |
| 2026-01-18 | 7e3da877 (feat/where-clause-executor) | `run_realdata_benchmarks.py --datasets redteam50k,transactions,facebook_combined` (median-of-7, warmup-1) baseline vs `--non-adj-mode value_prefilter --non-adj-value-card-max 500 --non-adj-order selectivity --non-adj-bounds` | WHERE: redteam 11.1s → 0.33s, transactions ~10.0s → ~10.1s, facebook ~239ms → ~244ms. | Raw outputs: `plans/pr-886-where/benchmarks/phase-18-realdata-baseline.md`, `plans/pr-886-where/benchmarks/phase-18-realdata-value_prefilter.md` |
| 2026-01-18 | 7e3da877 (feat/where-clause-executor) | `run_chain_vs_samepath.py` (median-of-7, warmup-1) baseline vs `--non-adj-mode value_prefilter --non-adj-value-card-max 500 --non-adj-order selectivity --non-adj-bounds` | Synthetic: small deltas; dense non-adj still slower than regular. | Raw outputs: `plans/pr-886-where/benchmarks/phase-18-synth-baseline.md`, `plans/pr-886-where/benchmarks/phase-18-synth-value_prefilter.md` |
66 changes: 66 additions & 0 deletions benchmarks/otel_setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""Optional OpenTelemetry setup for benchmarks.

This keeps deps optional: if opentelemetry is missing, it no-ops.
"""

from __future__ import annotations

import os
import sys
from typing import Optional


def setup_tracer() -> bool:
if os.environ.get("GRAPHISTRY_OTEL", "").strip().lower() not in {"1", "true", "yes", "on"}:
return False

try:
from opentelemetry import trace # type: ignore
from opentelemetry.sdk.trace import TracerProvider # type: ignore
from opentelemetry.sdk.trace.export import ( # type: ignore
BatchSpanProcessor,
ConsoleSpanExporter,
SimpleSpanProcessor,
)
from opentelemetry.sdk.resources import Resource # type: ignore
except Exception:
print("OpenTelemetry SDK not installed; spans will not be exported.", file=sys.stderr)
return False

exporter_kind = os.environ.get("GRAPHISTRY_OTEL_EXPORTER", "console").strip().lower()
processor = None

if exporter_kind == "otlp":
exporter = _make_otlp_exporter()
if exporter is None:
return False
processor = BatchSpanProcessor(exporter)
else:
processor = SimpleSpanProcessor(ConsoleSpanExporter())

provider = trace.get_tracer_provider()
if not hasattr(provider, "add_span_processor"):
service_name = os.environ.get("OTEL_SERVICE_NAME", "graphistry")
provider = TracerProvider(resource=Resource.create({"service.name": service_name}))
trace.set_tracer_provider(provider)

provider.add_span_processor(processor)
return True


def _make_otlp_exporter() -> Optional[object]:
endpoint = os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT", "").strip()
try:
from opentelemetry.exporter.otlp.proto.http.trace_exporter import ( # type: ignore
OTLPSpanExporter,
)
return OTLPSpanExporter(endpoint=endpoint or None)
except Exception:
try:
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import ( # type: ignore
OTLPSpanExporter,
)
return OTLPSpanExporter(endpoint=endpoint or None)
except Exception:
print("OTLP exporter not available; install opentelemetry-exporter-otlp.", file=sys.stderr)
return None
Loading
Loading