yao/sui/api/sui.go
Max 097fb73416 Refactor SUI Command Arguments and Enhance Default Template Handling
- Updated the `build` and `watch` commands to accept a single argument for `<sui>` and made the `<template>` argument optional, improving usability.
- Introduced a default template assignment for the `agent` SUI, ensuring a more intuitive setup for users.
- Enhanced error messages for command usage to provide clearer guidance on expected input format.
- Added new OpenAPI file to the bindata, improving the framework's capabilities for API integration.
2026-01-01 10:28:45 +08:00

198 lines
4.4 KiB
Go

package api
import (
"fmt"
"regexp"
"strings"
"github.com/yaoapp/gou/application"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/share"
"github.com/yaoapp/yao/sui/core"
"github.com/yaoapp/yao/sui/storages/agent"
"github.com/yaoapp/yao/sui/storages/azure"
"github.com/yaoapp/yao/sui/storages/local"
)
// New create a new sui
func New(dsl *core.DSL) (core.SUI, error) {
if dsl.Storage == nil {
return nil, fmt.Errorf("storage is not required")
}
switch strings.ToLower(dsl.Storage.Driver) {
case "local":
return local.New(dsl)
case "azure":
return azure.New(dsl)
case "agent":
return agent.New(dsl)
default:
return nil, fmt.Errorf("%s is not a valid driver", dsl.Storage.Driver)
}
}
// Load load the sui
func Load(cfg config.Config) error {
exts := []string{"*.sui.yao", "*.sui.jsonc", "*.sui.json"}
err := application.App.Walk("suis", func(root, file string, isdir bool) error {
if isdir {
return nil
}
id := share.ID(root, file)
_, err := loadFile(file, id)
if err != nil {
log.Error("[sui] Load sui %s error: %s", id, err.Error())
return nil
}
return nil
}, exts...)
if err != nil {
return err
}
// Auto-load Agent SUI if /agent directory exists and has pages
if err := loadAgentSUI(); err != nil {
log.Warn("[sui] Failed to load agent SUI: %s", err.Error())
}
buildRouteMatchers()
return registerAPI()
}
// loadAgentSUI automatically loads the agent SUI if /agent directory exists
func loadAgentSUI() error {
// Check if agent storage is available
if !agent.Exists() {
return nil // No agent directory, skip silently
}
// Check if there are any assistant pages
if !agent.HasAssistantPages() {
log.Debug("[sui] Agent directory exists but no assistant pages found")
return nil
}
// Create agent DSL
dsl := &core.DSL{
ID: "agent",
Name: "Agent",
Storage: &core.Storage{
Driver: "agent",
},
Public: &core.Public{
Root: "/agents",
Host: "/",
Index: "/index",
},
}
// Create agent SUI
sui, err := agent.New(dsl)
if err != nil {
return err
}
// Register the agent SUI
core.SUIs["agent"] = sui
log.Info("[sui] Agent SUI loaded successfully (public root: /agents)")
return nil
}
func loadFile(file string, id string) (core.SUI, error) {
dsl, err := core.Load(file, id)
if err != nil {
return nil, err
}
sui, err := New(dsl)
if err != nil {
return nil, err
}
core.SUIs[id] = sui
return core.SUIs[id], nil
}
// Reload reload the route matchers
func Reload() {
buildRouteMatchers()
}
func buildRouteMatchers() (map[*regexp.Regexp][][]*core.Matcher, map[string][][]*core.Matcher) {
matchers := map[*regexp.Regexp][][]*core.Matcher{}
exactMatchers := map[string][][]*core.Matcher{}
for id, sui := range core.SUIs {
suiMatcher := sui.PublicRootMatcher()
if suiMatcher.Regex != nil {
matchers[suiMatcher.Regex] = [][]*core.Matcher{}
} else if suiMatcher.Exact != "" {
exactMatchers[suiMatcher.Exact] = [][]*core.Matcher{}
} else {
log.Error("[sui] Load sui %s error: %s", id, "the public root is empty")
continue
}
tmpls, err := sui.GetTemplates()
if err != nil {
log.Error("[sui] Load sui %s error: %s", id, err.Error())
continue
}
for _, tmpl := range tmpls {
pages, err := tmpl.Pages()
if err != nil {
log.Error("[sui] Load sui %s error: %s", id, err.Error())
continue
}
for _, page := range pages {
route := page.Get().Route
parts := strings.Split(route, "/")[1:]
for i, part := range parts {
parent := ""
if i > 0 {
parent = parts[i-1]
}
matcher := &core.Matcher{Ref: part, Parent: parent}
if strings.HasPrefix(part, "[") && strings.HasSuffix(part, "]") {
matcher.Regex = core.RouteRegexp
} else {
matcher.Exact = part
}
if suiMatcher.Regex != nil {
if len(matchers[suiMatcher.Regex]) < i+1 {
matchers[suiMatcher.Regex] = append(matchers[suiMatcher.Regex], []*core.Matcher{})
}
matchers[suiMatcher.Regex][i] = append(matchers[suiMatcher.Regex][i], matcher)
}
if suiMatcher.Exact != "" {
if len(exactMatchers[suiMatcher.Exact]) < i+1 {
exactMatchers[suiMatcher.Exact] = append(exactMatchers[suiMatcher.Exact], []*core.Matcher{})
}
exactMatchers[suiMatcher.Exact][i] = append(exactMatchers[suiMatcher.Exact][i], matcher)
}
}
}
}
}
core.RouteMatchers = matchers
core.RouteExactMatchers = exactMatchers
return matchers, exactMatchers
}