File size: 7,372 Bytes
4f07f72
 
 
 
 
 
 
cc9a95f
d934e05
cc9a95f
88be29a
 
d934e05
7c479ac
59df961
cc9a95f
 
 
03dc960
 
 
88be29a
 
dd4879b
 
88be29a
 
 
 
 
57b94ca
88be29a
 
 
57b94ca
88be29a
57b94ca
 
 
 
 
88be29a
57b94ca
7c479ac
 
57b94ca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88be29a
 
03dc960
cc9a95f
 
 
 
 
2db4636
06b78a1
cc46ec6
cc9a95f
 
 
 
 
 
 
 
 
03dc960
 
cc9a95f
 
 
 
7c479ac
 
 
 
cc9a95f
 
 
 
 
7c479ac
 
cc9a95f
 
 
 
1262b99
cc9a95f
aff284c
cc9a95f
 
 
 
 
 
 
d934e05
cc9a95f
03dc960
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cc9a95f
 
 
 
 
 
03dc960
 
 
 
 
cc9a95f
 
 
03dc960
 
 
 
 
cc9a95f
03dc960
 
 
 
 
 
 
 
 
 
 
cc9a95f
 
03dc960
88be29a
cc9a95f
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
"""
This page displays the architectures which have been configured in the system and lets
users interact with them.  If users choose to interact with a single architecture they get
a more detailed breakdown of the architecture flow, if they choose to view side by side results
they get to see just the time taken and the response.
"""

import pandas as pd
import streamlit as st

from time import time

from src.st_helpers import st_setup
from src.data_synthesis.test_question_generator import generate_question
from src.common import img_dir, escape_dollars, generate_group_tag
from src.architectures import *


COMPARE = "Side by side compare"  # Constant value to use for the UI to select a side by side architecture comparison


def show_side_by_side() -> None:
    """
    Display a selector to pick a number of architectures to compare side by side and then the
    chat interaction box per architecture to see the response timing and response.
    """
    # Build the layout structure
    st.divider()
    header_container = st.container()
    arch_outer_container = st.container()

    # Build header
    with header_container:
        st.write("### Side by side comparison of architectures")
        st.write('Enter a question below to have it sent to the selected architectures to compare timing and response.')

        options = [a.name for a in Architecture.architectures]
        selected_archs = st.multiselect("Select architectures to use", options=options, default=options)

        if len(selected_archs) == 0:
            st.write("To get started select some architectures to compare")
        else:
            prompt = st.chat_input("Ask a question")
            if st.button("Or press to ask a random question"):
                prompt = generate_question()
            if prompt:
                st.write(f"**Question:** {prompt}")

    # Now build the columns
    if len(selected_archs) > 0:
        with arch_outer_container:
            arch_cols = st.columns(len(selected_archs))
            if prompt:
                # Build columns per architecture
                for i, a in enumerate(selected_archs):
                    with arch_cols[i]:
                        st.write(f'#### {a}')

                # Now dispatch the messages per architecture
                group_tag = generate_group_tag()
                for i, a in enumerate(selected_archs):
                    request = ArchitectureRequest(query=prompt)
                    arch = Architecture.get_architecture(a)
                    with arch_cols[i]:
                        with st.spinner('Architecture processing request'):
                            start = time()
                            arch(request, trace_tags=["UI", "SideBySideCompare", group_tag])
                            elapsed_in_s = (int((time() - start) * 10))/10  # round to 1dp in seconds
                            st.write('##### Timing')
                            st.write(f'Request took **{elapsed_in_s}s**')
                            st.write('##### Response')
                            st.write(request.response)
            else:
                # Build columns per architecture for display only
                for i, a in enumerate(selected_archs):
                    with arch_cols[i]:
                        st.write(f'#### {a}')


def display_architecture_in_container(arch, arch_container) -> None:
    with arch_container:
        st.divider()
        st.write(f'### {arch.name}')
        st.write('#### Architecture description')
        st.write(arch.description)
        if arch.img is not None:
            img = os.path.join(img_dir, arch.img)
            st.image(img, caption=f'{arch.name} As Built', width=1000)
        table_data = []
        for j, s in enumerate(arch.steps, start=1):
            table_data.append(
                [j, s.__class__.__name__, s.description, s.config_description()]
            )
        table_cols = ['Step', 'Name', 'Description', 'Config details']
        st.write('#### Architecture pipeline steps')
        st.table(pd.DataFrame(table_data, columns=table_cols))


def display_architecture_chat_in_container(arch, chat_container) -> None:
    with chat_container:
        st.write(f"### Chat with {arch.name}")
        st.write("Note this is a simple single query through the relevant architecture. This is just a sample so you can interact with it and does not manage a chat session history.")

        prompt = st.chat_input("Ask a question")
        if st.button("Or press to ask a random question"):
            prompt = generate_question()

        chat_col, trace_col, request_col = st.columns([3, 2, 2])

        with chat_col:
            with st.chat_message("assistant"):
                st.write("Chat with me in the box below")

        if prompt:
            with chat_col:
                with st.chat_message("user"):
                    st.write(prompt)
                request = ArchitectureRequest(query=prompt)
                trace = arch(request, trace_tags=["UI", "SingleArchTest"])
                with st.chat_message("assistant"):
                    st.write(escape_dollars(request.response))
                with trace_col:
                    st.write("#### Architecture Trace")
                    st.markdown(trace.as_markdown())
                with request_col:
                    st.write("#### Full Request/Response")
                    st.markdown(request.as_markdown())



def show_architecture(architecture: str) -> None:
    """
    Streamlit render an architecture details and the
    ability to interact with the architecture
    :param architecture: the name of the architecture to output
    """
    arch = Architecture.get_architecture(architecture)

    # Segment into two containers for organisation
    arch_container = st.container()
    chat_container = st.container()

    display_architecture_in_container(arch, arch_container)
    display_architecture_chat_in_container(arch, chat_container)


def show_sub_header() -> None:
    """
    Write a subheader to the page depending on how many architectures are configured
    """
    arch_count = len(Architecture.architectures)
    if arch_count == 1:
        st.write('### 1 Architecture available')
    else:
        st.write(f'### {arch_count} Architectures available')


def show_reload_button() -> None:
    """
    Shows a button to reload the architectures and force them to reload if clicked
    """
    if st.button("Force reload of architecture configs"):
        Architecture.load_architectures(force_reload=True)


def get_user_selected_architecture() -> Optional[str]:
    """
    Display a picker of all the architectures plus the option to do a side by side compare
    """
    arch_names = [a.name for a in Architecture.architectures]
    arch_names.append(COMPARE)
    return st.radio(label="Available architectures", label_visibility="hidden", options=arch_names, index=None)


if st_setup('LLM Arch'):
    st.write("# LLM Architectures")
    Architecture.load_architectures()
    show_sub_header()
    show_reload_button()

    selected_arch = get_user_selected_architecture()
    if selected_arch is None:
        st.info('Select an architecture from above to see details and interact with it')
    elif selected_arch == COMPARE:
        show_side_by_side()
    else:
        show_architecture(selected_arch)