mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-06-29 15:31:05 +08:00
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:
@@ -264,7 +264,7 @@ generate_key: GENERATE KEY FOR USER quoted_string ";"
|
||||
list_keys: LIST KEYS OF quoted_string ";"
|
||||
drop_key: DROP KEY quoted_string OF quoted_string ";"
|
||||
|
||||
set_variable: SET VAR identifier identifier ";"
|
||||
set_variable: SET VAR identifier variable_value ";"
|
||||
show_variable: SHOW VAR identifier ";"
|
||||
list_variables: LIST VARS ";"
|
||||
list_configs: LIST CONFIGS ";"
|
||||
@@ -378,6 +378,7 @@ update_chunk: UPDATE CHUNK quoted_string OF DATASET quoted_string SET quoted_str
|
||||
identifier_list: identifier (COMMA identifier)*
|
||||
|
||||
identifier: WORD
|
||||
variable_value: WORD | NUMBER | QUOTED_STRING
|
||||
quoted_string: QUOTED_STRING
|
||||
status: ON | WORD
|
||||
|
||||
|
||||
@@ -43,6 +43,12 @@ def encrypt(input_string):
|
||||
return base64.b64encode(cipher_text).decode("utf-8")
|
||||
|
||||
|
||||
def _strip_tree_value(value):
|
||||
if isinstance(value, Tree):
|
||||
value = value.children[0]
|
||||
return str(value).strip("'\"")
|
||||
|
||||
|
||||
class RAGFlowClient:
|
||||
def __init__(self, http_client: HttpClient, server_type: str):
|
||||
self.http_client = http_client
|
||||
@@ -526,10 +532,8 @@ class RAGFlowClient:
|
||||
if self.server_type != "admin":
|
||||
print("This command is only allowed in ADMIN mode")
|
||||
|
||||
var_name_tree: Tree = command["var_name"]
|
||||
var_name = var_name_tree.children[0].strip("'\"")
|
||||
var_value_tree: Tree = command["var_value"]
|
||||
var_value = var_value_tree.children[0].strip("'\"")
|
||||
var_name = _strip_tree_value(command["var_name"])
|
||||
var_value = _strip_tree_value(command["var_value"])
|
||||
response = self.http_client.request("PUT", "/admin/variables",
|
||||
json_body={"var_name": var_name, "var_value": var_value}, use_api_base=True,
|
||||
auth_kind="admin")
|
||||
@@ -544,8 +548,7 @@ class RAGFlowClient:
|
||||
if self.server_type != "admin":
|
||||
print("This command is only allowed in ADMIN mode")
|
||||
|
||||
var_name_tree: Tree = command["var_name"]
|
||||
var_name = var_name_tree.children[0].strip("'\"")
|
||||
var_name = _strip_tree_value(command["var_name"])
|
||||
response = self.http_client.request(method="GET", path="/admin/variables", json_body={"var_name": var_name},
|
||||
use_api_base=True, auth_kind="admin")
|
||||
res_json = response.json()
|
||||
|
||||
@@ -421,7 +421,7 @@ def get_user_permission(user_name: str):
|
||||
def set_variable():
|
||||
try:
|
||||
data = request.get_json()
|
||||
if not data and "var_name" not in data:
|
||||
if not data or "var_name" not in data:
|
||||
return error_response("Var name is required", 400)
|
||||
|
||||
if "var_value" not in data:
|
||||
@@ -449,7 +449,7 @@ def get_variable():
|
||||
|
||||
# get var
|
||||
data = request.get_json()
|
||||
if not data and "var_name" not in data:
|
||||
if not data or "var_name" not in data:
|
||||
return error_response("Var name is required", 400)
|
||||
var_name: str = data["var_name"]
|
||||
res = SettingsMgr.get_by_name(var_name)
|
||||
|
||||
@@ -330,36 +330,65 @@ class ServiceMgr:
|
||||
|
||||
|
||||
class SettingsMgr:
|
||||
@staticmethod
|
||||
def _format_setting(setting):
|
||||
return {
|
||||
"data_type": setting.data_type,
|
||||
"name": setting.name,
|
||||
"setting_type": "config",
|
||||
"value": setting.value,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _validate_value(name: str, data_type: str, value: str):
|
||||
data_type = data_type.lower()
|
||||
value = str(value)
|
||||
if data_type == "string":
|
||||
return
|
||||
if data_type == "integer":
|
||||
try:
|
||||
int(value)
|
||||
except ValueError:
|
||||
raise AdminException(f"Invalid integer value for {name}: {value}")
|
||||
return
|
||||
if data_type in {"bool", "boolean"}:
|
||||
if value not in {"true", "false"}:
|
||||
raise AdminException(f"Invalid bool value for {name}: expected true or false")
|
||||
return
|
||||
if data_type == "json":
|
||||
try:
|
||||
json.loads(value)
|
||||
except json.JSONDecodeError:
|
||||
raise AdminException(f"Invalid JSON value for {name}")
|
||||
return
|
||||
raise AdminException(f"Unsupported data type for {name}: {data_type}")
|
||||
|
||||
@staticmethod
|
||||
def _infer_data_type(name: str):
|
||||
if name.startswith("sandbox."):
|
||||
return "json"
|
||||
if name.endswith(".enabled"):
|
||||
return "bool"
|
||||
return "string"
|
||||
|
||||
@staticmethod
|
||||
def get_all():
|
||||
settings = SystemSettingsService.get_all()
|
||||
settings = SystemSettingsService.get_all(reverse=False, order_by="name")
|
||||
result = []
|
||||
for setting in settings:
|
||||
result.append(
|
||||
{
|
||||
"name": setting.name,
|
||||
"source": setting.source,
|
||||
"data_type": setting.data_type,
|
||||
"value": setting.value,
|
||||
}
|
||||
)
|
||||
result.append(SettingsMgr._format_setting(setting))
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def get_by_name(name: str):
|
||||
settings = SystemSettingsService.get_by_name(name)
|
||||
if len(settings) == 0:
|
||||
raise AdminException(f"Can't get setting: {name}")
|
||||
settings = SystemSettingsService.get_by_name_prefix(name)
|
||||
if len(settings) == 0:
|
||||
raise AdminException(f"Can't get setting: {name}")
|
||||
result = []
|
||||
for setting in settings:
|
||||
result.append(
|
||||
{
|
||||
"name": setting.name,
|
||||
"source": setting.source,
|
||||
"data_type": setting.data_type,
|
||||
"value": setting.value,
|
||||
}
|
||||
)
|
||||
result.append(SettingsMgr._format_setting(setting))
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
@@ -367,6 +396,7 @@ class SettingsMgr:
|
||||
settings = SystemSettingsService.get_by_name(name)
|
||||
if len(settings) == 1:
|
||||
setting = settings[0]
|
||||
SettingsMgr._validate_value(name, setting.data_type, value)
|
||||
setting.value = value
|
||||
setting_dict = setting.to_dict()
|
||||
SystemSettingsService.update_by_name(name, setting_dict)
|
||||
@@ -376,12 +406,8 @@ class SettingsMgr:
|
||||
# Create new setting if it doesn't exist
|
||||
|
||||
# Determine data_type based on name and value
|
||||
if name.startswith("sandbox."):
|
||||
data_type = "json"
|
||||
elif name.endswith(".enabled"):
|
||||
data_type = "boolean"
|
||||
else:
|
||||
data_type = "string"
|
||||
data_type = SettingsMgr._infer_data_type(name)
|
||||
SettingsMgr._validate_value(name, data_type, value)
|
||||
|
||||
new_setting = {
|
||||
"name": name,
|
||||
|
||||
Reference in New Issue
Block a user