slackapi / bolt-python

A framework to build Slack apps using Python

Home Page:https://slack.dev/bolt-python/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

views_update not updating initial_value of text area when initial_value was set while generating the initial view

RovoMe opened this issue · comments

For a project to integrate some AI models via Slack as GUI basically we came up with a modal view where users can configure the parameters for an initial conversation with a particular LLM / service:

class AIBotModal():
    def __init__(self):
        pass

    def view(self, channel_id: str, prompt: str = None):
        ret = {
            "type": "modal",
            "callback_id": "ai_modal_view",
            "title": {
                "type": "plain_text",
                "text": "Configure conversation"
            },
            "submit": {
                "type": "plain_text",
                "text": "Submit"
            },
            "type": "modal",
            "close": {
                "type": "plain_text",
                "text": "Cancel"
            },
            "blocks": [
                {
                    "type": "section",
                    "block_id": "model_selection",
                    "text": {
                        "type": "mrkdwn",
                        "text": "Pick an item from the dropdown list"
                    },
                    "accessory": {
                        "type": "static_select",
                        "placeholder": {
                            "type": "plain_text",
                            "text": "Select an item"
                        },
                        "initial_option": {
                            "text": {
                                "type": "plain_text",
                                "text": "General"
                            },
                            "value": "general"
                        },
                        "options": [
                            {
                                "text": {
                                    "type": "plain_text",
                                    "text": "General"
                                },
                                "value": "general"
                            },
                            {
                                "text": {
                                    "type": "plain_text",
                                    "text": "ChatGPT"
                                },
                                "value": "gpt"
                            },
                            {
                                "text": {
                                    "type": "plain_text",
                                    "text": "Mistral"
                                },
                                "value": "mistral"
                            }
                        ],
                        "action_id": "model_selection"
                    }
                },
                {
                    "type": "section",
                    "block_id": "select_prompt",
                    "text": {
                        "type": "mrkdwn",
                        "text": "Choose a prompt"
                    },
                    "accessory": {
                        "type": "static_select",
                        "placeholder": {
                            "type": "plain_text",
                            "text": "Select an item"
                        },
                        "initial_option": {
                            "text": {
                                "type": "plain_text",
                                "text": "Default"
                            },
                            "value": "You are a helpful assistant for ..."
                        },
                        "options": [
                            {
                                "text": {
                                    "type": "plain_text",
                                    "text": "Default"
                                },
                                "value": "You are a helpful assistant for ..."
                            },
                            {
                                "text": {
                                    "type": "plain_text",
                                    "text": "Email"
                                },
                                "value": "TODO1"
                            },
                            {
                                "text": {
                                    "type": "plain_text",
                                    "text": "Slack announcement"
                                },
                                "value": "TODO2"
                            }
                        ],
                        "action_id": "prompt_selection"
                    }
                },
                {
                    "type": "input",
                    "block_id": "prompt_area",
                    "element": {
                        "type": "plain_text_input",
                        "action_id": "prompt_input",
                        "multiline": True
                    },
                    "label": {
                        "type": "plain_text",
                        "text": "Prompt"
                    }
                },
                {
                    "type": "input",
                    "block_id": "question_input_block",
                    "element": {
                        "type": "plain_text_input",
                        "action_id": "question_input",
                        "multiline": True
                    },
                    "label": {
                        "type": "plain_text",
                        "text": "Question"
                    }
                }
            ],
            "private_metadata": "channel=" + channel_id
        }

        if prompt is not None:
            print(f"prompt in modal: {prompt}")
            element: dict = ret["blocks"][2]["element"]
            element.update({ 
                "initial_value": prompt
            })
        #else:
        #    default_prompt = "You are a helpful assistant for ..."
        #    print(f"Using default prompt: {default_prompt}")
        #    element: dict = ret["blocks"][2]["element"]
        #    element.update({ 
        #        "initial_value": default_prompt
        #    })

        return ret

The second input selection should allow a user to load some predefined prompts into the prompt input text area. In order to achieve that we have defined a block action listener for the second input selection like this:

@app.action("prompt_selection")
async def prompt_selection_block_action(ack, body, logger):
    await ack()
    logger.debug(body)

    view: object = body["view"]
    metadata: dict = parse_metadata_from_view(view)
    channel_id: str = metadata.get("channel")
    
    prompt_selection = view["state"]["values"]["select_prompt"]["prompt_selection"]
    prompt: str = None
    if "selected_option" in prompt_selection:
        selected: dict = prompt_selection["selected_option"]
        prompt: str = selected["value"]
    
    print(f"view.hash before update: {view["hash"]}")
    # TODO: load prompt from DB based on selected value
    response: object = await app.client.views_update(
        view_id= view["id"],
        hash=view["hash"],
        view = AIBotModal().view(channel_id=channel_id, prompt=prompt)
    )
    print(f"view.hash after update: {response["view"]["hash"]}")
    if response["ok"] == False:
        print(f"update response failed: {response}")

...

async def main():
    print("Starting Slack socket-mode handler")
    handler = AsyncSocketModeHandler(app, os.getenv('SLACK_APP_TOKEN'))
    await handler.start_async()

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

On selecting one of the options in the promt choice the respective prompt input text area is successfully updated. However, when the currently commented out part is enabled again and the default value written to the input box, the views_update(...) method receives the correct modal view representation though the view in Slack never updates to the value of the selected option. With the block commented out though, the values update perfectly.

As this issue sound familiar in a way to the this one here I also made sure that the hash is set to the one of the previous view before doing an update. The log for the case when the commented code is enabled again looks like this, which looks like the view should be updated with the specified value:

Using default prompt: You are a helpful assistant for ...
...
view.hash before update: 1711650243.bSBK8SEm
prompt in modal: TODO1
view.hash after update: 1711650246.Wy5q5xZa
view.hash before update: 1711650246.Wy5q5xZa
prompt in modal: TODO2
view.hash after update: 1711650252.rZlp1tj8
view.hash before update: 1711650252.rZlp1tj8
prompt in modal: You are a helpful assistant for ...
view.hash after update: 1711650266.VsmlUXhz

However, the prompt_input field remains at "You are a helpful assistant for ..." no matter what I select. If I omit the default prompt setting, values update successfully in the prompt_input when I change a value in the prompt selection.

As Python is not my main language, chances are that I'm doing something compeltely wrong here. But I'm still wondering why in the case where no initial_value is available before the request hits the block action handler for the prompt selection selecting a value in the respective prompt selection drop down updates the input field perfectly and in the case an initial value was set beforehand it does not. I also checked for any particular errors returned, but response["ok"] returns True in all cases.

Hi, @RovoMe! Thanks for submitting your question! 🙌

If I understand your question correctly, it looks like it might be similar to what is described here. The course of action they recommend is:

force slack to rebuild the ui element (in this case the text input field) by giving it a new block id when calling views_update().

I hope this helps! Let me know if you have further questions.

Ok, I got this now working with this following modification:

if prompt is not None:
    rand_str: str = "".join(choice(ascii_lowercase) for i in range(12))
    print(f"prompt in modal: {prompt} - new block_id: {rand_str}")
    block: dict = ret["blocks"][2]
    block.update({
        "block_id": rand_str
    })
    element: dict = block["element"]
    element.update({ 
        "initial_value": prompt
    })
else:
    default_prompt = "You are a helpful assistant for ..."
    print(f"Using default prompt: {default_prompt}")
    element: dict = ret["blocks"][2]["element"]
    element.update({ 
        "initial_value": default_prompt
    })

Still feels odd to add a random block_id on each selection change when the same block_id works on chaning the prompt selection when no initial value was set in the initial view.

👋 It looks like this issue has been open for 30 days with no activity. We'll mark this as stale for now, and wait 10 days for an update or for further comment before closing this issue out. If you think this issue needs to be prioritized, please comment to get the thread going again! Maintainers also review issues marked as stale on a regular basis and comment or adjust status if the issue needs to be reprioritized.

As this issue has been inactive for more than one month, we will be closing it. Thank you to all the participants! If you would like to raise a related issue, please create a new issue which includes your specific details and references this issue number.