qnixsynapse / rich-chat

Python console application designed to provide an engaging and visually appealing LLM chat experience on Unix-like consoles or Terminals.
MIT License
18 stars 2 forks source link

Implement a proper mechanism to copy code blocks outputted by the model #3

Open qnixsynapse opened 8 months ago

qnixsynapse commented 8 months ago

Currently, copying through console ends up copying the frame as well. Need to implement a mechanism to bind a shortcut key which when pressed will copy the code within the 3 backtics outputted by the model.

teleprint-me commented 8 months ago

Posting this here so I don't forget.

textualize offers a lot of features that rich doesn't support out of the box. official documentation can be found here. The guide has a more solid introduction to generally available API.

It should offers tools we might need such as dynamically assigning text at runtime to the clipboard, e.g. using something like clip. For example, the Events API has DOM support which might be a potential blueprint for handling something like this.

teleprint-me commented 8 months ago

In the spirit of keeping it as simple as possible, I have local branch I've been chipping away at with minor improvements over time.

I decided to dig a bit deeper into prompt_toolkit and read up on the KeyBindings and ClipBoard classes.

I think with a bit of clever hacking, we can probably prototype a keybind that gets the last message in the sequence for the list of chat messages.

I haven't posted the branch yet, but this is what I have so far.

def key_bindings(chat_history: "ChatHistory") -> KeyBindings:
    kb = KeyBindings()
    # Initialize the clipboard
    clipboard = PyperclipClipboard()

    # c-s -> select text and a -> copy entire message into clipboard
    @kb.add("c-s", "a")  # Ctrl+S then C
    def _(event):  # add content from the last message from chat_history.messages
        """
        Copy predefined content to the system clipboard when Ctrl+S then C is pressed.
        """
        # only str, int, float, and bool values can be copied to the clipboard, not dict
        clipboard.set_text(chat_history.messages[-1]["content"])
        print("Event:", event)
        # event.app.exit(message="Content copied to clipboard!")

    # c-s select text and s -> copy all snippets, then merge them into clipboard
    # note: snippets (or code blocks) use triple-backticks to open and close the code block
    # the open set of triple-backticks may be followed by any form of text, usually a reference to some programming language utilized by the parser for visual formatting
    # we only care about the content within the code blocks within the last message
    @kb.add("c-s", "s")  # Ctrl+S then S
    def _(event):  # add only code snippets to clipboard
        """
        Copy predefined content to the system clipboard when Ctrl+S then C is pressed.
        """
        # only str, int, float, and bool values can be copied to the clipboard, not dict
        clipboard.set_text(chat_history.messages[-1]["content"])
        print("Event:", event)
        # event.app.exit(message="Content copied to clipboard!")

    return kb

I'll link to the branch once it's a bit more fleshed out. Still a work in progress.

teleprint-me commented 8 months ago

Yeah, I think this might be a step in the right direction.

def key_bindings(chat_history: "ChatHistory") -> KeyBindings:
    kb = KeyBindings()
    clipboard = PyperclipClipboard()

    @kb.add("c-s", "a")
    def _(event):
        """Copy the entire last message to the system clipboard."""
        if chat_history.messages:
            last_message_content = chat_history.messages[-1]["content"]
            clipboard.set_text(last_message_content)
            print("Entire message copied to clipboard!")

    @kb.add("c-s", "s")
    def _(event):
        """Copy only code snippets from the last message to the system clipboard."""
        if chat_history.messages:
            last_message_content = chat_history.messages[-1]["content"]
            code_snippets = re.findall(r"```(.*?)```", last_message_content, re.DOTALL)
            snippets_content = "\n\n".join(code_snippets)
            clipboard.set_text(snippets_content)
            print("Code snippets copied to clipboard!")

    return kb

You can fetch request clipboard to test it out. Let me know what you think.

qnixsynapse commented 8 months ago

4 is good though, just need to be sure if people can understand those symbols.

Currently preoccupied with something else.

Also I would prefer a vim like shortcut here. (I already have an implementation with bs4 and python's markdown, which seems to preserve the indentation in my testing).

teleprint-me commented 8 months ago

https://github.com/akarshanbiswas/rich-chat/pull/4 is good though

4 and user-prompt are the same branch. The other 3 branches are built on top #4. So clipboard is already merged with the others.

just need to be sure if people can understand those symbols.

We can document them. It's experimental. I do like it because it's compact. I'm not married to it, so we can just do the literal alt and ctrl if that's what you prefer.

Currently preoccupied with something else.

It's cool. Just sharing.

Also I would prefer a vim like shortcut here.

prompt-toolkit supports vi keybindings. The custom keybindings are vi extensions. The docs go into more depth about it.

(I already have an implementation with bs4 and python's markdown, which seems to preserve the indentation in my testing).

I don't see the branch. Let me know when it's up. I'll take a look at it when I can.

I need an interface and this seems like a good start. It isolates me from my other project too, so it's been nice to just focus on the front-end instead of all of the back-end details for once.

RAG is alright, especially for local models. Function calling works way better but the open source models aren't there yet. There are a few that are trained, but few interfaces actually support it ATM.