Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

user_app.py 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  1. #
  2. # Copyright 2024 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. import logging
  17. import json
  18. import re
  19. from datetime import datetime
  20. from flask import request, session, redirect
  21. from werkzeug.security import generate_password_hash, check_password_hash
  22. from flask_login import login_required, current_user, login_user, logout_user
  23. from api.db.db_models import TenantLLM
  24. from api.db.services.llm_service import TenantLLMService, LLMService
  25. from api.utils.api_utils import (
  26. server_error_response,
  27. validate_request,
  28. get_data_error_result,
  29. )
  30. from api.utils import (
  31. get_uuid,
  32. get_format_time,
  33. decrypt,
  34. download_img,
  35. current_timestamp,
  36. datetime_format,
  37. )
  38. from api.db import UserTenantRole, FileType
  39. from api import settings
  40. from api.db.services.user_service import UserService, TenantService, UserTenantService
  41. from api.db.services.file_service import FileService
  42. from api.utils.api_utils import get_json_result, construct_response
  43. from api.apps.auth import get_auth_client
  44. @manager.route("/login", methods=["POST", "GET"]) # noqa: F821
  45. def login():
  46. """
  47. User login endpoint.
  48. ---
  49. tags:
  50. - User
  51. parameters:
  52. - in: body
  53. name: body
  54. description: Login credentials.
  55. required: true
  56. schema:
  57. type: object
  58. properties:
  59. email:
  60. type: string
  61. description: User email.
  62. password:
  63. type: string
  64. description: User password.
  65. responses:
  66. 200:
  67. description: Login successful.
  68. schema:
  69. type: object
  70. 401:
  71. description: Authentication failed.
  72. schema:
  73. type: object
  74. """
  75. if not request.json:
  76. return get_json_result(
  77. data=False, code=settings.RetCode.AUTHENTICATION_ERROR, message="Unauthorized!"
  78. )
  79. email = request.json.get("email", "")
  80. users = UserService.query(email=email)
  81. if not users:
  82. return get_json_result(
  83. data=False,
  84. code=settings.RetCode.AUTHENTICATION_ERROR,
  85. message=f"Email: {email} is not registered!",
  86. )
  87. password = request.json.get("password")
  88. try:
  89. password = decrypt(password)
  90. except BaseException:
  91. return get_json_result(
  92. data=False, code=settings.RetCode.SERVER_ERROR, message="Fail to crypt password"
  93. )
  94. user = UserService.query_user(email, password)
  95. if user:
  96. response_data = user.to_json()
  97. user.access_token = get_uuid()
  98. login_user(user)
  99. user.update_time = (current_timestamp(),)
  100. user.update_date = (datetime_format(datetime.now()),)
  101. user.save()
  102. msg = "Welcome back!"
  103. return construct_response(data=response_data, auth=user.get_id(), message=msg)
  104. else:
  105. return get_json_result(
  106. data=False,
  107. code=settings.RetCode.AUTHENTICATION_ERROR,
  108. message="Email and password do not match!",
  109. )
  110. @manager.route("/login/channels", methods=["GET"]) # noqa: F821
  111. def get_login_channels():
  112. """
  113. Get all supported authentication channels.
  114. """
  115. try:
  116. channels = []
  117. for channel, config in settings.OAUTH_CONFIG.items():
  118. channels.append({
  119. "channel": channel,
  120. "display_name": config.get("display_name", channel.title()),
  121. "icon": config.get("icon", "sso"),
  122. })
  123. return get_json_result(data=channels)
  124. except Exception as e:
  125. logging.exception(e)
  126. return get_json_result(
  127. data=[],
  128. message=f"Load channels failure, error: {str(e)}",
  129. code=settings.RetCode.EXCEPTION_ERROR
  130. )
  131. @manager.route("/login/<channel>", methods=["GET"]) # noqa: F821
  132. def oauth_login(channel):
  133. channel_config = settings.OAUTH_CONFIG.get(channel)
  134. if not channel_config:
  135. raise ValueError(f"Invalid channel name: {channel}")
  136. auth_cli = get_auth_client(channel_config)
  137. auth_url = auth_cli.get_authorization_url()
  138. return redirect(auth_url)
  139. @manager.route("/oauth/callback/<channel>", methods=["GET"]) # noqa: F821
  140. def oauth_callback(channel):
  141. """
  142. Handle the OAuth/OIDC callback for various channels dynamically.
  143. """
  144. try:
  145. channel_config = settings.OAUTH_CONFIG.get(channel)
  146. if not channel_config:
  147. raise ValueError(f"Invalid channel name: {channel}")
  148. auth_cli = get_auth_client(channel_config)
  149. # Obtain the authorization code
  150. code = request.args.get("code")
  151. if not code:
  152. return redirect("/?error=missing_code")
  153. # Exchange authorization code for access token
  154. token_info = auth_cli.exchange_code_for_token(code)
  155. access_token = token_info.get("access_token")
  156. if not access_token:
  157. return redirect("/?error=token_failed")
  158. id_token = token_info.get("id_token")
  159. # Fetch user info
  160. user_info = auth_cli.fetch_user_info(access_token, id_token=id_token)
  161. if not user_info.email:
  162. return redirect("/?error=email_missing")
  163. # Login or register
  164. users = UserService.query(email=user_info.email)
  165. user_id = get_uuid()
  166. if not users:
  167. try:
  168. try:
  169. avatar = download_img(user_info.avatar_url)
  170. except Exception as e:
  171. logging.exception(e)
  172. avatar = ""
  173. users = user_register(
  174. user_id,
  175. {
  176. "access_token": get_uuid(),
  177. "email": user_info.email,
  178. "avatar": avatar,
  179. "nickname": user_info.nickname,
  180. "login_channel": channel,
  181. "last_login_time": get_format_time(),
  182. "is_superuser": False,
  183. },
  184. )
  185. if not users:
  186. raise Exception(f"Failed to register {user_info.email}")
  187. if len(users) > 1:
  188. raise Exception(f"Same email: {user_info.email} exists!")
  189. # Try to log in
  190. user = users[0]
  191. login_user(user)
  192. return redirect(f"/?auth={user.get_id()}")
  193. except Exception as e:
  194. rollback_user_registration(user_id)
  195. logging.exception(e)
  196. return redirect(f"/?error={str(e)}")
  197. # User exists, try to log in
  198. user = users[0]
  199. user.access_token = get_uuid()
  200. login_user(user)
  201. user.save()
  202. return redirect(f"/?auth={user.get_id()}")
  203. except Exception as e:
  204. logging.exception(e)
  205. return redirect(f"/?error={str(e)}")
  206. @manager.route("/github_callback", methods=["GET"]) # noqa: F821
  207. def github_callback():
  208. """
  209. **Deprecated**, Use `/oauth/callback/<channel>` instead.
  210. GitHub OAuth callback endpoint.
  211. ---
  212. tags:
  213. - OAuth
  214. parameters:
  215. - in: query
  216. name: code
  217. type: string
  218. required: true
  219. description: Authorization code from GitHub.
  220. responses:
  221. 200:
  222. description: Authentication successful.
  223. schema:
  224. type: object
  225. """
  226. import requests
  227. res = requests.post(
  228. settings.GITHUB_OAUTH.get("url"),
  229. data={
  230. "client_id": settings.GITHUB_OAUTH.get("client_id"),
  231. "client_secret": settings.GITHUB_OAUTH.get("secret_key"),
  232. "code": request.args.get("code"),
  233. },
  234. headers={"Accept": "application/json"},
  235. )
  236. res = res.json()
  237. if "error" in res:
  238. return redirect("/?error=%s" % res["error_description"])
  239. if "user:email" not in res["scope"].split(","):
  240. return redirect("/?error=user:email not in scope")
  241. session["access_token"] = res["access_token"]
  242. session["access_token_from"] = "github"
  243. user_info = user_info_from_github(session["access_token"])
  244. email_address = user_info["email"]
  245. users = UserService.query(email=email_address)
  246. user_id = get_uuid()
  247. if not users:
  248. # User isn't try to register
  249. try:
  250. try:
  251. avatar = download_img(user_info["avatar_url"])
  252. except Exception as e:
  253. logging.exception(e)
  254. avatar = ""
  255. users = user_register(
  256. user_id,
  257. {
  258. "access_token": session["access_token"],
  259. "email": email_address,
  260. "avatar": avatar,
  261. "nickname": user_info["login"],
  262. "login_channel": "github",
  263. "last_login_time": get_format_time(),
  264. "is_superuser": False,
  265. },
  266. )
  267. if not users:
  268. raise Exception(f"Fail to register {email_address}.")
  269. if len(users) > 1:
  270. raise Exception(f"Same email: {email_address} exists!")
  271. # Try to log in
  272. user = users[0]
  273. login_user(user)
  274. return redirect("/?auth=%s" % user.get_id())
  275. except Exception as e:
  276. rollback_user_registration(user_id)
  277. logging.exception(e)
  278. return redirect("/?error=%s" % str(e))
  279. # User has already registered, try to log in
  280. user = users[0]
  281. user.access_token = get_uuid()
  282. login_user(user)
  283. user.save()
  284. return redirect("/?auth=%s" % user.get_id())
  285. @manager.route("/feishu_callback", methods=["GET"]) # noqa: F821
  286. def feishu_callback():
  287. """
  288. Feishu OAuth callback endpoint.
  289. ---
  290. tags:
  291. - OAuth
  292. parameters:
  293. - in: query
  294. name: code
  295. type: string
  296. required: true
  297. description: Authorization code from Feishu.
  298. responses:
  299. 200:
  300. description: Authentication successful.
  301. schema:
  302. type: object
  303. """
  304. import requests
  305. app_access_token_res = requests.post(
  306. settings.FEISHU_OAUTH.get("app_access_token_url"),
  307. data=json.dumps(
  308. {
  309. "app_id": settings.FEISHU_OAUTH.get("app_id"),
  310. "app_secret": settings.FEISHU_OAUTH.get("app_secret"),
  311. }
  312. ),
  313. headers={"Content-Type": "application/json; charset=utf-8"},
  314. )
  315. app_access_token_res = app_access_token_res.json()
  316. if app_access_token_res["code"] != 0:
  317. return redirect("/?error=%s" % app_access_token_res)
  318. res = requests.post(
  319. settings.FEISHU_OAUTH.get("user_access_token_url"),
  320. data=json.dumps(
  321. {
  322. "grant_type": settings.FEISHU_OAUTH.get("grant_type"),
  323. "code": request.args.get("code"),
  324. }
  325. ),
  326. headers={
  327. "Content-Type": "application/json; charset=utf-8",
  328. "Authorization": f"Bearer {app_access_token_res['app_access_token']}",
  329. },
  330. )
  331. res = res.json()
  332. if res["code"] != 0:
  333. return redirect("/?error=%s" % res["message"])
  334. if "contact:user.email:readonly" not in res["data"]["scope"].split():
  335. return redirect("/?error=contact:user.email:readonly not in scope")
  336. session["access_token"] = res["data"]["access_token"]
  337. session["access_token_from"] = "feishu"
  338. user_info = user_info_from_feishu(session["access_token"])
  339. email_address = user_info["email"]
  340. users = UserService.query(email=email_address)
  341. user_id = get_uuid()
  342. if not users:
  343. # User isn't try to register
  344. try:
  345. try:
  346. avatar = download_img(user_info["avatar_url"])
  347. except Exception as e:
  348. logging.exception(e)
  349. avatar = ""
  350. users = user_register(
  351. user_id,
  352. {
  353. "access_token": session["access_token"],
  354. "email": email_address,
  355. "avatar": avatar,
  356. "nickname": user_info["en_name"],
  357. "login_channel": "feishu",
  358. "last_login_time": get_format_time(),
  359. "is_superuser": False,
  360. },
  361. )
  362. if not users:
  363. raise Exception(f"Fail to register {email_address}.")
  364. if len(users) > 1:
  365. raise Exception(f"Same email: {email_address} exists!")
  366. # Try to log in
  367. user = users[0]
  368. login_user(user)
  369. return redirect("/?auth=%s" % user.get_id())
  370. except Exception as e:
  371. rollback_user_registration(user_id)
  372. logging.exception(e)
  373. return redirect("/?error=%s" % str(e))
  374. # User has already registered, try to log in
  375. user = users[0]
  376. user.access_token = get_uuid()
  377. login_user(user)
  378. user.save()
  379. return redirect("/?auth=%s" % user.get_id())
  380. def user_info_from_feishu(access_token):
  381. import requests
  382. headers = {
  383. "Content-Type": "application/json; charset=utf-8",
  384. "Authorization": f"Bearer {access_token}",
  385. }
  386. res = requests.get(
  387. "https://open.feishu.cn/open-apis/authen/v1/user_info", headers=headers
  388. )
  389. user_info = res.json()["data"]
  390. user_info["email"] = None if user_info.get("email") == "" else user_info["email"]
  391. return user_info
  392. def user_info_from_github(access_token):
  393. import requests
  394. headers = {"Accept": "application/json", "Authorization": f"token {access_token}"}
  395. res = requests.get(
  396. f"https://api.github.com/user?access_token={access_token}", headers=headers
  397. )
  398. user_info = res.json()
  399. email_info = requests.get(
  400. f"https://api.github.com/user/emails?access_token={access_token}",
  401. headers=headers,
  402. ).json()
  403. user_info["email"] = next(
  404. (email for email in email_info if email["primary"]), None
  405. )["email"]
  406. return user_info
  407. @manager.route("/logout", methods=["GET"]) # noqa: F821
  408. @login_required
  409. def log_out():
  410. """
  411. User logout endpoint.
  412. ---
  413. tags:
  414. - User
  415. security:
  416. - ApiKeyAuth: []
  417. responses:
  418. 200:
  419. description: Logout successful.
  420. schema:
  421. type: object
  422. """
  423. current_user.access_token = ""
  424. current_user.save()
  425. logout_user()
  426. return get_json_result(data=True)
  427. @manager.route("/setting", methods=["POST"]) # noqa: F821
  428. @login_required
  429. def setting_user():
  430. """
  431. Update user settings.
  432. ---
  433. tags:
  434. - User
  435. security:
  436. - ApiKeyAuth: []
  437. parameters:
  438. - in: body
  439. name: body
  440. description: User settings to update.
  441. required: true
  442. schema:
  443. type: object
  444. properties:
  445. nickname:
  446. type: string
  447. description: New nickname.
  448. email:
  449. type: string
  450. description: New email.
  451. responses:
  452. 200:
  453. description: Settings updated successfully.
  454. schema:
  455. type: object
  456. """
  457. update_dict = {}
  458. request_data = request.json
  459. if request_data.get("password"):
  460. new_password = request_data.get("new_password")
  461. if not check_password_hash(
  462. current_user.password, decrypt(request_data["password"])
  463. ):
  464. return get_json_result(
  465. data=False,
  466. code=settings.RetCode.AUTHENTICATION_ERROR,
  467. message="Password error!",
  468. )
  469. if new_password:
  470. update_dict["password"] = generate_password_hash(decrypt(new_password))
  471. for k in request_data.keys():
  472. if k in [
  473. "password",
  474. "new_password",
  475. "email",
  476. "status",
  477. "is_superuser",
  478. "login_channel",
  479. "is_anonymous",
  480. "is_active",
  481. "is_authenticated",
  482. "last_login_time",
  483. ]:
  484. continue
  485. update_dict[k] = request_data[k]
  486. try:
  487. UserService.update_by_id(current_user.id, update_dict)
  488. return get_json_result(data=True)
  489. except Exception as e:
  490. logging.exception(e)
  491. return get_json_result(
  492. data=False, message="Update failure!", code=settings.RetCode.EXCEPTION_ERROR
  493. )
  494. @manager.route("/info", methods=["GET"]) # noqa: F821
  495. @login_required
  496. def user_profile():
  497. """
  498. Get user profile information.
  499. ---
  500. tags:
  501. - User
  502. security:
  503. - ApiKeyAuth: []
  504. responses:
  505. 200:
  506. description: User profile retrieved successfully.
  507. schema:
  508. type: object
  509. properties:
  510. id:
  511. type: string
  512. description: User ID.
  513. nickname:
  514. type: string
  515. description: User nickname.
  516. email:
  517. type: string
  518. description: User email.
  519. """
  520. return get_json_result(data=current_user.to_dict())
  521. def rollback_user_registration(user_id):
  522. try:
  523. UserService.delete_by_id(user_id)
  524. except Exception:
  525. pass
  526. try:
  527. TenantService.delete_by_id(user_id)
  528. except Exception:
  529. pass
  530. try:
  531. u = UserTenantService.query(tenant_id=user_id)
  532. if u:
  533. UserTenantService.delete_by_id(u[0].id)
  534. except Exception:
  535. pass
  536. try:
  537. TenantLLM.delete().where(TenantLLM.tenant_id == user_id).execute()
  538. except Exception:
  539. pass
  540. def user_register(user_id, user):
  541. user["id"] = user_id
  542. tenant = {
  543. "id": user_id,
  544. "name": user["nickname"] + "‘s Kingdom",
  545. "llm_id": settings.CHAT_MDL,
  546. "embd_id": settings.EMBEDDING_MDL,
  547. "asr_id": settings.ASR_MDL,
  548. "parser_ids": settings.PARSERS,
  549. "img2txt_id": settings.IMAGE2TEXT_MDL,
  550. "rerank_id": settings.RERANK_MDL,
  551. }
  552. usr_tenant = {
  553. "tenant_id": user_id,
  554. "user_id": user_id,
  555. "invited_by": user_id,
  556. "role": UserTenantRole.OWNER,
  557. }
  558. file_id = get_uuid()
  559. file = {
  560. "id": file_id,
  561. "parent_id": file_id,
  562. "tenant_id": user_id,
  563. "created_by": user_id,
  564. "name": "/",
  565. "type": FileType.FOLDER.value,
  566. "size": 0,
  567. "location": "",
  568. }
  569. tenant_llm = []
  570. for llm in LLMService.query(fid=settings.LLM_FACTORY):
  571. tenant_llm.append(
  572. {
  573. "tenant_id": user_id,
  574. "llm_factory": settings.LLM_FACTORY,
  575. "llm_name": llm.llm_name,
  576. "model_type": llm.model_type,
  577. "api_key": settings.API_KEY,
  578. "api_base": settings.LLM_BASE_URL,
  579. "max_tokens": llm.max_tokens if llm.max_tokens else 8192
  580. }
  581. )
  582. if not UserService.save(**user):
  583. return
  584. TenantService.insert(**tenant)
  585. UserTenantService.insert(**usr_tenant)
  586. TenantLLMService.insert_many(tenant_llm)
  587. FileService.insert(file)
  588. return UserService.query(email=user["email"])
  589. @manager.route("/register", methods=["POST"]) # noqa: F821
  590. @validate_request("nickname", "email", "password")
  591. def user_add():
  592. """
  593. Register a new user.
  594. ---
  595. tags:
  596. - User
  597. parameters:
  598. - in: body
  599. name: body
  600. description: Registration details.
  601. required: true
  602. schema:
  603. type: object
  604. properties:
  605. nickname:
  606. type: string
  607. description: User nickname.
  608. email:
  609. type: string
  610. description: User email.
  611. password:
  612. type: string
  613. description: User password.
  614. responses:
  615. 200:
  616. description: Registration successful.
  617. schema:
  618. type: object
  619. """
  620. if not settings.REGISTER_ENABLED:
  621. return get_json_result(
  622. data=False,
  623. message="User registration is disabled!",
  624. code=settings.RetCode.OPERATING_ERROR,
  625. )
  626. req = request.json
  627. email_address = req["email"]
  628. # Validate the email address
  629. if not re.match(r"^[\w\._-]+@([\w_-]+\.)+[\w-]{2,}$", email_address):
  630. return get_json_result(
  631. data=False,
  632. message=f"Invalid email address: {email_address}!",
  633. code=settings.RetCode.OPERATING_ERROR,
  634. )
  635. # Check if the email address is already used
  636. if UserService.query(email=email_address):
  637. return get_json_result(
  638. data=False,
  639. message=f"Email: {email_address} has already registered!",
  640. code=settings.RetCode.OPERATING_ERROR,
  641. )
  642. # Construct user info data
  643. nickname = req["nickname"]
  644. user_dict = {
  645. "access_token": get_uuid(),
  646. "email": email_address,
  647. "nickname": nickname,
  648. "password": decrypt(req["password"]),
  649. "login_channel": "password",
  650. "last_login_time": get_format_time(),
  651. "is_superuser": False,
  652. }
  653. user_id = get_uuid()
  654. try:
  655. users = user_register(user_id, user_dict)
  656. if not users:
  657. raise Exception(f"Fail to register {email_address}.")
  658. if len(users) > 1:
  659. raise Exception(f"Same email: {email_address} exists!")
  660. user = users[0]
  661. login_user(user)
  662. return construct_response(
  663. data=user.to_json(),
  664. auth=user.get_id(),
  665. message=f"{nickname}, welcome aboard!",
  666. )
  667. except Exception as e:
  668. rollback_user_registration(user_id)
  669. logging.exception(e)
  670. return get_json_result(
  671. data=False,
  672. message=f"User registration failure, error: {str(e)}",
  673. code=settings.RetCode.EXCEPTION_ERROR,
  674. )
  675. @manager.route("/tenant_info", methods=["GET"]) # noqa: F821
  676. @login_required
  677. def tenant_info():
  678. """
  679. Get tenant information.
  680. ---
  681. tags:
  682. - Tenant
  683. security:
  684. - ApiKeyAuth: []
  685. responses:
  686. 200:
  687. description: Tenant information retrieved successfully.
  688. schema:
  689. type: object
  690. properties:
  691. tenant_id:
  692. type: string
  693. description: Tenant ID.
  694. name:
  695. type: string
  696. description: Tenant name.
  697. llm_id:
  698. type: string
  699. description: LLM ID.
  700. embd_id:
  701. type: string
  702. description: Embedding model ID.
  703. """
  704. try:
  705. tenants = TenantService.get_info_by(current_user.id)
  706. if not tenants:
  707. return get_data_error_result(message="Tenant not found!")
  708. return get_json_result(data=tenants[0])
  709. except Exception as e:
  710. return server_error_response(e)
  711. @manager.route("/set_tenant_info", methods=["POST"]) # noqa: F821
  712. @login_required
  713. @validate_request("tenant_id", "asr_id", "embd_id", "img2txt_id", "llm_id")
  714. def set_tenant_info():
  715. """
  716. Update tenant information.
  717. ---
  718. tags:
  719. - Tenant
  720. security:
  721. - ApiKeyAuth: []
  722. parameters:
  723. - in: body
  724. name: body
  725. description: Tenant information to update.
  726. required: true
  727. schema:
  728. type: object
  729. properties:
  730. tenant_id:
  731. type: string
  732. description: Tenant ID.
  733. llm_id:
  734. type: string
  735. description: LLM ID.
  736. embd_id:
  737. type: string
  738. description: Embedding model ID.
  739. asr_id:
  740. type: string
  741. description: ASR model ID.
  742. img2txt_id:
  743. type: string
  744. description: Image to Text model ID.
  745. responses:
  746. 200:
  747. description: Tenant information updated successfully.
  748. schema:
  749. type: object
  750. """
  751. req = request.json
  752. try:
  753. tid = req.pop("tenant_id")
  754. TenantService.update_by_id(tid, req)
  755. return get_json_result(data=True)
  756. except Exception as e:
  757. return server_error_response(e)