Initial commit: Cyrene Plugins SDK + Plugin Manager

Extracted from Cyrene main repo (backend/pkg/plugins + backend/plugin-manager).
Contains SDK interfaces (Plugin/Tool/HostAPI), 13 built-in plugins,
ToolRegistry with call log ring buffer, and Plugin Manager REST API service.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 09:49:12 +08:00
commit 5c807d76a0
27 changed files with 3609 additions and 0 deletions
+40
View File
@@ -0,0 +1,40 @@
package sdk
import (
"context"
"fmt"
)
// BasePlugin provides default implementations for optional Plugin methods.
type BasePlugin struct{}
func (BasePlugin) Init(_ context.Context, _ PluginConfig) error { return nil }
func (BasePlugin) Start(_ context.Context, _ HostAPI) error { return nil }
func (BasePlugin) Stop(_ context.Context) error { return nil }
func (BasePlugin) Health(_ context.Context) error { return nil }
// BaseTool provides a Validate default that checks required parameters.
type BaseTool struct {
Def ToolDefinition
Required []string
}
func (b BaseTool) Definition() ToolDefinition { return b.Def }
func (b BaseTool) Complexity() ToolComplexity { return ComplexitySimple }
func (b BaseTool) Validate(args map[string]interface{}) error {
for _, key := range b.Required {
if _, ok := args[key]; !ok {
return fmt.Errorf("missing required parameter: %s", key)
}
}
return nil
}
func (b BaseTool) Execute(_ context.Context, _ map[string]interface{}) (*ToolResult, error) {
return nil, fmt.Errorf("not implemented")
}
+35
View File
@@ -0,0 +1,35 @@
package sdk
// PluginPermissions defines what a plugin is allowed to do.
type PluginPermissions struct {
NetworkAllowed bool `json:"networkAllowed"`
AllowedHosts []string `json:"allowedHosts,omitempty"`
IoTRead bool `json:"iotRead"`
IoTWrite bool `json:"iotWrite"`
MemoryRead bool `json:"memoryRead"`
MemoryWrite bool `json:"memoryWrite"`
FileRead bool `json:"fileRead"`
FileWrite bool `json:"fileWrite"`
AllowedPaths []string `json:"allowedPaths,omitempty"`
ExecAllowed bool `json:"execAllowed"`
MaxCPUPercent float64 `json:"maxCPUPercent"`
MaxMemoryMB int `json:"maxMemoryMB"`
}
// DefaultPermissions returns a safe default permission set.
func DefaultPermissions() PluginPermissions {
return PluginPermissions{
NetworkAllowed: false,
AllowedHosts: []string{},
IoTRead: false,
IoTWrite: false,
MemoryRead: false,
MemoryWrite: false,
FileRead: false,
FileWrite: false,
AllowedPaths: []string{},
ExecAllowed: false,
MaxCPUPercent: 10.0,
MaxMemoryMB: 128,
}
}
+49
View File
@@ -0,0 +1,49 @@
package sdk
import (
"context"
"net/http"
)
// Plugin is the main interface every plugin must implement.
type Plugin interface {
Metadata() PluginMetadata
Init(ctx context.Context, config PluginConfig) error
Start(ctx context.Context, host HostAPI) error
Stop(ctx context.Context) error
Health(ctx context.Context) error
Tools() []Tool
}
// Tool is the interface every tool must implement.
type Tool interface {
Definition() ToolDefinition
Execute(ctx context.Context, args map[string]interface{}) (*ToolResult, error)
Validate(args map[string]interface{}) error
Complexity() ToolComplexity
}
// ComplexTool extends Tool for async multi-round execution.
type ComplexTool interface {
Tool
ExecuteAsync(ctx context.Context, args map[string]interface{}) (<-chan ToolProgress, error)
Cancel(ctx context.Context, executionID string) error
}
// HostAPI gives plugins access to Cyrene core capabilities.
type HostAPI interface {
CallLLM(ctx context.Context, messages []LLMMessage) (*LLMResponse, error)
SearchMemory(ctx context.Context, userID, query string, limit int) ([]MemoryEntry, error)
StoreMemory(ctx context.Context, entry MemoryEntry) error
Logger() Logger
GetConfig(key string) (string, error)
SetConfig(key, value string) error
PublishEvent(ctx context.Context, event map[string]interface{}) error
HTTPClient() *http.Client
}
// Logger is a minimal logging interface for plugins.
type Logger interface {
Printf(format string, args ...interface{})
Println(args ...interface{})
}
+134
View File
@@ -0,0 +1,134 @@
package sdk
import "time"
// ToolComplexity grades tools into simple (single-call, <2s) and complex (multi-round, async).
type ToolComplexity string
const (
ComplexitySimple ToolComplexity = "simple"
ComplexityComplex ToolComplexity = "complex"
)
// PluginMetadata describes a plugin's identity and requirements.
type PluginMetadata struct {
Name string `json:"name"`
DisplayName string `json:"displayName"`
Version string `json:"version"`
MinCyreneVersion string `json:"minCyreneVersion"`
Author PluginAuthor `json:"author"`
Description string `json:"description"`
License string `json:"license"`
Keywords []string `json:"keywords,omitempty"`
Category string `json:"category"`
Dependencies map[string]string `json:"dependencies,omitempty"` // plugin name -> version range
Homepage string `json:"homepage,omitempty"`
Repository string `json:"repository,omitempty"`
}
type PluginAuthor struct {
Name string `json:"name"`
Email string `json:"email,omitempty"`
URL string `json:"url,omitempty"`
}
// PluginConfig holds runtime configuration for a plugin.
type PluginConfig map[string]interface{}
// ToolDefinition describes a tool's interface for LLM function calling.
type ToolDefinition struct {
ID string `json:"id"`
Name string `json:"name"`
DisplayName string `json:"displayName"`
Description string `json:"description"`
Category string `json:"category"`
Complexity ToolComplexity `json:"complexity"`
Parameters map[string]interface{} `json:"parameters"`
Returns map[string]interface{} `json:"returns,omitempty"`
TimeoutMs int `json:"timeout_ms,omitempty"`
MaxRetries int `json:"max_retries,omitempty"`
DangerLevel string `json:"danger_level,omitempty"` // low / medium / high
}
// ToolResult is the standard tool execution result.
type ToolResult struct {
ToolName string `json:"tool_name"`
Success bool `json:"success"`
Output string `json:"output,omitempty"`
Error string `json:"error,omitempty"`
DurationMs int64 `json:"duration_ms,omitempty"`
}
// ToolProgress reports execution progress for complex (async) tools.
type ToolProgress struct {
ExecutionID string `json:"execution_id"`
Status string `json:"status"` // started / running / completed / failed / cancelled
Progress float64 `json:"progress"` // 0.0 - 1.0
Message string `json:"message,omitempty"`
Error string `json:"error,omitempty"`
Result *ToolResult `json:"result,omitempty"`
}
// PluginStatus represents the current lifecycle state of a plugin.
type PluginStatus string
const (
StatusInstalled PluginStatus = "installed"
StatusLoaded PluginStatus = "loaded"
StatusRunning PluginStatus = "running"
StatusPaused PluginStatus = "paused"
StatusError PluginStatus = "error"
StatusDisabled PluginStatus = "disabled"
)
// PluginInfo is the runtime view of an installed plugin.
type PluginInfo struct {
Metadata PluginMetadata `json:"metadata"`
Status PluginStatus `json:"status"`
Tools []string `json:"tools"` // tool IDs provided by this plugin
InstalledAt time.Time `json:"installed_at"`
Enabled bool `json:"enabled"`
}
// LLMMessage is a message in an LLM conversation.
type LLMMessage struct {
Role string `json:"role"`
Content string `json:"content"`
}
// LLMResponse is the result of an LLM call.
type LLMResponse struct {
Content string `json:"content"`
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
}
// ToolCall represents a tool call requested by the LLM.
type ToolCall struct {
ID string `json:"id"`
Name string `json:"name"`
Arguments map[string]interface{} `json:"arguments"`
}
// IoTDeviceState is the shared device state across IoT plugins.
type IoTDeviceState struct {
ID string `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Status string `json:"status"`
Brightness int `json:"brightness,omitempty"`
Color string `json:"color,omitempty"`
Mode string `json:"mode,omitempty"`
Temperature float64 `json:"temperature,omitempty"`
Position int `json:"position,omitempty"`
Value float64 `json:"value,omitempty"`
Unit string `json:"unit,omitempty"`
Battery int `json:"battery,omitempty"`
}
// MemoryEntry is a memory record.
type MemoryEntry struct {
UserID string `json:"user_id"`
Content string `json:"content"`
Type string `json:"type"`
Meta map[string]interface{} `json:"meta,omitempty"`
}