-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathprompt_utils.py
More file actions
156 lines (126 loc) · 4.92 KB
/
prompt_utils.py
File metadata and controls
156 lines (126 loc) · 4.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import copy
import logging
import re
from typing import Any, Dict, List, Optional, Sequence, TypeVar
from .prompts.requests.prompt_request_template import (
PromptRequestTemplateParams,
)
from .requests.chat_message import ChatMessageParams
logger = logging.getLogger(__name__)
class PromptVariablesNotFoundError(ValueError):
"""Raised when inputs do not satisfy prompt variables."""
missing_variables: List[str]
"""Missing variables"""
def __init__(self, missing_variables: List[str]) -> None:
self.missing_variables = missing_variables
super().__init__(f"Prompt requires inputs for the following " f"variables: {self.missing_variables}")
def populate_prompt_template(
template: str,
inputs: Optional[Dict[str, Any]],
) -> str:
"""Interpolate a string template with kwargs, where template variables
are specified using double curly bracket syntax: {{variable}}.
args:
template: str - string template where template variables are specified
using double curly bracket syntax: {{variable}}.
inputs - represent the key, value string pairs to inject into the template
variables, where key corresponds to the template variable name and
value to the variable value to inject
return:
The interpolated template string
raises:
PromptVariablesNotFoundError - if any variables are missing from inputs
"""
template_variables: List[str] = re.findall(
# Matching variables: `{{ variable_2 }}`
r"{{\s?([a-zA-Z_\d\.\[\]]+)\s?}}",
template,
) + re.findall(
# Matching tools: `{{ tool_2("all characters$#@$!") }}`
# https://regexr.com/7nvrf
r"\{\{\s?([a-zA-Z_\-\d]+\([a-zA-Z_\-\d,\s\"]+\))\s?\}\}",
template,
)
# populate the template variables, tracking if any are missing
prompt = template
missing_vars = []
if inputs is None:
inputs = {}
# e.g. var: input_name, sig(input_name), sig(other_name), sig("string")
for var in template_variables:
text: Optional[str] = None
if var in inputs:
text = inputs[var]
if text is None:
missing_vars.append(var)
else:
if not isinstance(text, str):
logger.info(f"Converting input value for variable '{var}' to string for prompt template: " f"{text}")
text = str(text)
replacement = sanitize_prompt(prompt=text) if text else text
prompt = re.sub(
r"{{\s?" + re.escape(var) + r"\s?}}",
replacement,
prompt,
)
if missing_vars:
missing_vars.sort()
raise PromptVariablesNotFoundError(
missing_variables=missing_vars,
)
return prompt
def sanitize_prompt(prompt: str):
return prompt.replace("\\", "\\\\")
def populate_chat_template(
chat_template: Sequence[ChatMessageParams],
inputs: Optional[Dict[str, str]] = None,
) -> List[ChatMessageParams]:
"""Interpolate a chat template with kwargs, where template variables."""
messages = []
message: ChatMessageParams
for message in chat_template:
if "content" not in message:
messages.append(message)
continue
message_content = copy.deepcopy(message["content"])
if isinstance(message_content, str):
message_content = populate_prompt_template(
template=message_content,
inputs=inputs,
)
elif isinstance(message_content, list):
for j, content_item in enumerate(message_content):
if content_item["type"] == "text":
content_item_text = content_item["text"]
(content_item_text,) = populate_prompt_template(
template=content_item_text,
inputs=inputs,
)
content_item["text"] = content_item_text
messages.append(
ChatMessageParams(
role=message["role"],
content=message_content,
)
)
return messages
T = TypeVar("T", bound=PromptRequestTemplateParams)
def populate_template(template: T, inputs: Dict[str, str]) -> T:
"""Populate a Prompt's template with the given inputs.
Humanloop supports insertion of variables of the form `{{variable}}` in
Prompt templates.
E.g. If you provide the template `Hello {{name}}` and the input
`{"name": "Alice"}`, the populated template will be `Hello Alice`.
This function supports both completion and chat models. For completion
models, provide template as a string. For chat models, provide template
as a list of messages.
"""
if isinstance(template, str):
return populate_prompt_template(
template=template,
inputs=inputs,
)
return populate_chat_template(
chat_template=template,
inputs=inputs,
)