application_manager.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  1. import json
  2. import logging
  3. import threading
  4. import uuid
  5. from typing import Any, Generator, Optional, Tuple, Union, cast
  6. from core.app_runner.assistant_app_runner import AssistantApplicationRunner
  7. from core.app_runner.basic_app_runner import BasicApplicationRunner
  8. from core.app_runner.generate_task_pipeline import GenerateTaskPipeline
  9. from core.application_queue_manager import ApplicationQueueManager, ConversationTaskStoppedException, PublishFrom
  10. from core.entities.application_entities import (AdvancedChatPromptTemplateEntity,
  11. AdvancedCompletionPromptTemplateEntity, AgentEntity, AgentToolEntity,
  12. ApplicationGenerateEntity, AppOrchestrationConfigEntity, DatasetEntity,
  13. DatasetRetrieveConfigEntity, ExternalDataVariableEntity,
  14. FileUploadEntity, InvokeFrom, ModelConfigEntity, PromptTemplateEntity,
  15. SensitiveWordAvoidanceEntity, AgentPromptEntity)
  16. from core.entities.model_entities import ModelStatus
  17. from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError
  18. from core.file.file_obj import FileObj
  19. from core.model_runtime.entities.message_entities import PromptMessageRole
  20. from core.model_runtime.entities.model_entities import ModelType
  21. from core.model_runtime.errors.invoke import InvokeAuthorizationError, InvokeError
  22. from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
  23. from core.prompt.prompt_template import PromptTemplateParser
  24. from core.provider_manager import ProviderManager
  25. from core.tools.prompt.template import REACT_PROMPT_TEMPLATES
  26. from extensions.ext_database import db
  27. from flask import Flask, current_app
  28. from models.account import Account
  29. from models.model import App, Conversation, EndUser, Message, MessageFile
  30. from pydantic import ValidationError
  31. logger = logging.getLogger(__name__)
  32. class ApplicationManager:
  33. """
  34. This class is responsible for managing application
  35. """
  36. def generate(self, tenant_id: str,
  37. app_id: str,
  38. app_model_config_id: str,
  39. app_model_config_dict: dict,
  40. app_model_config_override: bool,
  41. user: Union[Account, EndUser],
  42. invoke_from: InvokeFrom,
  43. inputs: dict[str, str],
  44. query: Optional[str] = None,
  45. files: Optional[list[FileObj]] = None,
  46. conversation: Optional[Conversation] = None,
  47. stream: bool = False,
  48. extras: Optional[dict[str, Any]] = None) \
  49. -> Union[dict, Generator]:
  50. """
  51. Generate App response.
  52. :param tenant_id: workspace ID
  53. :param app_id: app ID
  54. :param app_model_config_id: app model config id
  55. :param app_model_config_dict: app model config dict
  56. :param app_model_config_override: app model config override
  57. :param user: account or end user
  58. :param invoke_from: invoke from source
  59. :param inputs: inputs
  60. :param query: query
  61. :param files: file obj list
  62. :param conversation: conversation
  63. :param stream: is stream
  64. :param extras: extras
  65. """
  66. # init task id
  67. task_id = str(uuid.uuid4())
  68. # init application generate entity
  69. application_generate_entity = ApplicationGenerateEntity(
  70. task_id=task_id,
  71. tenant_id=tenant_id,
  72. app_id=app_id,
  73. app_model_config_id=app_model_config_id,
  74. app_model_config_dict=app_model_config_dict,
  75. app_orchestration_config_entity=self._convert_from_app_model_config_dict(
  76. tenant_id=tenant_id,
  77. app_model_config_dict=app_model_config_dict
  78. ),
  79. app_model_config_override=app_model_config_override,
  80. conversation_id=conversation.id if conversation else None,
  81. inputs=conversation.inputs if conversation else inputs,
  82. query=query.replace('\x00', '') if query else None,
  83. files=files if files else [],
  84. user_id=user.id,
  85. stream=stream,
  86. invoke_from=invoke_from,
  87. extras=extras
  88. )
  89. if not stream and application_generate_entity.app_orchestration_config_entity.agent:
  90. raise ValueError("Agent app is not supported in blocking mode.")
  91. # init generate records
  92. (
  93. conversation,
  94. message
  95. ) = self._init_generate_records(application_generate_entity)
  96. # init queue manager
  97. queue_manager = ApplicationQueueManager(
  98. task_id=application_generate_entity.task_id,
  99. user_id=application_generate_entity.user_id,
  100. invoke_from=application_generate_entity.invoke_from,
  101. conversation_id=conversation.id,
  102. app_mode=conversation.mode,
  103. message_id=message.id
  104. )
  105. # new thread
  106. worker_thread = threading.Thread(target=self._generate_worker, kwargs={
  107. 'flask_app': current_app._get_current_object(),
  108. 'application_generate_entity': application_generate_entity,
  109. 'queue_manager': queue_manager,
  110. 'conversation_id': conversation.id,
  111. 'message_id': message.id,
  112. })
  113. worker_thread.start()
  114. # return response or stream generator
  115. return self._handle_response(
  116. application_generate_entity=application_generate_entity,
  117. queue_manager=queue_manager,
  118. conversation=conversation,
  119. message=message,
  120. stream=stream
  121. )
  122. def _generate_worker(self, flask_app: Flask,
  123. application_generate_entity: ApplicationGenerateEntity,
  124. queue_manager: ApplicationQueueManager,
  125. conversation_id: str,
  126. message_id: str) -> None:
  127. """
  128. Generate worker in a new thread.
  129. :param flask_app: Flask app
  130. :param application_generate_entity: application generate entity
  131. :param queue_manager: queue manager
  132. :param conversation_id: conversation ID
  133. :param message_id: message ID
  134. :return:
  135. """
  136. with flask_app.app_context():
  137. try:
  138. # get conversation and message
  139. conversation = self._get_conversation(conversation_id)
  140. message = self._get_message(message_id)
  141. if application_generate_entity.app_orchestration_config_entity.agent:
  142. # agent app
  143. runner = AssistantApplicationRunner()
  144. runner.run(
  145. application_generate_entity=application_generate_entity,
  146. queue_manager=queue_manager,
  147. conversation=conversation,
  148. message=message
  149. )
  150. else:
  151. # basic app
  152. runner = BasicApplicationRunner()
  153. runner.run(
  154. application_generate_entity=application_generate_entity,
  155. queue_manager=queue_manager,
  156. conversation=conversation,
  157. message=message
  158. )
  159. except ConversationTaskStoppedException:
  160. pass
  161. except InvokeAuthorizationError:
  162. queue_manager.publish_error(
  163. InvokeAuthorizationError('Incorrect API key provided'),
  164. PublishFrom.APPLICATION_MANAGER
  165. )
  166. except ValidationError as e:
  167. logger.exception("Validation Error when generating")
  168. queue_manager.publish_error(e, PublishFrom.APPLICATION_MANAGER)
  169. except (ValueError, InvokeError) as e:
  170. queue_manager.publish_error(e, PublishFrom.APPLICATION_MANAGER)
  171. except Exception as e:
  172. logger.exception("Unknown Error when generating")
  173. queue_manager.publish_error(e, PublishFrom.APPLICATION_MANAGER)
  174. finally:
  175. db.session.remove()
  176. def _handle_response(self, application_generate_entity: ApplicationGenerateEntity,
  177. queue_manager: ApplicationQueueManager,
  178. conversation: Conversation,
  179. message: Message,
  180. stream: bool = False) -> Union[dict, Generator]:
  181. """
  182. Handle response.
  183. :param application_generate_entity: application generate entity
  184. :param queue_manager: queue manager
  185. :param conversation: conversation
  186. :param message: message
  187. :param stream: is stream
  188. :return:
  189. """
  190. # init generate task pipeline
  191. generate_task_pipeline = GenerateTaskPipeline(
  192. application_generate_entity=application_generate_entity,
  193. queue_manager=queue_manager,
  194. conversation=conversation,
  195. message=message
  196. )
  197. try:
  198. return generate_task_pipeline.process(stream=stream)
  199. except ValueError as e:
  200. if e.args[0] == "I/O operation on closed file.": # ignore this error
  201. raise ConversationTaskStoppedException()
  202. else:
  203. logger.exception(e)
  204. raise e
  205. finally:
  206. db.session.remove()
  207. def _convert_from_app_model_config_dict(self, tenant_id: str, app_model_config_dict: dict) \
  208. -> AppOrchestrationConfigEntity:
  209. """
  210. Convert app model config dict to entity.
  211. :param tenant_id: tenant ID
  212. :param app_model_config_dict: app model config dict
  213. :raises ProviderTokenNotInitError: provider token not init error
  214. :return: app orchestration config entity
  215. """
  216. properties = {}
  217. copy_app_model_config_dict = app_model_config_dict.copy()
  218. provider_manager = ProviderManager()
  219. provider_model_bundle = provider_manager.get_provider_model_bundle(
  220. tenant_id=tenant_id,
  221. provider=copy_app_model_config_dict['model']['provider'],
  222. model_type=ModelType.LLM
  223. )
  224. provider_name = provider_model_bundle.configuration.provider.provider
  225. model_name = copy_app_model_config_dict['model']['name']
  226. model_type_instance = provider_model_bundle.model_type_instance
  227. model_type_instance = cast(LargeLanguageModel, model_type_instance)
  228. # check model credentials
  229. model_credentials = provider_model_bundle.configuration.get_current_credentials(
  230. model_type=ModelType.LLM,
  231. model=copy_app_model_config_dict['model']['name']
  232. )
  233. if model_credentials is None:
  234. raise ProviderTokenNotInitError(f"Model {model_name} credentials is not initialized.")
  235. # check model
  236. provider_model = provider_model_bundle.configuration.get_provider_model(
  237. model=copy_app_model_config_dict['model']['name'],
  238. model_type=ModelType.LLM
  239. )
  240. if provider_model is None:
  241. model_name = copy_app_model_config_dict['model']['name']
  242. raise ValueError(f"Model {model_name} not exist.")
  243. if provider_model.status == ModelStatus.NO_CONFIGURE:
  244. raise ProviderTokenNotInitError(f"Model {model_name} credentials is not initialized.")
  245. elif provider_model.status == ModelStatus.NO_PERMISSION:
  246. raise ModelCurrentlyNotSupportError(f"Dify Hosted OpenAI {model_name} currently not support.")
  247. elif provider_model.status == ModelStatus.QUOTA_EXCEEDED:
  248. raise QuotaExceededError(f"Model provider {provider_name} quota exceeded.")
  249. # model config
  250. completion_params = copy_app_model_config_dict['model'].get('completion_params')
  251. stop = []
  252. if 'stop' in completion_params:
  253. stop = completion_params['stop']
  254. del completion_params['stop']
  255. # get model mode
  256. model_mode = copy_app_model_config_dict['model'].get('mode')
  257. if not model_mode:
  258. mode_enum = model_type_instance.get_model_mode(
  259. model=copy_app_model_config_dict['model']['name'],
  260. credentials=model_credentials
  261. )
  262. model_mode = mode_enum.value
  263. model_schema = model_type_instance.get_model_schema(
  264. copy_app_model_config_dict['model']['name'],
  265. model_credentials
  266. )
  267. if not model_schema:
  268. raise ValueError(f"Model {model_name} not exist.")
  269. properties['model_config'] = ModelConfigEntity(
  270. provider=copy_app_model_config_dict['model']['provider'],
  271. model=copy_app_model_config_dict['model']['name'],
  272. model_schema=model_schema,
  273. mode=model_mode,
  274. provider_model_bundle=provider_model_bundle,
  275. credentials=model_credentials,
  276. parameters=completion_params,
  277. stop=stop,
  278. )
  279. # prompt template
  280. prompt_type = PromptTemplateEntity.PromptType.value_of(copy_app_model_config_dict['prompt_type'])
  281. if prompt_type == PromptTemplateEntity.PromptType.SIMPLE:
  282. simple_prompt_template = copy_app_model_config_dict.get("pre_prompt", "")
  283. properties['prompt_template'] = PromptTemplateEntity(
  284. prompt_type=prompt_type,
  285. simple_prompt_template=simple_prompt_template
  286. )
  287. else:
  288. advanced_chat_prompt_template = None
  289. chat_prompt_config = copy_app_model_config_dict.get("chat_prompt_config", {})
  290. if chat_prompt_config:
  291. chat_prompt_messages = []
  292. for message in chat_prompt_config.get("prompt", []):
  293. chat_prompt_messages.append({
  294. "text": message["text"],
  295. "role": PromptMessageRole.value_of(message["role"])
  296. })
  297. advanced_chat_prompt_template = AdvancedChatPromptTemplateEntity(
  298. messages=chat_prompt_messages
  299. )
  300. advanced_completion_prompt_template = None
  301. completion_prompt_config = copy_app_model_config_dict.get("completion_prompt_config", {})
  302. if completion_prompt_config:
  303. completion_prompt_template_params = {
  304. 'prompt': completion_prompt_config['prompt']['text'],
  305. }
  306. if 'conversation_histories_role' in completion_prompt_config:
  307. completion_prompt_template_params['role_prefix'] = {
  308. 'user': completion_prompt_config['conversation_histories_role']['user_prefix'],
  309. 'assistant': completion_prompt_config['conversation_histories_role']['assistant_prefix']
  310. }
  311. advanced_completion_prompt_template = AdvancedCompletionPromptTemplateEntity(
  312. **completion_prompt_template_params
  313. )
  314. properties['prompt_template'] = PromptTemplateEntity(
  315. prompt_type=prompt_type,
  316. advanced_chat_prompt_template=advanced_chat_prompt_template,
  317. advanced_completion_prompt_template=advanced_completion_prompt_template
  318. )
  319. # external data variables
  320. properties['external_data_variables'] = []
  321. # old external_data_tools
  322. external_data_tools = copy_app_model_config_dict.get('external_data_tools', [])
  323. for external_data_tool in external_data_tools:
  324. if 'enabled' not in external_data_tool or not external_data_tool['enabled']:
  325. continue
  326. properties['external_data_variables'].append(
  327. ExternalDataVariableEntity(
  328. variable=external_data_tool['variable'],
  329. type=external_data_tool['type'],
  330. config=external_data_tool['config']
  331. )
  332. )
  333. # current external_data_tools
  334. for variable in copy_app_model_config_dict.get('user_input_form', []):
  335. typ = list(variable.keys())[0]
  336. if typ == 'external_data_tool':
  337. val = variable[typ]
  338. properties['external_data_variables'].append(
  339. ExternalDataVariableEntity(
  340. variable=val['variable'],
  341. type=val['type'],
  342. config=val['config']
  343. )
  344. )
  345. # show retrieve source
  346. show_retrieve_source = False
  347. retriever_resource_dict = copy_app_model_config_dict.get('retriever_resource')
  348. if retriever_resource_dict:
  349. if 'enabled' in retriever_resource_dict and retriever_resource_dict['enabled']:
  350. show_retrieve_source = True
  351. properties['show_retrieve_source'] = show_retrieve_source
  352. dataset_ids = []
  353. if 'datasets' in copy_app_model_config_dict.get('dataset_configs', {}):
  354. datasets = copy_app_model_config_dict.get('dataset_configs', {}).get('datasets', {
  355. 'strategy': 'router',
  356. 'datasets': []
  357. })
  358. for dataset in datasets.get('datasets', []):
  359. keys = list(dataset.keys())
  360. if len(keys) == 0 or keys[0] != 'dataset':
  361. continue
  362. dataset = dataset['dataset']
  363. if 'enabled' not in dataset or not dataset['enabled']:
  364. continue
  365. dataset_id = dataset.get('id', None)
  366. if dataset_id:
  367. dataset_ids.append(dataset_id)
  368. else:
  369. datasets = {'strategy': 'router', 'datasets': []}
  370. if 'agent_mode' in copy_app_model_config_dict and copy_app_model_config_dict['agent_mode'] \
  371. and 'enabled' in copy_app_model_config_dict['agent_mode'] \
  372. and copy_app_model_config_dict['agent_mode']['enabled']:
  373. agent_dict = copy_app_model_config_dict.get('agent_mode', {})
  374. agent_strategy = agent_dict.get('strategy', 'cot')
  375. if agent_strategy == 'function_call':
  376. strategy = AgentEntity.Strategy.FUNCTION_CALLING
  377. elif agent_strategy == 'cot' or agent_strategy == 'react':
  378. strategy = AgentEntity.Strategy.CHAIN_OF_THOUGHT
  379. else:
  380. # old configs, try to detect default strategy
  381. if copy_app_model_config_dict['model']['provider'] == 'openai':
  382. strategy = AgentEntity.Strategy.FUNCTION_CALLING
  383. else:
  384. strategy = AgentEntity.Strategy.CHAIN_OF_THOUGHT
  385. agent_tools = []
  386. for tool in agent_dict.get('tools', []):
  387. keys = tool.keys()
  388. if len(keys) >= 4:
  389. if "enabled" not in tool or not tool["enabled"]:
  390. continue
  391. agent_tool_properties = {
  392. 'provider_type': tool['provider_type'],
  393. 'provider_id': tool['provider_id'],
  394. 'tool_name': tool['tool_name'],
  395. 'tool_parameters': tool['tool_parameters'] if 'tool_parameters' in tool else {}
  396. }
  397. agent_tools.append(AgentToolEntity(**agent_tool_properties))
  398. elif len(keys) == 1:
  399. # old standard
  400. key = list(tool.keys())[0]
  401. if key != 'dataset':
  402. continue
  403. tool_item = tool[key]
  404. if "enabled" not in tool_item or not tool_item["enabled"]:
  405. continue
  406. dataset_id = tool_item['id']
  407. dataset_ids.append(dataset_id)
  408. if 'strategy' in copy_app_model_config_dict['agent_mode'] and \
  409. copy_app_model_config_dict['agent_mode']['strategy'] not in ['react_router', 'router']:
  410. agent_prompt = agent_dict.get('prompt', None) or {}
  411. # check model mode
  412. model_mode = copy_app_model_config_dict.get('model', {}).get('mode', 'completion')
  413. if model_mode == 'completion':
  414. agent_prompt_entity = AgentPromptEntity(
  415. first_prompt=agent_prompt.get('first_prompt', REACT_PROMPT_TEMPLATES['english']['completion']['prompt']),
  416. next_iteration=agent_prompt.get('next_iteration', REACT_PROMPT_TEMPLATES['english']['completion']['agent_scratchpad']),
  417. )
  418. else:
  419. agent_prompt_entity = AgentPromptEntity(
  420. first_prompt=agent_prompt.get('first_prompt', REACT_PROMPT_TEMPLATES['english']['chat']['prompt']),
  421. next_iteration=agent_prompt.get('next_iteration', REACT_PROMPT_TEMPLATES['english']['chat']['agent_scratchpad']),
  422. )
  423. properties['agent'] = AgentEntity(
  424. provider=properties['model_config'].provider,
  425. model=properties['model_config'].model,
  426. strategy=strategy,
  427. prompt=agent_prompt_entity,
  428. tools=agent_tools,
  429. max_iteration=agent_dict.get('max_iteration', 5)
  430. )
  431. if len(dataset_ids) > 0:
  432. # dataset configs
  433. dataset_configs = copy_app_model_config_dict.get('dataset_configs', {'retrieval_model': 'single'})
  434. query_variable = copy_app_model_config_dict.get('dataset_query_variable')
  435. if dataset_configs['retrieval_model'] == 'single':
  436. properties['dataset'] = DatasetEntity(
  437. dataset_ids=dataset_ids,
  438. retrieve_config=DatasetRetrieveConfigEntity(
  439. query_variable=query_variable,
  440. retrieve_strategy=DatasetRetrieveConfigEntity.RetrieveStrategy.value_of(
  441. dataset_configs['retrieval_model']
  442. ),
  443. single_strategy=datasets.get('strategy', 'router')
  444. )
  445. )
  446. else:
  447. properties['dataset'] = DatasetEntity(
  448. dataset_ids=dataset_ids,
  449. retrieve_config=DatasetRetrieveConfigEntity(
  450. query_variable=query_variable,
  451. retrieve_strategy=DatasetRetrieveConfigEntity.RetrieveStrategy.value_of(
  452. dataset_configs['retrieval_model']
  453. ),
  454. top_k=dataset_configs.get('top_k'),
  455. score_threshold=dataset_configs.get('score_threshold'),
  456. reranking_model=dataset_configs.get('reranking_model')
  457. )
  458. )
  459. # file upload
  460. file_upload_dict = copy_app_model_config_dict.get('file_upload')
  461. if file_upload_dict:
  462. if 'image' in file_upload_dict and file_upload_dict['image']:
  463. if 'enabled' in file_upload_dict['image'] and file_upload_dict['image']['enabled']:
  464. properties['file_upload'] = FileUploadEntity(
  465. image_config={
  466. 'number_limits': file_upload_dict['image']['number_limits'],
  467. 'detail': file_upload_dict['image']['detail'],
  468. 'transfer_methods': file_upload_dict['image']['transfer_methods']
  469. }
  470. )
  471. # opening statement
  472. properties['opening_statement'] = copy_app_model_config_dict.get('opening_statement')
  473. # suggested questions after answer
  474. suggested_questions_after_answer_dict = copy_app_model_config_dict.get('suggested_questions_after_answer')
  475. if suggested_questions_after_answer_dict:
  476. if 'enabled' in suggested_questions_after_answer_dict and suggested_questions_after_answer_dict['enabled']:
  477. properties['suggested_questions_after_answer'] = True
  478. # more like this
  479. more_like_this_dict = copy_app_model_config_dict.get('more_like_this')
  480. if more_like_this_dict:
  481. if 'enabled' in more_like_this_dict and more_like_this_dict['enabled']:
  482. properties['more_like_this'] = True
  483. # speech to text
  484. speech_to_text_dict = copy_app_model_config_dict.get('speech_to_text')
  485. if speech_to_text_dict:
  486. if 'enabled' in speech_to_text_dict and speech_to_text_dict['enabled']:
  487. properties['speech_to_text'] = True
  488. # text to speech
  489. text_to_speech_dict = copy_app_model_config_dict.get('text_to_speech')
  490. if text_to_speech_dict:
  491. if 'enabled' in text_to_speech_dict and text_to_speech_dict['enabled']:
  492. properties['text_to_speech'] = True
  493. # sensitive word avoidance
  494. sensitive_word_avoidance_dict = copy_app_model_config_dict.get('sensitive_word_avoidance')
  495. if sensitive_word_avoidance_dict:
  496. if 'enabled' in sensitive_word_avoidance_dict and sensitive_word_avoidance_dict['enabled']:
  497. properties['sensitive_word_avoidance'] = SensitiveWordAvoidanceEntity(
  498. type=sensitive_word_avoidance_dict.get('type'),
  499. config=sensitive_word_avoidance_dict.get('config'),
  500. )
  501. return AppOrchestrationConfigEntity(**properties)
  502. def _init_generate_records(self, application_generate_entity: ApplicationGenerateEntity) \
  503. -> Tuple[Conversation, Message]:
  504. """
  505. Initialize generate records
  506. :param application_generate_entity: application generate entity
  507. :return:
  508. """
  509. app_orchestration_config_entity = application_generate_entity.app_orchestration_config_entity
  510. model_type_instance = app_orchestration_config_entity.model_config.provider_model_bundle.model_type_instance
  511. model_type_instance = cast(LargeLanguageModel, model_type_instance)
  512. model_schema = model_type_instance.get_model_schema(
  513. model=app_orchestration_config_entity.model_config.model,
  514. credentials=app_orchestration_config_entity.model_config.credentials
  515. )
  516. app_record = (db.session.query(App)
  517. .filter(App.id == application_generate_entity.app_id).first())
  518. app_mode = app_record.mode
  519. # get from source
  520. end_user_id = None
  521. account_id = None
  522. if application_generate_entity.invoke_from in [InvokeFrom.WEB_APP, InvokeFrom.SERVICE_API]:
  523. from_source = 'api'
  524. end_user_id = application_generate_entity.user_id
  525. else:
  526. from_source = 'console'
  527. account_id = application_generate_entity.user_id
  528. override_model_configs = None
  529. if application_generate_entity.app_model_config_override:
  530. override_model_configs = application_generate_entity.app_model_config_dict
  531. introduction = ''
  532. if app_mode == 'chat':
  533. # get conversation introduction
  534. introduction = self._get_conversation_introduction(application_generate_entity)
  535. if not application_generate_entity.conversation_id:
  536. conversation = Conversation(
  537. app_id=app_record.id,
  538. app_model_config_id=application_generate_entity.app_model_config_id,
  539. model_provider=app_orchestration_config_entity.model_config.provider,
  540. model_id=app_orchestration_config_entity.model_config.model,
  541. override_model_configs=json.dumps(override_model_configs) if override_model_configs else None,
  542. mode=app_mode,
  543. name='New conversation',
  544. inputs=application_generate_entity.inputs,
  545. introduction=introduction,
  546. system_instruction="",
  547. system_instruction_tokens=0,
  548. status='normal',
  549. from_source=from_source,
  550. from_end_user_id=end_user_id,
  551. from_account_id=account_id,
  552. )
  553. db.session.add(conversation)
  554. db.session.commit()
  555. else:
  556. conversation = (
  557. db.session.query(Conversation)
  558. .filter(
  559. Conversation.id == application_generate_entity.conversation_id,
  560. Conversation.app_id == app_record.id
  561. ).first()
  562. )
  563. currency = model_schema.pricing.currency if model_schema.pricing else 'USD'
  564. message = Message(
  565. app_id=app_record.id,
  566. model_provider=app_orchestration_config_entity.model_config.provider,
  567. model_id=app_orchestration_config_entity.model_config.model,
  568. override_model_configs=json.dumps(override_model_configs) if override_model_configs else None,
  569. conversation_id=conversation.id,
  570. inputs=application_generate_entity.inputs,
  571. query=application_generate_entity.query or "",
  572. message="",
  573. message_tokens=0,
  574. message_unit_price=0,
  575. message_price_unit=0,
  576. answer="",
  577. answer_tokens=0,
  578. answer_unit_price=0,
  579. answer_price_unit=0,
  580. provider_response_latency=0,
  581. total_price=0,
  582. currency=currency,
  583. from_source=from_source,
  584. from_end_user_id=end_user_id,
  585. from_account_id=account_id,
  586. agent_based=app_orchestration_config_entity.agent is not None
  587. )
  588. db.session.add(message)
  589. db.session.commit()
  590. for file in application_generate_entity.files:
  591. message_file = MessageFile(
  592. message_id=message.id,
  593. type=file.type.value,
  594. transfer_method=file.transfer_method.value,
  595. belongs_to='user',
  596. url=file.url,
  597. upload_file_id=file.upload_file_id,
  598. created_by_role=('account' if account_id else 'end_user'),
  599. created_by=account_id or end_user_id,
  600. )
  601. db.session.add(message_file)
  602. db.session.commit()
  603. return conversation, message
  604. def _get_conversation_introduction(self, application_generate_entity: ApplicationGenerateEntity) -> str:
  605. """
  606. Get conversation introduction
  607. :param application_generate_entity: application generate entity
  608. :return: conversation introduction
  609. """
  610. app_orchestration_config_entity = application_generate_entity.app_orchestration_config_entity
  611. introduction = app_orchestration_config_entity.opening_statement
  612. if introduction:
  613. try:
  614. inputs = application_generate_entity.inputs
  615. prompt_template = PromptTemplateParser(template=introduction)
  616. prompt_inputs = {k: inputs[k] for k in prompt_template.variable_keys if k in inputs}
  617. introduction = prompt_template.format(prompt_inputs)
  618. except KeyError:
  619. pass
  620. return introduction
  621. def _get_conversation(self, conversation_id: str) -> Conversation:
  622. """
  623. Get conversation by conversation id
  624. :param conversation_id: conversation id
  625. :return: conversation
  626. """
  627. conversation = (
  628. db.session.query(Conversation)
  629. .filter(Conversation.id == conversation_id)
  630. .first()
  631. )
  632. return conversation
  633. def _get_message(self, message_id: str) -> Message:
  634. """
  635. Get message by message id
  636. :param message_id: message id
  637. :return: message
  638. """
  639. message = (
  640. db.session.query(Message)
  641. .filter(Message.id == message_id)
  642. .first()
  643. )
  644. return message