Metadata-Version: 2.4
Name: veculo
Version: 0.4.12
Summary: Python SDK for Veculo — AI-native multi-modal graph+vector database
Project-URL: Homepage, https://veculo.com
Project-URL: Documentation, https://docs.veculo.com
Project-URL: Repository, https://github.com/sentrius/veculo-python
Project-URL: Issues, https://github.com/sentrius/veculo-python/issues
Author-email: Sentrius LLC <support@sentrius.ai>
License-Expression: Apache-2.0
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Database
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24
Requires-Dist: pydantic>=2.0
Provides-Extra: all
Requires-Dist: anyio>=4.0; extra == 'all'
Requires-Dist: google-cloud-aiplatform>=1.38; extra == 'all'
Requires-Dist: mcp>=1.0; extra == 'all'
Requires-Dist: openai>=1.0; extra == 'all'
Requires-Dist: sentence-transformers>=2.2; extra == 'all'
Provides-Extra: local
Requires-Dist: sentence-transformers>=2.2; extra == 'local'
Provides-Extra: mcp
Requires-Dist: anyio>=4.0; extra == 'mcp'
Requires-Dist: mcp>=1.0; extra == 'mcp'
Provides-Extra: openai
Requires-Dist: openai>=1.0; extra == 'openai'
Provides-Extra: vertexai
Requires-Dist: google-cloud-aiplatform>=1.38; extra == 'vertexai'
Description-Content-Type: text/markdown

<!--

    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

      https://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.

-->
# Veculo Python SDK

Python client for [Veculo](https://veculo.com) — a managed graph+vector database built on Apache Accumulo.

## Installation

```bash
pip install veculo               # base SDK
pip install 'veculo[mcp]'        # + MCP server for Claude Code / Cursor / Codex
pip install 'veculo[all]'        # all optional providers + MCP
```

## Quick Start

### With auto-generated embeddings (easiest)

```python
from veculo import VeculoClient

client = VeculoClient(api_key="vk-...", cluster_id="cl-a7f3b2")

# Insert vertices — Veculo generates embeddings from text automatically
client.put_vertex_with_text(
    id="doc-1",
    text="Q1 revenue exceeded expectations with 40% YoY growth driven by enterprise expansion",
    label="document",
    properties={"author": "Alice", "quarter": "Q1"},
    embed_server_side=True,
)

client.put_vertex_with_text(
    id="doc-2",
    text="Project Plan for Q2 focuses on APAC market entry and partner channel development",
    label="document",
    properties={"author": "Bob", "quarter": "Q2"},
    embed_server_side=True,
)

# Create edges
client.put_edge(source="doc-1", target="doc-2", edge_type="references")

# Ask questions in natural language — answers grounded in your graph
answer = client.rag_query(
    question="What drove Q1 growth and what's planned for Q2?",
    context_hops=2,
)
print(answer["answer"])    # LLM-synthesized answer with citations
print(answer["sources"])   # ["doc-1", "doc-2"]
```

### With your own embeddings

```python
from veculo import VeculoClient

client = VeculoClient(api_key="vk-...", cluster_id="cl-a7f3b2")

# Insert vertices with pre-computed embedding vectors
client.put_vertex(
    id="doc-1",
    label="document",
    properties={"title": "Quarterly Report", "author": "Alice"},
    embedding=[0.12, 0.45, 0.78, 0.33, 0.21, 0.56, 0.89, 0.12],
    visibility="INTERNAL",
)

client.put_vertex(
    id="doc-2",
    label="document",
    properties={"title": "Project Plan", "author": "Bob"},
    embedding=[0.11, 0.44, 0.80, 0.31, 0.19, 0.58, 0.87, 0.14],
)

# Create edges
client.put_edge(
    source="doc-1",
    target="doc-2",
    edge_type="references",
    properties={"section": "appendix"},
)

# Hybrid query: vector similarity + graph traversal
results = client.query(
    embedding=[0.12, 0.44, 0.79, 0.32, 0.15, 0.67, 0.23, 0.91],
    top_k=5,
    edge_type="references",
    depth=2,
    authorizations="INTERNAL",
)

for match in results["results"]:
    print(f"{match['vertex_id']}: {match['score']:.3f}")
```

## Environment Variables

Instead of passing credentials to the constructor, you can set:

| Variable | Description |
|---|---|
| `VECULO_API_KEY` | API key for authentication |
| `VECULO_ENDPOINT` | API endpoint (default: `https://api.veculo.com`) |
| `VECULO_CLUSTER_ID` | Target cluster ID |

```python
# With env vars set, no arguments needed:
client = VeculoClient()
```

## CLI

The SDK includes a command-line interface:

```bash
# Save connection configuration
veculo connect --endpoint https://api.veculo.com --api-key vk-... --cluster-id cl-a7f3b2

# Check cluster status
veculo status

# Insert a vertex
veculo put-vertex --id alice --label person --property name=Alice --property role=engineer

# Retrieve a vertex
veculo get-vertex --id alice

# Create an edge
veculo put-edge --source alice --target bob --type knows

# Run a hybrid query
veculo query --embedding "0.1,0.2,0.3,0.4" --top-k 10
```

Configuration is stored in `~/.veculo/config.json`.

## MCP Server (Claude Code, Cursor, Codex)

Veculo ships an MCP (Model Context Protocol) stdio server so AI agents can query
your graph directly. Install with the `mcp` extra and register the `veculo-mcp`
console script:

```bash
pip install 'veculo[mcp]'
```

Then add to your agent's MCP config. For Claude Code (`~/.claude.json` or
`.mcp.json` in a project), Cursor (`~/.cursor/mcp.json`), and Codex use the
same shape:

```json
{
  "mcpServers": {
    "veculo": {
      "command": "veculo-mcp",
      "env": {
        "VECULO_API_KEY": "vk_live_...",
        "VECULO_CLUSTER_ID": "cl-..."
      }
    }
  }
}
```

The agent gains eleven tools — read-side retrieval plus a write-side memory layer:

**Read / retrieval**

| Tool | Purpose |
|---|---|
| `search_vertices` | Lexical (inverted-index) search — fast, exact term matching |
| `hybrid_search` | Lexical + semantic (IVF-PQ) blended via Reciprocal Rank Fusion |
| `find_similar` | "More like this" by stored top-K terms on a vertex |
| `get_vertex` | Fetch full vertex properties by id |
| `get_neighbors` | Walk outgoing edges (optionally filtered by edge type) |
| `mesh_lineage` | Walk multi-agent provenance (REMIX_OF / DERIVED_FROM / CRITIQUES) |
| `get_context` | **One-call complete answer** — hybrid search + hydrate + 1-hop neighbors in a single bundle. Best default for agentic workflows |

**Memory write side** — turns Veculo into a pluggable memory layer for Claude

| Tool | Purpose |
|---|---|
| `remember` | Save a fact / decision / preference / observation as a vertex (server-side embedded). Idempotent on content hash so duplicate calls overwrite cleanly |
| `link` | Connect two memories with a typed edge (REMIX_OF, DERIVED_FROM, CRITIQUES, SUPERSEDES, etc.) — builds the graph structure that future `recall` / `mesh_lineage` traverses |
| `forget` | Soft-delete (archive) a memory. Audit trail preserved — Veculo never destroys provenance |
| `recall` | Memory-friendly alias for `hybrid_search`. Use first when the user asks "what do you know about X" |

A handy system-prompt addition for Claude Desktop / Code:

> When the user shares facts, decisions, or preferences worth keeping across
> sessions, call `remember` with the content. Before answering questions about
> past conversations or stored knowledge, call `recall` first. Use `link` to
> express provenance (e.g., `link(answer, source, "DERIVED_FROM")`) and
> `forget` only when the user explicitly retracts something.

Set `VECULO_MCP_LOG_LEVEL=DEBUG` to surface tool-call details on stderr (stdout
is reserved for MCP protocol traffic).

## Search & Similarity

```python
from veculo import VeculoClient
client = VeculoClient()

# Lexical search — server-side inverted-index lookup
hits = client.search_vertices("quantum tunneling", limit=10)

# Hybrid (lexical + semantic via RRF). Embedding auto-filled by the API.
hits = client.hybrid_search("quantum tunneling", limit=10)
for r in hits["results"]:
    print(r["vertex_id"], r["score"], r["source"])  # source = "lexical+semantic", "lexical", or "semantic"

# Find similar vertices via stored top-K terms
similar = client.find_similar(vertex_id="document:2604-18838", limit=5)
print(similar["terms_used"])  # which terms drove the match
```

## Mesh Memory (multi-agent provenance)

Mesh-memory vertices are content-addressed (`mesh:<sha256-prefix>`) bundles of
fields where each field carries its own per-role visibility — selective field
acceptance falls out of the existing tserver visibility evaluator.

```python
# Write a planner-authored memory; per-field role visibilities encode SVAF
v = client.mesh_write(
    author_role="planner",
    fields={
        "_title":   {"value": "Plan: ship v3.2 score index",
                     "accepted_by": ["planner", "critic"]},
        "_summary": {"value": "private rationale only planner sees",
                     "accepted_by": ["planner"]},
    },
)
vid = v["vertex_id"]   # e.g. "mesh:fd4aac00297fb39f"

# Read as critic — _summary is filtered out by the tserver
client.mesh_read(vid, roles=["critic"])

# Remix as critic with relation = CRITIQUES
client.mesh_remix(
    source_vertex_id=vid,
    author_role="critic",
    fields={"_title": {"value": "Critique: latency budget unclear",
                       "accepted_by": ["critic", "planner"]}},
    relation="CRITIQUES",
)

# Walk the provenance chain
client.mesh_lineage(vid, direction="ancestors")
```

## Swarm View (M.6)

The swarm-view API aggregates attestations, lineage, and critiques across the
tenant's graph for a given topic. Returns the substrate the
`veculo.corpus_soundness` verifier consumes — every attestation carries the
actor, correlation id, and trust signal needed to verify attribution
downstream.

```python
view = client.swarm_view("finding:treatment-protocol-v3", depth=2)

# Per-claim attestations
for a in view["attestations"]:
    print(a["actor"], a["verdict"], a["confidence"], a["trust"])

# Critiques that were never rebutted ('addressed': False) — the most
# common multi-agent failure mode (silent disagreement-dropping).
unresolved = [c for c in view["critiques"] if not c["addressed"]]

# Trust-weighted consensus
view["consensus"]   # {support_count, dispute_count,
                    #  trust_weighted_support, method}
```

`topic` can be an exact vertex id (`finding:...`, `mesh:...`, `fold:...`) or a
free-text term — the latter resolves against the type index for `finding`,
`mesh_memory`, and `fold` rows. Pass `roles=["planner", ...]` to apply
mesh-role visibility filters (SVAF).

## Corpus Soundness (`veculo.corpus_soundness`)

Proves whether an agentic decision is defensible against the enterprise's own
graph. "Correctness" in the absolute sense is unprovable from any system;
**corpus-soundness** is the verifiable substitute: the decision was grounded
in the corpus and conformant to its policies, with a forensically
reproducible chain. The graph is the canonical reference; the verifier is the
audit lens.

Four independent axes, conservatively composed (any `FAILS` dominates;
`INCONCLUSIVE` never silently coerces to a pass):

| Axis | What it checks | Substrate |
| --- | --- | --- |
| `attribution` | Every claim cites a row (PART_OF / VALIDATES / DERIVED_FROM / CITES / REMIX_OF). Unsigned claims surface as witnesses. | Edge rows around each produced vertex |
| `conformance` | Every replay step's `would_pass_today` holds against the current PIC predicate. Failures distinguish policy drift from substantive violations. | `replay:<corr>:<seq>` ledger rows |
| `reproducibility` | `recorded_command_hash == replayed_command_hash` per step. Divergence = nondeterminism, the silent multi-run failure mode. | PIC command_hash on each ledger entry |
| `disagreement_resolution` | No unaddressed `CRITIQUES` against produced vertices (an `addressed` edge requires a `DERIVED_FROM` / `REMIX_OF` rebuttal from the cited source). | M.6 swarm view |

```python
from veculo import VeculoClient, corpus_soundness

client = VeculoClient(api_key="vk-...", cluster_id="cl-a7f3b2")

# Audit a single agentic decision by its X-Correlation-Id.
report = corpus_soundness.verify(
    client,
    correlation_id="abc-123",
    decision_topic="finding:treatment-protocol-v3",  # optional anchor
)

print(report)
# SoundnessReport(corr=abc-123, overall=FAILS)
#   attribution: HOLDS (3 witnesses) — all 3 claims cite at least one source row
#   conformance: HOLDS (3 witnesses) — all evaluated steps still satisfy the current PIC predicate
#   reproducibility: HOLDS (3 witnesses) — all re-derived steps match the recorded command_hash
#   disagreement_resolution: FAILS (1 witness) — 1 unaddressed critique across 1 target

# Drill into a specific axis.
axis = report.axis("disagreement_resolution")
for w in axis.witnesses:
    print(w)  # {"target": "...", "from": "finding:b", "to": "finding:a", "addressed": False}

# Audit a multi-step pipeline: returns one SoundnessReport per correlation.
# Deliberately NOT aggregated to a single number — aggregating IS the
# silent-loss pattern this SDK exists to surface.
pipeline = corpus_soundness.verify_pipeline(
    client,
    correlation_ids=["step1-corr", "step2-corr", "step3-corr"],
)
all_sound = all(r.overall == corpus_soundness.Verdict.HOLDS for r in pipeline)
```

`SoundnessReport.overall` is `HOLDS` iff every axis holds. The
`witnesses` lists on each `AxisResult` carry the rows / ledger entries /
critique pairs the verdict rests on, so an auditor can reproduce the check
by hand.

For regulated workflows the soundness check is the public defense: every
agentic decision ships with the report alongside the output, and the
report's witnesses are the chain that compliance, legal, or regulators
can audit.

## Error Handling

```python
from veculo import VeculoClient, VeculoError, NotFoundError, AuthenticationError

client = VeculoClient(api_key="vk-...", cluster_id="cl-a7f3b2")

try:
    vertex = client.get_vertex(id="nonexistent")
except NotFoundError:
    print("Vertex does not exist")
except AuthenticationError:
    print("Invalid or expired API key")
except VeculoError as e:
    print(f"API error {e.status_code}: {e.message}")
```

## Visibility Labels

Veculo supports Accumulo-style cell-level security via visibility expressions:

```python
# Write with visibility
client.put_vertex(
    id="doc:internal-report",
    label="document",
    properties={"title": "Q1 Revenue Analysis"},
    visibility="finance&internal",
)

# Read with authorizations
vertex = client.get_vertex(
    id="doc:internal-report",
    authorizations="finance,internal",
)
```

### Per-user cordon (automatic)

Personal writes — chat sessions/turns, MCP `remember` memories, and the
optional per-user PIC interaction log — are automatically tagged with a
`user:<uid>` cell-visibility token at write time. Reads derive the
caller's user token from the bearer credential and pass it as a
per-scan authorization, so the kernel hides another user's personal rows
**before** IVF/PQ similarity ranking can surface them. No SDK call needs
to opt in: the API stamps the token and the provisioner-side
`UserAuthHelper.scanAuthsFromContext` enforces it on every scan.

What this means in practice:

- A vector search on `chat_turn` only returns the caller's own turns,
  even if another user's turn is semantically more similar.
- An MCP `remember` memory written under one user's API key cannot leak
  into another user's RAG context within the same tenant.
- The `_user_uid` property is still stamped for ownership analytics
  (e.g. "list my chat sessions") on top of the kernel-level cordon.

The full visibility model composes as
`<classification_expr> & <mesh_role_expr> & <user_scope_expr>`. Each
tier is optional; classifications and mesh roles continue to work
exactly as before, the per-user tier is the new namespace.

### PIC interaction log (opt-in)

When a cluster operator flips `pic_log_all_requests` on, every inbound
tenant API call (reads and writes) writes one `replay:<corr>:<seq>`
audit row to the graph, tagged with the caller's `user:<uid>`. The row
records the synthesized SAG verb (`vertices.get`, `vertices.search`,
…), HTTP metadata, and links back into the existing PIC replay ledger
via the correlation id. Each user only reads their own audit trail —
the cordon is the same kernel-level filter described above.

The `veculo.corpus_soundness` verifier composes against the interaction
log the same way it does against agentic replay rows: every
verification reads the caller's own corpus, no special wiring needed.

## Embeddings

Veculo supports multiple ways to generate vector embeddings:

### Client-side (bring your own API key)

```python
from veculo import VeculoClient
from veculo.embeddings import OpenAIEmbeddings

client = VeculoClient(api_key="vk-...", cluster_name="production")
client.set_embedder(OpenAIEmbeddings(api_key="sk-..."))

# Automatically generates embedding from text
client.put_vertex_with_text(
    id="doc:report-q1",
    text="Q1 revenue exceeded expectations with 40% YoY growth",
    label="document",
    properties={"quarter": "Q1", "year": "2026"},
)
```

Other providers:

```python
from veculo.embeddings import VertexAIEmbeddings, SentenceTransformerEmbeddings

# Vertex AI
client.set_embedder(VertexAIEmbeddings(project="my-project"))

# Local (no API key needed)
client.set_embedder(SentenceTransformerEmbeddings())
```

Install extras: `pip install 'veculo[openai]'`, `pip install 'veculo[vertexai]'`, or `pip install 'veculo[local]'`

### Server-side (Veculo-managed, billed separately)

```python
# Veculo generates the embedding for you server-side
client.put_vertex_with_text(
    id="doc:report-q1",
    text="Q1 revenue exceeded expectations",
    label="document",
    embed_server_side=True,  # billed per request
)
```

## Multi-Modal Knowledge Graphs

Upload any file — Veculo automatically extracts text, generates embeddings, discovers entities, and builds a knowledge subgraph.

### Supported file types

| Type | What Veculo extracts |
|------|---------------------|
| PDF | Text, citations, entities, embeddings |
| Images | Visual description, objects, entities, embeddings |
| Audio | Transcript, entities, embeddings |
| Video | Audio transcript, entities, embeddings |
| Code | Functions, classes, imports, embeddings |

### Upload a file

```python
# Upload a PDF — Veculo does the rest
client.put_vertex_with_file(
    id="paper:arxiv-2401",
    file_path="attention-is-all-you-need.pdf",
    label="paper",
    properties={"source": "arxiv"},
)

# Upload an image
client.put_vertex_with_file(
    id="img:brain-scan-001",
    file_path="brain-scan.png",
    label="medical-image",
)

# Upload source code
client.put_vertex_with_file(
    id="code:transformer",
    file_path="transformer.py",
    label="code",
)
```

### Check extraction status

```python
jobs = client.list_jobs()
for job in jobs["jobs"]:
    print(f"{job['vertex_id']}: {job['status']}")
```

### CLI

```bash
veculo upload --id paper-1 --file paper.pdf --label paper
veculo jobs
veculo get-vertex --id paper-1
```

## AI-Native Queries

### Natural Language Query

Ask questions in plain English — the SDK translates them into graph queries via LLM:

```python
result = client.nl_query(
    question="Which documents reference the Q1 report?",
    authorizations="internal",
)

print(result["query_plan"]["explanation"])
for step_result in result["results"]:
    print(step_result)
```

### Graph-Augmented RAG

Retrieval-Augmented Generation that combines vector search with graph context:

```python
answer = client.rag_query(
    question="What were the key findings in the Q1 analysis?",
    context_hops=2,          # expand graph 2 hops for richer context
    model="claude-sonnet-4-20250514",  # optional model override
    top_k=10,
)

print(answer["answer"])
print("Sources:", answer["sources"])  # vertex IDs cited
```

### SSM Reasoning (Stateful Multi-Hop Traversal)

Follow semantic threads through your graph. Unlike BFS/DFS, the SSM accumulates
context at each hop — the hidden state guides which edge to follow next:

```python
# Text query — server generates the embedding
result = client.reason(
    query="how did neural networks evolve into large language models",
    max_depth=5,
    alpha=0.8,      # high momentum — remember the journey
    threshold=0.2,  # low threshold — keep following
)

for hop in result["path"]:
    print(f"  {hop['vertex_id']} (score: {hop['score']:.4f})")
print(f"Terminated: {result['termination_reason']}")
```

```python
# Start from a specific vertex
result = client.reason(
    query="trace the influence chain",
    start_vertex="ai-foundations",
    max_depth=10,
)
```

The `alpha` parameter controls state momentum:
- `alpha=0.9` — heavy history, follows long conceptual threads
- `alpha=0.5` — balanced, adapts quickly to new context
- `alpha=0.1` — almost stateless, similar to greedy nearest-neighbor

Reasoning queries run on dedicated **scan servers** (Accumulo 4.0) in an isolated
`inference` resource group — zero impact on write throughput.

### CLI

```bash
veculo reason --embedding "0.1,0.2,..." --max-depth 5 --alpha 0.8
veculo reason --embedding "0.1,0.2,..." --start-vertex ai-foundations
```

## Temporal Queries

Filter edges by time range — either write time (when the edge was stored) or event time (a user-supplied timestamp):

```python
# Trades executed in the last 7 days
import time
week_ago = int((time.time() - 7 * 86400) * 1000)

result = client.query_temporal(
    vertex_id="portfolio-global-macro",
    start_time=week_ago,
    edge_type="TRADED",
    time_field="write_time",  # or "event_time" for user-supplied timestamps
)

for edge in result["edges"]:
    print(f"  {edge['direction']} {edge['type']} → {edge.get('target', edge.get('source'))}")
```

### CLI

```bash
veculo temporal --id portfolio-global-macro --start-time 1700000000000 --edge-type TRADED
veculo temporal --id portfolio-global-macro --time-field event_time --start-time 1700000000000
```

## Aggregation Queries

Server-side aggregation — counts, grouping, and statistics computed inside Accumulo without pulling data to the client:

```python
# Count edges by type for a vertex
result = client.aggregate(
    aggregation="GROUP_BY_EDGE_TYPE",
    vertex_id="entity-acme-corp",
)
for group in result["results"]:
    print(f"  {group['group']}: {group.get('count', 0)}")

# Degree (in + out connections)
result = client.aggregate(aggregation="DEGREE", vertex_id="entity-acme-corp")

# Group trades by day
result = client.aggregate(
    aggregation="GROUP_BY_TIME",
    vertex_id="entity-acme-corp",
    edge_type="TRADED",
    time_bucket="DAY",
)

# Most connected entities in the graph
result = client.aggregate(aggregation="TOP_CONNECTED", limit=10)

# Count distinct counterparties
result = client.aggregate(
    aggregation="COUNT_DISTINCT",
    vertex_id="entity-acme-corp",
    edge_type="TRANSACTED_WITH",
)
```

Aggregation types: `COUNT`, `COUNT_DISTINCT`, `GROUP_BY_EDGE_TYPE`, `GROUP_BY_TIME`, `DEGREE`, `TOP_CONNECTED`

### CLI

```bash
veculo aggregate --aggregation GROUP_BY_EDGE_TYPE --id entity-acme-corp
veculo aggregate --aggregation GROUP_BY_TIME --id entity-acme-corp --time-bucket DAY
veculo aggregate --aggregation TOP_CONNECTED --limit 10
```

## Graph Pattern Queries

Structural queries that traverse the graph to find paths, intersections, and triangles:

### Find Paths

```python
# Shortest path between two entities
result = client.find_path(source="entity-acme-corp", target="entity-treasury-bonds")
for path in result["paths"]:
    print(" → ".join(path))

# All paths (up to 10)
result = client.find_path(
    source="entity-acme-corp",
    target="entity-treasury-bonds",
    edge_type="HOLDS",
    max_depth=4,
    find_all=True,
    max_paths=10,
)
```

### Find Intersection

Find vertices connected to ALL anchor vertices — e.g., "which funds hold positions in both AAPL and MSFT?":

```python
result = client.find_intersection(
    anchor_vertices=["security-aapl", "security-msft"],
    edge_types=["HOLDS"],
    direction="incoming",  # who holds both securities
)
print(f"Funds holding both: {result['vertices']}")
```

### Find Triangles

Discover triangular relationships — useful for detecting circular exposures and concentration risk:

```python
result = client.find_triangles(
    vertex_id="entity-acme-corp",
    edge_type="TRANSACTED_WITH",
    max_triangles=50,
)
for triangle in result["triangles"]:
    print(f"  {' — '.join(triangle)}")
```

### CLI

```bash
veculo find-path --source entity-acme-corp --target entity-treasury-bonds
veculo find-path --source entity-acme-corp --target entity-treasury-bonds --find-all --max-depth 4
veculo find-intersection --anchors security-aapl security-msft --edge-type HOLDS --direction incoming
veculo find-triangles --id entity-acme-corp --edge-type TRANSACTED_WITH
```

## Bulk Operations

Insert many vertices or edges in a single batch:

```python
client.put_vertices_bulk([
    {"id": "doc:1", "label": "document", "properties": {"title": "Report A"}},
    {"id": "doc:2", "label": "document", "properties": {"title": "Report B"}},
    {"id": "doc:3", "label": "document", "properties": {"title": "Report C"}},
])

client.put_edges_bulk([
    {"source": "doc:1", "target": "doc:2", "edge_type": "references"},
    {"source": "doc:2", "target": "doc:3", "edge_type": "references"},
])
```

## Hibernate / Resume

Stop compute costs while preserving all data in storage:

```python
# Hibernate — flushes tables, snapshots metadata, tears down compute
client.hibernate()
# Storage continues at ~$0.02/GB/month, compute costs stop immediately

# Later — resume with all data intact
client.resume()
```

Data, metadata, embeddings, and edges are all preserved. Only compute is stopped.

## Configuration

### Auto-Embed

Enable automatic embedding generation for new text vertices:

```python
client.configure_auto_embed(
    model="text-embedding-005",
    text_properties=["description", "content"],
)
```

### Semantic Edges

Enable automatic similarity edge creation during compaction:

```python
client.configure_semantic_edges(
    similarity_threshold=0.85,
    max_edges_per_vertex=10,
)
```

## Insights

Query AI-derived analytics:

```python
# Anomalous vertices (outliers by embedding distance)
anomalies = client.get_anomalies(authorizations="internal")

# Top vertices by PageRank
ranks = client.get_top_ranked()

# Pending processing queue status
status = client.get_processing_status()
print(f"Embeddings pending: {status['auto_embed']}")
```

## AI Reasoning Lab

Advanced graph reasoning powered by state space models, graph neural networks, and hyperbolic geometry.

### SSM Reasoning

```python
# Basic SSM reasoning — follows paths through the graph guided by a hidden state
result = client.reason(query="what drove Q3 revenue decline?", start_vertex="report-q3")
for hop in result["path"]:
    print(f"  {hop['vertex_id']} (score: {hop['score']:.3f})")
```

### Multi-Agent Reasoning

```python
# Run multiple reasoning strategies in parallel and measure agreement
result = client.reason_multi_agent(
    query="what drove Q3 revenue decline?",
    strategies=["root_cause", "knowledge", "influence"],
    max_depth=8,
)
print(f"Confidence: {result['confidence']:.0%}")
print(f"Agreed vertices: {result['agreed_vertices']}")
print(f"Divergent vertices: {result['divergent_vertices']}")
```

### Adversarial Verification

```python
# Verify a claim by running support and contradiction agents
result = client.verify_adversarial(
    query="ACME Corp's exposure to interest rate risk exceeds $2B",
    start_vertex="filing-10k-acme",
)
print(f"Verdict: {result['verdict']}")  # SUPPORTED, CONTESTED, or UNSUPPORTED
print(f"Trust score: {result['trust_score']:.0%}")
print(f"Spread kappa={result['kappa']:.3f} ({result['geometric_regime']})")
```

### Temporal SSM

```python
# Reasoning with time-decay — recent data gets more weight
result = client.reason_temporal(
    query="recent changes to portfolio allocation",
    recent_window_hours=48,
    max_depth=10,
)
```

### Graph Attention

```python
# Multi-head attention across a vertex's neighborhood
result = client.attend(
    query="emerging market equities",
    vertex_id="sector-em-equities",
    top_k=10,
)
for v in result["attended_vertices"]:
    print(f"  {v['vertex_id']} (attention: {v['attention_score']:.3f})")
```

### Causal Inference

```python
# Trace causes forward (what did X cause?)
effects = client.trace_causes(
    query="Fed rate hike",
    start_vertex="event-fed-rate-2024-03",
    max_depth=5,
)

# Trace causes backward (what caused X?)
causes = client.trace_caused_by(
    query="margin call",
    start_vertex="alert-margin-call-042",
)
```

### GNN Message Passing

```python
# Graph neural network propagation — aggregates neighbor information
result = client.gnn_propagate(
    query="counterparty credit exposure",
    start_vertex="entity-counterparty-a",
    rounds=3,       # aggregation rounds
    top_k=10,
    self_weight=0.7, # 70% self, 30% neighbors
)
```

### Rule-Based Inference

```python
# Apply logical rules to infer new edges
result = client.infer_edges(
    start_vertex="fund-global-macro",
    rules=[
        {"antecedent": ["HOLDS", "ISSUED_BY"], "consequent": "EXPOSED_TO", "min_confidence": 0.5},
        {"antecedent": ["BENCHMARKED_TO", "CONTAINS"], "consequent": "INDIRECTLY_TRACKS", "min_confidence": 0.4},
    ],
    max_inferences=50,
)
for edge in result["inferred_edges"]:
    print(f"  {edge['source']} --{edge['edge_type']}--> {edge['target']}")

# Discover rules from graph structure
rules = client.discover_rules(max_rules=10, sample_size=500)
for rule in rules["rules"]:
    print(f"  {rule['antecedent']} => {rule['consequent']} (conf: {rule['confidence']:.2f})")
```

### Hyperbolic Search

```python
# Search using hyperbolic geometry — naturally captures hierarchy
result = client.hyperbolic_search(
    query="fixed income derivatives",
    top_k=10,
)
for match in result["matches"]:
    depth = "ancestor" if match["is_ancestor"] else "descendant"
    print(f"  {match['vertex_id']} (depth: {match['hierarchy_depth']:.2f}, {depth})")
```

### Embedding Evolution

```python
# Track how a vertex's embedding has changed over time
result = client.track_evolution("entity-acme-corp", max_versions=10)
if result["significant"]:
    print(f"Embedding drifted {result['total_drift']:.4f} — ACME's risk profile has shifted")
```

### Neighborhood Enrichment

```python
# Enrich embeddings based on graph neighborhood (run periodically)
result = client.enrich_embeddings(learning_rate=0.2, max_neighbors=50)
print(f"Enriched {result['vertices_enriched']} vertices")
```

### Graph Compilation

```python
# Pre-compute frequently traversed reasoning paths
result = client.compile_frequent_paths(min_frequency=3, min_confidence=0.5)
print(f"Compiled {result['compiled_count']} paths")
```

## License

Apache License 2.0
