Official SDK for developing Nadoo AI KB plugins
Project description
Nadoo Plugin SDK
Official Python SDK for developing custom plugins for Nadoo AI Platform.
Extend Nadoo with custom tools, integrate external APIs, and build reusable AI components using a simple decorator-based API.
Features
- 🎯 Simple API - Decorator-based API for defining tools with minimal boilerplate
- 🤖 Built-in LLM Access - Invoke AI models configured in your workspace
- 📚 Knowledge Integration - Search and retrieve from knowledge bases
- 🐛 Powerful Debugging - Comprehensive logging, tracing, and variable watching
Additional Capabilities:
- 🔒 Type-safe with full runtime validation
- 🛠️ Rich CLI tooling for development lifecycle
- 🧪 Built-in testing utilities with PluginTestCase
- 🔄 Seamless workflow integration
- ⚡ Real-time performance tracking
Installation
pip install nadoo-plugin-sdk
Quick Start
1. Create a New Plugin
nadoo-plugin create my-awesome-plugin
cd my-awesome-plugin
2. Implement Your Plugin
# main.py
from nadoo_plugin import NadooPlugin, tool, parameter
class MyPlugin(NadooPlugin):
"""My awesome plugin"""
def on_initialize(self):
"""Called when plugin is loaded"""
self.api_key = self.require_env("API_KEY")
self.context.log("Plugin initialized!")
@tool(
name="process_text",
description="Process text using AI"
)
@parameter("text", type="string", required=True, description="Text to process")
@parameter("mode", type="string", default="standard", description="Processing mode")
def process_text(self, text: str, mode: str = "standard") -> dict:
"""Process text and return results"""
# Use Nadoo's LLM
response = self.api.llm.invoke(
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": f"Process this text in {mode} mode: {text}"}
],
temperature=0.7
)
return {
"processed": response.content,
"mode": mode,
"tokens_used": response.usage["total_tokens"]
}
3. Test Your Plugin
nadoo-plugin test --tool process_text --params '{"text": "Hello World"}'
4. Build and Deploy
nadoo-plugin build
# Creates: my-awesome-plugin-1.0.0.nadoo-plugin
# Upload to Nadoo workspace
nadoo-plugin install my-awesome-plugin-1.0.0.nadoo-plugin --workspace my-workspace
Core Concepts
Plugin Class
All plugins inherit from NadooPlugin:
from nadoo_plugin import NadooPlugin
class MyPlugin(NadooPlugin):
def on_initialize(self):
"""Optional: Initialize plugin"""
pass
def on_finalize(self):
"""Optional: Cleanup before shutdown"""
pass
Tools
Tools are functions that can be called by workflows:
@tool(name="my_tool", description="Does something useful")
@parameter("input", type="string", required=True)
def my_tool(self, input: str) -> dict:
return {"result": input.upper()}
Context
Access execution context and logging:
def my_tool(self, input: str) -> dict:
# Logging (supports debug, info, warn, error)
self.context.log("Processing input", level="info")
self.context.warn("This might take a while")
self.context.error("Something went wrong")
self.context.debug("Debug information")
# Execution Steps
self.context.start_step("fetch_data")
# ... do work ...
self.context.end_step() # Automatically tracks duration
# Variable Watching
self.context.watch_variable("input_length", len(input), metadata={"type": "metric"})
# Trace Events
self.context.add_trace("custom_event", data={"key": "value"})
return {"result": "done"}
Internal APIs
Access Nadoo services from your plugin:
LLM API
# Invoke AI models
response = self.api.llm.invoke(
messages=[{"role": "user", "content": "Hello!"}],
temperature=0.7,
max_tokens=100
)
print(response.content) # AI response
print(response.model_name) # Model used
print(response.usage) # Token usage
Tools API
# Call other tools
result = self.api.tools.invoke(
tool_name="web_search",
parameters={"query": "AI news"}
)
Knowledge Base API
# Search knowledge bases
results = self.api.knowledge.search(
knowledge_base_uuid="kb-uuid-here",
query="How to use plugins?",
top_k=5
)
for result in results:
print(result.content, result.score)
Storage API
# Store persistent data
self.api.storage.set("my_key", {"data": "value"}, ttl=3600)
# Retrieve data
value = self.api.storage.get("my_key")
# Delete data
self.api.storage.delete("my_key")
manifest.yaml
Every plugin needs a manifest.yaml:
name: my-awesome-plugin
display_name: My Awesome Plugin
version: 1.0.0
author: Your Name
description: A plugin that does awesome things
# SDK version requirement
# Supported formats:
# "0.1.0" - Exact version
# ">=0.1.0" - Greater than or equal
# "^0.1.0" - Compatible (allows minor/patch updates, same major)
# "~0.1.0" - Approximately (allows patch updates only)
sdk_version: ">=0.1.0"
entry_point: main.py
python_version: ">=3.9"
permissions:
- llm_access
- storage
environment_variables:
- name: API_KEY
required: true
description: Your API key
tools:
- name: process_text
description: Process text using AI
parameters:
- name: text
type: string
required: true
- name: mode
type: string
required: false
default: standard
resource_limits:
memory_mb: 100
timeout_seconds: 30
homepage: https://github.com/user/my-plugin
license: MIT
keywords:
- text
- ai
CLI Commands
# Create new plugin
nadoo-plugin create <name>
# Validate plugin
nadoo-plugin validate
# Test plugin locally
nadoo-plugin test --tool <tool-name> --params <json>
# Build plugin package
nadoo-plugin build
# Install to workspace
nadoo-plugin install <package-file> --workspace <workspace-id>
Testing Your Plugin
The SDK provides comprehensive testing utilities for unit testing plugins.
Basic Test Example
# test_my_plugin.py
from nadoo_plugin.testing import PluginTestCase
from my_plugin import MyPlugin
class TestMyPlugin(PluginTestCase):
plugin_class = MyPlugin
def test_process_text(self):
# Mock LLM response
self.mock_llm_response("Processed text result")
# Execute tool
result = self.execute_tool("process_text", {
"text": "Hello World",
"mode": "standard"
})
# Assertions
self.assertSuccess(result)
self.assertHasKey(result, "processed")
self.assertEqual(result["processed"], "Processed text result")
# Verify LLM was called
self.assertLLMCalled(temperature=0.7)
Available Mock Helpers
# Mock LLM responses
self.mock_llm_response("AI response", model_name="gpt-4")
# Mock tool responses
self.mock_tool_response("web_search", {"results": [...]})
# Mock knowledge search results
self.mock_knowledge_results([
{"content": "Result 1", "score": 0.9},
{"content": "Result 2", "score": 0.8}
])
# Pre-populate storage
self.set_storage("cache_key", "cached_value")
Available Assertions
# Result assertions
self.assertSuccess(result)
self.assertError(result, error_msg="Expected error")
self.assertHasKey(result, "key_name")
self.assertEqual(result["key"], expected_value)
self.assertIn("substring", result["text"])
# API call assertions
self.assertLLMCalled(temperature=0.7, max_tokens=500)
self.assertLLMNotCalled()
self.assertToolCalled("web_search", parameters={"query": "test"})
self.assertToolNotCalled("dangerous_tool")
self.assertStorageSet("key", "value")
self.assertStorageGet("key")
self.assertKnowledgeSearchCalled(query="test", top_k=5)
# Context assertions
self.assertLogContains("message", level="info")
self.assertVariableWatched("variable_name", value=123)
self.assertStepStarted("step_name")
self.assertStepCompleted("step_name")
self.assertTraceContains("event_name")
# Debug output
self.print_debug() # Print all debug information
debug_data = self.get_debug_data() # Get debug data dict
Running Tests
# Run tests
python -m pytest test_my_plugin.py -v
# Run with coverage
python -m pytest --cov=my_plugin test_my_plugin.py
Decorators
@validator Decorator
Validate tool parameters with declarative constraints:
from nadoo_plugin import NadooPlugin, tool, parameter, validator
class MyPlugin(NadooPlugin):
@tool(name="process_text", description="Process text")
@parameter("text", type="string", required=True)
@parameter("count", type="integer", default=5)
@validator("text", min_length=10, max_length=1000, pattern=r'^[a-zA-Z0-9\s]+$')
@validator("count", min_value=1, max_value=100)
def process_text(self, text: str, count: int = 5) -> dict:
# text and count are already validated
return {"result": text, "count": count}
@tool(name="format_text", description="Format text")
@parameter("format", type="string", required=True)
@validator("format", allowed_values=["upper", "lower", "title", "capitalize"])
def format_text(self, text: str, format: str) -> dict:
# format is validated to be one of the allowed values
formatters = {
"upper": text.upper,
"lower": text.lower,
"title": text.title,
"capitalize": text.capitalize,
}
return {"formatted": formatters[format]()}
Supported validator constraints:
allowed_values: List of allowed valuesmin_value,max_value: Numeric range (for int/float)min_length,max_length: String/array lengthpattern: Regex pattern (for strings)
@validate_parameters Decorator
For advanced validation with custom schemas:
from nadoo_plugin import NadooPlugin, tool, validate_parameters
class MyPlugin(NadooPlugin):
@tool(name="create_user", description="Create a user")
@validate_parameters({
"email": {"type": "string", "pattern": r"^[\w\.-]+@[\w\.-]+\.\w+$"},
"age": {"type": "number", "min": 0, "max": 150},
"name": {"type": "string", "min_length": 2, "max_length": 100}
})
def create_user(self, email: str, age: int, name: str) -> dict:
return {"success": True, "user": {"email": email, "age": age, "name": name}}
@retry Decorator
Automatically retry failed tool executions with exponential backoff:
from nadoo_plugin import NadooPlugin, tool, retry
class MyPlugin(NadooPlugin):
@tool(name="fetch_data", description="Fetch data from API")
@retry(max_attempts=3, delay=1.0, backoff=2.0)
def fetch_data(self, url: str) -> dict:
"""
Will retry up to 3 times with exponential backoff:
- First retry: 1.0s delay
- Second retry: 2.0s delay
- Third retry: 4.0s delay
"""
import httpx
response = httpx.get(url, timeout=5.0)
response.raise_for_status()
return {"data": response.json()}
@permission_required Decorator
Check permissions before tool execution:
from nadoo_plugin import NadooPlugin, tool, permission_required
class MyPlugin(NadooPlugin):
@tool(name="analyze_data", description="Analyze data")
@permission_required("llm_access", "storage")
def analyze_data(self, data: str) -> dict:
# Only executes if plugin has both llm_access and storage permissions
response = self.api.llm.invoke([{"role": "user", "content": data}])
self.api.storage.set("last_analysis", response.content)
return {"result": response.content}
Real-time Debugging
The SDK includes a comprehensive debugging system that streams execution data in real-time via WebSocket.
Debug Data Includes:
- Logs - All log messages with timestamps and levels
- Execution Trace - Timeline of events and steps
- Variables - Watched variables with values and metadata
- API Calls - All API calls with duration and success status
- Performance Metrics - Step durations, API timings, success rates
Using the Debug Inspector
Connect to the debug WebSocket endpoint:
ws://backend-url/ws/plugin-debug/{execution_uuid}
The frontend includes a built-in PluginDebugInspector component with:
- Log viewer with filtering
- Execution trace timeline
- Variable inspector
- API calls timeline
- Performance metrics dashboard
Examples
See the /examples directory for complete plugin examples:
- hello-world - Basic plugin demonstrating core features and decorators
- llm-summarizer - Advanced LLM integration with caching, batch processing, and KB search
- Text summarization with multiple styles
- Keyword extraction
- Question answering with Knowledge Base integration
- Batch summarization with error handling
Documentation
📖 Full Documentation: https://docs.nadoo.ai/plugin-sdk/overview
Support
- 🐛 Issues: https://github.com/nadoo-ai/nadoo-plugin-sdk/issues
- 💬 Discussions: https://github.com/nadoo-ai/nadoo-plugin-sdk/discussions
- 📧 Email: support@nadoo.ai
License
MIT License - see LICENSE file for details
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nadoo_plugin_sdk-0.1.0.tar.gz.
File metadata
- Download URL: nadoo_plugin_sdk-0.1.0.tar.gz
- Upload date:
- Size: 33.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
930fda01452c718ae6a39902c2a5be9b7a090a763f3cd4fd2693d9783db68ab7
|
|
| MD5 |
dfa9f568cb08b28f9ca073557bc4240e
|
|
| BLAKE2b-256 |
95365b5d00e8803d3aacdc9417f0119b478571f985d031f2151077beea5fe132
|
File details
Details for the file nadoo_plugin_sdk-0.1.0-py3-none-any.whl.
File metadata
- Download URL: nadoo_plugin_sdk-0.1.0-py3-none-any.whl
- Upload date:
- Size: 39.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9f61cf98990b534f103a93d4d7ac4544c015a07b478ff5b81f20a1f02d92e4cb
|
|
| MD5 |
2cae93b3c2aca8e52c98fb5c7c176002
|
|
| BLAKE2b-256 |
4405dfa846318a87aad7e5a3435103f7da11c66b31ead8a8314994186394017d
|