mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-09-01 04:00:28 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3c540cfd2 | ||
|
|
46b18fbee3 | ||
|
|
b0679d4f67 | ||
|
|
d135ad185f |
@@ -11,3 +11,4 @@ python-docx
|
||||
beautifulsoup4
|
||||
chardet
|
||||
langdetect
|
||||
en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.8.0/en_core_web_sm-3.8.0-py3-none-any.whl
|
||||
|
||||
@@ -408,6 +408,9 @@ cuda-toolkit==13.0.3.0 \
|
||||
# via
|
||||
# -c requirements-ci.txt
|
||||
# torch
|
||||
en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.8.0/en_core_web_sm-3.8.0-py3-none-any.whl \
|
||||
--hash=sha256:1932429db727d4bff3deed6b34cfc05df17794f4a52eeb26cf8928f7c1a0fb85
|
||||
# via -r .github/requirements/benchmark-extra.in
|
||||
et-xmlfile==2.0.0 \
|
||||
--hash=sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa \
|
||||
--hash=sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
"""Drop checkov-suppressed results from its SARIF output before upload.
|
||||
|
||||
checkov's SARIF exporter includes every evaluated check as an ordinary
|
||||
result, including ones it internally marked SKIPPED via an inline
|
||||
`# checkov:skip=` comment or a `checkov.io/skipN` resource annotation - it
|
||||
never uses SARIF's `suppressions` field, and never drops them. checkov's
|
||||
JSON output *does* correctly record which checks were skipped, so this
|
||||
cross-references the two: any SARIF result whose (check_id, file) pair
|
||||
appears in the JSON's skipped_checks is removed before GitHub ever sees it.
|
||||
|
||||
Without this, every already-suppressed finding reopens as a brand new code
|
||||
scanning alert on every run, forever (see #6035/#6036, #6112-6115,
|
||||
#6128-6131 for the pattern this was chasing before this script existed).
|
||||
|
||||
Usage: filter_checkov_skipped.py <json_path> <sarif_in_path> <sarif_out_path>
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
|
||||
|
||||
def path_suffix(path: str, segments: int = 2) -> str:
|
||||
"""Last N path segments, normalized to forward slashes, lowercased.
|
||||
|
||||
checkov's JSON file_path and SARIF artifactLocation.uri are relative to
|
||||
different roots (the scanned directory vs. a temp helm-render dir), so
|
||||
they can't be compared directly - but the last couple of segments
|
||||
(e.g. "templates/service.yaml") are stable across both and specific
|
||||
enough in practice to avoid cross-file collisions.
|
||||
"""
|
||||
normalized = path.replace("\\", "/").strip("/")
|
||||
return "/".join(normalized.split("/")[-segments:]).lower()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
json_path, sarif_in_path, sarif_out_path = sys.argv[1:4]
|
||||
|
||||
with open(json_path, encoding="utf-8") as f:
|
||||
checkov_json = json.load(f)
|
||||
if isinstance(checkov_json, dict):
|
||||
checkov_json = [checkov_json]
|
||||
|
||||
skipped = set()
|
||||
for block in checkov_json:
|
||||
for check in block.get("results", {}).get("skipped_checks", []):
|
||||
skipped.add((check["check_id"], path_suffix(check["file_path"])))
|
||||
|
||||
with open(sarif_in_path, encoding="utf-8") as f:
|
||||
sarif = json.load(f)
|
||||
|
||||
removed = 0
|
||||
for run in sarif.get("runs", []):
|
||||
kept = []
|
||||
for result in run.get("results", []):
|
||||
rule_id = result.get("ruleId")
|
||||
locations = result.get("locations") or [{}]
|
||||
uri = (
|
||||
locations[0]
|
||||
.get("physicalLocation", {})
|
||||
.get("artifactLocation", {})
|
||||
.get("uri", "")
|
||||
)
|
||||
if (rule_id, path_suffix(uri)) in skipped:
|
||||
removed += 1
|
||||
continue
|
||||
kept.append(result)
|
||||
run["results"] = kept
|
||||
|
||||
with open(sarif_out_path, "w", encoding="utf-8") as f:
|
||||
json.dump(sarif, f)
|
||||
|
||||
print(f"Removed {removed} checkov-suppressed result(s) from the SARIF before upload.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -44,12 +44,20 @@ jobs:
|
||||
pip install -r .github/requirements/pep517-build.txt --require-hashes
|
||||
pip install --no-deps --no-build-isolation -e .
|
||||
pip install -r .github/requirements/base-deps.txt --require-hashes
|
||||
# NOTE: benchmarks/ does not currently exist in this repo, so this
|
||||
# step and the run below it fail on any real invocation - pre-existing,
|
||||
# unrelated to this pinning change. Left as-is since there's nothing
|
||||
# to hash without knowing what belongs there.
|
||||
pip install -r benchmarks/requirements.txt
|
||||
python -m spacy download en_core_web_sm
|
||||
# NOTE: benchmarks/ does not currently exist in this repo (neither
|
||||
# requirements.txt nor benchmarks_runner.py below), so this job
|
||||
# already fails on any real invocation - pre-existing, unrelated to
|
||||
# this pinning change. The `pip install -r benchmarks/requirements.txt`
|
||||
# step that used to be here is dropped rather than fixed: there's
|
||||
# nothing to hash-pin without knowing what that file should
|
||||
# contain, and an unpinned install here would just re-trip
|
||||
# Scorecard's Pinned-Dependencies check for no real benefit, since
|
||||
# the job can't run to completion regardless.
|
||||
#
|
||||
# `python -m spacy download en_core_web_sm` fetches an unpinned,
|
||||
# unhashed wheel from spacy-models' GitHub releases - replaced with
|
||||
# a hash-pinned direct-URL install of the same 3.8.0 model (matches
|
||||
# the spacy==3.8.15 pinned in base-deps.txt) via benchmark-extra.txt.
|
||||
pip install -r .github/requirements/benchmark-extra.txt --require-hashes
|
||||
|
||||
- name: Execute Benchmarks (Real Mode)
|
||||
|
||||
@@ -76,12 +76,28 @@ jobs:
|
||||
PYTHONUTF8: "1"
|
||||
run: |
|
||||
New-Item -ItemType Directory -Force reports | Out-Null
|
||||
checkov --directory . --framework kubernetes helm dockerfile github_actions secrets bicep arm --soft-fail --output sarif --output-file-path reports/checkov.sarif
|
||||
if (-not (Test-Path reports/checkov.sarif)) {
|
||||
checkov --directory . --framework kubernetes helm dockerfile github_actions secrets bicep arm --soft-fail --output sarif --output json --output-file-path reports
|
||||
if (-not (Test-Path reports/results_sarif.sarif)) {
|
||||
$sarif = Get-ChildItem -Path reports -Recurse -Filter *.sarif | Select-Object -First 1
|
||||
if ($null -eq $sarif) { throw "Checkov did not produce a SARIF file" }
|
||||
Copy-Item $sarif.FullName reports/checkov.sarif
|
||||
Copy-Item $sarif.FullName reports/results_sarif.sarif
|
||||
}
|
||||
if (-not (Test-Path reports/results_json.json)) {
|
||||
$json = Get-ChildItem -Path reports -Recurse -Filter *.json | Select-Object -First 1
|
||||
if ($null -eq $json) { throw "Checkov did not produce a JSON file" }
|
||||
Copy-Item $json.FullName reports/results_json.json
|
||||
}
|
||||
|
||||
# checkov's SARIF exporter includes checks it internally marked SKIPPED
|
||||
# (via the inline `# checkov:skip=` comments / `checkov.io/skipN`
|
||||
# annotations already on the Helm chart) as ordinary un-suppressed
|
||||
# results - it never uses SARIF's own `suppressions` field, so GitHub
|
||||
# opens a fresh alert for the same already-suppressed finding on every
|
||||
# single run (see #6035/#6036, #6112-6115, #6128-6131). checkov's JSON
|
||||
# output does correctly record the skip, so cross-reference it here
|
||||
# instead of re-dismissing the same alerts by hand forever.
|
||||
- name: Filter checkov's own suppressed checks out of the SARIF
|
||||
run: python .github/scripts/filter_checkov_skipped.py reports/results_json.json reports/results_sarif.sarif reports/checkov.sarif
|
||||
|
||||
- name: Upload Checkov results to Security tab
|
||||
uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
> Ingest your enterprise data, extract what matters, build a Context Graph and knowledge graph (KG), and run graph analytics and causal reasoning over all of it, with full decision provenance baked in. Explainable, traceable, and trustworthy by design.
|
||||
|
||||
**Decision Intelligence · Context Management · Deterministic Reasoning · Ontology Management · Knowledge Modeling · End-to-End Traceability**
|
||||
**Context Management · Knowledge Modeling · Deterministic Reasoning · Ontology Management · Decision Intelligence · End-to-End Traceability**
|
||||
|
||||
**Open Source · Self-Hostable · Auditable · Governed · Zero Vendor Lock-In**
|
||||
|
||||
@@ -56,9 +56,7 @@ pip install semantica
|
||||
|
||||
---
|
||||
|
||||
Most AI agents act without a trail. They store embeddings, not meaning: context that can't be explained, decisions that can't be audited. In lending, that gap is a compliance exposure, not an inconvenience: an underwriting agent's approval has to survive a regulator's "why" months later.
|
||||
|
||||
Semantica sits underneath your LLM, vector store, and agent framework as a deterministic infrastructure layer: no LLM required for graph construction, reasoning, or provenance.
|
||||
Most AI agents run on embeddings, not meaning: similarity scores with no structure, no relationships, and no way to explain why a result came back. Semantica is the semantic/context layer underneath your LLM, vector store, and agent framework: a deterministic infrastructure layer (no LLM required for graph construction, reasoning, or provenance) that turns fragmented enterprise data into a structured, queryable Context Graph and knowledge graph, governed by ontologies and controlled vocabularies (OWL, SHACL, SKOS) so the meaning of your data is explicit, not just its embedding. Decision provenance and audit trails fall out of that structure as a property, not the product itself; in domains a regulator can question, that same structure just happens to double as a straight answer to "why."
|
||||
|
||||
> ⚠️ **System-level explainability, not foundation-model explainability.** Semantica does not expose or reconstruct what happens *inside* the LLM — its internal reasoning or chain-of-thought stays opaque, as it does for any external system. Semantica explains what's *outside* the model: the context and data fed in, the decision produced, its provenance, relevant relationships, applied policies, and the full execution trail.
|
||||
|
||||
@@ -279,7 +277,7 @@ retrieved = ctx.retrieve("who approved the Acme contract?")
|
||||
|
||||
## Recipe: Audit Trail for a Regulated Decision
|
||||
|
||||
The flagship pattern: record a causally-linked decision chain, attach provenance to every entity, and export a regulator-ready audit trail.
|
||||
One pattern built on the same Context Graph: record a causally-linked decision chain, attach provenance to every entity, and export a regulator-ready audit trail.
|
||||
|
||||
```python
|
||||
from semantica.context import ContextGraph
|
||||
@@ -1030,7 +1028,7 @@ team = Team(agents=[researcher, analyst], mode="coordinate")
|
||||
|
||||
## More Recipes
|
||||
|
||||
The flagship audit-trail recipe is [above](#recipe-audit-trail-for-a-regulated-decision). Here are three more common patterns.
|
||||
The audit-trail recipe is [above](#recipe-audit-trail-for-a-regulated-decision). Here are three more common patterns.
|
||||
|
||||
<details>
|
||||
<summary><b>End-to-End GraphRAG Pipeline</b></summary>
|
||||
|
||||
+3
-2
@@ -327,10 +327,11 @@ print(f"Relationships active in 2023: {result_2023['num_relationships']}")
|
||||
<Accordion title="Persistent graph store: Neo4j, FalkorDB, Apache AGE" icon="database">
|
||||
|
||||
```python
|
||||
from semantica.graph_store import Neo4jStore
|
||||
from semantica.graph_store import GraphStore
|
||||
from semantica.kg import GraphBuilder
|
||||
|
||||
store = Neo4jStore(
|
||||
store = GraphStore(
|
||||
backend="neo4j",
|
||||
uri="bolt://localhost:7687",
|
||||
user="neo4j",
|
||||
password="password",
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
"""
|
||||
Test for GraphBuilder with GraphStore backend (Issue #1135).
|
||||
|
||||
This test verifies that GraphBuilder correctly works with the GraphStore
|
||||
facade interface, not with raw backend stores like Neo4jStore.
|
||||
"""
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
|
||||
class TestGraphBuilderWithGraphStore(unittest.TestCase):
|
||||
"""Test GraphBuilder integration with GraphStore facade."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
# Mock progress tracker
|
||||
self.mock_tracker_patcher = patch("semantica.utils.progress_tracker.get_progress_tracker")
|
||||
self.mock_get_tracker = self.mock_tracker_patcher.start()
|
||||
self.mock_tracker = MagicMock()
|
||||
self.mock_get_tracker.return_value = self.mock_tracker
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up after tests."""
|
||||
self.mock_tracker_patcher.stop()
|
||||
|
||||
def test_graph_builder_with_graph_store_facade(self):
|
||||
"""Test that GraphBuilder works with GraphStore facade (Issue #1135)."""
|
||||
from semantica.kg.graph_builder import GraphBuilder
|
||||
from semantica.graph_store import GraphStore
|
||||
|
||||
# Create a mock GraphStore facade
|
||||
mock_store = MagicMock(spec=GraphStore)
|
||||
mock_store.add_nodes.return_value = 2
|
||||
mock_store.add_edges.return_value = 1
|
||||
|
||||
# Create GraphBuilder with the GraphStore facade
|
||||
builder = GraphBuilder(
|
||||
merge_entities=False,
|
||||
resolve_conflicts=False,
|
||||
graph_store=mock_store
|
||||
)
|
||||
|
||||
# Build a simple graph
|
||||
entities = [
|
||||
{"id": "alice", "type": "Person"},
|
||||
{"id": "bob", "type": "Person"},
|
||||
]
|
||||
relationships = [
|
||||
{"source": "alice", "target": "bob", "type": "knows"},
|
||||
]
|
||||
|
||||
graph = builder.build({
|
||||
"entities": entities,
|
||||
"relationships": relationships
|
||||
})
|
||||
|
||||
# Verify the graph was built
|
||||
self.assertEqual(len(graph["entities"]), 2)
|
||||
self.assertEqual(len(graph["relationships"]), 1)
|
||||
|
||||
# Verify that add_nodes and add_edges were called on the GraphStore
|
||||
mock_store.add_nodes.assert_called_once()
|
||||
mock_store.add_edges.assert_called_once()
|
||||
|
||||
def test_graph_builder_without_graph_store_still_works(self):
|
||||
"""Test that GraphBuilder still works without a graph_store parameter."""
|
||||
from semantica.kg.graph_builder import GraphBuilder
|
||||
|
||||
# Create GraphBuilder without graph_store
|
||||
builder = GraphBuilder(
|
||||
merge_entities=False,
|
||||
resolve_conflicts=False
|
||||
)
|
||||
|
||||
# Build a simple graph
|
||||
entities = [
|
||||
{"id": "alice", "type": "Person"},
|
||||
{"id": "bob", "type": "Person"},
|
||||
]
|
||||
relationships = [
|
||||
{"source": "alice", "target": "bob", "type": "knows"},
|
||||
]
|
||||
|
||||
graph = builder.build({
|
||||
"entities": entities,
|
||||
"relationships": relationships
|
||||
})
|
||||
|
||||
# Verify the graph was built
|
||||
self.assertEqual(len(graph["entities"]), 2)
|
||||
self.assertEqual(len(graph["relationships"]), 1)
|
||||
self.assertEqual(graph["metadata"]["num_entities"], 2)
|
||||
self.assertEqual(graph["metadata"]["num_relationships"], 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user