provider_manager.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. import json
  2. from collections import defaultdict
  3. from json import JSONDecodeError
  4. from typing import Optional
  5. from sqlalchemy.exc import IntegrityError
  6. from core.entities.model_entities import DefaultModelEntity, DefaultModelProviderEntity
  7. from core.entities.provider_configuration import ProviderConfigurations, ProviderConfiguration, ProviderModelBundle
  8. from core.entities.provider_entities import CustomConfiguration, CustomProviderConfiguration, CustomModelConfiguration, \
  9. SystemConfiguration, QuotaConfiguration
  10. from core.helper import encrypter
  11. from core.model_runtime.entities.model_entities import ModelType
  12. from core.model_runtime.entities.provider_entities import ProviderEntity, CredentialFormSchema, FormType
  13. from core.model_runtime.model_providers import model_provider_factory
  14. from extensions import ext_hosting_provider
  15. from extensions.ext_database import db
  16. from models.provider import TenantDefaultModel, Provider, ProviderModel, ProviderQuotaType, ProviderType, \
  17. TenantPreferredModelProvider
  18. class ProviderManager:
  19. """
  20. ProviderManager is a class that manages the model providers includes Hosting and Customize Model Providers.
  21. """
  22. def get_configurations(self, tenant_id: str) -> ProviderConfigurations:
  23. """
  24. Get model provider configurations.
  25. Construct ProviderConfiguration objects for each provider
  26. Including:
  27. 1. Basic information of the provider
  28. 2. Hosting configuration information, including:
  29. (1. Whether to enable (support) hosting type, if enabled, the following information exists
  30. (2. List of hosting type provider configurations
  31. (including quota type, quota limit, current remaining quota, etc.)
  32. (3. The current hosting type in use (whether there is a quota or not)
  33. paid quotas > provider free quotas > hosting trial quotas
  34. (4. Unified credentials for hosting providers
  35. 3. Custom configuration information, including:
  36. (1. Whether to enable (support) custom type, if enabled, the following information exists
  37. (2. Custom provider configuration (including credentials)
  38. (3. List of custom provider model configurations (including credentials)
  39. 4. Hosting/custom preferred provider type.
  40. Provide methods:
  41. - Get the current configuration (including credentials)
  42. - Get the availability and status of the hosting configuration: active available,
  43. quota_exceeded insufficient quota, unsupported hosting
  44. - Get the availability of custom configuration
  45. Custom provider available conditions:
  46. (1. custom provider credentials available
  47. (2. at least one custom model credentials available
  48. - Verify, update, and delete custom provider configuration
  49. - Verify, update, and delete custom provider model configuration
  50. - Get the list of available models (optional provider filtering, model type filtering)
  51. Append custom provider models to the list
  52. - Get provider instance
  53. - Switch selection priority
  54. :param tenant_id:
  55. :return:
  56. """
  57. # Get all provider records of the workspace
  58. provider_name_to_provider_records_dict = self._get_all_providers(tenant_id)
  59. # Initialize trial provider records if not exist
  60. provider_name_to_provider_records_dict = self._init_trial_provider_records(
  61. tenant_id,
  62. provider_name_to_provider_records_dict
  63. )
  64. # Get all provider model records of the workspace
  65. provider_name_to_provider_model_records_dict = self._get_all_provider_models(tenant_id)
  66. # Get all provider entities
  67. provider_entities = model_provider_factory.get_providers()
  68. # Get All preferred provider types of the workspace
  69. provider_name_to_preferred_model_provider_records_dict = self._get_all_preferred_model_providers(tenant_id)
  70. # Get decoding rsa key and cipher for decrypting credentials
  71. decoding_rsa_key, decoding_cipher_rsa = encrypter.get_decrypt_decoding(tenant_id)
  72. provider_configurations = ProviderConfigurations(
  73. tenant_id=tenant_id
  74. )
  75. # Construct ProviderConfiguration objects for each provider
  76. for provider_entity in provider_entities:
  77. provider_name = provider_entity.provider
  78. provider_records = provider_name_to_provider_records_dict.get(provider_entity.provider)
  79. if not provider_records:
  80. provider_records = []
  81. provider_model_records = provider_name_to_provider_model_records_dict.get(provider_entity.provider)
  82. if not provider_model_records:
  83. provider_model_records = []
  84. # Convert to custom configuration
  85. custom_configuration = self._to_custom_configuration(
  86. provider_entity,
  87. provider_records,
  88. provider_model_records,
  89. decoding_rsa_key,
  90. decoding_cipher_rsa
  91. )
  92. # Convert to system configuration
  93. system_configuration = self._to_system_configuration(
  94. provider_entity,
  95. provider_records,
  96. decoding_rsa_key,
  97. decoding_cipher_rsa
  98. )
  99. # Get preferred provider type
  100. preferred_provider_type_record = provider_name_to_preferred_model_provider_records_dict.get(provider_name)
  101. if preferred_provider_type_record:
  102. preferred_provider_type = ProviderType.value_of(preferred_provider_type_record.preferred_provider_type)
  103. else:
  104. if custom_configuration.provider or custom_configuration.models:
  105. preferred_provider_type = ProviderType.CUSTOM
  106. elif system_configuration.enabled:
  107. preferred_provider_type = ProviderType.SYSTEM
  108. else:
  109. preferred_provider_type = ProviderType.CUSTOM
  110. using_provider_type = preferred_provider_type
  111. if preferred_provider_type == ProviderType.SYSTEM:
  112. if not system_configuration.enabled:
  113. using_provider_type = ProviderType.CUSTOM
  114. has_valid_quota = False
  115. for quota_configuration in system_configuration.quota_configurations:
  116. if quota_configuration.is_valid:
  117. has_valid_quota = True
  118. break
  119. if not has_valid_quota:
  120. using_provider_type = ProviderType.CUSTOM
  121. else:
  122. if not custom_configuration.provider and not custom_configuration.models:
  123. if system_configuration.enabled:
  124. has_valid_quota = False
  125. for quota_configuration in system_configuration.quota_configurations:
  126. if quota_configuration.is_valid:
  127. has_valid_quota = True
  128. break
  129. if has_valid_quota:
  130. using_provider_type = ProviderType.SYSTEM
  131. provider_configuration = ProviderConfiguration(
  132. tenant_id=tenant_id,
  133. provider=provider_entity,
  134. preferred_provider_type=preferred_provider_type,
  135. using_provider_type=using_provider_type,
  136. system_configuration=system_configuration,
  137. custom_configuration=custom_configuration
  138. )
  139. provider_configurations[provider_name] = provider_configuration
  140. # Return the encapsulated object
  141. return provider_configurations
  142. def get_provider_model_bundle(self, tenant_id: str, provider: str, model_type: ModelType) -> ProviderModelBundle:
  143. """
  144. Get provider model bundle.
  145. :param tenant_id: workspace id
  146. :param provider: provider name
  147. :param model_type: model type
  148. :return:
  149. """
  150. provider_configurations = self.get_configurations(tenant_id)
  151. # get provider instance
  152. provider_configuration = provider_configurations.get(provider)
  153. if not provider_configuration:
  154. raise ValueError(f"Provider {provider} does not exist.")
  155. provider_instance = provider_configuration.get_provider_instance()
  156. model_type_instance = provider_instance.get_model_instance(model_type)
  157. return ProviderModelBundle(
  158. configuration=provider_configuration,
  159. provider_instance=provider_instance,
  160. model_type_instance=model_type_instance
  161. )
  162. def get_default_model(self, tenant_id: str, model_type: ModelType) -> Optional[DefaultModelEntity]:
  163. """
  164. Get default model.
  165. :param tenant_id: workspace id
  166. :param model_type: model type
  167. :return:
  168. """
  169. # Get the corresponding TenantDefaultModel record
  170. default_model = db.session.query(TenantDefaultModel) \
  171. .filter(
  172. TenantDefaultModel.tenant_id == tenant_id,
  173. TenantDefaultModel.model_type == model_type.to_origin_model_type()
  174. ).first()
  175. # If it does not exist, get the first available provider model from get_configurations
  176. # and update the TenantDefaultModel record
  177. if not default_model:
  178. # Get provider configurations
  179. provider_configurations = self.get_configurations(tenant_id)
  180. # get available models from provider_configurations
  181. available_models = provider_configurations.get_models(
  182. model_type=model_type,
  183. only_active=True
  184. )
  185. if available_models:
  186. available_model = available_models[0]
  187. default_model = TenantDefaultModel(
  188. tenant_id=tenant_id,
  189. model_type=model_type.to_origin_model_type(),
  190. provider_name=available_model.provider.provider,
  191. model_name=available_model.model
  192. )
  193. db.session.add(default_model)
  194. db.session.commit()
  195. if not default_model:
  196. return None
  197. provider_instance = model_provider_factory.get_provider_instance(default_model.provider_name)
  198. return DefaultModelEntity(
  199. model=default_model.model_name,
  200. model_type=model_type,
  201. provider=DefaultModelProviderEntity(**provider_instance.get_provider_schema().to_simple_provider().dict())
  202. )
  203. def update_default_model_record(self, tenant_id: str, model_type: ModelType, provider: str, model: str) \
  204. -> TenantDefaultModel:
  205. """
  206. Update default model record.
  207. :param tenant_id: workspace id
  208. :param model_type: model type
  209. :param provider: provider name
  210. :param model: model name
  211. :return:
  212. """
  213. provider_configurations = self.get_configurations(tenant_id)
  214. if provider not in provider_configurations:
  215. raise ValueError(f"Provider {provider} does not exist.")
  216. # get available models from provider_configurations
  217. available_models = provider_configurations.get_models(
  218. model_type=model_type,
  219. only_active=True
  220. )
  221. # check if the model is exist in available models
  222. model_names = [model.model for model in available_models]
  223. if model not in model_names:
  224. raise ValueError(f"Model {model} does not exist.")
  225. # Get the list of available models from get_configurations and check if it is LLM
  226. default_model = db.session.query(TenantDefaultModel) \
  227. .filter(
  228. TenantDefaultModel.tenant_id == tenant_id,
  229. TenantDefaultModel.model_type == model_type.to_origin_model_type()
  230. ).first()
  231. # create or update TenantDefaultModel record
  232. if default_model:
  233. # update default model
  234. default_model.provider_name = provider
  235. default_model.model_name = model
  236. db.session.commit()
  237. else:
  238. # create default model
  239. default_model = TenantDefaultModel(
  240. tenant_id=tenant_id,
  241. model_type=model_type.value,
  242. provider_name=provider,
  243. model_name=model,
  244. )
  245. db.session.add(default_model)
  246. db.session.commit()
  247. return default_model
  248. def _get_all_providers(self, tenant_id: str) -> dict[str, list[Provider]]:
  249. """
  250. Get all provider records of the workspace.
  251. :param tenant_id: workspace id
  252. :return:
  253. """
  254. providers = db.session.query(Provider) \
  255. .filter(
  256. Provider.tenant_id == tenant_id,
  257. Provider.is_valid == True
  258. ).all()
  259. provider_name_to_provider_records_dict = defaultdict(list)
  260. for provider in providers:
  261. provider_name_to_provider_records_dict[provider.provider_name].append(provider)
  262. return provider_name_to_provider_records_dict
  263. def _get_all_provider_models(self, tenant_id: str) -> dict[str, list[ProviderModel]]:
  264. """
  265. Get all provider model records of the workspace.
  266. :param tenant_id: workspace id
  267. :return:
  268. """
  269. # Get all provider model records of the workspace
  270. provider_models = db.session.query(ProviderModel) \
  271. .filter(
  272. ProviderModel.tenant_id == tenant_id,
  273. ProviderModel.is_valid == True
  274. ).all()
  275. provider_name_to_provider_model_records_dict = defaultdict(list)
  276. for provider_model in provider_models:
  277. provider_name_to_provider_model_records_dict[provider_model.provider_name].append(provider_model)
  278. return provider_name_to_provider_model_records_dict
  279. def _get_all_preferred_model_providers(self, tenant_id: str) -> dict[str, TenantPreferredModelProvider]:
  280. """
  281. Get All preferred provider types of the workspace.
  282. :param tenant_id:
  283. :return:
  284. """
  285. preferred_provider_types = db.session.query(TenantPreferredModelProvider) \
  286. .filter(
  287. TenantPreferredModelProvider.tenant_id == tenant_id
  288. ).all()
  289. provider_name_to_preferred_provider_type_records_dict = {
  290. preferred_provider_type.provider_name: preferred_provider_type
  291. for preferred_provider_type in preferred_provider_types
  292. }
  293. return provider_name_to_preferred_provider_type_records_dict
  294. def _init_trial_provider_records(self, tenant_id: str,
  295. provider_name_to_provider_records_dict: dict[str, list]) -> dict[str, list]:
  296. """
  297. Initialize trial provider records if not exists.
  298. :param tenant_id: workspace id
  299. :param provider_name_to_provider_records_dict: provider name to provider records dict
  300. :return:
  301. """
  302. # Get hosting configuration
  303. hosting_configuration = ext_hosting_provider.hosting_configuration
  304. for provider_name, configuration in hosting_configuration.provider_map.items():
  305. if not configuration.enabled:
  306. continue
  307. provider_records = provider_name_to_provider_records_dict.get(provider_name)
  308. if not provider_records:
  309. provider_records = []
  310. provider_quota_to_provider_record_dict = dict()
  311. for provider_record in provider_records:
  312. if provider_record.provider_type != ProviderType.SYSTEM.value:
  313. continue
  314. provider_quota_to_provider_record_dict[ProviderQuotaType.value_of(provider_record.quota_type)] \
  315. = provider_record
  316. for quota in configuration.quotas:
  317. if quota.quota_type == ProviderQuotaType.TRIAL:
  318. # Init trial provider records if not exists
  319. if ProviderQuotaType.TRIAL not in provider_quota_to_provider_record_dict:
  320. try:
  321. provider_record = Provider(
  322. tenant_id=tenant_id,
  323. provider_name=provider_name,
  324. provider_type=ProviderType.SYSTEM.value,
  325. quota_type=ProviderQuotaType.TRIAL.value,
  326. quota_limit=quota.quota_limit,
  327. quota_used=0,
  328. is_valid=True
  329. )
  330. db.session.add(provider_record)
  331. db.session.commit()
  332. except IntegrityError:
  333. db.session.rollback()
  334. provider_record = db.session.query(Provider) \
  335. .filter(
  336. Provider.tenant_id == tenant_id,
  337. Provider.provider_name == provider_name,
  338. Provider.provider_type == ProviderType.SYSTEM.value,
  339. Provider.quota_type == ProviderQuotaType.TRIAL.value,
  340. Provider.is_valid == True
  341. ).first()
  342. provider_name_to_provider_records_dict[provider_name].append(provider_record)
  343. return provider_name_to_provider_records_dict
  344. def _to_custom_configuration(self,
  345. provider_entity: ProviderEntity,
  346. provider_records: list[Provider],
  347. provider_model_records: list[ProviderModel],
  348. decoding_rsa_key,
  349. decoding_cipher_rsa) -> CustomConfiguration:
  350. """
  351. Convert to custom configuration.
  352. :param provider_entity: provider entity
  353. :param provider_records: provider records
  354. :param provider_model_records: provider model records
  355. :param decoding_rsa_key: decoding rsa key
  356. :param decoding_cipher_rsa: decoding cipher rsa
  357. :return:
  358. """
  359. # Get provider credential secret variables
  360. provider_credential_secret_variables = self._extract_secret_variables(
  361. provider_entity.provider_credential_schema.credential_form_schemas
  362. if provider_entity.provider_credential_schema else []
  363. )
  364. # Get custom provider record
  365. custom_provider_record = None
  366. for provider_record in provider_records:
  367. if provider_record.provider_type == ProviderType.SYSTEM.value:
  368. continue
  369. if not provider_record.encrypted_config:
  370. continue
  371. custom_provider_record = provider_record
  372. # Get custom provider credentials
  373. custom_provider_configuration = None
  374. if custom_provider_record:
  375. try:
  376. # fix origin data
  377. if (custom_provider_record.encrypted_config
  378. and not custom_provider_record.encrypted_config.startswith("{")):
  379. provider_credentials = {
  380. "openai_api_key": custom_provider_record.encrypted_config
  381. }
  382. else:
  383. provider_credentials = json.loads(custom_provider_record.encrypted_config)
  384. except JSONDecodeError:
  385. provider_credentials = {}
  386. for variable in provider_credential_secret_variables:
  387. if variable in provider_credentials:
  388. try:
  389. provider_credentials[variable] = encrypter.decrypt_token_with_decoding(
  390. provider_credentials.get(variable),
  391. decoding_rsa_key,
  392. decoding_cipher_rsa
  393. )
  394. except ValueError:
  395. pass
  396. custom_provider_configuration = CustomProviderConfiguration(
  397. credentials=provider_credentials
  398. )
  399. # Get provider model credential secret variables
  400. model_credential_secret_variables = self._extract_secret_variables(
  401. provider_entity.model_credential_schema.credential_form_schemas
  402. if provider_entity.model_credential_schema else []
  403. )
  404. # Get custom provider model credentials
  405. custom_model_configurations = []
  406. for provider_model_record in provider_model_records:
  407. if not provider_model_record.encrypted_config:
  408. continue
  409. try:
  410. provider_model_credentials = json.loads(provider_model_record.encrypted_config)
  411. except JSONDecodeError:
  412. continue
  413. for variable in model_credential_secret_variables:
  414. if variable in provider_model_credentials:
  415. try:
  416. provider_model_credentials[variable] = encrypter.decrypt_token_with_decoding(
  417. provider_model_credentials.get(variable),
  418. decoding_rsa_key,
  419. decoding_cipher_rsa
  420. )
  421. except ValueError:
  422. pass
  423. custom_model_configurations.append(
  424. CustomModelConfiguration(
  425. model=provider_model_record.model_name,
  426. model_type=ModelType.value_of(provider_model_record.model_type),
  427. credentials=provider_model_credentials
  428. )
  429. )
  430. return CustomConfiguration(
  431. provider=custom_provider_configuration,
  432. models=custom_model_configurations
  433. )
  434. def _to_system_configuration(self,
  435. provider_entity: ProviderEntity,
  436. provider_records: list[Provider],
  437. decoding_rsa_key,
  438. decoding_cipher_rsa) -> SystemConfiguration:
  439. """
  440. Convert to system configuration.
  441. :param provider_entity: provider entity
  442. :param provider_records: provider records
  443. :param decoding_rsa_key: decoding rsa key
  444. :param decoding_cipher_rsa: decoding cipher rsa
  445. :return:
  446. """
  447. # Get hosting configuration
  448. hosting_configuration = ext_hosting_provider.hosting_configuration
  449. if provider_entity.provider not in hosting_configuration.provider_map \
  450. or not hosting_configuration.provider_map.get(provider_entity.provider).enabled:
  451. return SystemConfiguration(
  452. enabled=False
  453. )
  454. provider_hosting_configuration = hosting_configuration.provider_map.get(provider_entity.provider)
  455. # Convert provider_records to dict
  456. quota_type_to_provider_records_dict = dict()
  457. for provider_record in provider_records:
  458. if provider_record.provider_type != ProviderType.SYSTEM.value:
  459. continue
  460. quota_type_to_provider_records_dict[ProviderQuotaType.value_of(provider_record.quota_type)] \
  461. = provider_record
  462. quota_configurations = []
  463. for provider_quota in provider_hosting_configuration.quotas:
  464. if provider_quota.quota_type not in quota_type_to_provider_records_dict:
  465. continue
  466. provider_record = quota_type_to_provider_records_dict[provider_quota.quota_type]
  467. quota_configuration = QuotaConfiguration(
  468. quota_type=provider_quota.quota_type,
  469. quota_unit=provider_hosting_configuration.quota_unit,
  470. quota_used=provider_record.quota_used,
  471. quota_limit=provider_record.quota_limit,
  472. is_valid=provider_record.quota_limit > provider_record.quota_used or provider_record.quota_limit == -1,
  473. restrict_llms=provider_quota.restrict_llms
  474. )
  475. quota_configurations.append(quota_configuration)
  476. if len(quota_configurations) == 0:
  477. return SystemConfiguration(
  478. enabled=False
  479. )
  480. current_quota_type = self._choice_current_using_quota_type(quota_configurations)
  481. current_using_credentials = provider_hosting_configuration.credentials
  482. if current_quota_type == ProviderQuotaType.FREE:
  483. provider_record = quota_type_to_provider_records_dict.get(current_quota_type)
  484. if provider_record:
  485. try:
  486. provider_credentials = json.loads(provider_record.encrypted_config)
  487. except JSONDecodeError:
  488. provider_credentials = {}
  489. # Get provider credential secret variables
  490. provider_credential_secret_variables = self._extract_secret_variables(
  491. provider_entity.provider_credential_schema.credential_form_schemas
  492. if provider_entity.provider_credential_schema else []
  493. )
  494. for variable in provider_credential_secret_variables:
  495. if variable in provider_credentials:
  496. try:
  497. provider_credentials[variable] = encrypter.decrypt_token_with_decoding(
  498. provider_credentials.get(variable),
  499. decoding_rsa_key,
  500. decoding_cipher_rsa
  501. )
  502. except ValueError:
  503. pass
  504. current_using_credentials = provider_credentials
  505. else:
  506. current_using_credentials = {}
  507. return SystemConfiguration(
  508. enabled=True,
  509. current_quota_type=current_quota_type,
  510. quota_configurations=quota_configurations,
  511. credentials=current_using_credentials
  512. )
  513. def _choice_current_using_quota_type(self, quota_configurations: list[QuotaConfiguration]) -> ProviderQuotaType:
  514. """
  515. Choice current using quota type.
  516. paid quotas > provider free quotas > hosting trial quotas
  517. If there is still quota for the corresponding quota type according to the sorting,
  518. :param quota_configurations:
  519. :return:
  520. """
  521. # convert to dict
  522. quota_type_to_quota_configuration_dict = {
  523. quota_configuration.quota_type: quota_configuration
  524. for quota_configuration in quota_configurations
  525. }
  526. last_quota_configuration = None
  527. for quota_type in [ProviderQuotaType.PAID, ProviderQuotaType.FREE, ProviderQuotaType.TRIAL]:
  528. if quota_type in quota_type_to_quota_configuration_dict:
  529. last_quota_configuration = quota_type_to_quota_configuration_dict[quota_type]
  530. if last_quota_configuration.is_valid:
  531. return quota_type
  532. if last_quota_configuration:
  533. return last_quota_configuration.quota_type
  534. raise ValueError('No quota type available')
  535. def _extract_secret_variables(self, credential_form_schemas: list[CredentialFormSchema]) -> list[str]:
  536. """
  537. Extract secret input form variables.
  538. :param credential_form_schemas:
  539. :return:
  540. """
  541. secret_input_form_variables = []
  542. for credential_form_schema in credential_form_schemas:
  543. if credential_form_schema.type == FormType.SECRET_INPUT:
  544. secret_input_form_variables.append(credential_form_schema.variable)
  545. return secret_input_form_variables