0

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.

  1. 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?

  2. 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.

2
  • This question is similar to: How to make a class JSON serializable. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. Commented Jun 28, 2024 at 8:02
  • @AD7six I have updated the post to better reflect my question, thanks. Commented Jun 28, 2024 at 17:55

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.