From a15499f527aae1b50d83d88b730284cb5f78011d Mon Sep 17 00:00:00 2001 From: Hassan Abedi Date: Fri, 12 Jun 2026 23:02:32 +0200 Subject: [PATCH 1/5] The base commit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ae2e687..a43fbfb 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ To build and run the example applications, you need: #### Running the Demos -You can run any of the demos using the provided `Makefile` targets: +You can run any of the demos using the provided [`Makefile`](Makefile) targets: - Run the GraphRAG pipeline: ```bash From a46a344992eb0bd15e157d2c76ea00ef83e96167 Mon Sep 17 00:00:00 2001 From: Hassan Abedi Date: Sat, 13 Jun 2026 12:14:58 +0200 Subject: [PATCH 2/5] Fix a few bugs --- README.md | 2 +- .../fraud/src/fraud_detection_stream/main.py | 6 ++- rust/code-explorer/src/main.rs | 13 +++-- rust/graphrag-agent/src/ingest.rs | 48 +++++++++++++++++-- 4 files changed, 59 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a43fbfb..fe9bbf8 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Tests](https://img.shields.io/github/actions/workflow/status/IssunDB/example-apps/tests.yml?label=tests&style=flat&labelColor=282c34&logo=github)](https://github.com/IssunDB/example-apps/actions/workflows/tests.yml) [![Code Coverage](https://img.shields.io/codecov/c/github/IssunDB/example-apps?label=coverage&style=flat&labelColor=282c34&logo=codecov)](https://codecov.io/gh/IssunDB/example-apps) [![Python version](https://img.shields.io/badge/python-%3E=3.10-3776ab?style=flat&labelColor=282c34&logo=python)](https://github.com/IssunDB/example-apps) -[![License: MIT](https://img.shields.io/badge/license-MIT-ffd343?style=flat&labelColor=282c34&logo=open-source-initiative)](LICENSE) +[![License: MIT](https://img.shields.io/badge/license-MIT-3776ab?style=flat&labelColor=282c34&logo=open-source-initiative)](LICENSE) This repository includes a collection of example applications that use the [IssunDB](https://github.com/IssunDB/issun-db) graph database. diff --git a/python/fraud/src/fraud_detection_stream/main.py b/python/fraud/src/fraud_detection_stream/main.py index f0b835c..444cb88 100644 --- a/python/fraud/src/fraud_detection_stream/main.py +++ b/python/fraud/src/fraud_detection_stream/main.py @@ -101,11 +101,15 @@ def main(argv: list[str] | None = None) -> int: if not batch: break batch_no += 1 + batch_first_ts = batch[0].ts for event in batch: store.apply(event) last_ts = max(last_ts, event.ts) - since_ts = last_ts - args.window + # Look back `window` seconds, but never start later than the oldest + # event in this batch: a batch whose span exceeds the window would + # otherwise drop patterns injected early in it before they are scored. + since_ts = min(last_ts - args.window, batch_first_ts) new_alerts: list[Alert] = [] for alert in run_all_detectors(db, since_ts): key = (alert.rule, alert.subject) diff --git a/rust/code-explorer/src/main.rs b/rust/code-explorer/src/main.rs index 35f482b..72d2af6 100644 --- a/rust/code-explorer/src/main.rs +++ b/rust/code-explorer/src/main.rs @@ -38,12 +38,12 @@ enum Command { Dead, /// Everything transitively affected if this function changes. Impact { name: String }, - /// Most structurally important functions (PageRank over the call graph). + /// Most structurally important functions (PageRank over the code graph). Rank { #[arg(long, default_value_t = 15)] top: usize, }, - /// Connected components and recursion cycles in the call graph. + /// Connected components and reference cycles across the code graph. Structure, /// Shortest call path between two functions. Path { from: String, to: String }, @@ -176,6 +176,9 @@ fn impact(graph: &Graph, name: &str) -> Result<()> { } fn rank(graph: &Graph, top: usize) -> Result<()> { + // PageRank runs over every edge type (CALLS, CONTAINS, METHOD_OF, + // IMPLEMENTS), so the score is whole-code-graph centrality, not pure + // call-graph importance; we then keep only the Function nodes. let scores = graph.page_rank(30, 0.85)?; let functions = graph.nodes_by_label("Function")?; let mut ranked: Vec<(NodeId, f32)> = functions @@ -184,7 +187,7 @@ fn rank(graph: &Graph, top: usize) -> Result<()> { .collect(); ranked.sort_by(|a, b| b.1.total_cmp(&a.1)); - println!("most depended-upon functions (PageRank over the call graph):"); + println!("most central functions (PageRank over the code graph):"); for (node, score) in ranked.into_iter().take(top) { println!( " {score:.5} {:<36} {}", @@ -208,8 +211,10 @@ fn structure(graph: &Graph) -> Result<()> { sizes.len(), &sizes[..sizes.len().min(8)] ); + // detect_cycle spans all edge types; in this schema a cycle is almost + // always CALLS recursion, but it is not restricted to it. println!( - "recursion present: {}", + "cycle present: {}", if graph.detect_cycle()? { "yes" } else { "no" } ); Ok(()) diff --git a/rust/graphrag-agent/src/ingest.rs b/rust/graphrag-agent/src/ingest.rs index 9d64154..e3d5e00 100644 --- a/rust/graphrag-agent/src/ingest.rs +++ b/rust/graphrag-agent/src/ingest.rs @@ -170,11 +170,10 @@ fn chunk(text: &str) -> Vec { current.push_str("\n\n"); } current.push_str(para); - // Hard-split oversized paragraphs on word boundaries. + // Hard-split oversized paragraphs, cutting only at UTF-8 char + // boundaries so multi-byte text never panics the slice. while current.len() > CHUNK_MAX { - let cut = current[..CHUNK_MAX] - .rfind(char::is_whitespace) - .unwrap_or(CHUNK_MAX); + let cut = split_point(¤t); let rest = current.split_off(cut); chunks.push(std::mem::take(&mut current)); current = rest.trim_start().to_owned(); @@ -186,6 +185,32 @@ fn chunk(text: &str) -> Vec { chunks } +/// Byte index at or below `CHUNK_MAX` at which to split a too-long chunk. +/// +/// Prefers the last whitespace boundary within the limit and always lands on a +/// UTF-8 char boundary, so a chunk containing multi-byte characters cannot +/// trigger a panic when sliced. The result is at least 1, so the split loop +/// always makes progress. +fn split_point(s: &str) -> usize { + if let Some((idx, _)) = s + .char_indices() + .take_while(|(i, _)| *i <= CHUNK_MAX) + .filter(|(_, c)| c.is_whitespace()) + .last() + { + if idx > 0 { + return idx; + } + } + // No usable whitespace in range: fall back to the largest char boundary + // at or below the limit. + let mut cut = CHUNK_MAX; + while cut > 1 && !s.is_char_boundary(cut) { + cut -= 1; + } + cut +} + /// Heuristic named-entity extraction: runs of capitalized words. /// /// A real pipeline would use an NER model or an LLM here; runs of TitleCase @@ -258,4 +283,19 @@ mod tests { assert_eq!(chunks.len(), 1); assert!(chunks[0].contains("para three")); } + + #[test] + fn chunking_splits_multibyte_text_without_panic() { + // A whitespace-free run of 3-byte characters: byte CHUNK_MAX lands + // mid-character, which a naive byte slice would panic on. + let text = "\u{4f60}".repeat(400); // 1200 bytes, 400 chars + let chunks = chunk(&text); + assert!( + chunks.len() > 1, + "expected a hard split, got {}", + chunks.len() + ); + // No whitespace in the input, so the split is lossless. + assert_eq!(chunks.concat().chars().count(), 400); + } } From 36c7d03f15c72aa48c51e439d7be88618e20a77b Mon Sep 17 00:00:00 2001 From: Hassan Abedi Date: Sat, 13 Jun 2026 12:29:52 +0200 Subject: [PATCH 3/5] WIP --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fe9bbf8..ccf2de9 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,12 @@ This repository includes a collection of example applications that use the [Issu Currently, the following table lists the included examples: -| # | Example | Language | Description | -|---|-------------------------------------------------------|----------|------------------------------------------------------------------------------------------| -| 1 | [GraphRAG Pipeline](rust/graphrag-agent) | Rust | Knowledge graph extraction and retrieval-augmented generation using an LLM. | -| 2 | [Codebase Explorer](rust/code-explorer) | Rust | Syntax dependency graph constructor and function ranking using PageRank. | -| 3 | [Fraud Detection System](python/fraud) | Python | Real-time financial transaction stream analyzer using Cypher queries. | -| 4 | [Social Recommendation System](python/recommendation) | Python | Hybrid friend and content recommender using collaborative filtering and semantic search. | +| # | Example | Language | Description | +|---|-------------------------------------------------------|----------|---------------------------------------------------------------------------------------------------| +| 1 | [GraphRAG Pipeline](rust/graphrag-agent) | Rust | Knowledge graph extraction and retrieval-augmented generation using an LLM. | +| 2 | [Codebase Explorer](rust/code-explorer) | Rust | Syntax dependency graph constructor and function ranking using PageRank. | +| 3 | [Fraud Detection System](python/fraud) | Python | Real-time financial transaction stream analyzer using Cypher queries. | +| 4 | [Social Recommendation System](python/recommendation) | Python | Hybrid friend and content recommender using graph traversal, vector search, and hybrid retrieval. | --- From b48b683a4595a779f675d1b8e9bbb25ddf2fde8a Mon Sep 17 00:00:00 2001 From: Hassan Abedi Date: Sat, 13 Jun 2026 12:34:27 +0200 Subject: [PATCH 4/5] WIP --- python/fraud/README.md | 8 +++++--- python/recommendation/README.md | 10 ++++++---- rust/code-explorer/README.md | 6 ++++-- rust/graphrag-agent/README.md | 2 +- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/python/fraud/README.md b/python/fraud/README.md index 68bc382..9bed49c 100644 --- a/python/fraud/README.md +++ b/python/fraud/README.md @@ -4,9 +4,11 @@ This example detects suspicious patterns and fraudulent behaviors in a real-time ### How It Works -1. Generates a continuous stream of financial transactions, accounts, and registration events. -2. Ingests the stream into IssunDB; map accounts, transactions, and devices as nodes and edges. -3. Executes Cypher queries concurrently over the database to identify circular transfers, shared devices, and stolen credentials. +1. Generates a deterministic, (seeded) stream of financial events (that includes account, device, and merchant registrations, transfers, payments, and + device logins information). +2. Ingests the stream into IssunDB; maps accounts, devices, and merchants as nodes and transfers, payments, and logins as edges. +3. Runs four Cypher-based detectors after every batch insert to flag things like circular transfer rings, shared devices, money-mule fan-in, and + velocity bursts. More detailed workflow is shown below: diff --git a/python/recommendation/README.md b/python/recommendation/README.md index 2fe6a33..25954f1 100644 --- a/python/recommendation/README.md +++ b/python/recommendation/README.md @@ -4,10 +4,12 @@ This example builds a social network graph and recommends content and friends to ### How It Works -1. Creates a graph of users, interests, followed tags, and published posts in IssunDB. -2. Computes text embeddings for posts and interests so we can run vector-based similarity checks on them. -3. Combines graph traversal (friend-of-a-friend collaborative filtering) and vector search (semantic similarity) to recommend relevant articles and - new connections. +1. Creates a graph of users, topics, and posts in IssunDB, with `FOLLOWS`, `POSTED`, `ABOUT`, and `LIKES` edges; each user has topic affinities + that drive their interest vector. +2. Computes interest-vector embeddings for users and posts and builds a full-text index over post text, so the graph supports both semantic and + keyword search. +3. Provides four recommendation features, including friend-of-friend suggestions through Cypher, kindred users and posts through vector search, + trending topics through Cypher aggregation over recent likes, and a hybrid discover feed that fuses vector, text, and one-hop graph expansion. More detailed workflow is shown below: diff --git a/rust/code-explorer/README.md b/rust/code-explorer/README.md index e2e2428..887d510 100644 --- a/rust/code-explorer/README.md +++ b/rust/code-explorer/README.md @@ -5,8 +5,10 @@ This example indexes Rust source code, builds a syntax dependency graph, and ran ### How It Works 1. Parses Rust source files into abstract syntax trees (using the `syn` library). -2. Stores syntax elements (like modules, functions, and structs) as nodes, and dependencies (like calls, imports, and definitions) as edges in IssunDB. -3. Computes the structural importance of functions using the PageRank algorithm on the constructed graph. +2. Stores files, functions, structs, enums, and traits as nodes, with `CONTAINS`, `CALLS`, `METHOD_OF`, and `IMPLEMENTS` edges between them. +3. Answers structural questions over the graph, including callers and callees through Cypher, dead-code candidates, transitive impact through native + adjacency traversal, the shortest call path between two functions, weakly connected components, and cycle detection. +4. Ranks functions by structural importance with PageRank over the code graph. More detailed workflow is shown below: diff --git a/rust/graphrag-agent/README.md b/rust/graphrag-agent/README.md index 00f77c3..664cae7 100644 --- a/rust/graphrag-agent/README.md +++ b/rust/graphrag-agent/README.md @@ -5,7 +5,7 @@ This example implements a knowledge graph extraction and question-answering pipe ### How It Works 1. Reads text documents, splits them into text chunks, extracts entities, and records entity co-occurrences. -2. Indexes chunks using a vector index (for semantic retrieval) and an BM25 text index (for keyword retrieval). +2. Indexes chunks using a vector index (for semantic retrieval) and a BM25 text index (for keyword retrieval). 3. Performs a hybrid search on both indexes and resolves the reciprocal-rank fusion of the results. 4. Traverses the graph structure to extract adjacent entities and document contexts. 5. Combines the retrieved context and queries an LLM to generate the final answer. From 5a94457cfb81f71a283facc7f9752c31185a4fb3 Mon Sep 17 00:00:00 2001 From: Hassan Abedi Date: Sat, 13 Jun 2026 13:11:26 +0200 Subject: [PATCH 5/5] Upgrade to version `0.1.0-alpha.6` of `issundb` --- Cargo.lock | 32 ++++++++++++++++---------------- Cargo.toml | 2 +- uv.lock | 10 +++++----- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba55f80..f5a0af0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1043,9 +1043,9 @@ checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "issundb" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e90dbef0feb38ec41a468b70d8af0bf6af5c9bcbac8f77eb55e16e3eb7ed5c" +checksum = "563fffafa9d34fe9b1abf738e8b7061663f5ee398aad342605317fbe215ea0b9" dependencies = [ "issundb-core", "issundb-cypher", @@ -1057,9 +1057,9 @@ dependencies = [ [[package]] name = "issundb-core" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a16184f6ed7d118c0cd05b6537a3d09af8aab0fa9217b88e90ba0f2e5d330647" +checksum = "7185f71c34ea188c8d462622987934cca3b1ae8cefdf8c22c6209f1be9e511fb" dependencies = [ "ahash", "arc-swap", @@ -1080,9 +1080,9 @@ dependencies = [ [[package]] name = "issundb-cypher" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a1796ceecf569179f0de4f5cf42af80d6b8a11035d2b2cbdbe721e1e571c43e" +checksum = "88a54a037f38d2c1517ce8af184e8149b0cd5e906dd58322b6fb7d39c69e2af5" dependencies = [ "ahash", "arrow-array", @@ -1103,9 +1103,9 @@ dependencies = [ [[package]] name = "issundb-graphblas" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cc9c41268ff34fc32180305a68bbc9e5de7fc03b3ccbc479ce2db173661a89f" +checksum = "53517f42b3108f3d5afe7f561ee325e5a37887c7f9d5bcc99f39f3698ca4b14f" dependencies = [ "issundb-graphblas-sys", "thiserror", @@ -1113,9 +1113,9 @@ dependencies = [ [[package]] name = "issundb-graphblas-sys" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5209ca20febfa7e984089eea80a1c0a162491b075e573d3f16727bcea1d9940" +checksum = "ed7aebeb33d006811b73b5e27222f03a33d0fb0c7cc5bb4b6a3a7ddf921dffad" dependencies = [ "bindgen", "cmake", @@ -1124,9 +1124,9 @@ dependencies = [ [[package]] name = "issundb-retrieval" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dc7f637a5d73834d5d38df7d7b9a002f6e4400413b3ebf0493a18f7d4acbaaa" +checksum = "0e6cd0b700c64627dfe6e2a88c1bf4abfd97b7242d1ed543ab7516173a276e25" dependencies = [ "ahash", "issundb-core", @@ -1137,9 +1137,9 @@ dependencies = [ [[package]] name = "issundb-text" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e50d11a0dc9c6a606e73788ef80a7fef86e3de2b83bff627c2694e716ac8f7c6" +checksum = "cd21a4e746a99d288a0229cfdc49487560aa64d60b8e6aef2b3f7a976fbb360f" dependencies = [ "issundb-core", "roaring", @@ -1148,9 +1148,9 @@ dependencies = [ [[package]] name = "issundb-vector" -version = "0.1.0-alpha.5" +version = "0.1.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a807280f765547a637a2eb68811172848a147ae7e0f31987d50fdab7b0689519" +checksum = "59d09a822a12ac77b90bc7cf86aee6624b3b0a7f19c739fe7df8a0881feeb434" dependencies = [ "issundb-core", "parking_lot", diff --git a/Cargo.toml b/Cargo.toml index 047c091..6c5ba81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ rust-version = "1.85.0" publish = false [workspace.dependencies] -issundb = "0.1.0-alpha.5" +issundb = "0.1.0-alpha.6" [profile.release] strip = "debuginfo" diff --git a/uv.lock b/uv.lock index d00957b..2d718ce 100644 --- a/uv.lock +++ b/uv.lock @@ -229,13 +229,13 @@ wheels = [ [[package]] name = "issundb" -version = "0.1.0a5" +version = "0.1.0a6" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/66/5c/2da51a93cfd663b0e7fb3600ab47a03566edb434ed68afe3adf9096a1610/issundb-0.1.0a5-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:e93e8a99d2dbb6f6217689d81ffa49d95a6fab490c86ae97f79fe6402cafcccb", size = 6765643, upload-time = "2026-06-12T07:08:07.819Z" }, - { url = "https://files.pythonhosted.org/packages/7d/61/a388ae043f9591413e7fbde040a3d9712dd61712200888042259c32d3ead/issundb-0.1.0a5-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:81323636bb28728e25efdbf3c4249080aa7f70713afffc050a476d0b92d97a6c", size = 8904389, upload-time = "2026-06-12T07:08:09.464Z" }, - { url = "https://files.pythonhosted.org/packages/db/e3/f27962a616d449dc1c3549487ea1934b8e92a2f45f94a7080b5261d821dc/issundb-0.1.0a5-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:563f22405e00b6a588eb0b9d2a15fa6f9080fdbca5e33d909b26a43303449974", size = 9996085, upload-time = "2026-06-12T07:08:11.312Z" }, - { url = "https://files.pythonhosted.org/packages/34/b0/0a604149968d669780cb80ea83d8e9054ce10b67bb6f41b0b04bcf18cc37/issundb-0.1.0a5-cp310-abi3-win_amd64.whl", hash = "sha256:abe61c032a7b15f406505cbd0db5bdb01d391678b757e85ccd94418bc0a83225", size = 8230228, upload-time = "2026-06-12T07:08:13.336Z" }, + { url = "https://files.pythonhosted.org/packages/62/5c/c344872ee5c6c202b19bae24050f0b1cec5b10e220567a9123968ef9f742/issundb-0.1.0a6-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:27e1187b48c3d32128a609fc446880e5c88b8a43a90310ce81a72ee194b0fa59", size = 6774961, upload-time = "2026-06-13T10:41:40.853Z" }, + { url = "https://files.pythonhosted.org/packages/54/bf/2daa7d88885a78b86b3d952eaee4f3dc5379f562acb882cac80a44a95868/issundb-0.1.0a6-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4604dc8db7eb25cf1a6f0952c594fe462cccd247996b410170e627bc7a6b160d", size = 8905689, upload-time = "2026-06-13T10:41:42.289Z" }, + { url = "https://files.pythonhosted.org/packages/0a/ad/37e07c8ccc9ee9995deac7febc98fd26910e957a129fbdc19ed141013e9e/issundb-0.1.0a6-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:766d4f61213dac6b730b008fca5a53a44b4e8a241e2d418a45f1332a60e9da96", size = 9997597, upload-time = "2026-06-13T10:41:44.327Z" }, + { url = "https://files.pythonhosted.org/packages/93/36/d48ac8203f18054025e01772f64812e7fd9683b7fc4ae743972090e39944/issundb-0.1.0a6-cp310-abi3-win_amd64.whl", hash = "sha256:cbe9c6e279805d800ca9f678dee96b989f7929be2a33853e4a93c5288c9cf3b6", size = 8233143, upload-time = "2026-06-13T10:41:46.071Z" }, ] [[package]]