ragflow-cli 0.23.1__tar.gz → 0.25.0.dev5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ragflow-cli
3
- Version: 0.23.1
3
+ Version: 0.25.0.dev5
4
4
  Summary: Admin Service's client of [RAGFlow](https://github.com/infiniflow/ragflow). The Admin Service provides user management and system monitoring.
5
5
  Author-email: Lynn <lynn_inf@hotmail.com>
6
6
  License: Apache License, Version 2.0
@@ -10,6 +10,7 @@ Requires-Dist: requests<3.0.0,>=2.30.0
10
10
  Requires-Dist: beartype<1.0.0,>=0.20.0
11
11
  Requires-Dist: pycryptodomex>=3.10.0
12
12
  Requires-Dist: lark>=1.1.0
13
+ Requires-Dist: requests-toolbelt>=1.0.0
13
14
 
14
15
  # RAGFlow Admin Service & CLI
15
16
 
@@ -61,7 +62,7 @@ It consists of a server-side Service and a command-line client (CLI), both imple
61
62
  1. Ensure the Admin Service is running.
62
63
  2. Install ragflow-cli.
63
64
  ```bash
64
- pip install ragflow-cli==0.23.1
65
+ pip install ragflow-cli==0.24.0
65
66
  ```
66
67
  3. Launch the CLI client:
67
68
  ```bash
@@ -48,7 +48,7 @@ It consists of a server-side Service and a command-line client (CLI), both imple
48
48
  1. Ensure the Admin Service is running.
49
49
  2. Install ragflow-cli.
50
50
  ```bash
51
- pip install ragflow-cli==0.23.1
51
+ pip install ragflow-cli==0.24.0
52
52
  ```
53
53
  3. Launch the CLI client:
54
54
  ```bash
@@ -0,0 +1,182 @@
1
+ #
2
+ # Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ import time
18
+ import json
19
+ import typing
20
+ from typing import Any, Dict, Optional
21
+
22
+ import requests
23
+ # from requests.sessions import HTTPAdapter
24
+
25
+
26
+ class HttpClient:
27
+ def __init__(
28
+ self,
29
+ host: str = "127.0.0.1",
30
+ port: int = 9381,
31
+ api_version: str = "v1",
32
+ api_key: Optional[str] = None,
33
+ connect_timeout: float = 5.0,
34
+ read_timeout: float = 60.0,
35
+ verify_ssl: bool = False,
36
+ ) -> None:
37
+ self.host = host
38
+ self.port = port
39
+ self.api_version = api_version
40
+ self.api_key = api_key
41
+ self.login_token: str | None = None
42
+ self.connect_timeout = connect_timeout
43
+ self.read_timeout = read_timeout
44
+ self.verify_ssl = verify_ssl
45
+
46
+ def api_base(self) -> str:
47
+ return f"{self.host}:{self.port}/api/{self.api_version}"
48
+
49
+ def non_api_base(self) -> str:
50
+ return f"{self.host}:{self.port}/{self.api_version}"
51
+
52
+ def build_url(self, path: str, use_api_base: bool = True) -> str:
53
+ base = self.api_base() if use_api_base else self.non_api_base()
54
+ if self.verify_ssl:
55
+ return f"https://{base}/{path.lstrip('/')}"
56
+ else:
57
+ return f"http://{base}/{path.lstrip('/')}"
58
+
59
+ def _headers(self, auth_kind: Optional[str], extra: Optional[Dict[str, str]]) -> Dict[str, str]:
60
+ headers = {}
61
+ if auth_kind == "api" and self.api_key:
62
+ headers["Authorization"] = f"Bearer {self.api_key}"
63
+ elif auth_kind == "web" and self.login_token:
64
+ headers["Authorization"] = self.login_token
65
+ elif auth_kind == "admin" and self.login_token:
66
+ headers["Authorization"] = self.login_token
67
+ else:
68
+ pass
69
+ if extra:
70
+ headers.update(extra)
71
+ return headers
72
+
73
+ def request(
74
+ self,
75
+ method: str,
76
+ path: str,
77
+ *,
78
+ use_api_base: bool = True,
79
+ auth_kind: Optional[str] = "api",
80
+ headers: Optional[Dict[str, str]] = None,
81
+ json_body: Optional[Dict[str, Any]] = None,
82
+ data: Any = None,
83
+ files: Any = None,
84
+ params: Optional[Dict[str, Any]] = None,
85
+ stream: bool = False,
86
+ iterations: int = 1,
87
+ ) -> requests.Response | dict:
88
+ url = self.build_url(path, use_api_base=use_api_base)
89
+ merged_headers = self._headers(auth_kind, headers)
90
+ # timeout: Tuple[float, float] = (self.connect_timeout, self.read_timeout)
91
+ session = requests.Session()
92
+ # adapter = HTTPAdapter(pool_connections=100, pool_maxsize=100)
93
+ # session.mount("http://", adapter)
94
+ http_function = typing.Any
95
+ match method:
96
+ case "GET":
97
+ http_function = session.get
98
+ case "POST":
99
+ http_function = session.post
100
+ case "PUT":
101
+ http_function = session.put
102
+ case "DELETE":
103
+ http_function = session.delete
104
+ case "PATCH":
105
+ http_function = session.patch
106
+ case _:
107
+ raise ValueError(f"Invalid HTTP method: {method}")
108
+
109
+ if iterations > 1:
110
+ response_list = []
111
+ total_duration = 0.0
112
+ for _ in range(iterations):
113
+ start_time = time.perf_counter()
114
+ response = http_function(url, headers=merged_headers, json=json_body, data=data, stream=stream)
115
+ # response = session.get(url, headers=merged_headers, json=json_body, data=data, stream=stream)
116
+ # response = requests.request(
117
+ # method=method,
118
+ # url=url,
119
+ # headers=merged_headers,
120
+ # json=json_body,
121
+ # data=data,
122
+ # files=files,
123
+ # params=params,
124
+ # stream=stream,
125
+ # verify=self.verify_ssl,
126
+ # )
127
+ end_time = time.perf_counter()
128
+ total_duration += end_time - start_time
129
+ response_list.append(response)
130
+ return {"duration": total_duration, "response_list": response_list}
131
+ else:
132
+ return http_function(url, headers=merged_headers, json=json_body, data=data, stream=stream)
133
+ # return session.get(url, headers=merged_headers, json=json_body, data=data, stream=stream)
134
+ # return requests.request(
135
+ # method=method,
136
+ # url=url,
137
+ # headers=merged_headers,
138
+ # json=json_body,
139
+ # data=data,
140
+ # files=files,
141
+ # params=params,
142
+ # stream=stream,
143
+ # verify=self.verify_ssl,
144
+ # )
145
+
146
+ def request_json(
147
+ self,
148
+ method: str,
149
+ path: str,
150
+ *,
151
+ use_api_base: bool = True,
152
+ auth_kind: Optional[str] = "api",
153
+ headers: Optional[Dict[str, str]] = None,
154
+ json_body: Optional[Dict[str, Any]] = None,
155
+ data: Any = None,
156
+ files: Any = None,
157
+ params: Optional[Dict[str, Any]] = None,
158
+ stream: bool = False,
159
+ ) -> Dict[str, Any]:
160
+ response = self.request(
161
+ method,
162
+ path,
163
+ use_api_base=use_api_base,
164
+ auth_kind=auth_kind,
165
+ headers=headers,
166
+ json_body=json_body,
167
+ data=data,
168
+ files=files,
169
+ params=params,
170
+ stream=stream,
171
+ )
172
+ try:
173
+ return response.json()
174
+ except Exception as exc:
175
+ raise ValueError(f"Non-JSON response from {path}: {exc}") from exc
176
+
177
+ @staticmethod
178
+ def parse_json_bytes(raw: bytes) -> Dict[str, Any]:
179
+ try:
180
+ return json.loads(raw.decode("utf-8"))
181
+ except Exception as exc:
182
+ raise ValueError(f"Invalid JSON payload: {exc}") from exc