mirror of
https://github.com/Mintplex-Labs/langchain-python.git
synced 2026-08-27 01:15:22 -04:00
69698be3e6
re https://github.com/hwchase17/langchain/issues/439#issuecomment-1510442791 I think it's not polite for a library to use the root logger both of these forms are also used: ``` logger = logging.getLogger(__name__) logger = logging.getLogger(__file__) ``` I am not sure if there is any reason behind one vs the other? (...I am guessing maybe just contributed by different people) it seems to me it'd be better to consistently use `logging.getLogger(__name__)` this makes it easier for consumers of the library to set up log handlers, e.g. for everything with `langchain.` prefix
165 lines
5.8 KiB
Python
165 lines
5.8 KiB
Python
"""Load prompts from disk."""
|
|
import importlib
|
|
import json
|
|
import logging
|
|
from pathlib import Path
|
|
from typing import Union
|
|
|
|
import yaml
|
|
|
|
from langchain.output_parsers.regex import RegexParser
|
|
from langchain.prompts.base import BasePromptTemplate
|
|
from langchain.prompts.few_shot import FewShotPromptTemplate
|
|
from langchain.prompts.prompt import PromptTemplate
|
|
from langchain.utilities.loading import try_load_from_hub
|
|
|
|
URL_BASE = "https://raw.githubusercontent.com/hwchase17/langchain-hub/master/prompts/"
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def load_prompt_from_config(config: dict) -> BasePromptTemplate:
|
|
"""Load prompt from Config Dict."""
|
|
if "_type" not in config:
|
|
logger.warning("No `_type` key found, defaulting to `prompt`.")
|
|
config_type = config.pop("_type", "prompt")
|
|
|
|
if config_type not in type_to_loader_dict:
|
|
raise ValueError(f"Loading {config_type} prompt not supported")
|
|
|
|
prompt_loader = type_to_loader_dict[config_type]
|
|
return prompt_loader(config)
|
|
|
|
|
|
def _load_template(var_name: str, config: dict) -> dict:
|
|
"""Load template from disk if applicable."""
|
|
# Check if template_path exists in config.
|
|
if f"{var_name}_path" in config:
|
|
# If it does, make sure template variable doesn't also exist.
|
|
if var_name in config:
|
|
raise ValueError(
|
|
f"Both `{var_name}_path` and `{var_name}` cannot be provided."
|
|
)
|
|
# Pop the template path from the config.
|
|
template_path = Path(config.pop(f"{var_name}_path"))
|
|
# Load the template.
|
|
if template_path.suffix == ".txt":
|
|
with open(template_path) as f:
|
|
template = f.read()
|
|
else:
|
|
raise ValueError
|
|
# Set the template variable to the extracted variable.
|
|
config[var_name] = template
|
|
return config
|
|
|
|
|
|
def _load_examples(config: dict) -> dict:
|
|
"""Load examples if necessary."""
|
|
if isinstance(config["examples"], list):
|
|
pass
|
|
elif isinstance(config["examples"], str):
|
|
with open(config["examples"]) as f:
|
|
if config["examples"].endswith(".json"):
|
|
examples = json.load(f)
|
|
elif config["examples"].endswith((".yaml", ".yml")):
|
|
examples = yaml.safe_load(f)
|
|
else:
|
|
raise ValueError(
|
|
"Invalid file format. Only json or yaml formats are supported."
|
|
)
|
|
config["examples"] = examples
|
|
else:
|
|
raise ValueError("Invalid examples format. Only list or string are supported.")
|
|
return config
|
|
|
|
|
|
def _load_output_parser(config: dict) -> dict:
|
|
"""Load output parser."""
|
|
if "output_parsers" in config:
|
|
if config["output_parsers"] is not None:
|
|
_config = config["output_parsers"]
|
|
output_parser_type = _config["_type"]
|
|
if output_parser_type == "regex_parser":
|
|
output_parser = RegexParser(**_config)
|
|
else:
|
|
raise ValueError(f"Unsupported output parser {output_parser_type}")
|
|
config["output_parsers"] = output_parser
|
|
return config
|
|
|
|
|
|
def _load_few_shot_prompt(config: dict) -> FewShotPromptTemplate:
|
|
"""Load the few shot prompt from the config."""
|
|
# Load the suffix and prefix templates.
|
|
config = _load_template("suffix", config)
|
|
config = _load_template("prefix", config)
|
|
# Load the example prompt.
|
|
if "example_prompt_path" in config:
|
|
if "example_prompt" in config:
|
|
raise ValueError(
|
|
"Only one of example_prompt and example_prompt_path should "
|
|
"be specified."
|
|
)
|
|
config["example_prompt"] = load_prompt(config.pop("example_prompt_path"))
|
|
else:
|
|
config["example_prompt"] = load_prompt_from_config(config["example_prompt"])
|
|
# Load the examples.
|
|
config = _load_examples(config)
|
|
config = _load_output_parser(config)
|
|
return FewShotPromptTemplate(**config)
|
|
|
|
|
|
def _load_prompt(config: dict) -> PromptTemplate:
|
|
"""Load the prompt template from config."""
|
|
# Load the template from disk if necessary.
|
|
config = _load_template("template", config)
|
|
config = _load_output_parser(config)
|
|
return PromptTemplate(**config)
|
|
|
|
|
|
def load_prompt(path: Union[str, Path]) -> BasePromptTemplate:
|
|
"""Unified method for loading a prompt from LangChainHub or local fs."""
|
|
if hub_result := try_load_from_hub(
|
|
path, _load_prompt_from_file, "prompts", {"py", "json", "yaml"}
|
|
):
|
|
return hub_result
|
|
else:
|
|
return _load_prompt_from_file(path)
|
|
|
|
|
|
def _load_prompt_from_file(file: Union[str, Path]) -> BasePromptTemplate:
|
|
"""Load prompt from file."""
|
|
# Convert file to Path object.
|
|
if isinstance(file, str):
|
|
file_path = Path(file)
|
|
else:
|
|
file_path = file
|
|
# Load from either json or yaml.
|
|
if file_path.suffix == ".json":
|
|
with open(file_path) as f:
|
|
config = json.load(f)
|
|
elif file_path.suffix == ".yaml":
|
|
with open(file_path, "r") as f:
|
|
config = yaml.safe_load(f)
|
|
elif file_path.suffix == ".py":
|
|
spec = importlib.util.spec_from_loader(
|
|
"prompt", loader=None, origin=str(file_path)
|
|
)
|
|
if spec is None:
|
|
raise ValueError("could not load spec")
|
|
helper = importlib.util.module_from_spec(spec)
|
|
with open(file_path, "rb") as f:
|
|
exec(f.read(), helper.__dict__)
|
|
if not isinstance(helper.PROMPT, BasePromptTemplate):
|
|
raise ValueError("Did not get object of type BasePromptTemplate.")
|
|
return helper.PROMPT
|
|
else:
|
|
raise ValueError(f"Got unsupported file type {file_path.suffix}")
|
|
# Load the prompt from the config now.
|
|
return load_prompt_from_config(config)
|
|
|
|
|
|
type_to_loader_dict = {
|
|
"prompt": _load_prompt,
|
|
"few_shot": _load_few_shot_prompt,
|
|
# "few_shot_with_templates": _load_few_shot_with_templates_prompt,
|
|
}
|