mirror of
https://github.com/K-Dense-AI/claude-scientific-skills.git
synced 2026-03-27 07:09:27 +08:00
Add open-notebook skill: self-hosted NotebookLM alternative (issue #56)
Implements the open-notebook skill as a comprehensive integration for the open-source, self-hosted alternative to Google NotebookLM. Addresses the gap created by Google not providing a public NotebookLM API. Developed using TDD with 44 tests covering skill structure, SKILL.md frontmatter/content, reference documentation, example scripts, API endpoint coverage, and marketplace.json registration. Includes: - SKILL.md with full documentation, code examples, and provider matrix - references/api_reference.md covering all 20+ REST API endpoint groups - references/examples.md with complete research workflow examples - references/configuration.md with Docker, env vars, and security setup - references/architecture.md with system design and data flow diagrams - scripts/ with 3 example scripts (notebook, source, chat) + test suite - marketplace.json updated to register the new skill Closes #56 https://claude.ai/code/session_015CqcNWNYmDF9sqxKxziXcz
This commit is contained in:
142
scientific-skills/open-notebook/scripts/notebook_management.py
Normal file
142
scientific-skills/open-notebook/scripts/notebook_management.py
Normal file
@@ -0,0 +1,142 @@
|
||||
"""
|
||||
Open Notebook - Notebook Management Example
|
||||
|
||||
Demonstrates creating, listing, updating, and deleting notebooks
|
||||
using the Open Notebook REST API.
|
||||
|
||||
Prerequisites:
|
||||
pip install requests
|
||||
|
||||
Usage:
|
||||
export OPEN_NOTEBOOK_URL="http://localhost:5055"
|
||||
python notebook_management.py
|
||||
"""
|
||||
|
||||
import os
|
||||
import requests
|
||||
|
||||
BASE_URL = os.getenv("OPEN_NOTEBOOK_URL", "http://localhost:5055") + "/api"
|
||||
|
||||
|
||||
def create_notebook(name, description=""):
|
||||
"""Create a new notebook."""
|
||||
response = requests.post(f"{BASE_URL}/notebooks", json={
|
||||
"name": name,
|
||||
"description": description,
|
||||
})
|
||||
response.raise_for_status()
|
||||
notebook = response.json()
|
||||
print(f"Created notebook: {notebook['id']} - {notebook['name']}")
|
||||
return notebook
|
||||
|
||||
|
||||
def list_notebooks(archived=False):
|
||||
"""List all notebooks, optionally filtering by archived status."""
|
||||
response = requests.get(f"{BASE_URL}/notebooks", params={
|
||||
"archived": archived,
|
||||
})
|
||||
response.raise_for_status()
|
||||
notebooks = response.json()
|
||||
print(f"Found {len(notebooks)} notebook(s):")
|
||||
for nb in notebooks:
|
||||
print(f" - {nb['id']}: {nb['name']} "
|
||||
f"(sources: {nb.get('source_count', 0)}, "
|
||||
f"notes: {nb.get('note_count', 0)})")
|
||||
return notebooks
|
||||
|
||||
|
||||
def get_notebook(notebook_id):
|
||||
"""Retrieve a single notebook by ID."""
|
||||
response = requests.get(f"{BASE_URL}/notebooks/{notebook_id}")
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
|
||||
def update_notebook(notebook_id, name=None, description=None, archived=None):
|
||||
"""Update notebook fields."""
|
||||
payload = {}
|
||||
if name is not None:
|
||||
payload["name"] = name
|
||||
if description is not None:
|
||||
payload["description"] = description
|
||||
if archived is not None:
|
||||
payload["archived"] = archived
|
||||
response = requests.put(
|
||||
f"{BASE_URL}/notebooks/{notebook_id}", json=payload
|
||||
)
|
||||
response.raise_for_status()
|
||||
updated = response.json()
|
||||
print(f"Updated notebook: {updated['id']} - {updated['name']}")
|
||||
return updated
|
||||
|
||||
|
||||
def delete_notebook(notebook_id, delete_sources=False):
|
||||
"""Delete a notebook and optionally its exclusive sources."""
|
||||
# Preview what will be deleted
|
||||
preview = requests.get(
|
||||
f"{BASE_URL}/notebooks/{notebook_id}/delete-preview"
|
||||
).json()
|
||||
print(f"Deletion will affect {preview.get('note_count', 0)} notes "
|
||||
f"and {preview.get('source_count', 0)} sources")
|
||||
|
||||
response = requests.delete(
|
||||
f"{BASE_URL}/notebooks/{notebook_id}",
|
||||
params={"delete_sources": delete_sources},
|
||||
)
|
||||
response.raise_for_status()
|
||||
print(f"Deleted notebook: {notebook_id}")
|
||||
|
||||
|
||||
def link_source_to_notebook(notebook_id, source_id):
|
||||
"""Associate an existing source with a notebook."""
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/notebooks/{notebook_id}/sources/{source_id}"
|
||||
)
|
||||
response.raise_for_status()
|
||||
print(f"Linked source {source_id} to notebook {notebook_id}")
|
||||
|
||||
|
||||
def unlink_source_from_notebook(notebook_id, source_id):
|
||||
"""Remove the association between a source and a notebook."""
|
||||
response = requests.delete(
|
||||
f"{BASE_URL}/notebooks/{notebook_id}/sources/{source_id}"
|
||||
)
|
||||
response.raise_for_status()
|
||||
print(f"Unlinked source {source_id} from notebook {notebook_id}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Demo workflow
|
||||
print("=== Notebook Management Demo ===\n")
|
||||
|
||||
# Create notebooks
|
||||
nb1 = create_notebook(
|
||||
"Protein Folding Research",
|
||||
"Literature review on AlphaFold and related methods"
|
||||
)
|
||||
nb2 = create_notebook(
|
||||
"CRISPR Gene Editing",
|
||||
"Survey of CRISPR-Cas9 applications in therapeutics"
|
||||
)
|
||||
|
||||
# List all notebooks
|
||||
print()
|
||||
list_notebooks()
|
||||
|
||||
# Update a notebook
|
||||
print()
|
||||
update_notebook(nb1["id"], description="Updated: Including ESMFold comparisons")
|
||||
|
||||
# Archive a notebook
|
||||
print()
|
||||
update_notebook(nb2["id"], archived=True)
|
||||
print("\nActive notebooks:")
|
||||
list_notebooks(archived=False)
|
||||
|
||||
print("\nArchived notebooks:")
|
||||
list_notebooks(archived=True)
|
||||
|
||||
# Clean up
|
||||
print()
|
||||
delete_notebook(nb1["id"])
|
||||
delete_notebook(nb2["id"])
|
||||
Reference in New Issue
Block a user