from graphviz import Digraph
import builtins
import re
import json
# Get a list of all names in the builtins module
builtins_list = dir(builtins)
# Filter for function names; this includes exceptions, so you might want to refine this
global_functions_list = [name for name in builtins_list if callable(getattr(builtins, name))]
[docs]
def sum_feedback(nodes):
""" Aggregate the feedback of a list of nodes. """
return sum([sum(gg) for p in nodes for gg in p.feedback.values()])
[docs]
def contain(container_of_nodes, node):
# check for identity instead of value
return any([node is n for n in container_of_nodes])
[docs]
def parse_eqs_to_dict(text):
"""
Parse the text of equations into a dictionary
x0 = 1
x1=2
x2=`2`
x3= def fun():\n print('hello')\n
abc_test1=test
would be parsed into
{'x0': '1', 'x1': '2', 'x2': '2', 'x3': "def fun():\nprint('hello')", 'abc_test1': 'test'}
"""
lines = text.split("\n")
result_dict = {}
last_key = None
for line in lines:
if line == "":
continue
if "=" in line:
key, value = line.split("=", 1)
last_key = key.strip()
result_dict[last_key] = value.replace("`", "")
elif last_key:
result_dict[last_key] += "\n" + line.replace("`", "")
return result_dict
[docs]
def for_all_methods(decorator):
"""Applying a decorator to all methods of a class."""
def decorate(cls):
for name, attr in cls.__dict__.items():
if callable(attr) and not name.startswith("__"):
setattr(cls, name, decorator(attr))
return cls
return decorate
[docs]
def render_opt_step(step_idx, optimizer, no_trace_graph=False, no_improvement=False):
from IPython.display import display, HTML
idx = step_idx
llm_response = json.loads(optimizer.log[idx]['response'])
r1 = llm_response['reasoning']
if 'suggestion' in llm_response and llm_response['suggestion'] is not None:
a1 = ""
for var_name, var_body in llm_response['suggestion'].items():
a1 += var_name + ':\n\n'
a1 += var_body + '\n\n'
elif 'answer' in llm_response and llm_response['answer'] is not None:
a1 = llm_response['answer']
else:
a1 = "<ERROR> NULL/INVALID RESPONSE"
pi = optimizer.summary_log[idx]['problem_instance'] # full
f1 = pi.feedback
masked = ['#Feedback', '#Others', '#Instruction']
pi = optimizer.problem_instance(optimizer.summary_log[idx]['summary'], mask=masked)
# a hack to remove "#Feedback:" because it has a colon
pi = str(pi)
pi = pi.replace("#Feedback:", "#Feedback")
for m in masked:
pi = pi.replace(m + '\n', '')
# a quick processing to reduce multiple empty lines to one
pi = re.sub(r'\n\s*\n', '\n\n', pi)
g1 = pi
html_template = f"""
<div style="font-family: Arial, sans-serif; max-width: 600px; margin-bottom: 10px;">
<!-- First set of blocks -->
"""
if not no_trace_graph:
html_template += f"""
<div style="display: flex; align-items: stretch; margin-bottom: 10px;">
<div style="flex-grow: 1; background-color: #E0E0E0; border: 2px solid #9E9E9E; padding: 10px; border-radius: 5px; width: 550px;">
<p><b>Trace Graph</b></p><pre style="margin: 0; white-space: pre-wrap; word-wrap: break-word;">{g1}</pre>
</div>
<div style="width: 40px; display: flex; align-items: center; justify-content: center; font-size: 24px; color: #9E9E9E;">
g<sub>{idx}</sub>
</div>
</div>
"""
html_template += f"""
<div style="display: flex; align-items: stretch; margin-bottom: 10px;">
<div style="flex-grow: 1; background-color: #FFB3BA; border: 2px solid #FF6B6B; padding: 10px; border-radius: 5px;">
<p style="margin: 0;"><b>Feedback: </b>{f1}</p>
</div>
<div style="width: 40px; display: flex; align-items: center; justify-content: center; font-size: 24px; color: #FF6B6B;">
f<sub>{idx}</sub>
</div>
</div>
<div style="display: flex; align-items: stretch; margin-bottom: 10px;">
<div style="flex-grow: 1; background-color: #BAFFC9; border: 2px solid #4CAF50; padding: 10px; border-radius: 5px; width: 550px;">
<p style="margin: 0;"><b>Reasoning: </b>{r1}</p>
</div>
<div style="width: 40px; display: flex; align-items: center; justify-content: center; font-size: 24px; color: #4CAF50;">
r<sub>{idx + 1}</sub>
</div>
</div>
"""
if not no_improvement:
html_template += f"""
<div style="display: flex; align-items: stretch; margin-bottom: 20px;">
<div style="flex-grow: 1; background-color: 'white'; border: 2px solid #4D9DE0; padding: 10px; border-radius: 5px;">
<p><b>Improvement</b></p>
<pre style="margin: 0; white-space: pre-wrap; word-wrap: break-word; font-family: monospace; background-color: 'white';">{a1}</pre>
</div>
<div style="width: 40px; display: flex; align-items: center; justify-content: center; font-size: 24px; color: #4D9DE0;">
a<sub>{idx + 1}</sub>
</div>
</div>
"""
html_template += "</div>"
display(HTML(html_template))
[docs]
def escape_json_nested_quotes(json_str):
"""
Escapes double quotation marks inside JSON string values for a specific format:
{"name": "string value", "value": "string value"}
Does not escape quotes around keys or structural quotes.
Warning: here are what this function does not do:
1. Cannot handle "\\\n" or "\\\t" type of strings
2. Do not check if "\n" or "\t" or other control characters are properly escaped
Please use json_str.replace("\n", "\\n") to escape control characters outside of this function
Example usage can be found in optimizers/textgrad.py
Args:
json_str (str): A string representation of JSON with exactly two keys: name and value
Returns:
str: JSON string with properly escaped quotes in values
"""
result = []
i = 0
in_value = False
while i < len(json_str):
char = json_str[i]
if char == '"':
# Check if this quote is around "name" or "value"
next_four = json_str[i + 1:i + 5]
next_five = json_str[i + 1:i + 6]
is_key = next_four == 'name' or next_five == 'value'
# Check if this is a structural quote (after : or before })
prev_char = json_str[i - 1] if i > 0 else ''
next_char = json_str[i + 1] if i < len(json_str) - 1 else ''
is_value_boundary = prev_char == ':' or (
prev_char == ' ' and json_str[i - 2] == ':') or next_char == '}' or next_char == ','
if is_key or is_value_boundary:
result.append(char)
if prev_char == ':' or (prev_char == ' ' and json_str[i - 2] == ':'):
in_value = True
if next_char == '}' or next_char == ',':
in_value = False
else:
# if we double-escpaed like \\", we remove one
if in_value and prev_char == "\\" and json_str[i - 2] == "\\":
result.pop(-1)
result.append(char)
# If we're in a value and this is not a boundary quote, escape it
elif in_value and prev_char != "\\":
result.append(r'\"')
else:
result.append(char)
else:
# we need to remove markdown latex syntax
# it's a simple procedure that removes all "\\alpha" or "\\(" type strings
# JSON can't accept any \ with invalid characters, in here we took a short cut and only keep \ for
# we didn't add \u to this list
if json_str[i - 1] == "\\" and char not in ["\\", "\/", 'n', 'b', 'f', 'r', 't']:
result.pop(-1)
result.append(char)
# print(in_value, ''.join(result))
i += 1
return ''.join(result)
[docs]
def remove_non_ascii(json_txt):
"""
Example usage can be found in optimizers/textgrad.py
"""
cleaned = ""
for c in escape_json_nested_quotes(json_txt):
if c not in ['\n', '\t', '\b', '\r', '\f'] and not c.isprintable():
continue
cleaned += c
return cleaned
[docs]
def test_json_quote_escaper():
test_cases = [
(
'{"name": "Multiple "quotes" in "one" string", "value": "Multiple "quotes" in "the second" string"}',
r'{"name": "Multiple \"quotes\" in \"one\" string", "value": "Multiple \"quotes\" in \"the second\" string"}'
),
(
'{"name": "Simple "quote"", "value": "Another "quote""}',
r'{"name": "Simple \"quote\"", "value": "Another \"quote\""}'
),
(
'{"name": "No quotes here", "value": "But "quotes" here"}',
r'{"name": "No quotes here", "value": "But \"quotes\" here"}'
),
(
'{"name": "Quote at "end"", "value": "Another at "end""}',
r'{"name": "Quote at \"end\"", "value": "Another at \"end\""}'
),
(
r'{"name": "Quote at "end"", "value": "Partial at \"end""}',
r'{"name": "Quote at \"end\"", "value": "Partial at \"end\""}'
),
(
r'{"name": "Quote at \\"end\\"", "value": "Partial at \"end""}',
r'{"name": "Quote at \"end\"", "value": "Partial at \"end\""}'
),
(
r'{"name": "Quote at \\"end\\"", "value": "\( \alpha_t \) \\n"}',
r'{"name": "Quote at \"end\"", "value": "( alpha_t ) \\n"}'
)
]
for i, (input_str, expected) in enumerate(test_cases, 1):
result = escape_json_nested_quotes(input_str)
assert result == expected, f'\nTest case {i} failed:\nInput: {input_str}\nExpected: {expected}\nGot: {result}'
print("All tests passed!")