Skip to content

GO-CAM AI Demo

This notebook demonstrates the key functionality of the noctua-py library for programmatically manipulating GO-CAM models via the Noctua/Minerva/Barista API stack.

Setup

First, ensure you have noctua-py installed:

pip install noctua-py
# or with uv:
uv add noctua-py

Environment Setup

You'll need a Barista token to interact with the API. For development/testing, you can use the dev server token:

import os
import json
from noctua.barista import BaristaClient, BaristaError

# Ensure BARISTA_TOKEN is set in your environment
# For development: Contact the GO team for a dev token
# For production: Get token from Noctua login
if not os.environ.get("BARISTA_TOKEN"):
    print("ERROR: BARISTA_TOKEN environment variable not set")
    print("Please set: export BARISTA_TOKEN=your-token-here")
    raise SystemExit(1)

# Test model ID - change this to an existing model on your server
TEST_MODEL_ID = "gomodel:6796b94c00003233"

print("Environment configured for Barista API")

1. Creating a Barista Client

The BaristaClient is your main interface to the GO-CAM API:

# Create a client for the dev server (default)
client = BaristaClient()

print(f"Client configured for: {client.base_url}")
print(f"Namespace: {client.namespace}")

2. Listing Models

List all available GO-CAM models with filtering options:

# List models with various filters
result = client.list_models(
    limit=5,  # Limit to 5 models
    state="development"  # Only development models
)

models = result.get("models", [])
total = result.get("n", 0)

print(f"Found {total} total models, showing {len(models)}:")
print()

for model in models:
    model_id = model.get("id", "")
    title = model.get("title", "(no title)")
    state = model.get("state", "")
    print(f"ID: {model_id}")
    print(f"   Title: {title[:80]}..." if len(title) > 80 else f"   Title: {title}")
    print(f"   State: {state}")
    print()

3. Creating a New Model

Create a new empty GO-CAM model:

# Create a new model with a title
response = client.create_model(title="Demo GO-CAM Model from Jupyter")

if response.ok:
    new_model_id = response.model_id
    print(f"Successfully created model: {new_model_id}")
else:
    print(f"Failed to create model: {response.raw}")
    new_model_id = TEST_MODEL_ID  # Fall back to test model
    print(f"Using existing test model: {new_model_id}")

4. Getting Model Details

Retrieve the full details of a GO-CAM model:

# Get model details
response = client.get_model(new_model_id)

if response.ok:
    print(f"Model ID: {response.model_id}")
    print(f"State: {response.model_state}")
    print(f"Number of individuals: {len(response.individuals)}")
    print(f"Number of facts/edges: {len(response.facts)}")

    # Show first few individuals if any
    if response.individuals:
        print("\nFirst 3 individuals:")
        for ind in response.individuals[:3]:
            ind_id = ind.get("id", "")
            types = ind.get("type", [])
            if types:
                type_str = ", ".join([t.get("label", t.get("id", "")) for t in types if t])
                print(f"  - {ind_id}: {type_str}")
else:
    print(f"Failed to get model: {response.raw}")

5. Adding Individuals (Nodes)

Add molecular function individuals to the model:

# Add some molecular function individuals
activities = [
    ("GO:0003924", "GTPase activity", "ras"),
    ("GO:0004674", "protein serine/threonine kinase activity", "raf"),
    ("GO:0004707", "MAP kinase activity", "erk")
]

added_individuals = {}

for go_id, label, var_name in activities:
    response = client.add_individual(new_model_id, go_id, assign_var=var_name)
    if response.ok:
        # Find the newly added individual
        for ind in response.individuals:
            types = ind.get("type", [])
            for t in types:
                if t.get("id") == go_id:
                    added_individuals[var_name] = ind["id"]
                    print(f"Added {label}: {ind['id']}")
                    break
    else:
        print(f"Failed to add {label}: {response.raw.get('message', 'Unknown error')}")

6. Adding Facts (Edges)

Connect individuals with causal relationships:

# Add causal relationships if we have individuals
if len(added_individuals) >= 2:
    # Get the actual IDs
    ras_id = added_individuals.get("ras")
    raf_id = added_individuals.get("raf")
    erk_id = added_individuals.get("erk")

    # Add edges if we have the nodes
    if ras_id and raf_id:
        response = client.add_fact(
            new_model_id,
            subject_id=ras_id,
            object_id=raf_id,
            predicate_id="RO:0002413"  # directly positively regulates
        )
        if response.ok:
            print("Added edge: RAS -> RAF")
        else:
            print(f"Failed to add edge: {response.raw.get('message', '')}")

    if raf_id and erk_id:
        response = client.add_fact(
            new_model_id,
            subject_id=raf_id,
            object_id=erk_id,
            predicate_id="RO:0002413"
        )
        if response.ok:
            print("Added edge: RAF -> ERK")
        else:
            print(f"Failed to add edge: {response.raw.get('message', '')}")
else:
    print("Not enough individuals to create edges")

7. Adding Evidence

Add evidence annotations to support facts:

# Add evidence to an edge if we have one
if len(added_individuals) >= 2 and "ras" in added_individuals and "raf" in added_individuals:
    ras_id = added_individuals["ras"]
    raf_id = added_individuals["raf"]

    # Build the evidence requests
    evidence_requests = client.req_add_evidence_to_fact(
        new_model_id,
        subject_id=ras_id,
        object_id=raf_id,
        predicate_id="RO:0002413",
        eco_id="ECO:0000314",  # direct assay evidence
        sources=["PMID:12345678"],
        with_from=["UniProtKB:P01112"]  # HRAS
    )

    # Execute the batch request
    response = client.m3_batch(evidence_requests)

    if response.ok:
        print("Successfully added evidence to RAS->RAF edge")
    else:
        print(f"Failed to add evidence: {response.raw.get('message', '')}")
else:
    print("No edges available to add evidence")

8. Exporting Models

Export models in different formats:

# Get the model in native Minerva JSON format
response = client.get_model(new_model_id)

if response.ok:
    print("Model structure (native Minerva JSON):")
    print(f"- Model ID: {response.model_id}")
    print(f"- Individuals: {len(response.individuals)}")
    print(f"- Facts: {len(response.facts)}")

    # Show compact version
    data = response.raw.get("data", {})
    compact = {
        "id": data.get("id"),
        "individuals_count": len(data.get("individuals", [])),
        "facts_count": len(data.get("facts", [])),
        "annotations": data.get("annotations", [])
    }
    print("\nCompact view:")
    print(json.dumps(compact, indent=2))
else:
    print(f"Failed to export: {response.raw}")

9. Converting to GO-CAM Format

Convert the Minerva JSON to standard GO-CAM format (if the model has proper structure):

try:
    from gocam.translation.minerva_wrapper import MinervaWrapper

    # Get the model data
    response = client.get_model(new_model_id)
    if response.ok:
        minerva_data = response.raw.get("data", {})

        # Ensure required fields
        if "id" not in minerva_data:
            minerva_data["id"] = new_model_id
        if "title" not in minerva_data:
            minerva_data["title"] = "Demo Model"

        # Try to convert (may fail if model doesn't have enabled_by facts)
        try:
            gocam_model = MinervaWrapper.minerva_object_to_model(minerva_data)
            print("Successfully converted to GO-CAM format!")
            print(f"Model ID: {gocam_model.id}")
            print(f"Title: {gocam_model.title}")
            if gocam_model.activities:
                print(f"Activities: {len(gocam_model.activities)}")
        except Exception as e:
            print(f"Note: Model doesn't follow standard GO-CAM structure: {e}")
            print("This is normal for models without enabled_by facts linking to gene products")
    else:
        print("Failed to get model")

except ImportError:
    print("gocam package not installed. Install with: pip install gocam")

10. Deleting Model Elements

Remove edges and individuals from the model:

# Delete an edge if we have one
if len(added_individuals) >= 2 and "raf" in added_individuals and "erk" in added_individuals:
    raf_id = added_individuals["raf"]
    erk_id = added_individuals["erk"]

    response = client.delete_edge(
        new_model_id,
        subject_id=raf_id,
        object_id=erk_id,
        predicate_id="RO:0002413"
    )

    if response.ok:
        print("Successfully deleted RAF->ERK edge")
    else:
        print(f"Failed to delete edge: {response.raw.get('message', '')}")

# Delete an individual
if "erk" in added_individuals:
    erk_id = added_individuals["erk"]

    response = client.delete_individual(new_model_id, erk_id)

    if response.ok:
        print(f"Successfully deleted ERK individual: {erk_id}")
    else:
        print(f"Failed to delete individual: {response.raw.get('message', '')}")

11. Clearing a Model

Remove all content from a model (use with caution!):

# Only clear if this is a test model we created
if new_model_id != TEST_MODEL_ID:
    try:
        response = client.clear_model(new_model_id)
        if response.ok:
            print(f"Successfully cleared model {new_model_id}")
        else:
            print(f"Failed to clear: {response.raw}")
    except BaristaError as e:
        print(f"Cannot clear model: {e}")
else:
    print("Not clearing the shared test model")

12. Using the CLI

You can also use the command-line interface for all these operations:

# List models
noctua-py barista list-models --limit 10 --state production

# Create a model
noctua-py barista create-model --title "My Model"

# Add an individual
noctua-py barista add-individual --model gomodel:XXX --class GO:0003924

# Add a fact/edge
noctua-py barista add-fact --model gomodel:XXX --subject id1 --object id2 --predicate RO:0002413

# Export model in different formats
noctua-py barista export-model --model gomodel:XXX --format minerva-json
noctua-py barista export-model --model gomodel:XXX --format gocam-json
noctua-py barista export-model --model gomodel:XXX --format gocam-yaml

# Delete elements
noctua-py barista delete-edge --model gomodel:XXX --subject id1 --object id2 --predicate RO:0002413
noctua-py barista delete-individual --model gomodel:XXX --individual id1

# Clear a model
noctua-py barista clear-model --model gomodel:XXX

Add --live flag to use production server instead of dev server. Add --dry-run to see the request without executing it.

Summary

This notebook demonstrated:

  1. Client Setup - Configuring BaristaClient for dev/production servers
  2. Model Management - Creating, listing, and retrieving models
  3. Building Models - Adding individuals (nodes) and facts (edges)
  4. Evidence - Annotating facts with evidence and references
  5. Export Formats - Native Minerva JSON, GO-CAM JSON, and YAML
  6. Model Editing - Deleting elements and clearing models
  7. CLI Usage - Command-line interface for all operations

For more information: - GO-CAM Documentation - noctua-py GitHub Repository