base.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. import json
  2. import logging
  3. import re
  4. from string import Template
  5. from typing import Any, Mapping, Optional, Dict, Union
  6. import httpx
  7. from embedchain.config.base_config import BaseConfig
  8. from embedchain.helpers.json_serializable import register_deserializable
  9. logger = logging.getLogger(__name__)
  10. DEFAULT_PROMPT = """
  11. You are a Q&A expert system. Your responses must always be rooted in the context provided for each query. Here are some guidelines to follow:
  12. 1. Refrain from explicitly mentioning the context provided in your response.
  13. 2. The context should silently guide your answers without being directly acknowledged.
  14. 3. Do not use phrases such as 'According to the context provided', 'Based on the context, ...' etc.
  15. Context information:
  16. ----------------------
  17. $context
  18. ----------------------
  19. Query: $query
  20. Answer:
  21. """ # noqa:E501
  22. DEFAULT_PROMPT_WITH_HISTORY = """
  23. You are a Q&A expert system. Your responses must always be rooted in the context provided for each query. You are also provided with the conversation history with the user. Make sure to use relevant context from conversation history as needed.
  24. Here are some guidelines to follow:
  25. 1. Refrain from explicitly mentioning the context provided in your response.
  26. 2. The context should silently guide your answers without being directly acknowledged.
  27. 3. Do not use phrases such as 'According to the context provided', 'Based on the context, ...' etc.
  28. Context information:
  29. ----------------------
  30. $context
  31. ----------------------
  32. Conversation history:
  33. ----------------------
  34. $history
  35. ----------------------
  36. Query: $query
  37. Answer:
  38. """ # noqa:E501
  39. DEFAULT_PROMPT_WITH_MEM0_MEMORY = """
  40. You are a Q&A expert system. Your responses must always be rooted in the context provided for each query. You are also provided with the conversation history and memories with the user. Make sure to use relevant context from conversation history and memories as needed.
  41. Here are some guidelines to follow:
  42. 1. Refrain from explicitly mentioning the context provided in your response.
  43. 2. Take into consideration the conversation history and memories provided.
  44. 3. The context should silently guide your answers without being directly acknowledged.
  45. 4. Do not use phrases such as 'According to the context provided', 'Based on the context, ...' etc.
  46. Context information:
  47. ----------------------
  48. $context
  49. ----------------------
  50. Conversation history:
  51. ----------------------
  52. $history
  53. ----------------------
  54. Memories/Preferences:
  55. ----------------------
  56. $memories
  57. ----------------------
  58. Query: $query
  59. Answer:
  60. """ # noqa:E501
  61. DOCS_SITE_DEFAULT_PROMPT = """
  62. You are an expert AI assistant for developer support product. Your responses must always be rooted in the context provided for each query. Wherever possible, give complete code snippet. Dont make up any code snippet on your own.
  63. Here are some guidelines to follow:
  64. 1. Refrain from explicitly mentioning the context provided in your response.
  65. 2. The context should silently guide your answers without being directly acknowledged.
  66. 3. Do not use phrases such as 'According to the context provided', 'Based on the context, ...' etc.
  67. Context information:
  68. ----------------------
  69. $context
  70. ----------------------
  71. Query: $query
  72. Answer:
  73. """ # noqa:E501
  74. DEFAULT_PROMPT_TEMPLATE = Template(DEFAULT_PROMPT)
  75. DEFAULT_PROMPT_WITH_HISTORY_TEMPLATE = Template(DEFAULT_PROMPT_WITH_HISTORY)
  76. DEFAULT_PROMPT_WITH_MEM0_MEMORY_TEMPLATE = Template(DEFAULT_PROMPT_WITH_MEM0_MEMORY)
  77. DOCS_SITE_PROMPT_TEMPLATE = Template(DOCS_SITE_DEFAULT_PROMPT)
  78. query_re = re.compile(r"\$\{*query\}*")
  79. context_re = re.compile(r"\$\{*context\}*")
  80. history_re = re.compile(r"\$\{*history\}*")
  81. @register_deserializable
  82. class BaseLlmConfig(BaseConfig):
  83. """
  84. Config for the `query` method.
  85. """
  86. def __init__(
  87. self,
  88. number_documents: int = 3,
  89. template: Optional[Template] = None,
  90. prompt: Optional[Template] = None,
  91. model: Optional[str] = None,
  92. temperature: float = 0,
  93. max_tokens: int = 1000,
  94. top_p: float = 1,
  95. stream: bool = False,
  96. online: bool = False,
  97. token_usage: bool = False,
  98. deployment_name: Optional[str] = None,
  99. system_prompt: Optional[str] = None,
  100. where: dict[str, Any] = None,
  101. query_type: Optional[str] = None,
  102. callbacks: Optional[list] = None,
  103. api_key: Optional[str] = None,
  104. base_url: Optional[str] = None,
  105. endpoint: Optional[str] = None,
  106. model_kwargs: Optional[dict[str, Any]] = None,
  107. http_client_proxies: Optional[Union[Dict, str]] = None,
  108. http_async_client_proxies: Optional[Union[Dict, str]] = None,
  109. local: Optional[bool] = False,
  110. default_headers: Optional[Mapping[str, str]] = None,
  111. api_version: Optional[str] = None,
  112. ):
  113. """
  114. Initializes a configuration class instance for the LLM.
  115. Takes the place of the former `QueryConfig` or `ChatConfig`.
  116. :param number_documents: Number of documents to pull from the database as
  117. context, defaults to 1
  118. :type number_documents: int, optional
  119. :param template: The `Template` instance to use as a template for
  120. prompt, defaults to None (deprecated)
  121. :type template: Optional[Template], optional
  122. :param prompt: The `Template` instance to use as a template for
  123. prompt, defaults to None
  124. :type prompt: Optional[Template], optional
  125. :param model: Controls the OpenAI model used, defaults to None
  126. :type model: Optional[str], optional
  127. :param temperature: Controls the randomness of the model's output.
  128. Higher values (closer to 1) make output more random, lower values make it more deterministic, defaults to 0
  129. :type temperature: float, optional
  130. :param max_tokens: Controls how many tokens are generated, defaults to 1000
  131. :type max_tokens: int, optional
  132. :param top_p: Controls the diversity of words. Higher values (closer to 1) make word selection more diverse,
  133. defaults to 1
  134. :type top_p: float, optional
  135. :param stream: Control if response is streamed back to user, defaults to False
  136. :type stream: bool, optional
  137. :param online: Controls whether to use internet for answering query, defaults to False
  138. :type online: bool, optional
  139. :param token_usage: Controls whether to return token usage in response, defaults to False
  140. :type token_usage: bool, optional
  141. :param deployment_name: t.b.a., defaults to None
  142. :type deployment_name: Optional[str], optional
  143. :param system_prompt: System prompt string, defaults to None
  144. :type system_prompt: Optional[str], optional
  145. :param where: A dictionary of key-value pairs to filter the database results., defaults to None
  146. :type where: dict[str, Any], optional
  147. :param api_key: The api key of the custom endpoint, defaults to None
  148. :type api_key: Optional[str], optional
  149. :param endpoint: The api url of the custom endpoint, defaults to None
  150. :type endpoint: Optional[str], optional
  151. :param model_kwargs: A dictionary of key-value pairs to pass to the model, defaults to None
  152. :type model_kwargs: Optional[Dict[str, Any]], optional
  153. :param callbacks: Langchain callback functions to use, defaults to None
  154. :type callbacks: Optional[list], optional
  155. :param query_type: The type of query to use, defaults to None
  156. :type query_type: Optional[str], optional
  157. :param http_client_proxies: The proxy server settings used to create self.http_client, defaults to None
  158. :type http_client_proxies: Optional[Dict | str], optional
  159. :param http_async_client_proxies: The proxy server settings for async calls used to create
  160. self.http_async_client, defaults to None
  161. :type http_async_client_proxies: Optional[Dict | str], optional
  162. :param local: If True, the model will be run locally, defaults to False (for huggingface provider)
  163. :type local: Optional[bool], optional
  164. :param default_headers: Set additional HTTP headers to be sent with requests to OpenAI
  165. :type default_headers: Optional[Mapping[str, str]], optional
  166. :raises ValueError: If the template is not valid as template should
  167. contain $context and $query (and optionally $history)
  168. :raises ValueError: Stream is not boolean
  169. """
  170. if template is not None:
  171. logger.warning(
  172. "The `template` argument is deprecated and will be removed in a future version. "
  173. + "Please use `prompt` instead."
  174. )
  175. if prompt is None:
  176. prompt = template
  177. if prompt is None:
  178. prompt = DEFAULT_PROMPT_TEMPLATE
  179. self.number_documents = number_documents
  180. self.temperature = temperature
  181. self.max_tokens = max_tokens
  182. self.model = model
  183. self.top_p = top_p
  184. self.online = online
  185. self.token_usage = token_usage
  186. self.deployment_name = deployment_name
  187. self.system_prompt = system_prompt
  188. self.query_type = query_type
  189. self.callbacks = callbacks
  190. self.api_key = api_key
  191. self.base_url = base_url
  192. self.endpoint = endpoint
  193. self.model_kwargs = model_kwargs
  194. self.http_client = httpx.Client(proxies=http_client_proxies) if http_client_proxies else None
  195. self.http_async_client = (
  196. httpx.AsyncClient(proxies=http_async_client_proxies) if http_async_client_proxies else None
  197. )
  198. self.local = local
  199. self.default_headers = default_headers
  200. self.online = online
  201. self.api_version = api_version
  202. if token_usage:
  203. f = open("model_prices_and_context_window.json")
  204. self.model_pricing_map = json.load(f)
  205. if isinstance(prompt, str):
  206. prompt = Template(prompt)
  207. if self.validate_prompt(prompt):
  208. self.prompt = prompt
  209. else:
  210. raise ValueError("The 'prompt' should have 'query' and 'context' keys and potentially 'history' (if used).")
  211. if not isinstance(stream, bool):
  212. raise ValueError("`stream` should be bool")
  213. self.stream = stream
  214. self.where = where
  215. @staticmethod
  216. def validate_prompt(prompt: Template) -> Optional[re.Match[str]]:
  217. """
  218. validate the prompt
  219. :param prompt: the prompt to validate
  220. :type prompt: Template
  221. :return: valid (true) or invalid (false)
  222. :rtype: Optional[re.Match[str]]
  223. """
  224. return re.search(query_re, prompt.template) and re.search(context_re, prompt.template)
  225. @staticmethod
  226. def _validate_prompt_history(prompt: Template) -> Optional[re.Match[str]]:
  227. """
  228. validate the prompt with history
  229. :param prompt: the prompt to validate
  230. :type prompt: Template
  231. :return: valid (true) or invalid (false)
  232. :rtype: Optional[re.Match[str]]
  233. """
  234. return re.search(history_re, prompt.template)