Files
ragflow/internal/handler/admin_runtime.go

109 lines
3.4 KiB
Go
Raw Permalink Normal View History

feat(agent): Go port — canvas engine, 22 components, DSL v2, 13 endpoints (#15952) Ports the agent canvas subsystem from Python to Go. ## What's included ### Canvas Engine (Phase 0/1) - State engine, scheduler, variable resolver, Redis checkpoint store, cancel protocol - **209 tests** across canvas / component / io packages ### 22 Components (P0–P4) | Tier | Components | |---|---| | P0 T1+T2+T3 | LLM, Agent, ExitLoop, Switch, Categorize, Begin, Message, Invoke | | P1 T3 | VariableAggregator, VariableAssigner, StringTransform, ListOperations, DataOperations | | P2 T3 | Iteration, IterationItem, Loop, LoopItem | | P3 T3 | UserFillUp, Fillup | | P4 T5 | Browser, ExcelProcessor, DocsGenerator | ### DSL v2 Schema (Phase 2.5) - Typed v2 in-memory model with v1-to-v2 auto-detect converter - v1 legacy field stripping per plan §2.11.7 ### HTTP Endpoints & Bug Fixes (Plans PR1–PR3) - **DELETE SQL bug fix**: gorm v2 `Where("id = ?", id).Delete(...)` pattern - **CreateAgent validation**: title/DSL required, duplicate check, 103 envelope - **13 new endpoints**: templates, prompts, tags, sessions CRUD, chat/completions (SSE + non-stream stubs), rerun, test_db_connection, logs, webhook/logs - **756 Go unit tests** (745 → 756, +18) - **17 → 0 Python integration test failures** (test_agents.py + test_session_management/) ### Tools 21 eino tools: HTTPHelper, search tools, financial/data tools, mandatory stubs ### Infrastructure OTel observability, NATS message queue, DeepDoc gRPC client, SSRF guards, IDOR mitigation
2026-06-12 22:58:28 +08:00
//
// Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package handler
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
"ragflow/internal/agent/runtime"
"ragflow/internal/common"
)
// AdminRuntimeHandler exposes the per-tenant canvas-runtime override API
// used by the Phase 6 canary operators. It is intentionally small — the
// selector is the only collaborator it needs.
type AdminRuntimeHandler struct {
selector *runtime.Selector
}
// NewAdminRuntimeHandler constructs an AdminRuntimeHandler backed by the
// supplied Selector. A nil selector is treated as a misconfiguration and
// the handler refuses every request with HTTP 500.
func NewAdminRuntimeHandler(selector *runtime.Selector) *AdminRuntimeHandler {
return &AdminRuntimeHandler{selector: selector}
}
// setRuntimeRequest is the wire shape for POST
// /api/v1/admin/canvas-runtime/:tenant_id. The mode is required; empty or
// unknown values yield 400.
type setRuntimeRequest struct {
Runtime string `json:"runtime"`
}
// setRuntimeResponse is what the operator sees in the 200 body.
type setRuntimeResponse struct {
Code common.ErrorCode `json:"code"`
TenantID string `json:"tenant_id"`
Runtime string `json:"runtime"`
Message string `json:"message"`
}
// ErrSelectorNotConfigured is returned when the handler was constructed
// without a backing Selector. It maps to HTTP 500 in the response path.
var ErrSelectorNotConfigured = errors.New("admin runtime: selector not configured")
// SetTenantRuntime implements POST /api/v1/admin/canvas-runtime/:tenant_id.
//
// Auth gap: this handler accepts any authenticated request. The dedicated
// admin-role middleware is a separate workstream; the Phase 6 PR documents
// the gap here so the staging canary operator flips tenants only via a
// trusted network. Production rollout MUST wire admin auth before opening
// this endpoint publicly.
func (h *AdminRuntimeHandler) SetTenantRuntime(c *gin.Context) {
if h.selector == nil {
jsonError(c, common.CodeExceptionError, ErrSelectorNotConfigured.Error())
return
}
tenantID := c.Param("tenant_id")
if tenantID == "" {
jsonError(c, common.CodeArgumentError, "tenant_id is required")
return
}
var req setRuntimeRequest
if err := c.ShouldBindJSON(&req); err != nil {
jsonError(c, common.CodeArgumentError, "Invalid request body: "+err.Error())
return
}
mode := runtime.RuntimeMode(req.Runtime)
switch mode {
case runtime.RuntimeGo, runtime.RuntimePython, runtime.RuntimeAuto:
// allowed
default:
jsonError(c, common.CodeArgumentError,
"runtime must be one of: go, python, auto")
return
}
if err := h.selector.Set(c.Request.Context(), tenantID, mode); err != nil {
jsonError(c, common.CodeDataError, err.Error())
return
}
c.JSON(http.StatusOK, setRuntimeResponse{
Code: common.CodeSuccess,
TenantID: tenantID,
Runtime: string(mode),
Message: "ok",
})
}