Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,11 @@ docker-run-raphtory-arm64:
$(if $(OTLP_AGENT_HOST),--otlp-agent-host=$(OTLP_AGENT_HOST)) \
$(if $(OTLP_AGENT_PORT),--otlp-agent-port=$(OTLP_AGENT_PORT)) \
$(if $(OTLP_TRACING_SERVICE_NAME),--otlp-tracing-service-name=$(OTLP_TRACING_SERVICE_NAME))

################
# Claude Skill #
################

claude-skill: build-python-public
cat llms/generate-raphtory-skill.md | claude --permission-mode dontAsk --allowedTools 'Bash,Read,Write,Glob,Grep'
python llms/build_skill_md.py
42 changes: 42 additions & 0 deletions llms/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
name: raphtory-python-api
description: >
Use this skill when the user asks to write Python code using raphtory,
create temporal graphs, run graph algorithms, query graph properties,
use time views or layers, load graph data, or work with the raphtory
Python library in any way. Also use when discussing raphtory API usage,
graph analytics, or temporal network analysis with raphtory.
version: 0.17.0
---

# Raphtory Python API Examples


## Graph Creation & Basics
```python
from raphtory import Graph
```


```python
g = Graph()
print(g)
# => Graph(number_of_nodes=0, number_of_edges=0, number_of_temporal_edges=0, earliest_time=None, latest_time=None)
```


# Add nodes with timestamps and properties
```python
g.add_node(1, "alice", properties={"age": 30})
g.add_node(2, "bob", properties={"age": 25})
print(g.count_nodes())
# => 2
```


# Add edges with layers
```python
g.add_edge(3, "alice", "bob", properties={"weight": 1.0}, layer="friends")
print(g.count_edges())
# => 1
```
140 changes: 140 additions & 0 deletions llms/build_skill_md.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#!/usr/bin/env python3
"""
Build SKILL.md from raphtory_python_examples.py by executing the script
line-by-line and interleaving code with captured output.

Usage: python3 scripts/build_skill_md.py
"""

import ast
import io
import contextlib
import subprocess
import sys
from pathlib import Path

SCRIPT_PATH = Path(__file__).parent / "raphtory_python_examples.py"
OUTPUT_PATH = Path(__file__).parent / "SKILL.md"

FRONTMATTER = """\
---
name: raphtory-python-api
description: >
Use this skill when the user asks to write Python code using raphtory,
create temporal graphs, run graph algorithms, query graph properties,
use time views or layers, load graph data, or work with the raphtory
Python library in any way. Also use when discussing raphtory API usage,
graph analytics, or temporal network analysis with raphtory.
version: {version}
---

# Raphtory Python API Examples

"""


def get_version():
result = subprocess.run(
[sys.executable, "-c", "import raphtory; print(raphtory.__version__)"],
capture_output=True,
text=True,
)
return result.stdout.strip()


def build_skill():
version = get_version()
source = SCRIPT_PATH.read_text()
lines = source.splitlines()

# Parse the script into blocks: comments, blank lines, and code chunks.
# A code chunk is one or more consecutive non-comment, non-blank lines
# that form a complete statement (or group of statements).
blocks = [] # each block is ("comment", text) | ("blank",) | ("code", lines_text)
i = 0
while i < len(lines):
line = lines[i]
if line.strip() == "":
blocks.append(("blank",))
i += 1
elif line.lstrip().startswith("#"):
blocks.append(("comment", line))
i += 1
else:
# Collect consecutive code lines until we hit a blank or comment
code_lines = []
while i < len(lines) and lines[i].strip() != "" and not (
lines[i].lstrip().startswith("#") and not code_lines_need_continuation(code_lines)
):
code_lines.append(lines[i])
i += 1
blocks.append(("code", "\n".join(code_lines)))

# Now execute code blocks incrementally and capture output
namespace = {}
md_lines = [FRONTMATTER.format(version=version)]
in_code_block = False

for block in blocks:
if block[0] == "blank":
if in_code_block:
md_lines.append("```\n")
in_code_block = False
md_lines.append("")
elif block[0] == "comment":
if in_code_block:
md_lines.append("```\n")
in_code_block = False
text = block[1]
if text.startswith("## "):
# Section header
md_lines.append(text)
else:
md_lines.append(text)
elif block[0] == "code":
code_text = block[1]
if not in_code_block:
md_lines.append("```python")
in_code_block = True

# Capture stdout from executing this code block
stdout_capture = io.StringIO()
try:
with contextlib.redirect_stdout(stdout_capture):
exec(compile(code_text, SCRIPT_PATH, "exec"), namespace)
except Exception as e:
print(f"Error executing:\n{code_text}\n\nException: {e}", file=sys.stderr)
sys.exit(1)

captured = stdout_capture.getvalue()

# Add code lines to markdown
for code_line in code_text.splitlines():
md_lines.append(code_line)

# Add captured output as comments
if captured.strip():
for out_line in captured.strip().splitlines():
md_lines.append(f"# => {out_line}")

if in_code_block:
md_lines.append("```\n")

OUTPUT_PATH.write_text("\n".join(md_lines))
print(f"Written {OUTPUT_PATH} ({len(md_lines)} lines, version {version})")


def code_lines_need_continuation(code_lines):
"""Check if the accumulated code lines are an incomplete statement."""
if not code_lines:
return False
text = "\n".join(code_lines)
try:
ast.parse(text)
return False
except SyntaxError:
return True


if __name__ == "__main__":
build_skill()
105 changes: 105 additions & 0 deletions llms/generate-raphtory-skill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Generate Raphtory Python API Examples

You are generating a Python script that demonstrates the entire raphtory Python API through compact, runnable examples. This script will later be converted into a Claude Code skill file automatically.

## Step 1: Discover the API surface

First, get the installed version:

```bash
python3 -c "import raphtory; print(raphtory.__version__)"
```

Then dynamically discover all raphtory submodules and read their pydoc:

```bash
python3 -c "
import raphtory, pkgutil, importlib
modules = ['raphtory']
for importer, modname, ispkg in pkgutil.walk_packages(raphtory.__path__, prefix='raphtory.'):
if not modname.startswith('raphtory._'):
modules.append(modname)
print('\n'.join(modules))
"
```

Run `pydoc <module>` for the top-level `raphtory` module and every discovered submodule. Read through ALL of them carefully — this is the authoritative source for the current API.

## Step 2: Generate the examples script

Write the file `llms/raphtory_python_examples.py`. This is a plain Python script where:

- **Section headers** are comments starting with `## ` (e.g. `## Graph Creation & Basics`)
- **Explanations** are regular `#` comments
- **Code** is normal Python statements
- Lines that produce meaningful output should use `print()` so the output can be captured later
- The script must run top-to-bottom without errors

### Topics to cover (adapt based on what actually exists in the API):

- Graph creation and basic metrics
- Adding nodes and edges (with timestamps, properties, types, layers)
- Bulk loading from DataFrames, dicts, CSV
- Querying nodes and edges (iteration, properties, temporal properties, history)
- Time views and windowing
- Layer views
- Subgraph and filtering
- ALL algorithms (grouped by category)
- Graph generators and built-in dataset loaders
- GraphQL server basics (import and setup, but don't actually start the server)
- Saving and loading graphs

### Style rules

- Keep the script to ~300-500 lines
- Use comments for section headers and brief explanations only — no walls of text
- Group related operations together
- Use built-in loaders (e.g. `raphtory.graph_loader.karate_club_graph()`) or simple hand-built graphs — no external CSV files
- Every algorithm should have at least a one-line example
- Use `print()` for outputs you want to show (e.g. `print(g.count_nodes())`)
- Do NOT wrap everything in functions or classes — keep it as a flat script

### Example of expected style:

```python
## Graph Creation & Basics
from raphtory import Graph

g = Graph()
print(g)

# Add nodes with timestamps and properties
g.add_node(1, "alice", properties={"age": 30})
g.add_node(2, "bob", properties={"age": 25})
print(g.count_nodes())

# Add edges with layers
g.add_edge(3, "alice", "bob", properties={"weight": 1.0}, layer="friends")
print(g.count_edges())
```

## Step 3: Verify the script

**This is critical.** Run the entire script and confirm it executes without errors:

```bash
python llms/raphtory_python_examples.py
```

If any part fails:

1. Debug why it failed
2. Fix the script
3. Re-run to verify

## Step 4: Final review

Before writing the final file, verify:

- [ ] All major API areas discovered in Step 1 are covered
- [ ] All algorithms have at least a one-line example
- [ ] The entire script executes successfully end-to-end
- [ ] The script is ~300-500 lines
- [ ] No external data file dependencies

Write the result to `llms/raphtory_python_examples.py` using Bash (do NOT use the Write tool).
14 changes: 14 additions & 0 deletions llms/raphtory_python_examples.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Graph Creation & Basics
from raphtory import Graph

g = Graph()
print(g)

# Add nodes with timestamps and properties
g.add_node(1, "alice", properties={"age": 30})
g.add_node(2, "bob", properties={"age": 25})
print(g.count_nodes())

# Add edges with layers
g.add_edge(3, "alice", "bob", properties={"weight": 1.0}, layer="friends")
print(g.count_edges())
19 changes: 19 additions & 0 deletions raphtory-graphql/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ use clap::{command, Parser, Subcommand};
use std::path::PathBuf;
use tokio::io::Result as IoResult;

const SKILL_CONTENT: &str = include_str!("../../llms/SKILL.md");

#[derive(Parser)]
#[command(name = "raphtory", about = "Raphtory CLI", version = raphtory::version())]
struct Args {
Expand All @@ -32,6 +34,8 @@ enum Commands {
Server(ServerArgs),
#[command(about = "Print the GraphQL schema")]
Schema,
#[command(about = "Install Claude Code skill for the raphtory Python API")]
InstallSkill,
}

#[derive(clap::Args)]
Expand Down Expand Up @@ -97,6 +101,21 @@ where
let schema = App::create_schema().finish().unwrap();
println!("{}", schema.sdl());
}
Commands::InstallSkill => {
let home = std::env::var("HOME")
.map_err(|e| std::io::Error::new(std::io::ErrorKind::NotFound, e))?;
let skill_dir = PathBuf::from(home)
.join(".claude")
.join("skills")
.join("raphtory-python-api");
std::fs::create_dir_all(&skill_dir)?;
let skill_path = skill_dir.join("SKILL.md");
std::fs::write(&skill_path, SKILL_CONTENT)?;
println!(
"Installed raphtory Claude Code skill to {}",
skill_path.display()
);
}
Commands::Server(server_args) => {
let mut builder = AppConfigBuilder::new()
.with_cache_capacity(server_args.cache_capacity)
Expand Down
Loading