sqlite_graphrag/constants/limits.rs
1//! Length, size and count ceilings enforced on stored data.
2//!
3//! Split out of the former single-file `constants.rs` in v1.2.5;
4//! every item is re-exported by the parent module, so `crate::constants::X`
5//! resolves exactly as before.
6
7/// Maximum byte length for a memory `name` field in kebab-case.
8pub const MAX_MEMORY_NAME_LEN: usize = 80;
9
10/// Maximum byte length for an `ingest`-derived kebab-case name.
11///
12/// Stricter than `MAX_MEMORY_NAME_LEN` (80) to leave headroom for collision
13/// suffixes (`-2`, `-10`, ...) when multiple files derive to the same base.
14/// Used exclusively by `src/commands/ingest.rs`.
15pub const DERIVED_NAME_MAX_LEN: usize = 60;
16
17/// Maximum character length for a memory `description` field.
18pub const MAX_MEMORY_DESCRIPTION_LEN: usize = 500;
19
20/// Hard upper bound on memory `body` length in bytes.
21pub const MAX_MEMORY_BODY_LEN: usize = 512_000;
22
23/// Body character count above which the body is split into chunks.
24pub const MAX_BODY_CHARS_BEFORE_CHUNK: usize = 8_000;
25
26/// Default upper bound on distinct entities persisted per memory.
27///
28/// Bumped from 30 → 50 in v1.0.43 to reduce semantic loss on rich documents.
29/// Configurable at runtime via XDG / runtime_config (not product env).
30pub const MAX_ENTITIES_PER_MEMORY: usize = 50;
31
32/// Resolves the per-memory entity cap (flag/XDG/`runtime_config`).
33///
34/// v1.0.43: makes the cap (default 50) configurable without product env.
35/// Stress tests showed inputs with 33-46 candidates being truncated at the old cap of 30.
36/// Values outside [1, 1000] fall back to the default.
37pub fn max_entities_per_memory() -> usize {
38 let n = crate::runtime_config::max_entities_per_memory(MAX_ENTITIES_PER_MEMORY);
39 if (1..=1_000).contains(&n) {
40 n
41 } else {
42 MAX_ENTITIES_PER_MEMORY
43 }
44}
45
46/// Upper bound on distinct relationships persisted per memory.
47pub const MAX_RELATIONSHIPS_PER_MEMORY: usize = 50;
48
49/// Resolves the per-memory relationship cap (flag/XDG/`runtime_config`).
50///
51/// v1.0.22: makes the cap (default 50) configurable without product env.
52/// Audit found that rich documents silently hit the cap; users with dense technical corpora
53/// can raise it via XDG. Values outside [1, 10000] fall back to the default.
54pub fn max_relationships_per_memory() -> usize {
55 let n = crate::runtime_config::max_relations_per_memory(MAX_RELATIONSHIPS_PER_MEMORY);
56 if (1..=10_000).contains(&n) {
57 n
58 } else {
59 MAX_RELATIONSHIPS_PER_MEMORY
60 }
61}
62
63/// Character length of the description preview shown in `list` output.
64pub const TEXT_DESCRIPTION_PREVIEW_LEN: usize = 100;
65
66/// Chunk size expressed in tokens for body splitting.
67pub const CHUNK_SIZE_TOKENS: usize = 400;
68
69/// Token overlap between consecutive chunks.
70pub const CHUNK_OVERLAP_TOKENS: usize = 50;
71
72/// Explicit operational guard for multi-chunk documents in `remember`.
73///
74/// The multi-chunk path uses serial embeddings to avoid ONNX memory amplification.
75/// This limit preserves a clear operational ceiling for agents and scripts.
76pub const REMEMBER_MAX_SAFE_MULTI_CHUNKS: usize = 512;
77
78/// Ceiling on chunks per controlled micro-batch in `remember`.
79///
80/// The `fastembed` runtime uses `BatchLongest` padding, so oversized batches amplify
81/// the cost of the longest chunk. This ceiling keeps batches small even when chunks are short.
82pub const REMEMBER_MAX_CONTROLLED_BATCH_CHUNKS: usize = 4;
83
84/// Maximum padded-token budget per controlled micro-batch in `remember`.
85///
86/// The budget uses `max_tokens_no_batch * batch_size`, approximating the real cost of
87/// `BatchLongest` padding. Values exceeding this fall back to smaller batches or serialisation.
88pub const REMEMBER_MAX_CONTROLLED_BATCH_PADDED_TOKENS: usize = 512;
89
90/// PRD-canonical regex that validates names and namespaces. Allows 1 char `[a-z0-9]`
91/// OR a 2-80 char string starting with a letter and ending with a letter/digit,
92/// containing only `[a-z0-9-]`. Rejects the `__` prefix (internal reserved).
93pub const NAME_SLUG_REGEX: &str = r"^[a-z][a-z0-9-]{0,78}[a-z0-9]$|^[a-z0-9]$";
94
95static NAME_SLUG_RE: std::sync::OnceLock<regex::Regex> = std::sync::OnceLock::new();
96
97/// Returns a reference to the compiled [`NAME_SLUG_REGEX`] pattern.
98/// Compiled once on first call, cached via `OnceLock`.
99// expect_used (audited v1.0.97): NAME_SLUG_REGEX is a const literal; a parse
100// failure would be a compile-reproducible bug, never a runtime condition.
101#[allow(clippy::expect_used)]
102pub fn name_slug_regex() -> &'static regex::Regex {
103 NAME_SLUG_RE.get_or_init(|| {
104 regex::Regex::new(NAME_SLUG_REGEX).expect("NAME_SLUG_REGEX is a valid pattern")
105 })
106}
107
108/// Default retention period (days) used by `purge` when `--retention-days` is omitted.
109pub const PURGE_RETENTION_DAYS_DEFAULT: u32 = 90;
110
111/// Maximum number of simultaneously active namespaces (deleted_at IS NULL). Exit 5 when exceeded.
112pub const MAX_NAMESPACES_ACTIVE: u32 = 100;
113
114/// Byte budget for one auto-split partition (sub-memory) in `ingest`
115/// (GAP-SG-04/07).
116///
117/// Chosen below the 127 KB body margin so each partition also stays under
118/// [`REMEMBER_MAX_SAFE_MULTI_CHUNKS`] chunks and [`EMBEDDING_REQUEST_MAX_TOKENS`]
119/// tokens, even for multibyte/CJK text (~1 cl100k token per UTF-8 char, so
120/// 80 KiB / 3 bytes-per-char yields about 27K tokens, below the 30K ceiling).
121pub const AUTOSPLIT_PARTITION_MAX_BYTES: usize = 80 * 1024;
122
123/// Degree above which `health` reports an entity as a super-hub.
124///
125/// A hub this wide makes graph traversal fan out badly, so the check exists to
126/// prompt a `prune-relations` or `merge-entities` pass.
127pub const HEALTH_SUPER_HUB_DEGREE_THRESHOLD: i64 = 50;
128
129/// How many super-hubs `health` names in its warning string.
130///
131/// This bounds the *sample* shown to a human. It must never bound the reported
132/// count, which is measured separately over the whole graph.
133pub const HEALTH_SUPER_HUB_SAMPLE_LIMIT: usize = 5;
134
135/// Character size of the body preview emitted in text/markdown formats.
136pub const TEXT_BODY_PREVIEW_LEN: usize = 200;