Time Series Analytics — Dev
Work on the service's source. This skill assumes a repo clone of
edge-ai-libraries with this microservice at
microservices/time-series-analytics/; if there is no clone, clone the
repo first.
Run all commands from the microservice root.
When to Use
- Add, modify, or remove a REST route in
src/main.py
- Change Kapacitor/UDF lifecycle behavior in
src/classifier_startup.py
- Run or extend the unit test suite, or the Docker/Helm functional suite
- Build the image from source, debug a running container, or tune GPU/core-pinning behavior
- Cut a release: bump the version consistently across the files that track it
Example Prompts
Sample problem-solving scenarios this skill handles end-to-end:
Reference Lookup
The one gotcha to know first
src/main.py imports classifier_startup at module load, and importing
that for real starts an actual Kapacitor daemon subprocess. Any test that
imports main must mock classifier_startup in sys.modules before
the import — tests/test_main.py already does this; reuse its pattern
rather than re-importing main fresh in a new test module. Details:
references/testing.md.
Environment setup
python3 -m venv env && source env/bin/activate
pip install -r requirements.txt -r tests/requirements.txt
Test / verify loop
./tests/run_tests.sh # full unit suite + coverage (see references/testing.md)
PYTHONPATH=./src pytest tests -k <name> -v # fast iteration on one test
Functional (slow — builds the image / stands up Helm):
cd tests-functional && pip install -r requirements.txt
pytest -q -vv --self-contained-html --html=./test_report/report.html .
Source map (summary)
src/main.py (~900 lines) — every route: ingestion, config, UDF package
upload/validation, OPC UA alerts. The module-level config dict is the
single source of truth; POST /config is the only writer at runtime.
src/classifier_startup.py (~550 lines) — Kapacitor daemon lifecycle:
rewrites kapacitor.conf's [udf.functions.*]/[[mqtt]] sections from
config, validates the extracted UDF package's files exist, starts
kapacitord as a subprocess, enables the Kapacitor task via its CLI.
src/opcua_alerts.py (~210 lines) — asyncua-based OPC UA client used by
the /opcua_alerts route.
- Full annotated map:
references/source-map.md.
Build & deploy from source
cd docker && docker compose build && docker compose up -d
GPU driver setup, CPU core-pinning (CORE_PINNING env var), Helm chart
values, and the /dev/dri-mount gotcha on GPU-less hosts:
references/build-and-deploy.md.
Debug a running instance
docker logs -f ia-time-series-analytics-microservice — startup,
Kapacitor task enable/retry, request logs.
curl -sf http://localhost:5000/health (503 = Kapacitor daemon not
running, not just "process not ready").
- Kapacitor-internal errors aren't in the container's top-level log:
docker exec -it ia-time-series-analytics-microservice bash then
cat /tmp/log/kapacitor/kapacitor.log | grep -i error.
Contribution gotchas
| Gotcha |
Consequence |
classifier_startup starts a real Kapacitor daemon on import |
tests must mock it in sys.modules before importing main (see above) |
The three UDF names (config.json's udfs.name, .py filename, .tick filename, tick script's @name() node) must be identical |
a mismatch fails silently at the pipeline level, not loudly — worth checking first when a "deployment succeeded but nothing happens" bug report comes in |
kapacitord runs as a subprocess inside this same container, not a sidecar |
don't assume container-to-container networking semantics when tracing a startup failure |
Compose unconditionally mounts /dev/dri |
fails container startup on hosts with no Intel iGPU — see references/build-and-deploy.md |
A version bump touches docker/.env, helm/values.yaml, and README-dockerhub.md together |
see references/release-conventions.md — don't bump only one |
| Every new source/config/doc file needs the SPDX header |
matches the existing files' Apache v2 license / Copyright (C) 2026 Intel Corporation / SPDX-License-Identifier: Apache-2.0 block |
1---2name: time-series-analytics-dev3description: Develop the Time Series Analytics microservice itself (FastAPI + Kapacitor) — build and deploy it locally via Docker Compose or Helm, run the mocked unit test suite (tests/run_tests.sh) and the slower Docker/Helm end-to-end functional suite (tests-functional/), navigate and modify src/main.py (routes), src/classifier_startup.py (Kapacitor/UDF lifecycle), and src/opcua_alerts.py, and follow this service's release conventions (CHANGELOG.md, image-tag bump locations, Dockerfile build args). Use when modifying, testing, debugging, or releasing this service's own code. Not for merely deploying the prebuilt image to build a new UDF-based use case on top of it — that is time-series-analytics-user.4---56# Time Series Analytics — Dev78Work on the service's source. **This skill assumes a repo clone** of9`edge-ai-libraries` with this microservice at10`microservices/time-series-analytics/`; if there is no clone, clone the11repo first.12Run all commands from the microservice root.1314## When to Use1516- Add, modify, or remove a REST route in `src/main.py`17- Change Kapacitor/UDF lifecycle behavior in `src/classifier_startup.py`18- Run or extend the unit test suite, or the Docker/Helm functional suite19- Build the image from source, debug a running container, or tune GPU/core-pinning behavior20- Cut a release: bump the version consistently across the files that track it2122## Example Prompts2324Sample problem-solving scenarios this skill handles end-to-end:2526| Example | Problem it solves |27|---|---|28| [add-udf-list-endpoint.md](./example-prompts/add-udf-list-endpoint.md) | Add a new REST route with test coverage |29| [debug-udf-not-starting.md](./example-prompts/debug-udf-not-starting.md) | Diagnose a deployed UDF that silently isn't processing data |3031## Reference Lookup3233| File | Load when… |34|---|---|35| [`references/source-map.md`](./references/source-map.md) | locating where a route, config field, or lifecycle step lives before editing |36| [`references/testing.md`](./references/testing.md) | writing new tests, running a subset, or avoiding the import-time Kapacitor-startup trap |37| [`references/build-and-deploy.md`](./references/build-and-deploy.md) | building the image, GPU/core-pinning setup, Helm deployment |38| [`references/release-conventions.md`](./references/release-conventions.md) | bumping the version, updating CHANGELOG.md, touching Dockerfile build args |3940## The one gotcha to know first4142`src/main.py` imports `classifier_startup` at module load, and importing43*that* for real starts an actual Kapacitor daemon subprocess. Any test that44imports `main` must mock `classifier_startup` in `sys.modules` **before**45the import — `tests/test_main.py` already does this; reuse its pattern46rather than re-importing `main` fresh in a new test module. Details:47[`references/testing.md`](./references/testing.md).4849## Environment setup5051```bash52python3 -m venv env && source env/bin/activate53pip install -r requirements.txt -r tests/requirements.txt54```5556## Test / verify loop5758```bash59./tests/run_tests.sh # full unit suite + coverage (see references/testing.md)60PYTHONPATH=./src pytest tests -k <name> -v # fast iteration on one test61```6263Functional (slow — builds the image / stands up Helm):64```bash65cd tests-functional && pip install -r requirements.txt66pytest -q -vv --self-contained-html --html=./test_report/report.html .67```6869## Source map (summary)7071- `src/main.py` (~900 lines) — every route: ingestion, config, UDF package72 upload/validation, OPC UA alerts. The module-level `config` dict is the73 single source of truth; `POST /config` is the only writer at runtime.74- `src/classifier_startup.py` (~550 lines) — Kapacitor daemon lifecycle:75 rewrites `kapacitor.conf`'s `[udf.functions.*]`/`[[mqtt]]` sections from76 `config`, validates the extracted UDF package's files exist, starts77 `kapacitord` as a subprocess, enables the Kapacitor task via its CLI.78- `src/opcua_alerts.py` (~210 lines) — `asyncua`-based OPC UA client used by79 the `/opcua_alerts` route.80- Full annotated map: [`references/source-map.md`](./references/source-map.md).8182## Build & deploy from source8384```bash85cd docker && docker compose build && docker compose up -d86```8788GPU driver setup, CPU core-pinning (`CORE_PINNING` env var), Helm chart89values, and the `/dev/dri`-mount gotcha on GPU-less hosts:90[`references/build-and-deploy.md`](./references/build-and-deploy.md).9192## Debug a running instance93941. `docker logs -f ia-time-series-analytics-microservice` — startup,95 Kapacitor task enable/retry, request logs.962. `curl -sf http://localhost:5000/health` (503 = Kapacitor daemon not97 running, not just "process not ready").983. Kapacitor-internal errors aren't in the container's top-level log:99 `docker exec -it ia-time-series-analytics-microservice bash` then100 `cat /tmp/log/kapacitor/kapacitor.log | grep -i error`.101102## Contribution gotchas103104| Gotcha | Consequence |105|---|---|106| `classifier_startup` starts a real Kapacitor daemon on import | tests must mock it in `sys.modules` before importing `main` (see above) |107| The three UDF names (`config.json`'s `udfs.name`, `.py` filename, `.tick` filename, tick script's `@name()` node) must be identical | a mismatch fails silently at the pipeline level, not loudly — worth checking first when a "deployment succeeded but nothing happens" bug report comes in |108| `kapacitord` runs as a subprocess *inside this same container*, not a sidecar | don't assume container-to-container networking semantics when tracing a startup failure |109| Compose unconditionally mounts `/dev/dri` | fails container startup on hosts with no Intel iGPU — see `references/build-and-deploy.md` |110| A version bump touches `docker/.env`, `helm/values.yaml`, and `README-dockerhub.md` together | see `references/release-conventions.md` — don't bump only one |111| Every new source/config/doc file needs the SPDX header | matches the existing files' `Apache v2 license` / `Copyright (C) 2026 Intel Corporation` / `SPDX-License-Identifier: Apache-2.0` block |