Skip to content

Development merge for 0.5.3 - #287

Open
ipmach wants to merge 41 commits into
mainfrom
development
Open

Development merge for 0.5.3#287
ipmach wants to merge 41 commits into
mainfrom
development

Conversation

@ipmach

@ipmach ipmach commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Task

Description

How Has This Been Tested?

Checklist

  • This Pull-Request goes to the development branch.
  • I have successfully run prek locally.
  • I have added tests to cover my changes.
  • I have linked the issue-id to the task-description.
  • I have performed a self-review of my own code.

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

ipmach and others added 30 commits July 22, 2026 15:52
Updated text for clarity and corrected typos in the federation documentation.
Both detectors kept their learned count vectors in a plain in-memory set,
so a trained model was lost on restart. They now store them as keys in
EventPersistency.events_seen, the pattern EventSequenceDetector already
uses, which gives them save, load and auto-load for free.

ECVC derives its matrix and threshold from those vectors in _derive(),
called after training and after a load. The vectors are sorted first:
restored keys are strings whose set iteration order is hash-randomized
per process, and the seeded shuffle splits train from validation by that
order, so sorting is what makes a restored model equal a trained one.

Keys carry the window size, since a count vector's length is
max(EventID) + 1 and says nothing about the window it was counted over.
Restoring state at a different window_size now logs a warning instead of
silently alerting on every window.

The count vector and sequence codec helpers move to
utils/sequence_encoding.py so no detector imports from another. As a
result build_count_vec is no longer importable from scvs_detector.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
StabilityClassifier.incline() computes where in a binary change series the
changes sit, in [-0.5, +0.5]: -0.5 is every change at the very start, 0 is
uniform churn, +0.5 is every change at the end. It is the least-squares
slope with its data-free factors divided out, so the value is comparable
between events instead of scaling with the change count. RLELists are read
via runs(), one pass, no expansion.

Trackers opt in with require_declining, which adds a conjunct to STABLE
requiring the variable's changes to sit early in its series. Independent of
segmentation -- it reads index positions, not timestamps.

Committed with --no-verify: this is a snapshot of work that predates the
branch and was never hook-clean. The lint and mypy fixes land in the
following commit, so the branch tip is clean.
Configure-phase inputs move out of `params` into their own top-level
`auto_config_params` block, so a config makes plain which settings shape
auto-configuration and which drive training and detection.

  AutoConfigParams (common/detector.py) is the base; each family narrows it:
  - VariableAutoConfigParams -- use_stable_vars, use_static_vars,
    segmentation, timestamp_variable, timestamp_format, require_declining,
    incline_threshold
  - ComboAutoConfigParams    -- adds max_combo_size
  - SequenceAutoConfigParams -- min_window_size, max_window_size

The `stability_` prefix is dropped inside the block (stability_segmentation
-> segmentation, stability_require_declining -> require_declining); the
prefix only existed to disambiguate names sharing a flat namespace.

incline_threshold becomes configuration rather than a constant reachable
only through tracker.stability_classifier.

set_configuration() no longer rebuilds self.config. It writes only what the
configure phase produced -- config.events (via the new generate_events_config)
and, for EventSequenceDetector, fixed_window_size -- then flips auto_config
off. The wholesale rebuild is what silently dropped `persist` and every
other operator setting, and is why _CARRIED_SETTINGS and the four
hand-written restore lists existed; all are deleted. AutoConfigWarning goes
too: it warned about params being lost, which no longer happens.

Auto-config settings now shape the configure-phase persistency only. The
trained persistency is built without stability kwargs and _ingest no longer
feeds it timestamps -- stability classification is never consulted at
detect time, so those were an unread O(N) list per tracked variable in the
detector that actually runs in production.

BREAKING: no legacy compatibility. The old flat spellings are validation
errors, not deprecated aliases.

BREAKING: persisted tracker state written before this commit no longer
loads. SingleStabilityTracker.to_state() serializes the whole detector
config verbatim and from_state() reinflates it through an extra="forbid"
class, so old blobs carrying the old flat field names raise
PersistencyLoadError. This is not new to this change -- any field rename in
any detector config breaks old state the same way -- but this commit renames
fields, so it triggers it. Existing state files must be regenerated.
The section's only config block showed `segmentation: time`, which reads as
the recommended setup. The default is `count` -- as the fields table below
it already says -- so name that at the point the example appears.
…ectors

represent ECVC and SCVS state with persistency (#255)
[1/3] Federation implementation
Leokaufi and others added 11 commits August 26, 2026 16:02
Renamed 'New Value Detector' to 'Charset Detector' for clarity.
Rename New Value Detector to Charset Detector
docs: add guide for adding tested doc examples
…on rule

Replace the `segmentation` enum plus the `require_declining` /
`incline_threshold` flags with four independently selectable stability
classification methods, combined by a configurable decision rule.

- Add `ClassificationMethods` (pydantic): `index`, `time`, `slope_index`,
  `slope_time` booleans, `slope_threshold` (default -0.05), and
  `decision` ("consensus" | "majority"). At least one method must be
  enabled; `extra="forbid"`.
- `StabilityClassifier` gains `verdicts()` (per-method votes) and
  `decide()` (applies the decision rule). `is_stable()` now routes
  through them. Segment methods (`index`, `time`) cut the change series
  by equal count or equal duration; slope methods vote STABLE when the
  change centroid `k <= slope_threshold`.
- Timestamps are now only consulted when a time-based method is
  enabled -- previously they were self-sufficient. Call sites that
  relied on passing timestamps alone must enable `time` or `slope_time`.
- `StabilityTracker` translates legacy persisted state into the new
  model, so existing event stores keep loading.
- Rename the old "count"/"decline" vocabulary to "index"/"slope"
  throughout, and document the breaking changes plus the fallback
  behaviour in docs/detectors.md.

Tests: replace test_incline_stability.py with test_slope_stability.py
and add test_classification_methods.py; update the time-dependent and
detector tests for the new configuration surface.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review feedback on #275: the block was declared only on
CoreDetectorConfig, yet `auto_config` and `Component.configure()` are
both declared on the shared base, and `BasicConfig.to_dict` and
`ConfigMethods.process` already handled `auto_config_params` generically
for every component type. The base knew about a subclass-only field.

`AutoConfigParams` moves to `common/_config` (declaring it on
`BasicConfig` from `detector.py` would be an import cycle) and is
re-exported from `common.detector` so existing subclass imports keep
working. The redundant-alias form satisfies mypy's no_implicit_reexport.

Parsers and alert aggregators inherit the block empty; `to_dict` omits a
block at its default, so no existing YAML changes. `get_config()` is a
`model_dump()`, so it now reports `auto_config_params: {}` on every
config -- hence the test_core default_args update.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…eparation

Feat/auto config params separation
@ipmach
ipmach requested a review from viktorbeck98 September 2, 2026 07:09
out = [False] * n
out[0] = True
for r in change_ranges:
for i in r:
assert parsed["template"] == "hello there <*> kenobi"

parser.update_state("keep_training")
parsed = parser.process(schemas.LogSchema({"log": "bella ciao bella ciao"}))
assert parsed["template"] == "hello there <*> kenobi"

parser.update_state("keep_training")
parsed = parser.process(schemas.LogSchema({"log": "bella ciao bella ciao"}))
) -> None:
pass

def set_configuration(self) -> None:
@@ -1,4 +1,6 @@
from detectmatelibrary.common._config._formats import EventsConfig, _EventInstance
# Re-exported: subclasses spell it `from detectmatelibrary.common.detector import AutoConfigParams`.
from detectmatelibrary.common._config import AutoConfigParams as AutoConfigParams # noqa: F401
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants