Merge pull request #1468 from trheyi/main
Enhance guard handling and template configuration merging
This commit is contained in:
commit
846acd1882
10 changed files with 172 additions and 59 deletions
|
|
@ -362,6 +362,23 @@ func (p *Provider) streamWithRetry(ctx *context.Context, messages []context.Mess
|
||||||
Index: event.Index,
|
Index: event.Index,
|
||||||
}
|
}
|
||||||
startToolCallMessage(msgTracker, toolCallInfo, handler)
|
startToolCallMessage(msgTracker, toolCallInfo, handler)
|
||||||
|
|
||||||
|
// Send initial ChunkToolCall with id and function name
|
||||||
|
// to match OpenAI format so CUI can resolve tool name from stored chunks
|
||||||
|
if handler != nil {
|
||||||
|
toolCallData, _ := jsoniter.Marshal([]map[string]interface{}{
|
||||||
|
{
|
||||||
|
"index": event.Index,
|
||||||
|
"id": event.ContentBlock.ID,
|
||||||
|
"type": "function",
|
||||||
|
"function": map[string]interface{}{
|
||||||
|
"name": event.ContentBlock.Name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
handler(message.ChunkToolCall, toolCallData)
|
||||||
|
incrementChunk(msgTracker)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
44
cmd/start.go
44
cmd/start.go
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
@ -82,32 +81,15 @@ var startCmd = &cobra.Command{
|
||||||
config.Development()
|
config.Development()
|
||||||
}
|
}
|
||||||
|
|
||||||
startTime := time.Now()
|
|
||||||
|
|
||||||
// load the application engine
|
// load the application engine
|
||||||
var progressCallback func(string, string)
|
|
||||||
if config.Conf.Mode == "development" {
|
|
||||||
fmt.Println(color.CyanString("Loading application engine..."))
|
|
||||||
progressCallback = func(name string, duration string) {
|
|
||||||
fmt.Printf(" %s %s %s\n", color.GreenString("✓"), name, color.GreenString("(%s)", duration))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadWarnings, err := engine.Load(config.Conf, engine.LoadOption{
|
loadWarnings, err := engine.Load(config.Conf, engine.LoadOption{
|
||||||
Action: "start",
|
Action: "start",
|
||||||
}, progressCallback)
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(color.RedString(L("Load: %s"), err.Error()))
|
fmt.Println(color.RedString(L("Load: %s"), err.Error()))
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
loadDuration := time.Since(startTime)
|
|
||||||
if config.Conf.Mode == "development" {
|
|
||||||
fmt.Printf("\n%s Engine loaded successfully in %s\n\n",
|
|
||||||
color.GreenString("✓"),
|
|
||||||
color.CyanString("%v", loadDuration))
|
|
||||||
}
|
|
||||||
|
|
||||||
port := fmt.Sprintf(":%d", config.Conf.Port)
|
port := fmt.Sprintf(":%d", config.Conf.Port)
|
||||||
if port == ":80" {
|
if port == ":80" {
|
||||||
port = ""
|
port = ""
|
||||||
|
|
@ -205,12 +187,20 @@ var startCmd = &cobra.Command{
|
||||||
fmt.Println(color.WhiteString("\n---------------------------------"))
|
fmt.Println(color.WhiteString("\n---------------------------------"))
|
||||||
fmt.Println(color.WhiteString(L("Access Points")))
|
fmt.Println(color.WhiteString(L("Access Points")))
|
||||||
fmt.Println(color.WhiteString("---------------------------------"))
|
fmt.Println(color.WhiteString("---------------------------------"))
|
||||||
|
apiRoot := "/api"
|
||||||
|
if openapi.Server != nil {
|
||||||
|
apiRoot = openapi.Server.Config.BaseURL
|
||||||
|
}
|
||||||
for _, endpoint := range endpoints {
|
for _, endpoint := range endpoints {
|
||||||
fmt.Println(color.CyanString("\n%s", endpoint.Interface))
|
fmt.Println(color.CyanString("\n%s", endpoint.Interface))
|
||||||
fmt.Println(color.WhiteString("--------------------------"))
|
fmt.Println(color.WhiteString("--------------------------"))
|
||||||
fmt.Println(color.WhiteString(L("Website")), color.GreenString(" %s", endpoint.URL))
|
fmt.Println(color.WhiteString(L("Website")), color.GreenString(" %s", endpoint.URL))
|
||||||
fmt.Println(color.WhiteString(L("Admin")), color.GreenString(" %s/%s/login/admin", endpoint.URL, strings.Trim(root, "/")))
|
fmt.Println(color.WhiteString(L("Dashboard")), color.GreenString(" %s/%s/auth/entry", endpoint.URL, strings.Trim(root, "/")))
|
||||||
fmt.Println(color.WhiteString(L("API")), color.GreenString(" %s/api", endpoint.URL))
|
if openapi.Server != nil {
|
||||||
|
fmt.Println(color.WhiteString(L("OpenAPI")), color.GreenString(" %s%s", endpoint.URL, apiRoot))
|
||||||
|
} else {
|
||||||
|
fmt.Println(color.WhiteString(L("API")), color.GreenString(" %s%s", endpoint.URL, apiRoot))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
|
|
||||||
|
|
@ -472,17 +462,15 @@ func printApis(silent bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip detailed API list when OpenAPI is enabled
|
||||||
|
if openapi.Server != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println(color.WhiteString("\n---------------------------------"))
|
fmt.Println(color.WhiteString("\n---------------------------------"))
|
||||||
fmt.Println(color.WhiteString(L("APIs List")))
|
fmt.Println(color.WhiteString(L("APIs List")))
|
||||||
fmt.Println(color.WhiteString("---------------------------------"))
|
fmt.Println(color.WhiteString("---------------------------------"))
|
||||||
|
|
||||||
// Show OpenAPI mode info if enabled
|
|
||||||
if openapi.Server != nil {
|
|
||||||
fmt.Println(color.CyanString("\nOpenAPI Mode: %s", apiRoot))
|
|
||||||
fmt.Println(color.WhiteString("Developer APIs: %s/api/*", apiRoot))
|
|
||||||
fmt.Println(color.WhiteString("Widgets: %s/__yao/*", apiRoot))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, api := range api.APIs { // API info
|
for _, api := range api.APIs { // API info
|
||||||
if len(api.HTTP.Paths) <= 0 {
|
if len(api.HTTP.Paths) <= 0 {
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,6 @@ func withStaticFileServer(c *gin.Context) {
|
||||||
|
|
||||||
// Sui file server
|
// Sui file server
|
||||||
if strings.HasSuffix(c.Request.URL.Path, ".sui") {
|
if strings.HasSuffix(c.Request.URL.Path, ".sui") {
|
||||||
|
|
||||||
// Default index.sui
|
// Default index.sui
|
||||||
if filepath.Base(c.Request.URL.Path) == ".sui" {
|
if filepath.Base(c.Request.URL.Path) == ".sui" {
|
||||||
c.Request.URL.Path = strings.TrimSuffix(c.Request.URL.Path, ".sui") + "index.sui"
|
c.Request.URL.Path = strings.TrimSuffix(c.Request.URL.Path, ".sui") + "index.sui"
|
||||||
|
|
@ -94,12 +93,17 @@ func withStaticFileServer(c *gin.Context) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if code == 301 || code == 302 {
|
if code == 301 || code == 302 {
|
||||||
url := err.Error()
|
url := err.Error()
|
||||||
// fmt.Println("Redirect to: ", url)
|
|
||||||
c.Redirect(code, url)
|
c.Redirect(code, url)
|
||||||
c.Done()
|
c.Done()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Guard already sent response (e.g., OAuth writes its own 401)
|
||||||
|
if c.Writer.Written() {
|
||||||
|
c.Done()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
log.Error("Sui Render Error: %s", err.Error())
|
log.Error("Sui Render Error: %s", err.Error())
|
||||||
c.AbortWithStatusJSON(code, gin.H{"code": code, "message": err.Error()})
|
c.AbortWithStatusJSON(code, gin.H{"code": code, "message": err.Error()})
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,8 @@ func guardCookieTrace(r *Request) error {
|
||||||
// OAuth 2.1 guard - authentication only
|
// OAuth 2.1 guard - authentication only
|
||||||
// This guard validates the token and sets authorized info
|
// This guard validates the token and sets authorized info
|
||||||
// ACL checks are performed separately in Run() for API calls
|
// ACL checks are performed separately in Run() for API calls
|
||||||
|
// NOTE: This guard does NOT write HTTP responses on failure, so that
|
||||||
|
// the caller (Guard/apiGuard) can handle redirects or custom error responses.
|
||||||
func guardOAuth(r *Request) error {
|
func guardOAuth(r *Request) error {
|
||||||
if r.context == nil {
|
if r.context == nil {
|
||||||
return fmt.Errorf("Context is nil")
|
return fmt.Errorf("Context is nil")
|
||||||
|
|
@ -108,11 +110,22 @@ func guardOAuth(r *Request) error {
|
||||||
|
|
||||||
c := r.context
|
c := r.context
|
||||||
|
|
||||||
// Authenticate only (validates token and sets authorized info)
|
// Check token first without writing response.
|
||||||
if !oauth.OAuth.Authenticate(c) {
|
// oauth.Authenticate() writes JSON + aborts on failure, which prevents
|
||||||
return fmt.Errorf("Not authenticated")
|
// the caller from doing redirects. So we check the token manually first.
|
||||||
|
token := oauth.OAuth.GetAccessToken(c)
|
||||||
|
if token == "" {
|
||||||
|
return fmt.Errorf("Exception|401:Not authenticated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := oauth.OAuth.VerifyToken(token); err != nil {
|
||||||
|
return fmt.Errorf("Exception|401:Invalid or expired token")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token is valid, now call Authenticate to set up the full context
|
||||||
|
// (session ID, authorized info, etc.). This will succeed since token is valid.
|
||||||
|
oauth.OAuth.Authenticate(c)
|
||||||
|
|
||||||
// Get authorized info from context
|
// Get authorized info from context
|
||||||
info := authorized.GetInfo(c)
|
info := authorized.GetInfo(c)
|
||||||
if info != nil {
|
if info != nil {
|
||||||
|
|
|
||||||
|
|
@ -237,6 +237,13 @@ func (r *Request) MakeCache() (*core.Cache, int, error) {
|
||||||
guardRedirect = parts[1]
|
guardRedirect = parts[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback: if guard has no redirect, check template default redirect
|
||||||
|
if guardRedirect == "" && guard != "" && guard != "-" {
|
||||||
|
if defaultRedirect, has := core.DefaultGuardRedirects[guard]; has {
|
||||||
|
guardRedirect = defaultRedirect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Cache store
|
// Cache store
|
||||||
cacheStore = conf.CacheStore
|
cacheStore = conf.CacheStore
|
||||||
cacheTime = conf.Cache
|
cacheTime = conf.Cache
|
||||||
|
|
@ -303,8 +310,8 @@ func (r *Request) MakeCache() (*core.Cache, int, error) {
|
||||||
// Guard the page
|
// Guard the page
|
||||||
func (r *Request) Guard(c *core.Cache) (int, error) {
|
func (r *Request) Guard(c *core.Cache) (int, error) {
|
||||||
|
|
||||||
// Guard not set
|
// Guard not set or explicitly disabled
|
||||||
if c.Guard == "" || r.context == nil {
|
if c.Guard == "" || c.Guard == "-" || r.context == nil {
|
||||||
return 200, nil
|
return 200, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -312,32 +319,27 @@ func (r *Request) Guard(c *core.Cache) (int, error) {
|
||||||
if guard, has := Guards[c.Guard]; has {
|
if guard, has := Guards[c.Guard]; has {
|
||||||
err := guard(r)
|
err := guard(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Redirect the page (should refector before release)
|
// Redirect the page (takes priority over guard's own response)
|
||||||
if c.GuardRedirect != "" {
|
if c.GuardRedirect != "" {
|
||||||
redirect := c.GuardRedirect
|
redirect := c.GuardRedirect
|
||||||
data := core.Data{}
|
|
||||||
// Here may have a security issue, should be refector, in the future.
|
// Append error code and message as query parameters
|
||||||
// Copy the script pointer to the request For page backend script execution
|
ex := exception.Err(err, 403)
|
||||||
r.Request.Script = c.Script
|
msg := url.QueryEscape(ex.Message)
|
||||||
if c.Data != "" {
|
if strings.Contains(redirect, "?") {
|
||||||
data, err = r.Request.ExecString(c.Data)
|
redirect = fmt.Sprintf("%s&code=%d&message=%s", redirect, ex.Code, msg)
|
||||||
if err != nil {
|
} else {
|
||||||
return 500, fmt.Errorf("data error, please re-complie the page %s", err.Error())
|
redirect = fmt.Sprintf("%s?code=%d&message=%s", redirect, ex.Code, msg)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Global != "" {
|
|
||||||
global, err := r.Request.ExecString(c.Global)
|
|
||||||
if err != nil {
|
|
||||||
return 500, fmt.Errorf("global data error, please re-complie the page %s", err.Error())
|
|
||||||
}
|
|
||||||
data["$global"] = global
|
|
||||||
}
|
|
||||||
|
|
||||||
redirect, _ = data.Replace(redirect)
|
|
||||||
return 302, fmt.Errorf("%s", redirect)
|
return 302, fmt.Errorf("%s", redirect)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Guard already sent response (e.g., OAuth writes its own 401)
|
||||||
|
if r.context != nil && r.context.IsAborted() {
|
||||||
|
return 403, err
|
||||||
|
}
|
||||||
|
|
||||||
// Return the error
|
// Return the error
|
||||||
ex := exception.Err(err, 403)
|
ex := exception.Err(err, 403)
|
||||||
return ex.Code, fmt.Errorf("%s", ex.Message)
|
return ex.Code, fmt.Errorf("%s", ex.Message)
|
||||||
|
|
@ -348,6 +350,10 @@ func (r *Request) Guard(c *core.Cache) (int, error) {
|
||||||
// Developer custom guard
|
// Developer custom guard
|
||||||
err := r.processGuard(c.Guard)
|
err := r.processGuard(c.Guard)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Guard already sent response
|
||||||
|
if r.context != nil && r.context.IsAborted() {
|
||||||
|
return 403, err
|
||||||
|
}
|
||||||
ex := exception.Err(err, 403)
|
ex := exception.Err(err, 403)
|
||||||
return ex.Code, fmt.Errorf("%s", ex.Message)
|
return ex.Code, fmt.Errorf("%s", ex.Message)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,11 @@ import (
|
||||||
// SUIs the loaded SUI instances
|
// SUIs the loaded SUI instances
|
||||||
var SUIs = map[string]SUI{}
|
var SUIs = map[string]SUI{}
|
||||||
|
|
||||||
|
// DefaultGuardRedirects stores default guard redirect URLs from template configs.
|
||||||
|
// Key is guard name (e.g. "oauth"), value is redirect URL (e.g. "/dashboard/auth/entry").
|
||||||
|
// Registered by template loading (e.g. agent storage) and used by MakeCache as fallback.
|
||||||
|
var DefaultGuardRedirects = map[string]string{}
|
||||||
|
|
||||||
// RouteMatchers the route matchers for the SUI instance
|
// RouteMatchers the route matchers for the SUI instance
|
||||||
var RouteMatchers = map[*regexp.Regexp][][]*Matcher{}
|
var RouteMatchers = map[*regexp.Regexp][][]*Matcher{}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -185,8 +185,9 @@ type Template struct {
|
||||||
GlobalData []byte `json:"-"`
|
GlobalData []byte `json:"-"`
|
||||||
Scripts *TemplateScirpts `json:"scripts,omitempty"`
|
Scripts *TemplateScirpts `json:"scripts,omitempty"`
|
||||||
Translator string `json:"translator,omitempty"`
|
Translator string `json:"translator,omitempty"`
|
||||||
BuildScript *Script `json:"-"` // __build.backend.ts / __build.backend.js
|
Config *PageSetting `json:"config,omitempty"` // Default page config (guard, api, etc.)
|
||||||
GlobalScript *Script `json:"-"` // __global.backend.ts / __global.backend.js
|
BuildScript *Script `json:"-"` // __build.backend.ts / __build.backend.js
|
||||||
|
GlobalScript *Script `json:"-"` // __global.backend.ts / __global.backend.js
|
||||||
}
|
}
|
||||||
|
|
||||||
// TemplateScirpts is the struct for the template scripts
|
// TemplateScirpts is the struct for the template scripts
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,12 @@ func (agent *Agent) GetTemplate(id string) (core.ITemplate, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register default guard redirect from template config
|
||||||
|
if tmpl.Template.Config != nil && strings.Contains(tmpl.Template.Config.Guard, ":") {
|
||||||
|
parts := strings.SplitN(tmpl.Template.Config.Guard, ":", 2)
|
||||||
|
core.DefaultGuardRedirects[parts[0]] = parts[1]
|
||||||
|
}
|
||||||
|
|
||||||
// Load __document.html
|
// Load __document.html
|
||||||
documentFile := filepath.Join(agent.root, "__document.html")
|
documentFile := filepath.Join(agent.root, "__document.html")
|
||||||
if agent.fs.IsFile(documentFile) {
|
if agent.fs.IsFile(documentFile) {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
|
@ -157,17 +158,52 @@ func (page *Page) GetConfig() *core.PageConfig {
|
||||||
if fs.IsFile(confFile) {
|
if fs.IsFile(confFile) {
|
||||||
content, err := fs.ReadFile(confFile)
|
content, err := fs.ReadFile(confFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return page.mergeTemplateConfig(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
var config core.PageConfig
|
var config core.PageConfig
|
||||||
if err := jsoniter.Unmarshal(content, &config); err == nil {
|
if err := jsoniter.Unmarshal(content, &config); err == nil {
|
||||||
p.Config = &config
|
p.Config = &config
|
||||||
return p.Config
|
return page.mergeTemplateConfig(p.Config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return page.mergeTemplateConfig(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergeTemplateConfig merges template default config into page config (page config takes priority).
|
||||||
|
// Use guard: "-" in page config to explicitly disable guard inheritance.
|
||||||
|
func (page *Page) mergeTemplateConfig(cfg *core.PageConfig) *core.PageConfig {
|
||||||
|
tmplConfig := page.tmpl.Template.Config
|
||||||
|
if tmplConfig == nil {
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg == nil {
|
||||||
|
cfg = &core.PageConfig{PageSetting: *tmplConfig}
|
||||||
|
page.Page.Config = cfg
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge guard (page config takes priority, "-" means explicitly no guard)
|
||||||
|
if cfg.Guard == "" {
|
||||||
|
// Page has no guard, use template's guard (with redirect)
|
||||||
|
cfg.Guard = tmplConfig.Guard
|
||||||
|
} else if !strings.Contains(cfg.Guard, ":") && strings.Contains(tmplConfig.Guard, ":") {
|
||||||
|
// Page has guard without redirect (e.g. "oauth"), template has redirect (e.g. "oauth:/login")
|
||||||
|
// Inherit redirect from template if same guard type
|
||||||
|
tmplParts := strings.SplitN(tmplConfig.Guard, ":", 2)
|
||||||
|
if tmplParts[0] == cfg.Guard {
|
||||||
|
cfg.Guard = tmplConfig.Guard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge API guard config
|
||||||
|
if cfg.API == nil && tmplConfig.API != nil {
|
||||||
|
cfg.API = tmplConfig.API
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveTemp save the page temporarily (not supported for agent pages)
|
// SaveTemp save the page temporarily (not supported for agent pages)
|
||||||
|
|
@ -293,6 +329,9 @@ func (page *Page) Build(globalCtx *core.GlobalBuildContext, option *core.BuildOp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge template default config before compile (page config takes priority)
|
||||||
|
page.GetConfig()
|
||||||
|
|
||||||
html, config, warnings, err := page.Page.Compile(ctx, option)
|
html, config, warnings, err := page.Page.Compile(ctx, option)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return warnings, fmt.Errorf("Compile the page %s error: %s", page.Route, err.Error())
|
return warnings, fmt.Errorf("Compile the page %s error: %s", page.Route, err.Error())
|
||||||
|
|
@ -444,6 +483,9 @@ func (page *Page) Trans(globalCtx *core.GlobalBuildContext, option *core.BuildOp
|
||||||
warnings := []string{}
|
warnings := []string{}
|
||||||
ctx := core.NewBuildContext(globalCtx)
|
ctx := core.NewBuildContext(globalCtx)
|
||||||
|
|
||||||
|
// Merge template default config before compile
|
||||||
|
page.GetConfig()
|
||||||
|
|
||||||
_, _, messages, err := page.Page.Compile(ctx, option)
|
_, _, messages, err := page.Page.Compile(ctx, option)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return warnings, err
|
return warnings, err
|
||||||
|
|
|
||||||
|
|
@ -580,6 +580,36 @@ func processXgen(process *process.Process) interface{} {
|
||||||
// agentConfig["connectors"] = connector.AIConnectors
|
// agentConfig["connectors"] = connector.AIConnectors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// External tools availability (safe subset for frontend)
|
||||||
|
toolsConfig := map[string]interface{}{}
|
||||||
|
if share.Tools != nil {
|
||||||
|
safeTool := func(info *share.ExtToolInfo) map[string]interface{} {
|
||||||
|
if info == nil {
|
||||||
|
return map[string]interface{}{"available": false}
|
||||||
|
}
|
||||||
|
return map[string]interface{}{
|
||||||
|
"available": info.Available,
|
||||||
|
"name": info.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toolsConfig["ffmpeg"] = safeTool(share.Tools.FFmpeg)
|
||||||
|
toolsConfig["ffprobe"] = safeTool(share.Tools.FFprobe)
|
||||||
|
toolsConfig["pdftoppm"] = safeTool(share.Tools.Pdftoppm)
|
||||||
|
toolsConfig["mutool"] = safeTool(share.Tools.Mutool)
|
||||||
|
toolsConfig["imagemagick"] = safeTool(share.Tools.ImageMagick)
|
||||||
|
|
||||||
|
if share.Tools.Docker != nil {
|
||||||
|
docker := map[string]interface{}{
|
||||||
|
"available": share.Tools.Docker.Available,
|
||||||
|
"name": "docker",
|
||||||
|
}
|
||||||
|
if share.Tools.Docker.Mode != "" {
|
||||||
|
docker["mode"] = share.Tools.Docker.Mode
|
||||||
|
}
|
||||||
|
toolsConfig["docker"] = docker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// OpenAPI Settings
|
// OpenAPI Settings
|
||||||
openapiConfig := map[string]interface{}{}
|
openapiConfig := map[string]interface{}{}
|
||||||
if openapi.Server != nil {
|
if openapi.Server != nil {
|
||||||
|
|
@ -688,6 +718,7 @@ func processXgen(process *process.Process) interface{} {
|
||||||
"optional": Setting.Optional,
|
"optional": Setting.Optional,
|
||||||
"login": xgenLogin,
|
"login": xgenLogin,
|
||||||
"agent": agentConfig,
|
"agent": agentConfig,
|
||||||
|
"tools": toolsConfig,
|
||||||
"openapi": openapiConfig,
|
"openapi": openapiConfig,
|
||||||
"kb": kbConfig,
|
"kb": kbConfig,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue