I am trying to implement a Gradio app that takes in a json of a conversation between users and an AI. The input json is formatted as such, where there are multiple conversations, each has an associated set of possible responses from the AI:
[
{
"conversations": [
{
"from": "human",
"value": "query"
}
],
"responses": [
{
"from": "gpt",
"value": "response1"
},
{
"from": "gpt",
"value": "response2"
}
]
},
{
"conversations": [
{
"from": "human",
"value": "query"
}
],
"responses": [
{
"from": "gpt",
"value": "response1"
},
{
"from": "gpt",
"value": "response2"
}
]
}
]
My goal is to create a set of Radio buttons for each of the AI's possible responses and collect those responses such that I can produce an output json as such, where each of the AI's response has a "preference" field depending on what is selected on the Gradio button:
[
{
"conversations": [
{
"from": "human",
"value": "query"
}
],
"responses": [
{
"from": "gpt",
"value": "response1"
"preference": "Indifferent/Like/Dislike"
},
{
"from": "gpt",
"value": "response2"
"preference": "Indifferent/Like/Dislike"
}
]
},
{
"conversations": [
{
"from": "human",
"value": "query"
}
],
"responses": [
{
"from": "gpt",
"value": "response1"
"preference": "Indifferent/Like/Dislike"
},
{
"from": "gpt",
"value": "response2"
"preference": "Indifferent/Like/Dislike"
}
]
}
]
However, my current implementation yields the following Traceback:
Traceback (most recent call last):
File "C:\Users\aland\AppData\Local\Programs\Python\Python312\Lib\site-packages\gradio\queueing.py", line 532, in process_events
response = await route_utils.call_process_api(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\aland\AppData\Local\Programs\Python\Python312\Lib\site-packages\gradio\route_utils.py", line 276, in call_process_api
output = await app.get_blocks().process_api(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\aland\AppData\Local\Programs\Python\Python312\Lib\site-packages\gradio\blocks.py", line 1928, in process_api
result = await self.call_function(
^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\aland\AppData\Local\Programs\Python\Python312\Lib\site-packages\gradio\blocks.py", line 1514, in call_function
prediction = await anyio.to_thread.run_sync(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\aland\AppData\Local\Programs\Python\Python312\Lib\site-packages\anyio\to_thread.py", line 56, in run_sync
return await get_async_backend().run_sync_in_worker_thread(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\aland\AppData\Local\Programs\Python\Python312\Lib\site-packages\anyio\_backends\_asyncio.py", line 2177, in run_sync_in_worker_thread
return await future
^^^^^^^^^^^^
File "C:\Users\aland\AppData\Local\Programs\Python\Python312\Lib\site-packages\anyio\_backends\_asyncio.py", line 859, in run
result = context.run(func, *args)
^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\aland\AppData\Local\Programs\Python\Python312\Lib\site-packages\gradio\utils.py", line 832, in wrapper
response = f(*args, **kwargs)
^^^^^^^^^^^^^^^^^^
File "C:\Users\aland\pyproject\Gradio\userfb.py", line 50, in save_feedback_json
json.dump(json_out, tmp_file, indent=4)
File "C:\Users\aland\AppData\Local\Programs\Python\Python312\Lib\json\__init__.py", line 179, in dump
for chunk in iterable:
File "C:\Users\aland\AppData\Local\Programs\Python\Python312\Lib\json\encoder.py", line 430, in _iterencode
yield from _iterencode_list(o, _current_indent_level)
File "C:\Users\aland\AppData\Local\Programs\Python\Python312\Lib\json\encoder.py", line 326, in _iterencode_list
yield from chunks
File "C:\Users\aland\AppData\Local\Programs\Python\Python312\Lib\json\encoder.py", line 406, in _iterencode_dict
yield from chunks
File "C:\Users\aland\AppData\Local\Programs\Python\Python312\Lib\json\encoder.py", line 326, in _iterencode_list
yield from chunks
File "C:\Users\aland\AppData\Local\Programs\Python\Python312\Lib\json\encoder.py", line 406, in _iterencode_dict
yield from chunks
File "C:\Users\aland\AppData\Local\Programs\Python\Python312\Lib\json\encoder.py", line 439, in _iterencode
o = _default(o)
^^^^^^^^^^^
File "C:\Users\aland\AppData\Local\Programs\Python\Python312\Lib\json\encoder.py", line 180, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type Radio is not JSON serializable
Please note that the main issue is not that the JSON is not serializable, rather, the problem is that I am receiving a list of components instead of a list of the values that the components each hold. As a result, I am currently trying to serialize component buttons, hence the error (which is not my intention). There shouldn't be a need to serialize to begin with.
This arises when I try to run the line of code json.dump(json_out, tmp_file, indent=4) , which is an attempt to serialize the information into json format.
As far as I know in my code, I use State variables to collect the feedback components I wish to inspect. However, when passed into the save_feedback_json function, they are passed as the components themselves rather than the values, which then do not become serializable. I deemed this a reasonable outcome, as the variable is a gradio.State variable rather than a list of the actual components. Is there a way for me to collect the values of these variables, either through refactoring the code such that the input is the list of components, or through some mechanism of collecting values of components?
Furthermore, as a smaller side question, how do I update the display of the Gradio interface whenever I hit the 'upload' button for a submitted json? As I produce a variable amount of Markdown and Radio components, I don't know if there is a way to consolidate these output fields. Here is the code for reference:
import gradio as gr
import json
import tempfile
conversation_json = []
feedback_components = []
components = []
uploaded_json = []
def load_conversations(uploaded_json):
components = []
feedback_components = []
for convo in uploaded_json:
user_response=convo["conversations"][0]
user_md = gr.Markdown(f"**{user_response['from']}**: {user_response['value']}")
components.append(user_md)
for response in convo["responses"]:
res_md = gr.Markdown(f"**{response['from']}**: {response['value']}")
components.append(res_md)
fb = gr.Radio(["Like", "Dislike"], label="Feedback", type="value", value="Indifferent", interactive=True)
components.append(fb)
feedback_components.append(fb)
return components, feedback_components
def upload_conversation(file_obj):
with open(file_obj, 'r') as f:
uploaded_json=json.load(f)
return uploaded_json
def save_feedback_json(conversation_json, feedback_values):
json_out = []
fb_ind = 0
for entry in conversation_json:
c_entry = {"conversations": entry["conversations"], "responses": []}
for res in entry["responses"]:
print(fb_ind)
c_entry["responses"].append({
"from": res["from"],
"value": res["value"],
"preference": feedback_values[fb_ind]})
fb_ind += 1
json_out.append(c_entry)
with tempfile.NamedTemporaryFile(delete=False, suffix=".json", mode="w", encoding="utf-8") as tmp_file:
json.dump(json_out, tmp_file, indent=4)
tmp_file_path = tmp_file.name
return tmp_file_path
with gr.Blocks() as iface:
file_input = gr.File(label="Upload JSON files of conversation", file_types=["json"])
json_state = gr.State()
components_display = gr.State() # Use a container to dynamically display components
feedback_components = gr.State()
file_input.change(upload_conversation, inputs=[file_input], outputs=[json_state])
upload_btn = gr.Button("Load Conversations")
upload_btn.click(load_conversations, inputs=[json_state], outputs=[components_display, feedback_components])
submit_btn = gr.Button("Submit")
feedback_json = gr.File(label="Download JSON")
submit_btn.click(save_feedback_json, inputs=[json_state, feedback_components], outputs=[feedback_json])
iface.launch()
I created a global variable for feedback_components, which consolidates all feedback components, then tried passing this into the save_feedback_json function. This worked only when I had a static filepath to the json as such:
import gradio as gr
import json
import tempfile
conversation_json = []
feedback_components = []
def load_conversations(file_name):
with open(file_name, 'r') as f:
conversation_json=json.load(f)
components = []
feedback_components = []
for convo in conversation_json:
user_response=convo["conversations"][0]
components.append(gr.Markdown(f"**{user_response['from']}**: {user_response['value']}"))
for response in convo["responses"]:
components.append(gr.Markdown(f"**{response['from']}**: {response['value']}"))
fb = gr.Radio(["Like", "Dislike"], label="Feedback", type="value", value="Indifferent", interactive=True)
components.append(fb)
feedback_components.append(fb)
return conversation_json, feedback_components, components
def save_feedback_json(*feedback_values):
json_out = []
fb_ind = 0
for entry in conversation_json:
c_entry = {"conversations": entry["conversations"], "responses": []}
for res in entry["responses"]:
c_entry["responses"].append({
"from": res["from"],
"value": res["value"],
"preference": feedback_values[fb_ind]})
fb_ind += 1
json_out.append(c_entry)
with tempfile.NamedTemporaryFile(delete=False, suffix=".json", mode="w", encoding="utf-8") as tmp_file:
json.dump(json_out, tmp_file, indent=4)
tmp_file_path = tmp_file.name
return tmp_file_path
with gr.Blocks() as iface:
conversation_json, feedback_components, components = load_conversations("./data/example_in.json") # change path here
with gr.Row():
with gr.Column():
for component in components:
if not component.render:
component.render()
submit = gr.Button("Submit", variant="primary", interactive=True)
#submit.click(save_feedback_json, inputs = feedback_components)
json_output = gr.File(label="Download JSON", interactive=True)
submit.click(
fn=save_feedback_json,
inputs=feedback_components,
outputs=json_output
)
iface.launch()
However, this does not work in the 'json-uploading' version, as feedback_values is described as an empty tuple when I pass in feedback_values.
For the second problem, rendering, I tried returning gr.update() and just attempting a re-render with the nested 'with's described above, but neither really refreshes the interface.
I feel like as a whole, being a beginner with Gradio, my code is really messy and I lack the background knowledge as for how Gradio works beneath the surface. Any pointers for refactoring, tips, and tricks are greatly appreciated, as I am considering just restarting my code seeing that my code is such a mess.