Python API Reference¶
Top-level exports¶
polaroid
¶
polaroid - Embeddable CRDT scene graph for embodied AI agents.
ClosedLoopError
¶
Bases: ValueError
Raised when the scene gate refuses empty or detached structure.
GateOutcome(ok, verdict, reason, exit_code, node_count=0, edge_count=0, detached_count=0, orphan_room_count=0)
dataclass
¶
Result of a closed-loop scene graph read.
Attributes:
| Name | Type | Description |
|---|---|---|
ok |
bool
|
True only when navigation/attachment may proceed. |
verdict |
str
|
|
reason |
str
|
Always non-empty. |
exit_code |
int
|
0 PASS, 1 FAIL, 2 FAIL_LOUD. |
node_count |
int
|
Nodes examined. |
edge_count |
int
|
Edges examined. |
detached_count |
int
|
Nodes missing required attachment. |
orphan_room_count |
int
|
Rooms with no nav edges. |
MergeResult(added_nodes, updated_nodes, added_edges, conflicts_resolved)
dataclass
¶
Result of merging two scene graphs.
to_dict()
¶
Serialize to a plain dict.
Source code in src/polaroid/graph.py
summary()
¶
Return a one-line summary string.
Source code in src/polaroid/graph.py
SceneEdge(source_id, target_id, relation, confidence=1.0, observed_at=time.time())
dataclass
¶
A directed spatial relationship between two nodes.
to_dict()
¶
Serialize to a plain dict.
Source code in src/polaroid/graph.py
from_dict(d)
classmethod
¶
Deserialize from a plain dict.
Source code in src/polaroid/graph.py
SceneNode(label, node_type, properties, confidence=1.0, observed_at=time.time(), agent_id='')
dataclass
¶
A node in the scene graph (object, room, surface, region, or agent).
to_dict()
¶
Serialize to a plain dict.
Source code in src/polaroid/graph.py
from_dict(d)
classmethod
¶
Deserialize from a plain dict.
Source code in src/polaroid/graph.py
SceneMerger
¶
Merge two SceneStore instances using CRDT semantics.
This is a pure function wrapped in a class for extensibility.
Neither store is modified; the result is applied to local.
merge(local, remote)
¶
Merge remote into local.
CRDT properties guaranteed: - Idempotent: merging the same remote twice produces the same result. - Commutative: merge(A, B) and merge(B, A) produce the same final state. - Associative: merge order among multiple remotes does not matter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
local
|
SceneStore
|
The destination SceneStore (written to). |
required |
remote
|
SceneStore
|
The source SceneStore (read only). |
required |
Returns:
| Type | Description |
|---|---|
MergeResult
|
MergeResult describing what changed. |
Source code in src/polaroid/merger.py
SceneQuery(store)
¶
High-level query interface for a SceneStore.
All methods are read-only - they never modify the store.
Source code in src/polaroid/query.py
find_nodes(node_type=None, label_contains=None, min_confidence=0.0)
¶
Return nodes matching the given filters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node_type
|
str | None
|
Only return nodes of this type (e.g. "object", "room"). |
None
|
label_contains
|
str | None
|
Case-insensitive substring match on node label. |
None
|
min_confidence
|
float
|
Exclude nodes with confidence below this threshold. |
0.0
|
Returns:
| Type | Description |
|---|---|
list[SceneNode]
|
List of matching SceneNode objects. |
Source code in src/polaroid/query.py
find_neighbors(node_id, relation=None)
¶
Return nodes that are targets of edges originating from node_id.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node_id
|
str
|
Source node ID. |
required |
relation
|
str | None
|
If given, only follow edges with this relation type. |
None
|
Returns:
| Type | Description |
|---|---|
list[SceneNode]
|
List of neighbor SceneNode objects (may be empty). |
Source code in src/polaroid/query.py
context_summary(agent_id='')
¶
Return a human-readable description of the scene.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
agent_id
|
str
|
If non-empty, only count nodes observed by this agent. |
''
|
Returns:
| Type | Description |
|---|---|
str
|
A one-paragraph text summary of the scene graph. |
Source code in src/polaroid/query.py
GraphStats(node_count, edge_count, node_types, edge_relations, avg_degree, max_degree, connected_components, diameter)
dataclass
¶
Statistics about a scene graph.
SceneStore(path, *, data_root=None)
¶
SQLite-backed persistent store for scene nodes and edges.
All writes are immediately committed. One SceneStore per process.
Paths are confined to a data root (default .polaroid or
POLAROID_DATA_DIR) to prevent path-injection from API/MCP callers
(CWE-22 / CodeQL py/path-injection).
Open or create a store at path.
Parameters¶
path:
Store path or ":memory:". Relative paths are resolved under
data_root. Absolute paths must stay inside data_root.
data_root:
Optional root directory (defaults to env POLAROID_DATA_DIR or
.polaroid). Tests should pass the pytest tmp_path.
Source code in src/polaroid/store.py
upsert_node(node)
¶
Insert or update node - only overwrites if incoming confidence >= existing.
Source code in src/polaroid/store.py
get_node(node_id)
¶
Return a SceneNode by ID, or None if not found.
Source code in src/polaroid/store.py
list_nodes(node_type=None, min_confidence=0.0)
¶
Return nodes, optionally filtered by type and min confidence.
Source code in src/polaroid/store.py
upsert_edge(edge)
¶
Insert or update edge - only overwrites if incoming confidence >= existing.
Source code in src/polaroid/store.py
get_edge(edge_id)
¶
Return a SceneEdge by ID, or None if not found.
Source code in src/polaroid/store.py
list_edges(source_id=None, relation=None)
¶
Return edges, optionally filtered by source_id and/or relation.
Source code in src/polaroid/store.py
node_count()
¶
edge_count()
¶
assert_line_of_sight(store, observer_id, target_id, **kwargs)
¶
Raise :class:ClosedLoopError unless :func:gate_line_of_sight is ok.
Source code in src/polaroid/closed_loop.py
gate_attachment(store, part_id, parent_id)
¶
Gate a specific part→parent attachment claim (DETACHED-PART).
Source code in src/polaroid/closed_loop.py
gate_line_of_sight(store, observer_id, target_id, *, action='observe', max_hops=8, require_nodes=True)
¶
Refuse observe/target/interact when graph LOS is missing (LINE-OF-SIGHT).
Embodied agents often claim they can see or act on a target when walls /
occluders intervene. gate_navigable only checks room connectivity;
this gate checks visibility path observer→target.
Rules:
- Empty scene → FAIL_LOUD
- Missing observer/target nodes → FAIL_LOUD
- No LOS path → FAIL
- LOS present → PASS
Source code in src/polaroid/closed_loop.py
gate_navigable(store, *, min_rooms=2)
¶
Gate multi-room navigation: rooms need adjacent/connects edges.
PRIMAL3 / multi-agent pathfinding class - disconnected rooms are not a map.
Source code in src/polaroid/closed_loop.py
gate_scene(store, *, min_nodes=1, refuse_detached=True)
¶
Gate a scene store: empty FAIL_LOUD; detached parts FAIL.
This is the load-bearing closed-loop reader for embodied agents.
Source code in src/polaroid/closed_loop.py
has_line_of_sight(store, observer_id, target_id, *, max_hops=8)
¶
True if observer can reach target via LOS/nav edges without occlusion cut.
Rules (graph-level LOS, not full raycast):
- Direct
sees/visible-toedge observer→target → True - BFS on undirected LOS+nav edges up to
max_hops - If an
occludesedge names a node on the path as occluding the target, that path is rejected (simple: any occluder adjacent to target blocks unless a direct sees edge exists)
Source code in src/polaroid/closed_loop.py
to_adjacency_matrix(store)
¶
Return (node_ids, matrix) adjacency matrix for ML/analysis use.
Source code in src/polaroid/export.py
to_dot(store, graph_name='scene_graph')
¶
Export scene graph as Graphviz DOT format. Nodes colored by node_type.
Source code in src/polaroid/export.py
to_json(store)
¶
Export full scene graph as JSON with nodes and edges arrays.
Source code in src/polaroid/export.py
cluster_by_type(store)
¶
Return {node_type: [node_ids]} grouping.
Source code in src/polaroid/stats.py
compute_stats(store)
¶
Compute comprehensive statistics for the given scene store.
Source code in src/polaroid/stats.py
most_connected(store, n=10)
¶
Return top-n nodes by degree (node_id, degree).
Source code in src/polaroid/stats.py
extract_subgraph(store, root_id, max_depth=3)
¶
Extract the subgraph reachable from root_id within max_depth hops.
Returns a new in-memory SceneStore.
Source code in src/polaroid/subgraph.py
filter_by_type(store, node_types)
¶
Return a new SceneStore with only nodes of the given types (and edges between them).
Source code in src/polaroid/subgraph.py
neighborhood(store, node_id, radius=1)
¶
Return all node IDs within radius hops of node_id.