mirror of
https://github.com/Tencent/WeKnora.git
synced 2026-10-15 10:58:21 +08:00
Events:
- A plugin lists what it wants in permissions.events: knowledge.ingested,
knowledge.failed, knowledge.deleted or chat.answered. The admin reviews
the list at install; a plugin that only listens needs no other
contribution.
- A publish enqueues one plugin:event task per subscribed plugin the
workspace has on (maintenance queue, Redis or Lite).
- The task calls the plugin's new /v1/events at least once. Retryable
failures retry up to 10 times, keeping the event ID; other failures drop
the event.
- The plugin host refuses a process that subscribes without serving
events.
Where events come from:
- A document reaches completed or failed through a dozen paths but a few
repository writes, so a knowledge repository wrapper raises
knowledge.ingested and knowledge.failed on the transition only:
UpdateKnowledge, UpdateKnowledgeForTransfer, UpdateKnowledgeColumns,
FinalizeSubtask, CompleteProcessingWithoutSubtasks.
- knowledge.deleted comes from the shared delete path.
- chat.answered comes from where RAG and agent answers are persisted. The
stop button's early completion, which has no question, is skipped.
Webhooks:
- contributes.webhooks gives each workspace a secret URL per webhook:
/api/v1/plugin-callbacks/{plugin}/{webhook}/{tenant}.{mac}. The MAC is
keyed from SYSTEM_AES_KEY/JWT_SECRET.
- WeKnora answers 404 unless the URL is genuine, the webhook exists and the
workspace has the plugin on. Calls are rate-limited per URL, bodies are
capped at 1 MB and cookies are stripped.
- The call is relayed to the plugin's /v1/webhooks/{id} with that
workspace's envelope; the plugin verifies the sender.
- Admins get the URLs from GET /plugins/:id/webhooks, shown with copy
buttons in the plugin center's configure drawer. Plugins get them in
context.webhooks when APP_EXTERNAL_URL is set.
SDKs:
- Go: OnEvent and Webhook.
- Python: @plugin.on_event and @plugin.webhook.
- Conformance, OpenAPI and READMEs are updated.
Also: examples/plugins/activity (Python) keeps a workspace activity feed
from events and a secret-checked note webhook, and shows it on a toolbox
page.
76 lines
2.6 KiB
Go
76 lines
2.6 KiB
Go
package pluginapi
|
|
|
|
import (
|
|
"encoding/json"
|
|
"time"
|
|
)
|
|
|
|
// Envelope is the body of every call. Plugins must be stateless: everything
|
|
// about the tenant comes in the envelope, and credentials must not be cached
|
|
// across calls.
|
|
type Envelope struct {
|
|
Context Context `json:"context"`
|
|
Config Config `json:"config"`
|
|
// Input is the endpoint-specific request, such as a SearchInput.
|
|
Input json.RawMessage `json:"input,omitempty"`
|
|
}
|
|
|
|
// Context describes who is calling and until when.
|
|
type Context struct {
|
|
TenantID uint64 `json:"tenantId"`
|
|
UserID string `json:"userId,omitempty"`
|
|
Locale string `json:"locale,omitempty"`
|
|
RequestID string `json:"requestId,omitempty"`
|
|
// Deadline is when WeKnora stops waiting; plugins should give up by then.
|
|
Deadline *time.Time `json:"deadline,omitempty"`
|
|
// Host lets the plugin call back into WeKnora (Host API); absent when
|
|
// the plugin was granted no scopes.
|
|
Host *HostAccess `json:"host,omitempty"`
|
|
// Webhooks are this workspace's URLs of the plugin's webhooks, by
|
|
// webhook ID, for registering them with a third party. Present only
|
|
// when WeKnora knows its public address.
|
|
Webhooks map[string]string `json:"webhooks,omitempty"`
|
|
}
|
|
|
|
// HostAccess is where and how a plugin calls the WeKnora Host API.
|
|
type HostAccess struct {
|
|
URL string `json:"url"`
|
|
// Token is a short-lived bearer token bound to this tenant and the
|
|
// plugin's granted scopes.
|
|
Token string `json:"token"`
|
|
}
|
|
|
|
// Config carries the three configuration scopes separately, secrets already
|
|
// decrypted. They are never merged, so a key means the same thing wherever
|
|
// it appears.
|
|
type Config struct {
|
|
// System is the platform-wide configuration (config.system).
|
|
System map[string]any `json:"system,omitempty"`
|
|
// Tenant is the workspace configuration (config.tenant).
|
|
Tenant map[string]any `json:"tenant,omitempty"`
|
|
// Instance is the configuration of the integration instance being used:
|
|
// a data source, a web search provider.
|
|
Instance map[string]any `json:"instance,omitempty"`
|
|
}
|
|
|
|
// Output wraps a successful answer.
|
|
type Output struct {
|
|
Output json.RawMessage `json:"output"`
|
|
}
|
|
|
|
// Manifest is what GET /v1/manifest answers: enough for WeKnora to check that
|
|
// the process it started is the package it installed.
|
|
type Manifest struct {
|
|
ID string `json:"id"`
|
|
Version string `json:"version"`
|
|
APIVersion string `json:"apiVersion"`
|
|
// Contributes lists the contribution IDs the process serves per
|
|
// extension point, e.g. {"connectors": ["rss"]}.
|
|
Contributes map[string][]string `json:"contributes"`
|
|
}
|
|
|
|
// Health is what GET /v1/health answers.
|
|
type Health struct {
|
|
Status string `json:"status"` // "ok"
|
|
}
|