Fix admin CLI system variable commands (#14956)

## What

Fixes #12409.

Implements admin CLI support for:

- `list vars;`
- `show var <name-or-prefix>;`
- `set var <name> <value>;`

## Changes

- Wire Go CLI variable commands to the admin API.
- Support integer and quoted string values in `SET VAR`.
- Return variable rows as `data_type`, `name`, `setting_type`, and
`value`.
- Add exact-name lookup with prefix fallback for `SHOW VAR`.
- Validate values by stored data type: `string`, `integer`, `bool`, and
`json`.
- Keep the legacy Python admin CLI/server behavior aligned.
- Update admin CLI docs and add focused tests.

## Verification

- `go test -count=1 ./internal/cli`
- `python3.12 -m py_compile admin/server/services.py
admin/server/routes.py api/db/services/system_settings_service.py
admin/client/parser.py admin/client/ragflow_client.py`
- Python admin CLI parser smoke test for `SET VAR`, quoted values, `SHOW
VAR`, and `LIST VARS`.
- Attempted `./run_go_tests.sh`; local environment is missing native
tokenizer/linker artifacts:
  - `internal/cpp/cmake-build-release/librag_tokenizer_c_api.a`
  - `-lstdc++`

Co-authored-by: Jin Hai <haijin.chn@gmail.com>
This commit is contained in:
Jake Armstrong
2026-05-18 01:08:45 -10:00
committed by GitHub
parent 732e4741c4
commit 93d3deb5e4
16 changed files with 646 additions and 84 deletions

View File

@@ -596,6 +596,142 @@ func (c *RAGFlowClient) ShowService(cmd *Command) (ResponseIf, error) {
return &result, nil
}
func normalizeVariableRows(rows []map[string]interface{}) {
for _, row := range rows {
if _, ok := row["setting_type"]; ok {
delete(row, "source")
continue
}
if _, ok := row["source"]; ok {
row["setting_type"] = "config"
delete(row, "source")
}
}
}
// ListVariables lists all system variables (admin mode only).
func (c *RAGFlowClient) ListVariables(cmd *Command) (ResponseIf, error) {
if c.ServerType != "admin" {
return nil, fmt.Errorf("this command is only allowed in ADMIN mode")
}
iterations := 1
if val, ok := cmd.Params["iterations"].(int); ok && val > 1 {
iterations = val
}
if iterations > 1 {
return c.HTTPClient.RequestWithIterations("GET", "/admin/variables", "admin", nil, nil, iterations)
}
resp, err := c.HTTPClient.Request("GET", "/admin/variables", "admin", nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to list variables: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to list variables: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var result CommonResponse
if err = json.Unmarshal(resp.Body, &result); err != nil {
return nil, fmt.Errorf("list variables failed: invalid JSON (%w)", err)
}
if result.Code != 0 {
return nil, fmt.Errorf("%s", result.Message)
}
normalizeVariableRows(result.Data)
result.Duration = resp.Duration
return &result, nil
}
// ShowVariable shows system variables by exact name or name prefix (admin mode only).
func (c *RAGFlowClient) ShowVariable(cmd *Command) (ResponseIf, error) {
if c.ServerType != "admin" {
return nil, fmt.Errorf("this command is only allowed in ADMIN mode")
}
varName, ok := cmd.Params["var_name"].(string)
if !ok {
return nil, fmt.Errorf("var_name not provided")
}
iterations := 1
if val, ok := cmd.Params["iterations"].(int); ok && val > 1 {
iterations = val
}
payload := map[string]interface{}{"var_name": varName}
if iterations > 1 {
return c.HTTPClient.RequestWithIterations("GET", "/admin/variables", "admin", nil, payload, iterations)
}
resp, err := c.HTTPClient.Request("GET", "/admin/variables", "admin", nil, payload)
if err != nil {
return nil, fmt.Errorf("failed to show variable: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to show variable: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var result CommonResponse
if err = json.Unmarshal(resp.Body, &result); err != nil {
return nil, fmt.Errorf("show variable failed: invalid JSON (%w)", err)
}
if result.Code != 0 {
return nil, fmt.Errorf("%s", result.Message)
}
normalizeVariableRows(result.Data)
result.Duration = resp.Duration
return &result, nil
}
// SetVariable updates a system variable (admin mode only).
func (c *RAGFlowClient) SetVariable(cmd *Command) (ResponseIf, error) {
if c.ServerType != "admin" {
return nil, fmt.Errorf("this command is only allowed in ADMIN mode")
}
varName, ok := cmd.Params["var_name"].(string)
if !ok {
return nil, fmt.Errorf("var_name not provided")
}
varValue, ok := cmd.Params["var_value"].(string)
if !ok {
return nil, fmt.Errorf("var_value not provided")
}
payload := map[string]interface{}{
"var_name": varName,
"var_value": varValue,
}
resp, err := c.HTTPClient.Request("PUT", "/admin/variables", "admin", nil, payload)
if err != nil {
return nil, fmt.Errorf("failed to set variable: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to set variable: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var result MessageResponse
if err = json.Unmarshal(resp.Body, &result); err != nil {
return nil, fmt.Errorf("set variable failed: invalid JSON (%w)", err)
}
if result.Code != 0 {
return nil, fmt.Errorf("%s", result.Message)
}
result.Duration = resp.Duration
return &result, nil
}
// ListUsers lists all users (admin mode only)
// Returns (result_map, error) - result_map is non-nil for benchmark mode
func (c *RAGFlowClient) ListUsers(cmd *Command) (ResponseIf, error) {