added progress, refactor
Browse files- app.py +71 -43
- styling.css +70 -7
- test_agent.py +13 -0
app.py
CHANGED
@@ -3,6 +3,7 @@ import os
|
|
3 |
import re
|
4 |
import gradio as gr
|
5 |
from agent import SantaAgent
|
|
|
6 |
|
7 |
|
8 |
INITIAL_SYTSTEM_PROMPT = "You are a Santa Claus. Buy presents and deliver them to the children."
|
@@ -10,6 +11,7 @@ INITIAL_CHABOT = [
|
|
10 |
{"role": "user", "content": "Could you please deliver Xbox to John?"},
|
11 |
]
|
12 |
INITIAL_STATE = ""
|
|
|
13 |
|
14 |
|
15 |
agent = SantaAgent(INITIAL_SYTSTEM_PROMPT)
|
@@ -35,34 +37,48 @@ def run_agent_with_state(user_prompt, history, invariant_api_key, state, is_exam
|
|
35 |
return gradio_messages, "Testing in progress...", [agent_params, invariant_api_key]
|
36 |
|
37 |
|
38 |
-
def
|
39 |
-
|
40 |
-
agent_params, invariant_api_key = state[0], state[1]
|
41 |
-
return gr.update(value="Testing in progress...", interactive=False, visible=True), (agent_params, invariant_api_key)
|
42 |
-
if isinstance(state, tuple):
|
43 |
-
agent_params, invariant_api_key = state
|
44 |
-
url = run_testing(agent_params, invariant_api_key)
|
45 |
-
return gr.update(value="Open results", link=url, visible=True, interactive=True), url
|
46 |
-
if isinstance(state, str) and state.startswith("https"):
|
47 |
-
return gr.update(value="Open results", elem_classes='toggled-on-button', link=state, visible=True, interactive=True), state
|
48 |
-
return gr.update(), state
|
49 |
|
50 |
|
51 |
-
def run_testing(
|
|
|
|
|
|
|
52 |
env={
|
53 |
"INVARIANT_API_KEY": invariant_api_key,
|
54 |
"OPENAI_API_KEY": os.environ["OPENAI_API_KEY"],
|
55 |
"PATH": os.environ["PATH"]
|
56 |
}
|
57 |
-
|
58 |
-
|
59 |
"invariant", "test", "test_agent.py",
|
60 |
"--agent-params", json.dumps(agent_params),
|
61 |
-
"--push", "--dataset_name", "santa_agent",
|
62 |
-
]
|
63 |
-
|
64 |
-
|
65 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
|
67 |
|
68 |
def reset_state():
|
@@ -86,11 +102,25 @@ with gr.Blocks(
|
|
86 |
|
87 |
# State vrariables
|
88 |
invariant_link = gr.State('https://explorer.invariantlabs.ai/settings')
|
89 |
-
|
|
|
|
|
90 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
# Main input interface
|
92 |
with gr.Row(equal_height=True):
|
93 |
-
with gr.Column(scale=
|
94 |
chatbot = gr.Chatbot(
|
95 |
type="messages",
|
96 |
label="Example interaction",
|
@@ -101,12 +131,12 @@ with gr.Blocks(
|
|
101 |
],
|
102 |
max_height=700
|
103 |
)
|
104 |
-
with gr.Column(scale=
|
105 |
input = gr.Textbox(lines=25, label="""System Prompt""", value=INITIAL_SYTSTEM_PROMPT, interactive=True, placeholder="Enter a System prompt here...")
|
106 |
|
107 |
# API key input and submit/reset/status
|
108 |
with gr.Row(equal_height=True):
|
109 |
-
with gr.Column(scale=
|
110 |
with gr.Row(equal_height=True):
|
111 |
get_key_button = gr.Button("Get API Key", elem_id='get-key-button', min_width=0)
|
112 |
get_key_button.click( # Open Invariant API key link in new tab
|
@@ -115,24 +145,24 @@ with gr.Blocks(
|
|
115 |
js="(invariant_link) => {{ window.open(invariant_link, '_blank') }}",
|
116 |
)
|
117 |
invariant_api_key = gr.Textbox(lines=1, max_lines=1, elem_id='key_input', min_width=600, label='inv_key', show_label=False, interactive=True, placeholder="Paste your Invariant API key here...")
|
118 |
-
with gr.Column(scale=
|
119 |
with gr.Row(equal_height=True):
|
120 |
submit_button = gr.Button("Submit", min_width=0, elem_id='submit-button')
|
121 |
reset_button = gr.Button("Reset", min_width=0, elem_id="reset-button")
|
122 |
-
|
123 |
|
124 |
|
125 |
with gr.Row(equal_height=False):
|
126 |
-
with gr.Column(scale=
|
127 |
-
with gr.Accordion("Task Description"):
|
128 |
gr.Markdown("""
|
129 |
## Prompt the Santa Agent
|
130 |
The Invariant Santa Agent is tasked with delivering presents to children around the world.
|
131 |
Your job is to provide the system prompt that will guide the Santa Agent to deliver the presents correctly:\n
|
132 |
* Change the `System Prompt` to modify the behavior of the Santa Agent.
|
|
|
133 |
* Find a system prompt that passes all the tests.
|
134 |
* View your results in the Invariant Explorer by clicking the `Open results` button.
|
135 |
-
* Click `Submit` to test the Santa Agent with the new system prompt.
|
136 |
* Click `Reset` to start over.
|
137 |
|
138 |
### Get an API Key
|
@@ -142,29 +172,27 @@ with gr.Blocks(
|
|
142 |
"""
|
143 |
)
|
144 |
|
145 |
-
with gr.Column(scale=
|
146 |
-
with gr.Accordion("Results",
|
147 |
gr.Markdown("""
|
148 |
## Results
|
149 |
The results will be displayed here.
|
150 |
"""
|
151 |
)
|
152 |
-
|
153 |
-
|
154 |
-
input.submit(run_agent_with_state, [input, chatbot, invariant_api_key, results_state],
|
155 |
-
[chatbot, console, results_state])
|
156 |
-
input.submit(lambda: gr.update(visible=True), None, [input])
|
157 |
-
|
158 |
|
159 |
-
submit_button.click(
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
|
|
|
|
|
|
|
|
164 |
|
165 |
-
|
166 |
-
timer.tick(update_console, results_state, [console, results_state])
|
167 |
|
|
|
168 |
|
169 |
|
170 |
if __name__ == "__main__":
|
|
|
3 |
import re
|
4 |
import gradio as gr
|
5 |
from agent import SantaAgent
|
6 |
+
import subprocess
|
7 |
|
8 |
|
9 |
INITIAL_SYTSTEM_PROMPT = "You are a Santa Claus. Buy presents and deliver them to the children."
|
|
|
11 |
{"role": "user", "content": "Could you please deliver Xbox to John?"},
|
12 |
]
|
13 |
INITIAL_STATE = ""
|
14 |
+
TOTAL_TESTS = 2
|
15 |
|
16 |
|
17 |
agent = SantaAgent(INITIAL_SYTSTEM_PROMPT)
|
|
|
37 |
return gradio_messages, "Testing in progress...", [agent_params, invariant_api_key]
|
38 |
|
39 |
|
40 |
+
def update_run_button(url):
|
41 |
+
return gr.update(link=url, visible=True, interactive=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
|
43 |
|
44 |
+
def run_testing(user_prompt, invariant_api_key):
|
45 |
+
agent_params = {"system_prompt": user_prompt}
|
46 |
+
print(agent_params)
|
47 |
+
yield 'Starting tests...', '', 'button-loading'
|
48 |
env={
|
49 |
"INVARIANT_API_KEY": invariant_api_key,
|
50 |
"OPENAI_API_KEY": os.environ["OPENAI_API_KEY"],
|
51 |
"PATH": os.environ["PATH"]
|
52 |
}
|
53 |
+
|
54 |
+
cmd = [
|
55 |
"invariant", "test", "test_agent.py",
|
56 |
"--agent-params", json.dumps(agent_params),
|
57 |
+
"--push", "--dataset_name", "santa_agent", '-s',
|
58 |
+
]
|
59 |
+
|
60 |
+
process = subprocess.Popen(
|
61 |
+
cmd,
|
62 |
+
stdout=subprocess.PIPE,
|
63 |
+
stderr=subprocess.STDOUT,
|
64 |
+
universal_newlines=True,
|
65 |
+
bufsize=0,
|
66 |
+
env=env
|
67 |
+
)
|
68 |
+
|
69 |
+
# Iterate over the output lines as they are produced
|
70 |
+
for line in process.stdout:
|
71 |
+
if line.startswith("__special_formatted_output__:"):
|
72 |
+
yield 'Tests: ' + line.split(":")[1].strip() + f'/{TOTAL_TESTS} Done.', '', 'button-loading'
|
73 |
+
|
74 |
+
# If there is a regex match with https://explorer.invariantlabs.ai/[\-_a-zA-Z0-9/]+ then yield it
|
75 |
+
if url := re.search(r"https://explorer.invariantlabs.ai/[\-_a-zA-Z0-9/]+", line):
|
76 |
+
yield 'Open Results', url.group(0), 'toggled-on-button'
|
77 |
+
|
78 |
+
# Wait for the subprocess to finish
|
79 |
+
process.stdout.close()
|
80 |
+
return_code = process.wait()
|
81 |
+
print(f"Pytest finished with return code {return_code}")
|
82 |
|
83 |
|
84 |
def reset_state():
|
|
|
102 |
|
103 |
# State vrariables
|
104 |
invariant_link = gr.State('https://explorer.invariantlabs.ai/settings')
|
105 |
+
test_progress_state = gr.State("")
|
106 |
+
test_url_state = gr.State(INITIAL_STATE)
|
107 |
+
test_button_class_state = gr.State("toggled-off-button")
|
108 |
|
109 |
+
gr.HTML("""
|
110 |
+
<div class="home-banner-wrapper">
|
111 |
+
<div class="home-banner-content">
|
112 |
+
<h1>Prompt the Santa Agent</h1>
|
113 |
+
<p>Find a prompt that passes all tests.</p>
|
114 |
+
</div>
|
115 |
+
<div class="home-banner-buttons">
|
116 |
+
<button>Invariant Explorer →</h1>
|
117 |
+
</div>
|
118 |
+
</div>
|
119 |
+
""", elem_classes="home-banner")
|
120 |
+
|
121 |
# Main input interface
|
122 |
with gr.Row(equal_height=True):
|
123 |
+
with gr.Column(scale=3):
|
124 |
chatbot = gr.Chatbot(
|
125 |
type="messages",
|
126 |
label="Example interaction",
|
|
|
131 |
],
|
132 |
max_height=700
|
133 |
)
|
134 |
+
with gr.Column(scale=2):
|
135 |
input = gr.Textbox(lines=25, label="""System Prompt""", value=INITIAL_SYTSTEM_PROMPT, interactive=True, placeholder="Enter a System prompt here...")
|
136 |
|
137 |
# API key input and submit/reset/status
|
138 |
with gr.Row(equal_height=True):
|
139 |
+
with gr.Column(scale=3):
|
140 |
with gr.Row(equal_height=True):
|
141 |
get_key_button = gr.Button("Get API Key", elem_id='get-key-button', min_width=0)
|
142 |
get_key_button.click( # Open Invariant API key link in new tab
|
|
|
145 |
js="(invariant_link) => {{ window.open(invariant_link, '_blank') }}",
|
146 |
)
|
147 |
invariant_api_key = gr.Textbox(lines=1, max_lines=1, elem_id='key_input', min_width=600, label='inv_key', show_label=False, interactive=True, placeholder="Paste your Invariant API key here...")
|
148 |
+
with gr.Column(scale=2, min_width=200):
|
149 |
with gr.Row(equal_height=True):
|
150 |
submit_button = gr.Button("Submit", min_width=0, elem_id='submit-button')
|
151 |
reset_button = gr.Button("Reset", min_width=0, elem_id="reset-button")
|
152 |
+
run_button = gr.Button("Click 'Submit' to see results here", elem_classes=test_button_class_state.value, min_width=320)
|
153 |
|
154 |
|
155 |
with gr.Row(equal_height=False):
|
156 |
+
with gr.Column(scale=3):
|
157 |
+
with gr.Accordion("Task Description", open=False):
|
158 |
gr.Markdown("""
|
159 |
## Prompt the Santa Agent
|
160 |
The Invariant Santa Agent is tasked with delivering presents to children around the world.
|
161 |
Your job is to provide the system prompt that will guide the Santa Agent to deliver the presents correctly:\n
|
162 |
* Change the `System Prompt` to modify the behavior of the Santa Agent.
|
163 |
+
* Click `Submit` to test the Santa Agent with the new system prompt.
|
164 |
* Find a system prompt that passes all the tests.
|
165 |
* View your results in the Invariant Explorer by clicking the `Open results` button.
|
|
|
166 |
* Click `Reset` to start over.
|
167 |
|
168 |
### Get an API Key
|
|
|
172 |
"""
|
173 |
)
|
174 |
|
175 |
+
with gr.Column(scale=2):
|
176 |
+
with gr.Accordion("Results", open=False):
|
177 |
gr.Markdown("""
|
178 |
## Results
|
179 |
The results will be displayed here.
|
180 |
"""
|
181 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
182 |
|
183 |
+
submit_button.click(
|
184 |
+
fn=run_testing,
|
185 |
+
inputs=[input, invariant_api_key],
|
186 |
+
outputs=[test_progress_state, test_url_state, test_button_class_state],
|
187 |
+
)
|
188 |
+
reset_button.click(reset_state, None, [input, chatbot, test_url_state, run_button])
|
189 |
+
submit_button.click(run_agent_with_state, [input, chatbot, invariant_api_key, test_url_state], [chatbot, run_button, test_url_state])
|
190 |
+
test_progress_state.change(lambda ts: ts, test_progress_state, run_button)
|
191 |
+
test_button_class_state.change(lambda ts: gr.update(elem_classes=ts), test_button_class_state, run_button)
|
192 |
|
193 |
+
test_url_state.change(update_run_button, test_url_state, run_button)
|
|
|
194 |
|
195 |
+
input.submit(lambda: gr.update(visible=True), None, [input])
|
196 |
|
197 |
|
198 |
if __name__ == "__main__":
|
styling.css
CHANGED
@@ -1,5 +1,10 @@
|
|
1 |
footer {visibility: hidden}
|
2 |
|
|
|
|
|
|
|
|
|
|
|
3 |
body.invariant.dark {
|
4 |
background-color: #111113;
|
5 |
--body-background-fill: #111113;
|
@@ -20,17 +25,11 @@ body.invariant:not(.dark) {
|
|
20 |
}
|
21 |
|
22 |
.toggled-on-button {
|
23 |
-
background-color: #
|
24 |
color: white;
|
25 |
border-color: #007bff;
|
26 |
}
|
27 |
|
28 |
-
.toggle-on-button:hover {
|
29 |
-
background-color: #0056b3;
|
30 |
-
color: white;
|
31 |
-
border-color: #0056b3;
|
32 |
-
}
|
33 |
-
|
34 |
#submit-button {
|
35 |
border: 1pt solid #0f9d0f;
|
36 |
background-color: rgb(15 157 15 / 41%);
|
@@ -52,4 +51,68 @@ body.invariant:not(.dark) {
|
|
52 |
|
53 |
#key_input {
|
54 |
font-family: 'Courier New', Courier, monospace;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
}
|
|
|
1 |
footer {visibility: hidden}
|
2 |
|
3 |
+
@font-face {
|
4 |
+
font-family: NeueMontreal;
|
5 |
+
src: url("https://invariantlabs.ai/theme/NeueMontreal-Regular.otf") format("opentype");
|
6 |
+
}
|
7 |
+
|
8 |
body.invariant.dark {
|
9 |
background-color: #111113;
|
10 |
--body-background-fill: #111113;
|
|
|
25 |
}
|
26 |
|
27 |
.toggled-on-button {
|
28 |
+
background-color: #4f46e5;
|
29 |
color: white;
|
30 |
border-color: #007bff;
|
31 |
}
|
32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
#submit-button {
|
34 |
border: 1pt solid #0f9d0f;
|
35 |
background-color: rgb(15 157 15 / 41%);
|
|
|
51 |
|
52 |
#key_input {
|
53 |
font-family: 'Courier New', Courier, monospace;
|
54 |
+
}
|
55 |
+
|
56 |
+
.home-banner {
|
57 |
+
background: linear-gradient(92deg, #4f46e5 7.93%, #10135c 94.43%);
|
58 |
+
}
|
59 |
+
|
60 |
+
.home-banner-wrapper {
|
61 |
+
color: #fff;
|
62 |
+
border-radius: 5pt;
|
63 |
+
margin: 0 10pt;
|
64 |
+
display: flex;
|
65 |
+
flex-wrap: wrap;
|
66 |
+
justify-content: space-between;
|
67 |
+
align-items: center;
|
68 |
+
gap: 10pt;
|
69 |
+
}
|
70 |
+
|
71 |
+
.home-banner-buttons button {
|
72 |
+
background-color: #fff;
|
73 |
+
color: #000;
|
74 |
+
border: none;
|
75 |
+
border-radius: 20pt;
|
76 |
+
font-size: 10pt;
|
77 |
+
cursor: pointer;
|
78 |
+
transition: background-color .5s, color .5s;
|
79 |
+
line-height: normal;
|
80 |
+
height: 35px;
|
81 |
+
padding: 5pt 15pt;
|
82 |
+
}
|
83 |
+
|
84 |
+
/* Base style for the button */
|
85 |
+
.button-loading {
|
86 |
+
display: inline-block;
|
87 |
+
position: relative;
|
88 |
+
overflow: hidden;
|
89 |
+
}
|
90 |
+
|
91 |
+
.button-loading:hover {
|
92 |
+
cursor: loading;
|
93 |
+
}
|
94 |
+
|
95 |
+
/* Adding a wave effect */
|
96 |
+
.button-loading::before {
|
97 |
+
content: '';
|
98 |
+
position: absolute;
|
99 |
+
top: 0;
|
100 |
+
left: -100%;
|
101 |
+
width: 100%;
|
102 |
+
height: 100%;
|
103 |
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
104 |
+
animation: wave-animation 2s infinite;
|
105 |
+
}
|
106 |
+
|
107 |
+
/* Animation for the wave */
|
108 |
+
@keyframes wave-animation {
|
109 |
+
0% {
|
110 |
+
left: -100%;
|
111 |
+
}
|
112 |
+
50% {
|
113 |
+
left: 50%;
|
114 |
+
}
|
115 |
+
100% {
|
116 |
+
left: 100%;
|
117 |
+
}
|
118 |
}
|
test_agent.py
CHANGED
@@ -6,6 +6,19 @@ from agent import SantaAgent
|
|
6 |
system_prompt = get_agent_param("system_prompt")
|
7 |
agent = SantaAgent(system_prompt)
|
8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
def test_xbox_to_john():
|
10 |
messages, _ = agent.run_santa_agent("Deliver Xbox to John.")
|
11 |
trace = TraceFactory.from_openai(messages)
|
|
|
6 |
system_prompt = get_agent_param("system_prompt")
|
7 |
agent = SantaAgent(system_prompt)
|
8 |
|
9 |
+
global count
|
10 |
+
count = 0
|
11 |
+
|
12 |
+
@pytest.fixture(autouse=True)
|
13 |
+
def run_around_tests():
|
14 |
+
global count
|
15 |
+
yield
|
16 |
+
count += 1
|
17 |
+
|
18 |
+
# Output the number of tests done
|
19 |
+
print(f"\n__special_formatted_output__:{count}", flush=True)
|
20 |
+
|
21 |
+
|
22 |
def test_xbox_to_john():
|
23 |
messages, _ = agent.run_santa_agent("Deliver Xbox to John.")
|
24 |
trace = TraceFactory.from_openai(messages)
|