stemtrace 0.3.2__tar.gz → 0.3.3__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.
- {stemtrace-0.3.2 → stemtrace-0.3.3}/.bumpversion.toml +1 -1
- {stemtrace-0.3.2 → stemtrace-0.3.3}/.github/workflows/ci.yml +1 -1
- {stemtrace-0.3.2 → stemtrace-0.3.3}/.github/workflows/e2e.yml +4 -4
- {stemtrace-0.3.2 → stemtrace-0.3.3}/.github/workflows/release.yml +10 -6
- {stemtrace-0.3.2 → stemtrace-0.3.3}/CHANGELOG.md +9 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/PKG-INFO +6 -2
- {stemtrace-0.3.2 → stemtrace-0.3.3}/README.md +5 -1
- {stemtrace-0.3.2 → stemtrace-0.3.3}/pyproject.toml +1 -1
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/__init__.py +10 -1
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/library/config.py +6 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/api/routes.py +57 -4
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/fastapi/extension.py +6 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/fastapi/router.py +2 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/package-lock.json +313 -96
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/package.json +5 -5
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/test_init.py +2 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/test_routes.py +141 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/uv.lock +1 -1
- {stemtrace-0.3.2 → stemtrace-0.3.3}/.dockerignore +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/.github/dependabot.yml +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/.gitignore +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/.pre-commit-config.yaml +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/.python-version +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/CONTRIBUTING.md +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/Dockerfile +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/Dockerfile.e2e +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/LICENSE +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/Makefile +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/build_ui.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/docker-compose.e2e.rabbitmq.yml +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/docker-compose.e2e.yml +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/docker-compose.rabbitmq.yml +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/docker-compose.yml +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/docs/screenshots/task_details.png +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/docs/screenshots/unregistered.png +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/docs/screenshots/workflow.png +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/examples/celery_app.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/examples/fastapi_integration.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/examples/with_auth.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/examples/with_login.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/progress.md +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/scripts/wait_for_http.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/core/__init__.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/core/events.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/core/exceptions.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/core/graph.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/core/ports.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/library/__init__.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/library/bootsteps.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/library/scrubbing.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/library/signals.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/library/transports/__init__.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/library/transports/memory.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/library/transports/rabbitmq.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/library/transports/redis.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/py.typed +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/__init__.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/__main__.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/api/__init__.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/api/schemas.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/api/websocket.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/consumer.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/fastapi/__init__.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/fastapi/auth.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/fastapi/form_auth.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/fastapi/login_routes.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/store.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/__init__.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/biome.json +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/index.html +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/playwright.config.ts +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/postcss.config.js +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/api/client.ts +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/api/queries.ts +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/components/DateRangePicker.tsx +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/components/Filters.tsx +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/components/TaskGraph.tsx +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/components/TaskList.tsx +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/components/TaskStateBadge.tsx +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/components/TaskTimeline.tsx +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/components/TimelineBar.tsx +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/components/Tooltip.tsx +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/hooks/WebSocketContext.tsx +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/hooks/useInfiniteScroll.ts +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/index.css +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/main.tsx +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/routes/__root.tsx +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/routes/graph.$rootId.tsx +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/routes/graphs.tsx +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/routes/index.tsx +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/routes/registry.tsx +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/routes/tasks.$taskId.tsx +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/routes/workers.tsx +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/utils/format.ts +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/src/vite-env.d.ts +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/tests/fixtures/mock-api.ts +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/tests/fixtures/mock-data.ts +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/tests/graphs.spec.ts +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/tests/registry.spec.ts +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/tests/task-detail.spec.ts +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/tests/tasks.spec.ts +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/tests/workers.spec.ts +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/tsconfig.json +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/tsconfig.tsbuildinfo +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/frontend/vite.config.ts +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/static.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/ui/templates/login.html +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/src/stemtrace/server/websocket.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/__init__.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/conftest.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/e2e/__init__.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/e2e/conftest.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/e2e/tasks.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/e2e/test_api_e2e.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/e2e/test_workers_e2e.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/integration/__init__.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/integration/test_embedded_static.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/integration/test_fastapi_integration.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/integration/test_rabbitmq_transport.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/integration/test_redis_transport.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/integration/test_static_ui.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/integration/test_workers_refresh.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/__init__.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/test_auth.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/test_bootsteps.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/test_config.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/test_consumer.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/test_events.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/test_examples.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/test_exceptions.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/test_form_auth.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/test_form_login.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/test_graph.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/test_scrubbing.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/test_signals.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/test_store.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/test_transports.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/test_ui_static_prefix.py +0 -0
- {stemtrace-0.3.2 → stemtrace-0.3.3}/tests/unit/test_websocket.py +0 -0
|
@@ -24,7 +24,7 @@ jobs:
|
|
|
24
24
|
- uses: actions/checkout@v6
|
|
25
25
|
|
|
26
26
|
- name: Set up Docker Buildx
|
|
27
|
-
uses: docker/setup-buildx-action@
|
|
27
|
+
uses: docker/setup-buildx-action@v4
|
|
28
28
|
|
|
29
29
|
- name: Select compose file and Celery env
|
|
30
30
|
run: |
|
|
@@ -91,7 +91,7 @@ jobs:
|
|
|
91
91
|
- uses: actions/checkout@v6
|
|
92
92
|
|
|
93
93
|
- name: Set up Docker Buildx
|
|
94
|
-
uses: docker/setup-buildx-action@
|
|
94
|
+
uses: docker/setup-buildx-action@v4
|
|
95
95
|
|
|
96
96
|
- name: Select compose file and Celery env
|
|
97
97
|
run: |
|
|
@@ -167,7 +167,7 @@ jobs:
|
|
|
167
167
|
PLAYWRIGHT_BASE_URL: http://localhost:8000
|
|
168
168
|
|
|
169
169
|
- name: Upload Playwright report
|
|
170
|
-
uses: actions/upload-artifact@
|
|
170
|
+
uses: actions/upload-artifact@v7
|
|
171
171
|
if: always()
|
|
172
172
|
with:
|
|
173
173
|
name: playwright-report-${{ matrix.broker }}
|
|
@@ -175,7 +175,7 @@ jobs:
|
|
|
175
175
|
retention-days: 7
|
|
176
176
|
|
|
177
177
|
- name: Upload test results
|
|
178
|
-
uses: actions/upload-artifact@
|
|
178
|
+
uses: actions/upload-artifact@v7
|
|
179
179
|
if: failure()
|
|
180
180
|
with:
|
|
181
181
|
name: playwright-test-results-${{ matrix.broker }}
|
|
@@ -74,7 +74,7 @@ jobs:
|
|
|
74
74
|
run: twine check dist/*
|
|
75
75
|
|
|
76
76
|
- name: Upload dist artifacts
|
|
77
|
-
uses: actions/upload-artifact@
|
|
77
|
+
uses: actions/upload-artifact@v7
|
|
78
78
|
with:
|
|
79
79
|
name: dist
|
|
80
80
|
path: dist/
|
|
@@ -87,7 +87,7 @@ jobs:
|
|
|
87
87
|
id-token: write
|
|
88
88
|
steps:
|
|
89
89
|
- name: Download dist artifacts
|
|
90
|
-
uses: actions/download-artifact@
|
|
90
|
+
uses: actions/download-artifact@v8
|
|
91
91
|
with:
|
|
92
92
|
name: dist
|
|
93
93
|
path: dist/
|
|
@@ -104,8 +104,11 @@ jobs:
|
|
|
104
104
|
steps:
|
|
105
105
|
- uses: actions/checkout@v6
|
|
106
106
|
|
|
107
|
+
- name: Set up QEMU
|
|
108
|
+
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
|
|
109
|
+
|
|
107
110
|
- name: Set up Docker Buildx
|
|
108
|
-
uses: docker/setup-buildx-action@
|
|
111
|
+
uses: docker/setup-buildx-action@v4
|
|
109
112
|
|
|
110
113
|
- name: Log in to GitHub Container Registry
|
|
111
114
|
uses: docker/login-action@v3
|
|
@@ -116,7 +119,7 @@ jobs:
|
|
|
116
119
|
|
|
117
120
|
- name: Extract metadata
|
|
118
121
|
id: meta
|
|
119
|
-
uses: docker/metadata-action@
|
|
122
|
+
uses: docker/metadata-action@v6
|
|
120
123
|
with:
|
|
121
124
|
images: ghcr.io/${{ github.repository }}
|
|
122
125
|
tags: |
|
|
@@ -124,10 +127,11 @@ jobs:
|
|
|
124
127
|
type=raw,value=latest
|
|
125
128
|
|
|
126
129
|
- name: Build and push Docker image
|
|
127
|
-
uses: docker/build-push-action@
|
|
130
|
+
uses: docker/build-push-action@v7
|
|
128
131
|
with:
|
|
129
132
|
context: .
|
|
130
133
|
push: true
|
|
134
|
+
platforms: linux/amd64,linux/arm64
|
|
131
135
|
tags: ${{ steps.meta.outputs.tags }}
|
|
132
136
|
labels: ${{ steps.meta.outputs.labels }}
|
|
133
137
|
cache-from: type=gha
|
|
@@ -142,7 +146,7 @@ jobs:
|
|
|
142
146
|
- uses: actions/checkout@v6
|
|
143
147
|
|
|
144
148
|
- name: Download dist artifacts
|
|
145
|
-
uses: actions/download-artifact@
|
|
149
|
+
uses: actions/download-artifact@v8
|
|
146
150
|
with:
|
|
147
151
|
name: dist
|
|
148
152
|
path: dist/
|
|
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.3] - 2026-03-20
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Node alias from arguments: derive graph node display name from task args/kwargs via `node_alias_from_arguments` config option (thanks @ohadmata for the idea — #46, #48)
|
|
14
|
+
- Docker: multi-architecture image builds (amd64 + arm64) (thanks @ohadmata — #51)
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- Graph: order task nodes before synthetic container nodes (GROUP/CHORD) for correct rendering (thanks @codyburrito — #52)
|
|
18
|
+
|
|
10
19
|
## [0.3.2] - 2026-02-28
|
|
11
20
|
|
|
12
21
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stemtrace
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: A lightweight Celery task flow visualizer
|
|
5
5
|
Project-URL: Homepage, https://github.com/iansokolskyi/stemtrace
|
|
6
6
|
Project-URL: Documentation, https://github.com/iansokolskyi/stemtrace#readme
|
|
@@ -56,7 +56,7 @@ Description-Content-Type: text/markdown
|
|
|
56
56
|
|
|
57
57
|
**Zero-infrastructure Celery task flow visualizer**
|
|
58
58
|
|
|
59
|
-
[](https://pypi.org/project/stemtrace)
|
|
60
60
|
[](https://pypi.org/project/stemtrace/)
|
|
61
61
|
[](https://github.com/iansokolskyi/stemtrace/actions/workflows/ci.yml)
|
|
62
62
|
[](https://codecov.io/gh/iansokolskyi/stemtrace)
|
|
@@ -250,6 +250,10 @@ stemtrace.init_worker(
|
|
|
250
250
|
scrub_sensitive_data=True, # Scrub passwords, API keys, etc.
|
|
251
251
|
additional_sensitive_keys=frozenset({"my_secret"}), # Add custom keys
|
|
252
252
|
safe_keys=frozenset({"public_key"}), # Never scrub these keys
|
|
253
|
+
|
|
254
|
+
# UI display
|
|
255
|
+
node_alias_from_arguments="operator_type", # Use kwargs["operator_type"] as node name
|
|
256
|
+
# node_alias_from_arguments="0", # Or use args[0] (digit string = positional index)
|
|
253
257
|
)
|
|
254
258
|
|
|
255
259
|
# Introspection (after init)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Zero-infrastructure Celery task flow visualizer**
|
|
4
4
|
|
|
5
|
-
[](https://pypi.org/project/stemtrace)
|
|
6
6
|
[](https://pypi.org/project/stemtrace/)
|
|
7
7
|
[](https://github.com/iansokolskyi/stemtrace/actions/workflows/ci.yml)
|
|
8
8
|
[](https://codecov.io/gh/iansokolskyi/stemtrace)
|
|
@@ -196,6 +196,10 @@ stemtrace.init_worker(
|
|
|
196
196
|
scrub_sensitive_data=True, # Scrub passwords, API keys, etc.
|
|
197
197
|
additional_sensitive_keys=frozenset({"my_secret"}), # Add custom keys
|
|
198
198
|
safe_keys=frozenset({"public_key"}), # Never scrub these keys
|
|
199
|
+
|
|
200
|
+
# UI display
|
|
201
|
+
node_alias_from_arguments="operator_type", # Use kwargs["operator_type"] as node name
|
|
202
|
+
# node_alias_from_arguments="0", # Or use args[0] (digit string = positional index)
|
|
199
203
|
)
|
|
200
204
|
|
|
201
205
|
# Introspection (after init)
|
|
@@ -43,7 +43,7 @@ if TYPE_CHECKING:
|
|
|
43
43
|
from celery import Celery
|
|
44
44
|
from fastapi import FastAPI
|
|
45
45
|
|
|
46
|
-
__version__ = "0.3.
|
|
46
|
+
__version__ = "0.3.3"
|
|
47
47
|
__all__ = [
|
|
48
48
|
"ConfigurationError",
|
|
49
49
|
"StemtraceConfig",
|
|
@@ -93,6 +93,7 @@ def init_worker(
|
|
|
93
93
|
additional_sensitive_keys: frozenset[str] | None = None,
|
|
94
94
|
safe_keys: frozenset[str] | None = None,
|
|
95
95
|
max_data_size: int = 10240,
|
|
96
|
+
node_alias_from_arguments: str | None = None,
|
|
96
97
|
) -> None:
|
|
97
98
|
"""Initialize stemtrace for Celery worker instrumentation.
|
|
98
99
|
|
|
@@ -107,6 +108,9 @@ def init_worker(
|
|
|
107
108
|
additional_sensitive_keys: Extra keys to treat as sensitive.
|
|
108
109
|
safe_keys: Keys to never scrub (overrides sensitive).
|
|
109
110
|
max_data_size: Maximum size in bytes for serialized data (default: 10KB).
|
|
111
|
+
node_alias_from_arguments: Derive node display name from task arguments.
|
|
112
|
+
Pass a digit string to use args[index], a string key for kwargs[key],
|
|
113
|
+
or None to use the task name (default).
|
|
110
114
|
|
|
111
115
|
Raises:
|
|
112
116
|
ConfigurationError: If no broker URL can be determined.
|
|
@@ -130,6 +134,7 @@ def init_worker(
|
|
|
130
134
|
additional_sensitive_keys=additional_sensitive_keys or frozenset(),
|
|
131
135
|
safe_keys=safe_keys or frozenset(),
|
|
132
136
|
max_data_size=max_data_size,
|
|
137
|
+
node_alias_from_arguments=node_alias_from_arguments,
|
|
133
138
|
)
|
|
134
139
|
set_config(config)
|
|
135
140
|
|
|
@@ -153,6 +158,7 @@ def init_app(
|
|
|
153
158
|
login_password: str | None = None,
|
|
154
159
|
login_secret: str | None = None,
|
|
155
160
|
login_ttl_seconds: int = 86400,
|
|
161
|
+
node_alias_from_arguments: str | None = None,
|
|
156
162
|
) -> StemtraceExtension:
|
|
157
163
|
"""Initialize stemtrace as a FastAPI extension.
|
|
158
164
|
|
|
@@ -174,6 +180,8 @@ def init_app(
|
|
|
174
180
|
STEMTRACE_LOGIN_SECRET env var. If still missing, a random secret is
|
|
175
181
|
generated (sessions invalidated on app restart).
|
|
176
182
|
login_ttl_seconds: Session TTL in seconds (default: 24 hours).
|
|
183
|
+
node_alias_from_arguments: Derive graph node display name from task arguments.
|
|
184
|
+
Digit string for args[index], string for kwargs[key], or None (default).
|
|
177
185
|
|
|
178
186
|
Returns:
|
|
179
187
|
The initialized StemtraceExtension instance.
|
|
@@ -230,6 +238,7 @@ def init_app(
|
|
|
230
238
|
max_nodes=max_nodes,
|
|
231
239
|
auth_dependency=auth_dependency,
|
|
232
240
|
form_auth_config=form_auth_config,
|
|
241
|
+
node_alias_from_arguments=node_alias_from_arguments,
|
|
233
242
|
)
|
|
234
243
|
extension.init_app(fastapi_app, prefix=prefix)
|
|
235
244
|
|
|
@@ -16,6 +16,9 @@ class StemtraceConfig(BaseModel):
|
|
|
16
16
|
scrub_sensitive_data: Whether to scrub sensitive keys.
|
|
17
17
|
additional_sensitive_keys: Extra keys to treat as sensitive.
|
|
18
18
|
safe_keys: Keys to never scrub (overrides sensitive).
|
|
19
|
+
node_alias_from_arguments: Derive graph node display name from task
|
|
20
|
+
arguments. Pass a string key to use kwargs[key], a digit string
|
|
21
|
+
to use args[index], or None to use the task name (default).
|
|
19
22
|
"""
|
|
20
23
|
|
|
21
24
|
model_config = ConfigDict(frozen=True)
|
|
@@ -34,6 +37,9 @@ class StemtraceConfig(BaseModel):
|
|
|
34
37
|
additional_sensitive_keys: frozenset[str] = Field(default_factory=frozenset)
|
|
35
38
|
safe_keys: frozenset[str] = Field(default_factory=frozenset)
|
|
36
39
|
|
|
40
|
+
# UI display options
|
|
41
|
+
node_alias_from_arguments: str | None = None
|
|
42
|
+
|
|
37
43
|
|
|
38
44
|
_config: StemtraceConfig | None = None
|
|
39
45
|
|
|
@@ -123,9 +123,40 @@ def _node_to_response(node: TaskNode) -> TaskNodeResponse:
|
|
|
123
123
|
)
|
|
124
124
|
|
|
125
125
|
|
|
126
|
+
def _resolve_node_alias(node: TaskNode, key: str | None) -> str:
|
|
127
|
+
"""Resolve display name for a graph node from task arguments.
|
|
128
|
+
|
|
129
|
+
When a key is configured, attempts to extract a value from the first
|
|
130
|
+
event's args (if key is a digit string) or kwargs (if key is a regular
|
|
131
|
+
string). Falls back to the node's registered task name.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
node: Task node containing events with args/kwargs.
|
|
135
|
+
key: Digit string for args[index], string for kwargs[key],
|
|
136
|
+
or None to use the task name.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
Resolved display name for the node.
|
|
140
|
+
"""
|
|
141
|
+
if key is None or not node.events:
|
|
142
|
+
return node.name
|
|
143
|
+
|
|
144
|
+
event = node.events[0]
|
|
145
|
+
|
|
146
|
+
if key.isdigit():
|
|
147
|
+
index = int(key)
|
|
148
|
+
if isinstance(event.args, list) and index < len(event.args):
|
|
149
|
+
return str(event.args[index])
|
|
150
|
+
elif isinstance(event.kwargs, dict) and key in event.kwargs:
|
|
151
|
+
return str(event.kwargs[key])
|
|
152
|
+
|
|
153
|
+
return node.name
|
|
154
|
+
|
|
155
|
+
|
|
126
156
|
def _node_to_graph_response(
|
|
127
157
|
node: TaskNode,
|
|
128
158
|
all_nodes: dict[str, TaskNode] | None = None,
|
|
159
|
+
node_alias_key: str | None = None,
|
|
129
160
|
) -> GraphNodeResponse:
|
|
130
161
|
"""Convert TaskNode to graph response model.
|
|
131
162
|
|
|
@@ -134,6 +165,7 @@ def _node_to_graph_response(
|
|
|
134
165
|
Args:
|
|
135
166
|
node: Task node from graph store.
|
|
136
167
|
all_nodes: All nodes dict for child lookup.
|
|
168
|
+
node_alias_key: Key to resolve display name from task arguments.
|
|
137
169
|
|
|
138
170
|
Returns:
|
|
139
171
|
GraphNodeResponse with timing from children.
|
|
@@ -159,15 +191,25 @@ def _node_to_graph_response(
|
|
|
159
191
|
if first_seen and last_updated and first_seen != last_updated:
|
|
160
192
|
duration_ms = int((last_updated - first_seen).total_seconds() * 1000)
|
|
161
193
|
|
|
194
|
+
# UI traversal is order-sensitive when callbacks can appear as both direct
|
|
195
|
+
# children and CHORD-linked nodes. Prefer TASK children first so callback
|
|
196
|
+
# branches render before synthetic containers.
|
|
197
|
+
children = node.children
|
|
198
|
+
if all_nodes is not None:
|
|
199
|
+
children = sorted(
|
|
200
|
+
node.children,
|
|
201
|
+
key=lambda c: getattr(all_nodes.get(c), "node_type", None) != "TASK",
|
|
202
|
+
)
|
|
203
|
+
|
|
162
204
|
return GraphNodeResponse(
|
|
163
205
|
task_id=node.task_id,
|
|
164
|
-
name=node
|
|
206
|
+
name=_resolve_node_alias(node, node_alias_key),
|
|
165
207
|
state=node.state,
|
|
166
208
|
node_type=node.node_type,
|
|
167
209
|
group_id=node.group_id,
|
|
168
210
|
chord_id=node.chord_id,
|
|
169
211
|
parent_id=node.parent_id,
|
|
170
|
-
children=
|
|
212
|
+
children=children,
|
|
171
213
|
duration_ms=duration_ms,
|
|
172
214
|
first_seen=first_seen,
|
|
173
215
|
last_updated=last_updated,
|
|
@@ -289,6 +331,7 @@ def create_api_router(
|
|
|
289
331
|
ws_manager: WebSocketManager | None = None,
|
|
290
332
|
worker_registry: WorkerRegistry | None = None,
|
|
291
333
|
broker_url: str | None = None,
|
|
334
|
+
node_alias_from_arguments: str | None = None,
|
|
292
335
|
) -> APIRouter:
|
|
293
336
|
"""Create REST API router with task and graph endpoints.
|
|
294
337
|
|
|
@@ -298,6 +341,8 @@ def create_api_router(
|
|
|
298
341
|
ws_manager: Optional WebSocket manager.
|
|
299
342
|
worker_registry: Optional worker registry for lifecycle tracking.
|
|
300
343
|
broker_url: Optional Celery broker URL for on-demand inspection.
|
|
344
|
+
node_alias_from_arguments: Key to derive node display name from task
|
|
345
|
+
arguments. Digit string for args[index], string for kwargs[key].
|
|
301
346
|
|
|
302
347
|
Returns:
|
|
303
348
|
Configured API router.
|
|
@@ -562,7 +607,12 @@ def create_api_router(
|
|
|
562
607
|
for child in store.get_children(root.task_id):
|
|
563
608
|
all_nodes[child.task_id] = child
|
|
564
609
|
return GraphListResponse(
|
|
565
|
-
graphs=[
|
|
610
|
+
graphs=[
|
|
611
|
+
_node_to_graph_response(
|
|
612
|
+
r, all_nodes, node_alias_key=node_alias_from_arguments
|
|
613
|
+
)
|
|
614
|
+
for r in roots
|
|
615
|
+
],
|
|
566
616
|
total=total,
|
|
567
617
|
limit=limit,
|
|
568
618
|
offset=offset,
|
|
@@ -586,7 +636,10 @@ def create_api_router(
|
|
|
586
636
|
return GraphResponse(
|
|
587
637
|
root_id=root_id,
|
|
588
638
|
nodes={
|
|
589
|
-
tid: _node_to_graph_response(
|
|
639
|
+
tid: _node_to_graph_response(
|
|
640
|
+
n, all_nodes, node_alias_key=node_alias_from_arguments
|
|
641
|
+
)
|
|
642
|
+
for tid, n in graph.items()
|
|
590
643
|
},
|
|
591
644
|
)
|
|
592
645
|
|
|
@@ -67,6 +67,7 @@ class StemtraceExtension:
|
|
|
67
67
|
max_nodes: int = 10000,
|
|
68
68
|
auth_dependency: Any = None,
|
|
69
69
|
form_auth_config: FormAuthConfig | None = None,
|
|
70
|
+
node_alias_from_arguments: str | None = None,
|
|
70
71
|
) -> None:
|
|
71
72
|
"""Initialize extension with broker and transport configuration.
|
|
72
73
|
|
|
@@ -81,6 +82,8 @@ class StemtraceExtension:
|
|
|
81
82
|
max_nodes: Maximum number of nodes to keep in memory.
|
|
82
83
|
auth_dependency: Optional FastAPI dependency applied to all routes for authentication.
|
|
83
84
|
form_auth_config: Optional cookie-session configuration used to protect WebSocket.
|
|
85
|
+
node_alias_from_arguments: Key to derive graph node display name from task
|
|
86
|
+
arguments. Digit string for args[index], string for kwargs[key].
|
|
84
87
|
"""
|
|
85
88
|
self._broker_url = broker_url
|
|
86
89
|
self._transport_url = transport_url or broker_url
|
|
@@ -103,6 +106,8 @@ class StemtraceExtension:
|
|
|
103
106
|
worker_registry=self._worker_registry,
|
|
104
107
|
)
|
|
105
108
|
|
|
109
|
+
self._node_alias_from_arguments = node_alias_from_arguments
|
|
110
|
+
|
|
106
111
|
self._store.add_listener(self._ws_manager.queue_event)
|
|
107
112
|
|
|
108
113
|
@staticmethod
|
|
@@ -141,6 +146,7 @@ class StemtraceExtension:
|
|
|
141
146
|
broker_url=self._broker_url,
|
|
142
147
|
auth_dependency=self._auth_dependency,
|
|
143
148
|
form_auth_config=self._form_auth_config,
|
|
149
|
+
node_alias_from_arguments=self._node_alias_from_arguments,
|
|
144
150
|
)
|
|
145
151
|
|
|
146
152
|
if self._serve_ui:
|
|
@@ -24,6 +24,7 @@ def create_router(
|
|
|
24
24
|
broker_url: str | None = None,
|
|
25
25
|
auth_dependency: Any = None,
|
|
26
26
|
form_auth_config: FormAuthConfig | None = None,
|
|
27
|
+
node_alias_from_arguments: str | None = None,
|
|
27
28
|
) -> APIRouter:
|
|
28
29
|
"""Create API router. For embedded consumer, use StemtraceExtension."""
|
|
29
30
|
if store is None:
|
|
@@ -40,6 +41,7 @@ def create_router(
|
|
|
40
41
|
ws_manager,
|
|
41
42
|
worker_registry,
|
|
42
43
|
broker_url=broker_url,
|
|
44
|
+
node_alias_from_arguments=node_alias_from_arguments,
|
|
43
45
|
)
|
|
44
46
|
ws_router = create_websocket_router(ws_manager, form_auth_config=form_auth_config)
|
|
45
47
|
|