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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  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. GitHub OAuth callback endpoint.
  210. ---
  211. tags:
  212. - OAuth
  213. parameters:
  214. - in: query
  215. name: code
  216. type: string
  217. required: true
  218. description: Authorization code from GitHub.
  219. responses:
  220. 200:
  221. description: Authentication successful.
  222. schema:
  223. type: object
  224. """
  225. import requests
  226. res = requests.post(
  227. settings.GITHUB_OAUTH.get("url"),
  228. data={
  229. "client_id": settings.GITHUB_OAUTH.get("client_id"),
  230. "client_secret": settings.GITHUB_OAUTH.get("secret_key"),
  231. "code": request.args.get("code"),
  232. },
  233. headers={"Accept": "application/json"},
  234. )
  235. res = res.json()
  236. if "error" in res:
  237. return redirect("/?error=%s" % res["error_description"])
  238. if "user:email" not in res["scope"].split(","):
  239. return redirect("/?error=user:email not in scope")
  240. session["access_token"] = res["access_token"]
  241. session["access_token_from"] = "github"
  242. user_info = user_info_from_github(session["access_token"])
  243. email_address = user_info["email"]
  244. users = UserService.query(email=email_address)
  245. user_id = get_uuid()
  246. if not users:
  247. # User isn't try to register
  248. try:
  249. try:
  250. avatar = download_img(user_info["avatar_url"])
  251. except Exception as e:
  252. logging.exception(e)
  253. avatar = ""
  254. users = user_register(
  255. user_id,
  256. {
  257. "access_token": session["access_token"],
  258. "email": email_address,
  259. "avatar": avatar,
  260. "nickname": user_info["login"],
  261. "login_channel": "github",
  262. "last_login_time": get_format_time(),
  263. "is_superuser": False,
  264. },
  265. )
  266. if not users:
  267. raise Exception(f"Fail to register {email_address}.")
  268. if len(users) > 1:
  269. raise Exception(f"Same email: {email_address} exists!")
  270. # Try to log in
  271. user = users[0]
  272. login_user(user)
  273. return redirect("/?auth=%s" % user.get_id())
  274. except Exception as e:
  275. rollback_user_registration(user_id)
  276. logging.exception(e)
  277. return redirect("/?error=%s" % str(e))
  278. # User has already registered, try to log in
  279. user = users[0]
  280. user.access_token = get_uuid()
  281. login_user(user)
  282. user.save()
  283. return redirect("/?auth=%s" % user.get_id())
  284. @manager.route("/feishu_callback", methods=["GET"]) # noqa: F821
  285. def feishu_callback():
  286. """
  287. Feishu OAuth callback endpoint.
  288. ---
  289. tags:
  290. - OAuth
  291. parameters:
  292. - in: query
  293. name: code
  294. type: string
  295. required: true
  296. description: Authorization code from Feishu.
  297. responses:
  298. 200:
  299. description: Authentication successful.
  300. schema:
  301. type: object
  302. """
  303. import requests
  304. app_access_token_res = requests.post(
  305. settings.FEISHU_OAUTH.get("app_access_token_url"),
  306. data=json.dumps(
  307. {
  308. "app_id": settings.FEISHU_OAUTH.get("app_id"),
  309. "app_secret": settings.FEISHU_OAUTH.get("app_secret"),
  310. }
  311. ),
  312. headers={"Content-Type": "application/json; charset=utf-8"},
  313. )
  314. app_access_token_res = app_access_token_res.json()
  315. if app_access_token_res["code"] != 0:
  316. return redirect("/?error=%s" % app_access_token_res)
  317. res = requests.post(
  318. settings.FEISHU_OAUTH.get("user_access_token_url"),
  319. data=json.dumps(
  320. {
  321. "grant_type": settings.FEISHU_OAUTH.get("grant_type"),
  322. "code": request.args.get("code"),
  323. }
  324. ),
  325. headers={
  326. "Content-Type": "application/json; charset=utf-8",
  327. "Authorization": f"Bearer {app_access_token_res['app_access_token']}",
  328. },
  329. )
  330. res = res.json()
  331. if res["code"] != 0:
  332. return redirect("/?error=%s" % res["message"])
  333. if "contact:user.email:readonly" not in res["data"]["scope"].split():
  334. return redirect("/?error=contact:user.email:readonly not in scope")
  335. session["access_token"] = res["data"]["access_token"]
  336. session["access_token_from"] = "feishu"
  337. user_info = user_info_from_feishu(session["access_token"])
  338. email_address = user_info["email"]
  339. users = UserService.query(email=email_address)
  340. user_id = get_uuid()
  341. if not users:
  342. # User isn't try to register
  343. try:
  344. try:
  345. avatar = download_img(user_info["avatar_url"])
  346. except Exception as e:
  347. logging.exception(e)
  348. avatar = ""
  349. users = user_register(
  350. user_id,
  351. {
  352. "access_token": session["access_token"],
  353. "email": email_address,
  354. "avatar": avatar,
  355. "nickname": user_info["en_name"],
  356. "login_channel": "feishu",
  357. "last_login_time": get_format_time(),
  358. "is_superuser": False,
  359. },
  360. )
  361. if not users:
  362. raise Exception(f"Fail to register {email_address}.")
  363. if len(users) > 1:
  364. raise Exception(f"Same email: {email_address} exists!")
  365. # Try to log in
  366. user = users[0]
  367. login_user(user)
  368. return redirect("/?auth=%s" % user.get_id())
  369. except Exception as e:
  370. rollback_user_registration(user_id)
  371. logging.exception(e)
  372. return redirect("/?error=%s" % str(e))
  373. # User has already registered, try to log in
  374. user = users[0]
  375. user.access_token = get_uuid()
  376. login_user(user)
  377. user.save()
  378. return redirect("/?auth=%s" % user.get_id())
  379. def user_info_from_feishu(access_token):
  380. import requests
  381. headers = {
  382. "Content-Type": "application/json; charset=utf-8",
  383. "Authorization": f"Bearer {access_token}",
  384. }
  385. res = requests.get(
  386. "https://open.feishu.cn/open-apis/authen/v1/user_info", headers=headers
  387. )
  388. user_info = res.json()["data"]
  389. user_info["email"] = None if user_info.get("email") == "" else user_info["email"]
  390. return user_info
  391. def user_info_from_github(access_token):
  392. import requests
  393. headers = {"Accept": "application/json", "Authorization": f"token {access_token}"}
  394. res = requests.get(
  395. f"https://api.github.com/user?access_token={access_token}", headers=headers
  396. )
  397. user_info = res.json()
  398. email_info = requests.get(
  399. f"https://api.github.com/user/emails?access_token={access_token}",
  400. headers=headers,
  401. ).json()
  402. user_info["email"] = next(
  403. (email for email in email_info if email["primary"]), None
  404. )["email"]
  405. return user_info
  406. @manager.route("/logout", methods=["GET"]) # noqa: F821
  407. @login_required
  408. def log_out():
  409. """
  410. User logout endpoint.
  411. ---
  412. tags:
  413. - User
  414. security:
  415. - ApiKeyAuth: []
  416. responses:
  417. 200:
  418. description: Logout successful.
  419. schema:
  420. type: object
  421. """
  422. current_user.access_token = ""
  423. current_user.save()
  424. logout_user()
  425. return get_json_result(data=True)
  426. @manager.route("/setting", methods=["POST"]) # noqa: F821
  427. @login_required
  428. def setting_user():
  429. """
  430. Update user settings.
  431. ---
  432. tags:
  433. - User
  434. security:
  435. - ApiKeyAuth: []
  436. parameters:
  437. - in: body
  438. name: body
  439. description: User settings to update.
  440. required: true
  441. schema:
  442. type: object
  443. properties:
  444. nickname:
  445. type: string
  446. description: New nickname.
  447. email:
  448. type: string
  449. description: New email.
  450. responses:
  451. 200:
  452. description: Settings updated successfully.
  453. schema:
  454. type: object
  455. """
  456. update_dict = {}
  457. request_data = request.json
  458. if request_data.get("password"):
  459. new_password = request_data.get("new_password")
  460. if not check_password_hash(
  461. current_user.password, decrypt(request_data["password"])
  462. ):
  463. return get_json_result(
  464. data=False,
  465. code=settings.RetCode.AUTHENTICATION_ERROR,
  466. message="Password error!",
  467. )
  468. if new_password:
  469. update_dict["password"] = generate_password_hash(decrypt(new_password))
  470. for k in request_data.keys():
  471. if k in [
  472. "password",
  473. "new_password",
  474. "email",
  475. "status",
  476. "is_superuser",
  477. "login_channel",
  478. "is_anonymous",
  479. "is_active",
  480. "is_authenticated",
  481. "last_login_time",
  482. ]:
  483. continue
  484. update_dict[k] = request_data[k]
  485. try:
  486. UserService.update_by_id(current_user.id, update_dict)
  487. return get_json_result(data=True)
  488. except Exception as e:
  489. logging.exception(e)
  490. return get_json_result(
  491. data=False, message="Update failure!", code=settings.RetCode.EXCEPTION_ERROR
  492. )
  493. @manager.route("/info", methods=["GET"]) # noqa: F821
  494. @login_required
  495. def user_profile():
  496. """
  497. Get user profile information.
  498. ---
  499. tags:
  500. - User
  501. security:
  502. - ApiKeyAuth: []
  503. responses:
  504. 200:
  505. description: User profile retrieved successfully.
  506. schema:
  507. type: object
  508. properties:
  509. id:
  510. type: string
  511. description: User ID.
  512. nickname:
  513. type: string
  514. description: User nickname.
  515. email:
  516. type: string
  517. description: User email.
  518. """
  519. return get_json_result(data=current_user.to_dict())
  520. def rollback_user_registration(user_id):
  521. try:
  522. UserService.delete_by_id(user_id)
  523. except Exception:
  524. pass
  525. try:
  526. TenantService.delete_by_id(user_id)
  527. except Exception:
  528. pass
  529. try:
  530. u = UserTenantService.query(tenant_id=user_id)
  531. if u:
  532. UserTenantService.delete_by_id(u[0].id)
  533. except Exception:
  534. pass
  535. try:
  536. TenantLLM.delete().where(TenantLLM.tenant_id == user_id).execute()
  537. except Exception:
  538. pass
  539. def user_register(user_id, user):
  540. user["id"] = user_id
  541. tenant = {
  542. "id": user_id,
  543. "name": user["nickname"] + "‘s Kingdom",
  544. "llm_id": settings.CHAT_MDL,
  545. "embd_id": settings.EMBEDDING_MDL,
  546. "asr_id": settings.ASR_MDL,
  547. "parser_ids": settings.PARSERS,
  548. "img2txt_id": settings.IMAGE2TEXT_MDL,
  549. "rerank_id": settings.RERANK_MDL,
  550. }
  551. usr_tenant = {
  552. "tenant_id": user_id,
  553. "user_id": user_id,
  554. "invited_by": user_id,
  555. "role": UserTenantRole.OWNER,
  556. }
  557. file_id = get_uuid()
  558. file = {
  559. "id": file_id,
  560. "parent_id": file_id,
  561. "tenant_id": user_id,
  562. "created_by": user_id,
  563. "name": "/",
  564. "type": FileType.FOLDER.value,
  565. "size": 0,
  566. "location": "",
  567. }
  568. tenant_llm = []
  569. for llm in LLMService.query(fid=settings.LLM_FACTORY):
  570. tenant_llm.append(
  571. {
  572. "tenant_id": user_id,
  573. "llm_factory": settings.LLM_FACTORY,
  574. "llm_name": llm.llm_name,
  575. "model_type": llm.model_type,
  576. "api_key": settings.API_KEY,
  577. "api_base": settings.LLM_BASE_URL,
  578. "max_tokens": llm.max_tokens if llm.max_tokens else 8192
  579. }
  580. )
  581. if not UserService.save(**user):
  582. return
  583. TenantService.insert(**tenant)
  584. UserTenantService.insert(**usr_tenant)
  585. TenantLLMService.insert_many(tenant_llm)
  586. FileService.insert(file)
  587. return UserService.query(email=user["email"])
  588. @manager.route("/register", methods=["POST"]) # noqa: F821
  589. @validate_request("nickname", "email", "password")
  590. def user_add():
  591. """
  592. Register a new user.
  593. ---
  594. tags:
  595. - User
  596. parameters:
  597. - in: body
  598. name: body
  599. description: Registration details.
  600. required: true
  601. schema:
  602. type: object
  603. properties:
  604. nickname:
  605. type: string
  606. description: User nickname.
  607. email:
  608. type: string
  609. description: User email.
  610. password:
  611. type: string
  612. description: User password.
  613. responses:
  614. 200:
  615. description: Registration successful.
  616. schema:
  617. type: object
  618. """
  619. if not settings.REGISTER_ENABLED:
  620. return get_json_result(
  621. data=False,
  622. message="User registration is disabled!",
  623. code=settings.RetCode.OPERATING_ERROR,
  624. )
  625. req = request.json
  626. email_address = req["email"]
  627. # Validate the email address
  628. if not re.match(r"^[\w\._-]+@([\w_-]+\.)+[\w-]{2,}$", email_address):
  629. return get_json_result(
  630. data=False,
  631. message=f"Invalid email address: {email_address}!",
  632. code=settings.RetCode.OPERATING_ERROR,
  633. )
  634. # Check if the email address is already used
  635. if UserService.query(email=email_address):
  636. return get_json_result(
  637. data=False,
  638. message=f"Email: {email_address} has already registered!",
  639. code=settings.RetCode.OPERATING_ERROR,
  640. )
  641. # Construct user info data
  642. nickname = req["nickname"]
  643. user_dict = {
  644. "access_token": get_uuid(),
  645. "email": email_address,
  646. "nickname": nickname,
  647. "password": decrypt(req["password"]),
  648. "login_channel": "password",
  649. "last_login_time": get_format_time(),
  650. "is_superuser": False,
  651. }
  652. user_id = get_uuid()
  653. try:
  654. users = user_register(user_id, user_dict)
  655. if not users:
  656. raise Exception(f"Fail to register {email_address}.")
  657. if len(users) > 1:
  658. raise Exception(f"Same email: {email_address} exists!")
  659. user = users[0]
  660. login_user(user)
  661. return construct_response(
  662. data=user.to_json(),
  663. auth=user.get_id(),
  664. message=f"{nickname}, welcome aboard!",
  665. )
  666. except Exception as e:
  667. rollback_user_registration(user_id)
  668. logging.exception(e)
  669. return get_json_result(
  670. data=False,
  671. message=f"User registration failure, error: {str(e)}",
  672. code=settings.RetCode.EXCEPTION_ERROR,
  673. )
  674. @manager.route("/tenant_info", methods=["GET"]) # noqa: F821
  675. @login_required
  676. def tenant_info():
  677. """
  678. Get tenant information.
  679. ---
  680. tags:
  681. - Tenant
  682. security:
  683. - ApiKeyAuth: []
  684. responses:
  685. 200:
  686. description: Tenant information retrieved successfully.
  687. schema:
  688. type: object
  689. properties:
  690. tenant_id:
  691. type: string
  692. description: Tenant ID.
  693. name:
  694. type: string
  695. description: Tenant name.
  696. llm_id:
  697. type: string
  698. description: LLM ID.
  699. embd_id:
  700. type: string
  701. description: Embedding model ID.
  702. """
  703. try:
  704. tenants = TenantService.get_info_by(current_user.id)
  705. if not tenants:
  706. return get_data_error_result(message="Tenant not found!")
  707. return get_json_result(data=tenants[0])
  708. except Exception as e:
  709. return server_error_response(e)
  710. @manager.route("/set_tenant_info", methods=["POST"]) # noqa: F821
  711. @login_required
  712. @validate_request("tenant_id", "asr_id", "embd_id", "img2txt_id", "llm_id")
  713. def set_tenant_info():
  714. """
  715. Update tenant information.
  716. ---
  717. tags:
  718. - Tenant
  719. security:
  720. - ApiKeyAuth: []
  721. parameters:
  722. - in: body
  723. name: body
  724. description: Tenant information to update.
  725. required: true
  726. schema:
  727. type: object
  728. properties:
  729. tenant_id:
  730. type: string
  731. description: Tenant ID.
  732. llm_id:
  733. type: string
  734. description: LLM ID.
  735. embd_id:
  736. type: string
  737. description: Embedding model ID.
  738. asr_id:
  739. type: string
  740. description: ASR model ID.
  741. img2txt_id:
  742. type: string
  743. description: Image to Text model ID.
  744. responses:
  745. 200:
  746. description: Tenant information updated successfully.
  747. schema:
  748. type: object
  749. """
  750. req = request.json
  751. try:
  752. tid = req.pop("tenant_id")
  753. TenantService.update_by_id(tid, req)
  754. return get_json_result(data=True)
  755. except Exception as e:
  756. return server_error_response(e)