feat: publish sanitized custom.97 source

This commit is contained in:
cneicy committed 2026-10-27 20:43:39 +08:00
1 parent 8e6ed0317b
commit 86c0276156
1022 files changed
+69903 -19207

No files matched your search

+94 -18
View File
@@ -100,6 +100,7 @@ type Meta struct {
Models []string `json:"models"`
FetchMode string `json:"fetchMode"`
AllowedHosts []string `json:"allowedHosts"`
Upstreams []string `json:"upstreams,omitempty"`
Routes []Route `json:"routes"`
Protocols []ProtocolClaim `json:"protocols"`
UsageSchema map[string]UsageFieldSchema `json:"usageSchema,omitempty"`
@@ -108,6 +109,47 @@ type Meta struct {
Auth AuthMeta `json:"auth"`
}
// Upstream kinds a plugin driver can address. Every driver speaks to its
// vendor; a driver that also builds its own native-route URLs when the channel
// points at another New API gateway declares UpstreamKindNewAPI in
// meta.upstreams, which is what makes it bindable to a type-60 channel.
const (
UpstreamKindVendor = "vendor"
UpstreamKindNewAPI = "new_api"
)
// SupportsUpstream reports whether the driver handles channels of the given
// upstream kind. The vendor kind is implied for every plugin.
func (m Meta) SupportsUpstream(kind string) bool {
return kind == UpstreamKindVendor || slices.Contains(m.Upstreams, kind)
}
// PreservesJSONOrder reports whether the plugin declared json-order@1: its
// hooks receive JSON request bodies with members in the order the client sent
// them, and its JSON request bodies go upstream as JSON.stringify writes them.
// Other plugins keep the cheaper Go-map path, whose members enumerate sorted.
func (m Meta) PreservesJSONOrder() bool {
return slices.Contains(m.RequiredCapabilities, CapabilityJSONOrder)
}
// AcceptsAutoDuration reports whether the plugin declared duration-auto@1:
// request validation lets a duration of -1, the vendor's "the model picks the
// length", through to its hooks. The plugin reserves the longest duration the
// model can produce and settles the task on the usage the vendor reports.
func (m Meta) AcceptsAutoDuration() bool {
return slices.Contains(m.RequiredCapabilities, CapabilityDurationAuto)
}
// JSONTextMember names the member of a hook result (a decoded requestBody, a
// request descriptor's body) that the host also takes as JSON text, for a
// plugin that preserves JSON order; it is empty for other plugins.
func (m Meta) JSONTextMember(member string) string {
if !m.PreservesJSONOrder() {
return ""
}
return member
}
// UsageProfile replaces the plugin's default usage metadata for its models.
type UsageProfile struct {
Models []string `json:"models"`
@@ -119,11 +161,22 @@ type UsageProfile struct {
// must be resolved by the host first; an unknown or ambiguous model uses the
// plugin defaults. Profile examples never inherit the default examples.
func (m Meta) UsageForModel(model string) (map[string]UsageFieldSchema, []UsageExample) {
folded := asciiFold(model)
for _, profile := range m.UsageProfiles {
for _, declared := range profile.Models {
if asciiFold(declared) == folded {
return profile.Schema, profile.Examples
return m.UsageForModels(model)
}
// UsageForModels returns the usage metadata of the first candidate that a
// profile declares. Runtime callers pass the final upstream model before the
// client-facing model, so a channel mapping that sends a declared model to a
// vendor endpoint ID keeps the declared model's profile. When no candidate is
// profiled, the plugin defaults apply.
func (m Meta) UsageForModels(models ...string) (map[string]UsageFieldSchema, []UsageExample) {
for _, model := range models {
folded := asciiFold(model)
for _, profile := range m.UsageProfiles {
for _, declared := range profile.Models {
if asciiFold(declared) == folded {
return profile.Schema, profile.Examples
}
}
}
}
@@ -173,6 +226,15 @@ type LoadedPlugin struct {
Engine *Engine
}
// UsesSubmitEventDelta reports whether SSE submissions go through
// parseSubmitEventDelta: the plugin declares submit-sse-delta@1, or exports
// parseSubmitEventDelta without parseSubmitEvent. A plugin that exports both
// and declares nothing keeps the snapshot hook, as before.
func UsesSubmitEventDelta(meta Meta, engine *Engine) bool {
return slices.Contains(meta.RequiredCapabilities, CapabilitySubmitSSEDelta) ||
engine.HasExport("parseSubmitEventDelta") && !engine.HasExport("parseSubmitEvent")
}
// RegistrySnapshot is a read-only copy of the metadata currently stored in
// each registry layer.
type RegistrySnapshot struct {
@@ -296,7 +358,7 @@ func CompilePlugin(source string, options Options) (*LoadedPlugin, error) {
engine.version = meta.Version
requiredHooks := []string{"buildSubmitRequest", "parseSubmitResponse", "parseTaskResult"}
if slices.Contains(meta.SubmitResponseTypes, "sse") {
if slices.Contains(meta.RequiredCapabilities, CapabilitySubmitSSEDelta) {
if UsesSubmitEventDelta(meta, engine) {
requiredHooks = append(requiredHooks, "parseSubmitEventDelta")
} else {
requiredHooks = append(requiredHooks, "parseSubmitEvent")
@@ -318,11 +380,7 @@ func CompilePlugin(source string, options Options) (*LoadedPlugin, error) {
}
artifactHooks := make(map[string]bool, 2)
for _, hook := range []string{"listArtifacts", "buildContentRequest"} {
exported, exportErr := engine.HasExport(context.Background(), hook)
if exportErr != nil {
return nil, exportErr
}
if !exported {
if !engine.HasExport(hook) {
continue
}
callable, callableErr := engine.HasCallablePath(context.Background(), hook)
@@ -470,11 +528,7 @@ func CompilePlugin(source string, options Options) (*LoadedPlugin, error) {
}
}
for _, removed := range []string{"resolveRequest", "renderError", "renderers"} {
has, e := engine.HasExport(context.Background(), removed)
if e != nil {
return nil, e
}
if has {
if engine.HasExport(removed) {
return nil, fmt.Errorf("plugin %s export %q is no longer supported", meta.Key, removed)
}
}
@@ -834,6 +888,7 @@ func cloneMeta(meta Meta) Meta {
meta.ChannelTypes = append([]int(nil), meta.ChannelTypes...)
meta.Models = append([]string(nil), meta.Models...)
meta.AllowedHosts = append([]string(nil), meta.AllowedHosts...)
meta.Upstreams = slices.Clone(meta.Upstreams)
meta.Routes = append([]Route(nil), meta.Routes...)
for index := range meta.Routes {
meta.Routes[index].Models = append([]string(nil), meta.Routes[index].Models...)
@@ -975,7 +1030,7 @@ func decodeMeta(value any) (Meta, error) {
}
for field := range object {
switch field {
case "requiredCapabilities", "submitResponseTypes", "sortPriority", "website", "apiVersion", "key", "name", "icon", "description", "version", "author", "baseUrl", "channelTypes", "channelType", "compatibleChannelTypes", "models", "fetchMode", "allowedHosts", "routes", "protocols", "usageSchema", "usageExamples", "usageProfiles", "auth", "endpoints", "submitPaths", "actions":
case "requiredCapabilities", "submitResponseTypes", "sortPriority", "website", "apiVersion", "key", "name", "icon", "description", "version", "author", "baseUrl", "channelTypes", "channelType", "compatibleChannelTypes", "models", "fetchMode", "allowedHosts", "upstreams", "routes", "protocols", "usageSchema", "usageExamples", "usageProfiles", "auth", "endpoints", "submitPaths", "actions":
default:
return Meta{}, &UnknownMetaFieldError{Field: field}
}
@@ -1071,6 +1126,10 @@ func decodeMeta(value any) (Meta, error) {
if err != nil {
return Meta{}, err
}
meta.Upstreams, err = strictStringSlice(object, "upstreams")
if err != nil {
return Meta{}, err
}
meta.Routes, err = decodeRoutes(object["routes"])
if err != nil {
return Meta{}, err
@@ -1159,6 +1218,13 @@ func normalizeV1Meta(meta *Meta) error {
}
seenCapabilities[name] = true
}
seenUpstreams := make(map[string]bool, len(meta.Upstreams))
for _, kind := range meta.Upstreams {
if (kind != UpstreamKindVendor && kind != UpstreamKindNewAPI) || seenUpstreams[kind] {
return fmt.Errorf("unsupported or duplicate upstream kind %q", kind)
}
seenUpstreams[kind] = true
}
if meta.SortPriority < math.MinInt32 || meta.SortPriority > math.MaxInt32 {
return fmt.Errorf("plugin meta sortPriority must be a signed 32-bit integer")
}
@@ -1256,6 +1322,9 @@ func normalizeV1Meta(meta *Meta) error {
if channelType == constant.ChannelTypeTaskPlugin {
return fmt.Errorf("plugin meta channelTypes must not contain the task plugin channel type")
}
if channelType == constant.ChannelTypeNewAPI {
return fmt.Errorf("plugin meta channelTypes must not contain the New API channel type; bind the plugin through task_extend_plugin_keys instead")
}
if _, duplicate := seenChannelTypes[channelType]; duplicate {
return fmt.Errorf("plugin meta channelTypes must be unique")
}
@@ -1724,7 +1793,7 @@ func decodeRoutes(value any) ([]Route, error) {
}
for key := range object {
switch key {
case "method", "path", "type", "action", "decode", "render", "taskIdParam", "models":
case "method", "path", "type", "action", "decode", "render", "taskIdParam", "models", "retainResult":
default:
return nil, fmt.Errorf("plugin meta route %d has unknown field %q", index, key)
}
@@ -1762,6 +1831,13 @@ func decodeRoutes(value any) ([]Route, error) {
return nil, fmt.Errorf("plugin meta route %d models must contain at least one model", index)
}
}
if value, exists := object["retainResult"]; exists {
retain, ok := value.(bool)
if !ok {
return nil, fmt.Errorf("plugin meta route %d retainResult must be a boolean", index)
}
route.RetainResult = &retain
}
routes = append(routes, route)
}
return routes, nil