provider_manager.py 26 KB

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