The codebase¶
About 10,900 lines of package code in eight modules, plus a vendored copy of HIP-LLM that is never edited here. This page is the map: what each module owns, which types cross the boundaries between them, and the order to read them in.
Read these five, in this order¶
You do not need all of it to make a change. These five files, about 3,000 lines, carry every idea in the package:
architecture/model.py- what a component is, and how one is read out of a LangGraph object.faulttree/failure.py- what a failure is: deviations, Boolean expressions, and the archetype library that gives each role its local logic.faulttree/synthesis.py- the backward traversal that turns those two into a tree. This is the heart of HiP-HOPS.reliability/calibration.py- how a measured interval per component is written onto the tree’s leaves without changing what the tree says.pipeline.py- the façade that sequences all of it, and the guards that refuse to skip a step.
Everything else is analysis over those structures (faulttree/analysis.py),
translation of them (bayes/, faulttree/export.py, io/n8n.py), or drawing
them (viz/, bayes/viz.py).
The pipeline, as data¶
Each arrow is a type, and each type is the contract between two modules. If you are changing one module, this says what you must keep producing.
a LangGraph app, mermaid text, a dict spec, or an n8n export
│
│ extract_architecture / LangGraphExtractor / load_n8n
▼
SystemModel components, connections, resources
│ (architecture/model.py)
│ make_acyclic loops unrolled + feedback-cut components
▼
SystemModel (acyclic)
│
│ annotate_system one archetype per Role
▼
FailureModel ComponentFailureLogic per component,
│ BasicEvent registry, CCF groups
│ (faulttree/failure.py)
│
├── calibrate_failure_model ◀── ComponentEvidence ◀── outcomes + OperationalProfile
│ writes measured intervals onto BasicEvent.prob_interval
│ (reliability/calibration.py, reliability/hipllm.py)
│
│ synthesise_fault_tree backward traversal from a Hazard
▼
FaultTree FTNode DAG with shared sub-trees
│ (faulttree/synthesis.py)
│
├── analyse_tree ──▶ TreeAnalysis cut sets, MCUB, importance
│ (faulttree/analysis.py)
│
│ fault_tree_to_cpts one CPT per gate
▼
CPTSet ──▶ BayesianNetwork exact inference, diagnosis
(bayes/cpt.py, bayes/network.py)
SafetyReport (report.py) bundles the middle of that chain for one system;
AgenticReliabilityStudy (pipeline.py) drives the whole of it and owns the
state machine.
Module by module¶
architecture/ - HiP-HOPS Phase 0, “model the architecture”¶
File |
Owns |
|---|---|
|
|
|
|
|
|
Three things here are load-bearing and easy to break:
Router materialisation. add_conditional_edges has no node of its own, so a
routing mistake would have nowhere to live. materialise_routers=True (the
default) creates a <node>::router component and rewires the edges through it.
The node -> node::router feed edge must be emitted whether or not this call
created the router; a pre-declared router that loses it disconnects the graph.
Resource detection. Node source is scanned for model and tokenizer
assignments; two components naming the same snapshot become a common-cause
group. The matcher is a name filter, deliberately not a regex requiring
surrounding characters - model, model_deep and my_model must all match, and
an earlier regex that missed the bare names made shared snapshots invisible.
Loop elimination. A back edge is never deleted. It is replaced by a feedback-cut pseudo-component that carries iteration-budget and iteration-latency events, so the loop’s contribution stays in the tree. Deleting it would understate risk silently.
faulttree/ - Phases 1 to 3¶
File |
Owns |
|---|---|
|
|
|
|
|
|
|
|
The six failure classes are the vocabulary everything else is written in:
|
nothing was produced |
|
something was produced that should not have been |
|
wrong, and detectable - malformed, truncated |
|
wrong, and plausible - the undetectable case |
|
too soon |
|
too late, budget exhausted |
Keeping VC apart from VS is the single most consequential modelling decision
in the package. A coarse deviation can be caught by a validator downstream; a
subtle one propagates through every component that has no way to detect it, and
that transparency is what makes an apparently-checked pipeline unchecked.
A component’s local logic is an IF-FMEA table: for each output Deviation, a
Boolean Expr over input deviations and internal basic events. Synthesis walks
backwards from a hazard, substituting each table and resolving input deviations
across connections, memoised so shared sub-trees stay shared.
reliability/ - the numbers¶
File |
Owns |
|---|---|
|
|
|
the thin adapter onto the vendored HIP-LLM engine |
|
|
Two rules live here and are enforced by tests.
The union split is exact. A component’s measured failure probability P is
distributed over its w basic events by p_i = 1 - (1 - P)^{w_i}, so that
1 - Π(1 - p_i) = P holds to 1e-9. Splitting proportionally instead would
change the number the measurement produced.
Calibrating twice must not compound. The split weights events by their
prior probability, so each BasicEvent records baseline_prob once. Without
it the second calibrate() would use the first one’s output as weights.
hipllm.py is an adapter, not an implementation. The model itself is the
vendored code under src/HIPLLM/ and src/hip_llm/, which is a byte copy and
is never edited here - see Vendoring.
bayes/ - the tree as a network¶
File |
Owns |
|---|---|
|
|
|
|
|
|
|
|
Every probability is computable twice: through pyAgrum, and through a NumPy enumeration that needs no optional dependency. They are cross-checked against each other to nine significant figures, and CI runs the whole suite once with pyAgrum uninstalled. Anything added here must work on both paths or say in its docstring that it does not.
ImpreciseBayesianNetwork builds a lower and an upper twin network. That is only
valid because the top event is monotone in every basic event, which
test_network.py::test_monotone_in_every_basic_event checks. A non-monotone gate
would silently invalidate it, which is why soft_gates records a note.
io/ - getting systems in¶
File |
Owns |
|---|---|
|
|
|
|
n8n.py is the largest single file and the one most likely to need extending,
because it encodes knowledge about a third-party product that changes. Its
RULES tuple is ordered and first-match-wins; each rule carries a why string
that reaches the ledger the analyst reads. See
Analysing an n8n workflow.
pipeline.py and report.py - the façades¶
SafetyReport holds one system’s trees and analyses and knows how to render and
save them. AgenticReliabilityStudy is the object a notebook touches: it owns
the state machine (analyse → observe → calibrate → run), the guards that
refuse to run a step out of order, and run_and_observe, which invokes a live
graph and scores every node in one call.
The rule run_and_observe exists to enforce: a success predicate returning
None means not exercised and a crashed run means nothing observed. Both are
missing observations, never failures. Scoring an unreached node as failed blames
it for an upstream fault and inflates the top event - a mistake this package made
once, caught by testing against a replica of a real notebook.
viz/plots.py - drawing¶
plot_fault_tree, plot_architecture, plot_importance, plot_cutset_orders.
All return a matplotlib ax; none require Graphviz. Conventional FTA symbols,
transfer triangles for shared sub-trees, and a reserved colour for anything
critical that is always paired with a text tag, so no finding rests on colour
alone.
Naming¶
The package is hiphopsllm. src/HIP_HOPS_LLM.py is a compatibility alias that
registers the submodules in sys.modules; it exists so that from HIP_HOPS_LLM import ... keeps working for people who copied it from the original notebook.
Only one of the two spellings can ship, because Windows and macOS filesystems are
case-insensitive.
If you are reading the original Kaggle notebook alongside this code, its
single-cell module was called hipgraph and the sections map like this:
Notebook |
Here |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
What is deliberately not here¶
No solver. Cut sets are computed by MOCUS with absorption, not by handing the tree to an external engine.
to_openpsa_xmlexists for when you want one.No inference of the operational profile. Ever. See Operational profiles.
No silent fallback. If a function cannot do what was asked it raises, naming what was wrong and what the valid options are.