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:
serversdwn
2025-12-29 01:58:20 -05:00
parent 64429b19e6
commit 794baf2a96
14 changed files with 2063 additions and 39 deletions

View File

@@ -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",

View File

@@ -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)}"}

View File

@@ -26,7 +26,7 @@ class ToolRegistry:
# Feature flags from environment
self.code_execution_enabled = os.getenv("ENABLE_CODE_EXECUTION", "true").lower() == "true"
self.web_search_enabled = os.getenv("ENABLE_WEB_SEARCH", "true").lower() == "true"
self.trillium_enabled = os.getenv("ENABLE_TRILLIUM", "false").lower() == "true"
self.trilium_enabled = os.getenv("ENABLE_TRILIUM", "false").lower() == "true"
self._register_tools()
self._register_executors()
@@ -39,7 +39,7 @@ class ToolRegistry:
if self.web_search_enabled:
self.executors["search_web"] = search_web
if self.trillium_enabled:
if self.trilium_enabled:
self.executors["search_notes"] = search_notes
self.executors["create_note"] = create_note
@@ -85,10 +85,10 @@ class ToolRegistry:
"required": ["query"]
}
if self.trillium_enabled:
if self.trilium_enabled:
self.tools["search_notes"] = {
"name": "search_notes",
"description": "Search through Trillium notes to find relevant information. Use this to retrieve knowledge, context, or information previously stored in the user's notes.",
"description": "Search through Trilium notes to find relevant information. Use this to retrieve knowledge, context, or information previously stored in the user's notes.",
"parameters": {
"query": {
"type": "string",
@@ -104,7 +104,7 @@ class ToolRegistry:
self.tools["create_note"] = {
"name": "create_note",
"description": "Create a new note in Trillium. Use this to store important information, insights, or knowledge for future reference. Notes are stored in the user's Trillium knowledge base.",
"description": "Create a new note in Trilium. Use this to store important information, insights, or knowledge for future reference. Notes are stored in the user's Trilium knowledge base.",
"parameters": {
"title": {
"type": "string",

View File

@@ -4,8 +4,8 @@
"focus": "conversation",
"confidence": 0.7,
"curiosity": 1.0,
"last_updated": "2025-12-21T18:50:41.582043",
"interaction_count": 26,
"last_updated": "2025-12-27T18:16:00.152499",
"interaction_count": 27,
"learning_queue": [],
"active_goals": [],
"preferences": {