0.9.0 - Added Trilium ETAPI integration.
Lyra can now: Search trilium notes and create new notes. with proper ETAPI auth.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
from .code_executor import execute_code
|
||||
from .web_search import search_web
|
||||
from .trillium import search_notes, create_note
|
||||
from .trilium import search_notes, create_note
|
||||
|
||||
__all__ = [
|
||||
"execute_code",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
Trillium notes executor for searching and creating notes via ETAPI.
|
||||
Trilium notes executor for searching and creating notes via ETAPI.
|
||||
|
||||
This module provides integration with Trillium notes through the ETAPI HTTP API.
|
||||
This module provides integration with Trilium notes through the ETAPI HTTP API.
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -9,12 +9,12 @@ import aiohttp
|
||||
from typing import Dict
|
||||
|
||||
|
||||
TRILLIUM_URL = os.getenv("TRILLIUM_URL", "http://localhost:8080")
|
||||
TRILLIUM_TOKEN = os.getenv("TRILLIUM_ETAPI_TOKEN", "")
|
||||
TRILIUM_URL = os.getenv("TRILIUM_URL", "http://localhost:8080")
|
||||
TRILIUM_TOKEN = os.getenv("TRILIUM_ETAPI_TOKEN", "")
|
||||
|
||||
|
||||
async def search_notes(args: Dict) -> Dict:
|
||||
"""Search Trillium notes via ETAPI.
|
||||
"""Search Trilium notes via ETAPI.
|
||||
|
||||
Args:
|
||||
args: Dictionary containing:
|
||||
@@ -35,8 +35,8 @@ async def search_notes(args: Dict) -> Dict:
|
||||
if not query:
|
||||
return {"error": "No query provided"}
|
||||
|
||||
if not TRILLIUM_TOKEN:
|
||||
return {"error": "TRILLIUM_ETAPI_TOKEN not configured in environment"}
|
||||
if not TRILIUM_TOKEN:
|
||||
return {"error": "TRILIUM_ETAPI_TOKEN not configured in environment"}
|
||||
|
||||
# Cap limit
|
||||
limit = min(max(limit, 1), 20)
|
||||
@@ -44,30 +44,32 @@ async def search_notes(args: Dict) -> Dict:
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(
|
||||
f"{TRILLIUM_URL}/etapi/search-notes",
|
||||
f"{TRILIUM_URL}/etapi/notes",
|
||||
params={"search": query, "limit": limit},
|
||||
headers={"Authorization": TRILLIUM_TOKEN}
|
||||
headers={"Authorization": TRILIUM_TOKEN}
|
||||
) as resp:
|
||||
if resp.status == 200:
|
||||
data = await resp.json()
|
||||
# ETAPI returns {"results": [...]} format
|
||||
results = data.get("results", [])
|
||||
return {
|
||||
"notes": data,
|
||||
"count": len(data)
|
||||
"notes": results,
|
||||
"count": len(results)
|
||||
}
|
||||
elif resp.status == 401:
|
||||
return {"error": "Authentication failed. Check TRILLIUM_ETAPI_TOKEN"}
|
||||
return {"error": "Authentication failed. Check TRILIUM_ETAPI_TOKEN"}
|
||||
else:
|
||||
error_text = await resp.text()
|
||||
return {"error": f"HTTP {resp.status}: {error_text}"}
|
||||
|
||||
except aiohttp.ClientConnectorError:
|
||||
return {"error": f"Cannot connect to Trillium at {TRILLIUM_URL}"}
|
||||
return {"error": f"Cannot connect to Trilium at {TRILIUM_URL}"}
|
||||
except Exception as e:
|
||||
return {"error": f"Search failed: {str(e)}"}
|
||||
|
||||
|
||||
async def create_note(args: Dict) -> Dict:
|
||||
"""Create a note in Trillium via ETAPI.
|
||||
"""Create a note in Trilium via ETAPI.
|
||||
|
||||
Args:
|
||||
args: Dictionary containing:
|
||||
@@ -85,7 +87,7 @@ async def create_note(args: Dict) -> Dict:
|
||||
"""
|
||||
title = args.get("title")
|
||||
content = args.get("content")
|
||||
parent_note_id = args.get("parent_note_id")
|
||||
parent_note_id = args.get("parent_note_id", "root") # Default to root if not specified
|
||||
|
||||
# Validation
|
||||
if not title:
|
||||
@@ -94,26 +96,24 @@ async def create_note(args: Dict) -> Dict:
|
||||
if not content:
|
||||
return {"error": "No content provided"}
|
||||
|
||||
if not TRILLIUM_TOKEN:
|
||||
return {"error": "TRILLIUM_ETAPI_TOKEN not configured in environment"}
|
||||
if not TRILIUM_TOKEN:
|
||||
return {"error": "TRILIUM_ETAPI_TOKEN not configured in environment"}
|
||||
|
||||
# Prepare payload
|
||||
payload = {
|
||||
"parentNoteId": parent_note_id, # Always include parentNoteId
|
||||
"title": title,
|
||||
"content": content,
|
||||
"type": "text",
|
||||
"mime": "text/html"
|
||||
}
|
||||
|
||||
if parent_note_id:
|
||||
payload["parentNoteId"] = parent_note_id
|
||||
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
f"{TRILLIUM_URL}/etapi/create-note",
|
||||
f"{TRILIUM_URL}/etapi/create-note",
|
||||
json=payload,
|
||||
headers={"Authorization": TRILLIUM_TOKEN}
|
||||
headers={"Authorization": TRILIUM_TOKEN}
|
||||
) as resp:
|
||||
if resp.status in [200, 201]:
|
||||
data = await resp.json()
|
||||
@@ -123,12 +123,12 @@ async def create_note(args: Dict) -> Dict:
|
||||
"success": True
|
||||
}
|
||||
elif resp.status == 401:
|
||||
return {"error": "Authentication failed. Check TRILLIUM_ETAPI_TOKEN"}
|
||||
return {"error": "Authentication failed. Check TRILIUM_ETAPI_TOKEN"}
|
||||
else:
|
||||
error_text = await resp.text()
|
||||
return {"error": f"HTTP {resp.status}: {error_text}"}
|
||||
|
||||
except aiohttp.ClientConnectorError:
|
||||
return {"error": f"Cannot connect to Trillium at {TRILLIUM_URL}"}
|
||||
return {"error": f"Cannot connect to Trilium at {TRILIUM_URL}"}
|
||||
except Exception as e:
|
||||
return {"error": f"Note creation failed: {str(e)}"}
|
||||
Reference in New Issue
Block a user