codekingpro/portable-devtools
114k
1from __future__ import annotations2 3import logging4import os5import warnings6from typing import (7 Any,8 Callable,9 Dict,10 List,11 Literal,12 Mapping,13 Optional,14 Sequence,15 Set,16 Tuple,17 Union,18 cast,19)20 21import numpy as np22from langchain_core._api.deprecation import deprecated23from langchain_core.embeddings import Embeddings24from langchain_core.utils import (25 get_from_dict_or_env,26 get_pydantic_field_names,27 pre_init,28)29from pydantic import BaseModel, ConfigDict, Field, model_validator30from tenacity import (31 AsyncRetrying,32 before_sleep_log,33 retry,34 retry_if_exception_type,35 stop_after_attempt,36 wait_exponential,37)38 39from langchain_community.utils.openai import is_openai_v140 41logger = logging.getLogger(__name__)42 43 44def _create_retry_decorator(embeddings: OpenAIEmbeddings) -> Callable[[Any], Any]:45 import openai46 47 # Wait 2^x * 1 second between each retry starting with48 # retry_min_seconds seconds, then up to retry_max_seconds seconds,49 # then retry_max_seconds seconds afterwards50 # retry_min_seconds and retry_max_seconds are optional arguments of51 # OpenAIEmbeddings52 return retry(53 reraise=True,54 stop=stop_after_attempt(embeddings.max_retries),55 wait=wait_exponential(56 multiplier=1,57 min=embeddings.retry_min_seconds,58 max=embeddings.retry_max_seconds,59 ),60 retry=(61 retry_if_exception_type(openai.error.Timeout)62 | retry_if_exception_type(openai.error.APIError)63 | retry_if_exception_type(openai.error.APIConnectionError)64 | retry_if_exception_type(openai.error.RateLimitError)65 | retry_if_exception_type(openai.error.ServiceUnavailableError)66 ),67 before_sleep=before_sleep_log(logger, logging.WARNING),68 )69 70 71def _async_retry_decorator(embeddings: OpenAIEmbeddings) -> Any:72 import openai73 74 # Wait 2^x * 1 second between each retry starting with75 # retry_min_seconds seconds, then up to retry_max_seconds seconds,76 # then retry_max_seconds seconds afterwards77 # retry_min_seconds and retry_max_seconds are optional arguments of78 # OpenAIEmbeddings79 async_retrying = AsyncRetrying(80 reraise=True,81 stop=stop_after_attempt(embeddings.max_retries),82 wait=wait_exponential(83 multiplier=1,84 min=embeddings.retry_min_seconds,85 max=embeddings.retry_max_seconds,86 ),87 retry=(88 retry_if_exception_type(openai.error.Timeout)89 | retry_if_exception_type(openai.error.APIError)90 | retry_if_exception_type(openai.error.APIConnectionError)91 | retry_if_exception_type(openai.error.RateLimitError)92 | retry_if_exception_type(openai.error.ServiceUnavailableError)93 ),94 before_sleep=before_sleep_log(logger, logging.WARNING),95 )96 97 def wrap(func: Callable) -> Callable:98 async def wrapped_f(*args: Any, **kwargs: Any) -> Callable:99 async for _ in async_retrying:100 return await func(*args, **kwargs)101 raise AssertionError("this is unreachable")102 103 return wrapped_f104 105 return wrap106 107 108# https://stackoverflow.com/questions/76469415/getting-embeddings-of-length-1-from-langchain-openaiembeddings109def _check_response(response: dict, skip_empty: bool = False) -> dict:110 if any(len(d["embedding"]) == 1 for d in response["data"]) and not skip_empty:111 import openai112 113 raise openai.error.APIError("OpenAI API returned an empty embedding")114 return response115 116 117def embed_with_retry(embeddings: OpenAIEmbeddings, **kwargs: Any) -> Any:118 """Use tenacity to retry the embedding call."""119 if is_openai_v1():120 return embeddings.client.create(**kwargs)121 retry_decorator = _create_retry_decorator(embeddings)122 123 @retry_decorator124 def _embed_with_retry(**kwargs: Any) -> Any:125 response = embeddings.client.create(**kwargs)126 return _check_response(response, skip_empty=embeddings.skip_empty)127 128 return _embed_with_retry(**kwargs)129 130 131async def async_embed_with_retry(embeddings: OpenAIEmbeddings, **kwargs: Any) -> Any:132 """Use tenacity to retry the embedding call."""133 134 if is_openai_v1():135 return await embeddings.async_client.create(**kwargs)136 137 @_async_retry_decorator(embeddings)138 async def _async_embed_with_retry(**kwargs: Any) -> Any:139 response = await embeddings.client.acreate(**kwargs)140 return _check_response(response, skip_empty=embeddings.skip_empty)141 142 return await _async_embed_with_retry(**kwargs)143 144 145@deprecated(146 since="0.0.9",147 removal="1.0",148 alternative_import="langchain_openai.OpenAIEmbeddings",149)150class OpenAIEmbeddings(BaseModel, Embeddings):151 """OpenAI embedding models.152 153 To use, you should have the ``openai`` python package installed, and the154 environment variable ``OPENAI_API_KEY`` set with your API key or pass it155 as a named parameter to the constructor.156 157 Example:158 .. code-block:: python159 160 from langchain_community.embeddings import OpenAIEmbeddings161 openai = OpenAIEmbeddings(openai_api_key="my-api-key")162 163 In order to use the library with Microsoft Azure endpoints, you need to set164 the OPENAI_API_TYPE, OPENAI_API_BASE, OPENAI_API_KEY and OPENAI_API_VERSION.165 The OPENAI_API_TYPE must be set to 'azure' and the others correspond to166 the properties of your endpoint.167 In addition, the deployment name must be passed as the model parameter.168 169 Example:170 .. code-block:: python171 172 import os173 174 os.environ["OPENAI_API_TYPE"] = "azure"175 os.environ["OPENAI_API_BASE"] = "https://<your-endpoint.openai.azure.com/"176 os.environ["OPENAI_API_KEY"] = "your AzureOpenAI key"177 os.environ["OPENAI_API_VERSION"] = "2023-05-15"178 os.environ["OPENAI_PROXY"] = "http://your-corporate-proxy:8080"179 180 from langchain_community.embeddings.openai import OpenAIEmbeddings181 embeddings = OpenAIEmbeddings(182 deployment="your-embeddings-deployment-name",183 model="your-embeddings-model-name",184 openai_api_base="https://your-endpoint.openai.azure.com/",185 openai_api_type="azure",186 )187 text = "This is a test query."188 query_result = embeddings.embed_query(text)189 190 """191 192 client: Any = Field(default=None, exclude=True) #: :meta private:193 async_client: Any = Field(default=None, exclude=True) #: :meta private:194 model: str = "text-embedding-ada-002"195 # to support Azure OpenAI Service custom deployment names196 deployment: Optional[str] = model197 # TODO: Move to AzureOpenAIEmbeddings.198 openai_api_version: Optional[str] = Field(default=None, alias="api_version")199 """Automatically inferred from env var `OPENAI_API_VERSION` if not provided."""200 # to support Azure OpenAI Service custom endpoints201 openai_api_base: Optional[str] = Field(default=None, alias="base_url")202 """Base URL path for API requests, leave blank if not using a proxy or service 203 emulator."""204 # to support Azure OpenAI Service custom endpoints205 openai_api_type: Optional[str] = None206 # to support explicit proxy for OpenAI207 openai_proxy: Optional[str] = None208 embedding_ctx_length: int = 8191209 """The maximum number of tokens to embed at once."""210 openai_api_key: Optional[str] = Field(default=None, alias="api_key")211 """Automatically inferred from env var `OPENAI_API_KEY` if not provided."""212 openai_organization: Optional[str] = Field(default=None, alias="organization")213 """Automatically inferred from env var `OPENAI_ORG_ID` if not provided."""214 allowed_special: Union[Literal["all"], Set[str]] = set()215 disallowed_special: Union[Literal["all"], Set[str], Sequence[str]] = "all"216 chunk_size: int = 1000217 """Maximum number of texts to embed in each batch"""218 max_retries: int = 2219 """Maximum number of retries to make when generating."""220 request_timeout: Optional[Union[float, Tuple[float, float], Any]] = Field(221 default=None, alias="timeout"222 )223 """Timeout for requests to OpenAI completion API. Can be float, httpx.Timeout or 224 None."""225 headers: Any = None226 tiktoken_enabled: bool = True227 """Set this to False for non-OpenAI implementations of the embeddings API, e.g.228 the `--extensions openai` extension for `text-generation-webui`"""229 tiktoken_model_name: Optional[str] = None230 """The model name to pass to tiktoken when using this class. 231 Tiktoken is used to count the number of tokens in documents to constrain 232 them to be under a certain limit. By default, when set to None, this will 233 be the same as the embedding model name. However, there are some cases 234 where you may want to use this Embedding class with a model name not 235 supported by tiktoken. This can include when using Azure embeddings or 236 when using one of the many model providers that expose an OpenAI-like 237 API but with different models. In those cases, in order to avoid erroring 238 when tiktoken is called, you can specify a model name to use here."""239 show_progress_bar: bool = False240 """Whether to show a progress bar when embedding."""241 model_kwargs: Dict[str, Any] = Field(default_factory=dict)242 """Holds any model parameters valid for `create` call not explicitly specified."""243 skip_empty: bool = False244 """Whether to skip empty strings when embedding or raise an error.245 Defaults to not skipping."""246 default_headers: Union[Mapping[str, str], None] = None247 default_query: Union[Mapping[str, object], None] = None248 # Configure a custom httpx client. See the249 # [httpx documentation](https://www.python-httpx.org/api/#client) for more details.250 retry_min_seconds: int = 4251 """Min number of seconds to wait between retries"""252 retry_max_seconds: int = 20253 """Max number of seconds to wait between retries"""254 http_client: Union[Any, None] = None255 """Optional httpx.Client."""256 257 model_config = ConfigDict(258 populate_by_name=True, extra="forbid", protected_namespaces=()259 )260 261 @model_validator(mode="before")262 @classmethod263 def build_extra(cls, values: Dict[str, Any]) -> Any:264 """Build extra kwargs from additional params that were passed in."""265 all_required_field_names = get_pydantic_field_names(cls)266 extra = values.get("model_kwargs", {})267 for field_name in list(values):268 if field_name in extra:269 raise ValueError(f"Found {field_name} supplied twice.")270 if field_name not in all_required_field_names:271 warnings.warn(272 f"""WARNING! {field_name} is not default parameter.273 {field_name} was transferred to model_kwargs.274 Please confirm that {field_name} is what you intended."""275 )276 extra[field_name] = values.pop(field_name)277 278 invalid_model_kwargs = all_required_field_names.intersection(extra.keys())279 if invalid_model_kwargs:280 raise ValueError(281 f"Parameters {invalid_model_kwargs} should be specified explicitly. "282 f"Instead they were passed in as part of `model_kwargs` parameter."283 )284 285 values["model_kwargs"] = extra286 return values287 288 @pre_init289 def validate_environment(cls, values: Dict) -> Dict:290 """Validate that api key and python package exists in environment."""291 values["openai_api_key"] = get_from_dict_or_env(292 values, "openai_api_key", "OPENAI_API_KEY"293 )294 values["openai_api_base"] = values["openai_api_base"] or os.getenv(295 "OPENAI_API_BASE"296 )297 values["openai_api_type"] = get_from_dict_or_env(298 values,299 "openai_api_type",300 "OPENAI_API_TYPE",301 default="",302 )303 values["openai_proxy"] = get_from_dict_or_env(304 values,305 "openai_proxy",306 "OPENAI_PROXY",307 default="",308 )309 if values["openai_api_type"] in ("azure", "azure_ad", "azuread"):310 default_api_version = "2023-05-15"311 # Azure OpenAI embedding models allow a maximum of 2048312 # texts at a time in each batch313 # See: https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#embeddings314 values["chunk_size"] = min(values["chunk_size"], 2048)315 else:316 default_api_version = ""317 values["openai_api_version"] = get_from_dict_or_env(318 values,319 "openai_api_version",320 "OPENAI_API_VERSION",321 default=default_api_version,322 )323 # Check OPENAI_ORGANIZATION for backwards compatibility.324 values["openai_organization"] = (325 values["openai_organization"]326 or os.getenv("OPENAI_ORG_ID")327 or os.getenv("OPENAI_ORGANIZATION")328 )329 try:330 import openai331 except ImportError:332 raise ImportError(333 "Could not import openai python package. "334 "Please install it with `pip install openai`."335 )336 else:337 if is_openai_v1():338 if values["openai_api_type"] in ("azure", "azure_ad", "azuread"):339 warnings.warn(340 "If you have openai>=1.0.0 installed and are using Azure, "341 "please use the `AzureOpenAIEmbeddings` class."342 )343 client_params = {344 "api_key": values["openai_api_key"],345 "organization": values["openai_organization"],346 "base_url": values["openai_api_base"],347 "timeout": values["request_timeout"],348 "max_retries": values["max_retries"],349 "default_headers": values["default_headers"],350 "default_query": values["default_query"],351 "http_client": values["http_client"],352 }353 if not values.get("client"):354 values["client"] = openai.OpenAI(**client_params).embeddings355 if not values.get("async_client"):356 values["async_client"] = openai.AsyncOpenAI(357 **client_params358 ).embeddings359 elif not values.get("client"):360 values["client"] = openai.Embedding361 else:362 pass363 return values364 365 @property366 def _invocation_params(self) -> Dict[str, Any]:367 if is_openai_v1():368 openai_args: Dict = {"model": self.model, **self.model_kwargs}369 else:370 openai_args = {371 "model": self.model,372 "request_timeout": self.request_timeout,373 "headers": self.headers,374 "api_key": self.openai_api_key,375 "organization": self.openai_organization,376 "api_base": self.openai_api_base,377 "api_type": self.openai_api_type,378 "api_version": self.openai_api_version,379 **self.model_kwargs,380 }381 if self.openai_api_type in ("azure", "azure_ad", "azuread"):382 openai_args["engine"] = self.deployment383 # TODO: Look into proxy with openai v1.384 if self.openai_proxy:385 try:386 import openai387 except ImportError:388 raise ImportError(389 "Could not import openai python package. "390 "Please install it with `pip install openai`."391 )392 393 openai.proxy = {394 "http": self.openai_proxy,395 "https": self.openai_proxy,396 }397 return openai_args398 399 # please refer to400 # https://github.com/openai/openai-cookbook/blob/main/examples/Embedding_long_inputs.ipynb401 def _get_len_safe_embeddings(402 self, texts: List[str], *, engine: str, chunk_size: Optional[int] = None403 ) -> List[List[float]]:404 """405 Generate length-safe embeddings for a list of texts.406 407 This method handles tokenization and embedding generation, respecting the408 set embedding context length and chunk size. It supports both tiktoken409 and HuggingFace tokenizer based on the tiktoken_enabled flag.410 411 Args:412 texts (List[str]): A list of texts to embed.413 engine (str): The engine or model to use for embeddings.414 chunk_size (Optional[int]): The size of chunks for processing embeddings.415 416 Returns:417 List[List[float]]: A list of embeddings for each input text.418 """419 420 tokens = []421 indices = []422 model_name = self.tiktoken_model_name or self.model423 _chunk_size = chunk_size or self.chunk_size424 425 # If tiktoken flag set to False426 if not self.tiktoken_enabled:427 try:428 from transformers import AutoTokenizer429 except ImportError:430 raise ImportError(431 "Could not import transformers python package. "432 "This is needed in order to for OpenAIEmbeddings without "433 "`tiktoken`. Please install it with `pip install transformers`. "434 )435 436 tokenizer = AutoTokenizer.from_pretrained(437 pretrained_model_name_or_path=model_name438 )439 for i, text in enumerate(texts):440 # Tokenize the text using HuggingFace transformers441 tokenized = tokenizer.encode(text, add_special_tokens=False)442 443 # Split tokens into chunks respecting the embedding_ctx_length444 for j in range(0, len(tokenized), self.embedding_ctx_length):445 token_chunk = tokenized[j : j + self.embedding_ctx_length]446 447 # Convert token IDs back to a string448 chunk_text = tokenizer.decode(token_chunk)449 tokens.append(chunk_text)450 indices.append(i)451 else:452 try:453 import tiktoken454 except ImportError:455 raise ImportError(456 "Could not import tiktoken python package. "457 "This is needed in order to for OpenAIEmbeddings. "458 "Please install it with `pip install tiktoken`."459 )460 461 try:462 encoding = tiktoken.encoding_for_model(model_name)463 except KeyError:464 logger.warning("Warning: model not found. Using cl100k_base encoding.")465 model = "cl100k_base"466 encoding = tiktoken.get_encoding(model)467 for i, text in enumerate(texts):468 if self.model.endswith("001"):469 # See: https://github.com/openai/openai-python/470 # issues/418#issuecomment-1525939500471 # replace newlines, which can negatively affect performance.472 text = text.replace("\n", " ")473 474 token = encoding.encode(475 text=text,476 allowed_special=self.allowed_special,477 disallowed_special=self.disallowed_special,478 )479 480 # Split tokens into chunks respecting the embedding_ctx_length481 for j in range(0, len(token), self.embedding_ctx_length):482 tokens.append(token[j : j + self.embedding_ctx_length])483 indices.append(i)484 485 if self.show_progress_bar:486 try:487 from tqdm.auto import tqdm488 489 _iter = tqdm(range(0, len(tokens), _chunk_size))490 except ImportError:491 _iter = range(0, len(tokens), _chunk_size)492 else:493 _iter = range(0, len(tokens), _chunk_size)494 495 batched_embeddings: List[List[float]] = []496 for i in _iter:497 response = embed_with_retry(498 self,499 input=tokens[i : i + _chunk_size],500 **self._invocation_params,501 )502 if not isinstance(response, dict):503 response = response.dict()504 batched_embeddings.extend(r["embedding"] for r in response["data"])505 506 results: List[List[List[float]]] = [[] for _ in range(len(texts))]507 num_tokens_in_batch: List[List[int]] = [[] for _ in range(len(texts))]508 for i in range(len(indices)):509 if self.skip_empty and len(batched_embeddings[i]) == 1:510 continue511 results[indices[i]].append(batched_embeddings[i])512 num_tokens_in_batch[indices[i]].append(len(tokens[i]))513 514 embeddings: List[List[float]] = [[] for _ in range(len(texts))]515 for i in range(len(texts)):516 _result = results[i]517 if len(_result) == 0:518 average_embedded = embed_with_retry(519 self,520 input="",521 **self._invocation_params,522 )523 if not isinstance(average_embedded, dict):524 average_embedded = average_embedded.dict()525 average = average_embedded["data"][0]["embedding"]526 else:527 average = np.average(_result, axis=0, weights=num_tokens_in_batch[i])528 embeddings[i] = (average / np.linalg.norm(average)).tolist()529 530 return embeddings531 532 # please refer to533 # https://github.com/openai/openai-cookbook/blob/main/examples/Embedding_long_inputs.ipynb534 async def _aget_len_safe_embeddings(535 self, texts: List[str], *, engine: str, chunk_size: Optional[int] = None536 ) -> List[List[float]]:537 """538 Asynchronously generate length-safe embeddings for a list of texts.539 540 This method handles tokenization and asynchronous embedding generation,541 respecting the set embedding context length and chunk size. It supports both542 `tiktoken` and HuggingFace `tokenizer` based on the tiktoken_enabled flag.543 544 Args:545 texts (List[str]): A list of texts to embed.546 engine (str): The engine or model to use for embeddings.547 chunk_size (Optional[int]): The size of chunks for processing embeddings.548 549 Returns:550 List[List[float]]: A list of embeddings for each input text.551 """552 553 tokens = []554 indices = []555 model_name = self.tiktoken_model_name or self.model556 _chunk_size = chunk_size or self.chunk_size557 558 # If tiktoken flag set to False559 if not self.tiktoken_enabled:560 try:561 from transformers import AutoTokenizer562 except ImportError:563 raise ImportError(564 "Could not import transformers python package. "565 "This is needed in order to for OpenAIEmbeddings without "566 " `tiktoken`. Please install it with `pip install transformers`."567 )568 569 tokenizer = AutoTokenizer.from_pretrained(570 pretrained_model_name_or_path=model_name571 )572 for i, text in enumerate(texts):573 # Tokenize the text using HuggingFace transformers574 tokenized = tokenizer.encode(text, add_special_tokens=False)575 576 # Split tokens into chunks respecting the embedding_ctx_length577 for j in range(0, len(tokenized), self.embedding_ctx_length):578 token_chunk = tokenized[j : j + self.embedding_ctx_length]579 580 # Convert token IDs back to a string581 chunk_text = tokenizer.decode(token_chunk)582 tokens.append(chunk_text)583 indices.append(i)584 else:585 try:586 import tiktoken587 except ImportError:588 raise ImportError(589 "Could not import tiktoken python package. "590 "This is needed in order to for OpenAIEmbeddings. "591 "Please install it with `pip install tiktoken`."592 )593 594 try:595 encoding = tiktoken.encoding_for_model(model_name)596 except KeyError:597 logger.warning("Warning: model not found. Using cl100k_base encoding.")598 model = "cl100k_base"599 encoding = tiktoken.get_encoding(model)600 for i, text in enumerate(texts):601 if self.model.endswith("001"):602 # See: https://github.com/openai/openai-python/603 # issues/418#issuecomment-1525939500604 # replace newlines, which can negatively affect performance.605 text = text.replace("\n", " ")606 607 token = encoding.encode(608 text=text,609 allowed_special=self.allowed_special,610 disallowed_special=self.disallowed_special,611 )612 613 # Split tokens into chunks respecting the embedding_ctx_length614 for j in range(0, len(token), self.embedding_ctx_length):615 tokens.append(token[j : j + self.embedding_ctx_length])616 indices.append(i)617 618 batched_embeddings: List[List[float]] = []619 _chunk_size = chunk_size or self.chunk_size620 for i in range(0, len(tokens), _chunk_size):621 response = await async_embed_with_retry(622 self,623 input=tokens[i : i + _chunk_size],624 **self._invocation_params,625 )626 627 if not isinstance(response, dict):628 response = response.dict()629 batched_embeddings.extend(r["embedding"] for r in response["data"])630 631 results: List[List[List[float]]] = [[] for _ in range(len(texts))]632 num_tokens_in_batch: List[List[int]] = [[] for _ in range(len(texts))]633 for i in range(len(indices)):634 results[indices[i]].append(batched_embeddings[i])635 num_tokens_in_batch[indices[i]].append(len(tokens[i]))636 637 embeddings: List[List[float]] = [[] for _ in range(len(texts))]638 for i in range(len(texts)):639 _result = results[i]640 if len(_result) == 0:641 average_embedded = await async_embed_with_retry(642 self,643 input="",644 **self._invocation_params,645 )646 if not isinstance(average_embedded, dict):647 average_embedded = average_embedded.dict()648 average = average_embedded["data"][0]["embedding"]649 else:650 average = np.average(_result, axis=0, weights=num_tokens_in_batch[i])651 embeddings[i] = (average / np.linalg.norm(average)).tolist()652 653 return embeddings654 655 def embed_documents(656 self, texts: List[str], chunk_size: Optional[int] = 0657 ) -> List[List[float]]:658 """Call out to OpenAI's embedding endpoint for embedding search docs.659 660 Args:661 texts: The list of texts to embed.662 chunk_size: The chunk size of embeddings. If None, will use the chunk size663 specified by the class.664 665 Returns:666 List of embeddings, one for each text.667 """668 # NOTE: to keep things simple, we assume the list may contain texts longer669 # than the maximum context and use length-safe embedding function.670 engine = cast(str, self.deployment)671 return self._get_len_safe_embeddings(672 texts, engine=engine, chunk_size=chunk_size673 )674 675 async def aembed_documents(676 self, texts: List[str], chunk_size: Optional[int] = 0677 ) -> List[List[float]]:678 """Call out to OpenAI's embedding endpoint async for embedding search docs.679 680 Args:681 texts: The list of texts to embed.682 chunk_size: The chunk size of embeddings. If None, will use the chunk size683 specified by the class.684 685 Returns:686 List of embeddings, one for each text.687 """688 # NOTE: to keep things simple, we assume the list may contain texts longer689 # than the maximum context and use length-safe embedding function.690 engine = cast(str, self.deployment)691 return self._get_len_safe_embeddings(692 texts, engine=engine, chunk_size=chunk_size693 )694 695 def embed_query(self, text: str) -> List[float]:696 """Call out to OpenAI's embedding endpoint for embedding query text.697 698 Args:699 text: The text to embed.700 701 Returns:702 Embedding for the text.703 """704 return self.embed_documents([text])[0]705 706 async def aembed_query(self, text: str) -> List[float]:707 """Call out to OpenAI's embedding endpoint async for embedding query text.708 709 Args:710 text: The text to embed.711 712 Returns:713 Embedding for the text.714 """715 embeddings = await self.aembed_documents([text])716 return embeddings[0]717 