import gradio as gr import matplotlib.pyplot as plt import networkx as nx from collections.abc import Iterable from gradio.themes.base import Base from gradio.themes.utils import colors, fonts, sizes from model import Parser parser = Parser() def parse(text): output = parser.parse(text) dependency_tree = render_dependency_tree(output["forms"], output["heads"], output["deprel"]) table = render_table(output["forms"], output["lemmas"], output["upos"], output["xpos"], output["feats"], output["ne"]) return dependency_tree, table def render_dependency_tree(words, parents, labels): fig, ax = plt.subplots(figsize=(40, 16)) main_font_size = 40 if len(words) < 10 else 30 if len(words) < 20 else 24 if len(words) < 40 else 16 minor_font_size = 30 if len(words) < 10 else 22 if len(words) < 20 else 16 if len(words) < 40 else 12 pad = main_font_size // 2 # Create a directed graph G = nx.DiGraph() # Adding nodes to the graph for i, word in enumerate(words): G.add_node(i, label=word) # Adding edges with labels for i, (parent, label) in enumerate(zip(parents, labels)): if parent != 0: G.add_edge(parent - 1, i, label=label) # Position nodes using Graphviz pos = nx.nx_agraph.graphviz_layout(G, prog='dot') # Draw the graph nx.draw(G, pos, ax=ax, with_labels=True, labels=nx.get_node_attributes(G, 'label'), arrows=True, node_color='#ffffff', node_size=0, node_shape='s', font_size=main_font_size, bbox = dict(facecolor="white", pad=pad) ) # Draw edge labels edge_labels = nx.get_edge_attributes(G, 'label') nx.draw_networkx_edge_labels(G, pos, ax=ax, edge_labels=edge_labels, rotate=False, alpha=1.0, font_size=minor_font_size) return fig description = """

Norsk UD (Bokmål og Nynorsk)

""" def render_table(forms, lemmas, upos, xpos, feats, named_entities): feats = [[f"*{f.split('=')[0]}:* {f.split('=')[1]}" for f in (feat.split("|")) if '=' in f] for feat in feats] max_len = max(1, max([len(feat) for feat in feats])) feats = [feat + [""] * (max_len - len(feat)) for feat in feats] feats = list(zip(*feats)) named_entities_converted = [] for i, ne in enumerate(named_entities): if ne == "O": named_entities_converted.append("") elif ne.startswith("B") and (i + 1 == len(named_entities) or named_entities[i + 1].startswith("I")): named_entities_converted.append(f"<<— {ne.split('-')[1]} —") elif ne.startswith("B"): named_entities_converted.append(f"<<— {ne.split('-')[1]} —>>") elif ne.startswith("I") and i + 1 < len(named_entities) and named_entities[i + 1].startswith("I"): named_entities_converted.append("————") else: named_entities_converted.append(f"——>>") array = [ [""] + forms, ["*LEMMAS:*"] + lemmas, ["*UPOS:*"] + upos, ["*XPOS:*"] + xpos, ["*UFEATS:*"] + list(feats[0]), *([""] + list(row) for row in feats[1:]), ["*NE:*"] + named_entities_converted, ['' for _ in range(len(forms) + 1)] ] return {"data": array[1:], "headers": array[0]} class Soft(Base): def __init__( self, *, primary_hue: colors.Color | str = colors.indigo, secondary_hue: colors.Color | str = colors.indigo, neutral_hue: colors.Color | str = colors.gray, spacing_size: sizes.Size | str = sizes.spacing_md, radius_size: sizes.Size | str = sizes.radius_md, text_size: sizes.Size | str = sizes.text_md, font: fonts.Font | str | Iterable[fonts.Font | str] = ( # fonts.LocalFont("Montserrat"), "ui-sans-serif", "system-ui", "sans-serif", ), font_mono: fonts.Font | str | Iterable[fonts.Font | str] = ( # fonts.LocalFont("IBM Plex Mono"), "ui-monospace", "Consolas", "monospace", ), ): super().__init__( primary_hue=primary_hue, secondary_hue=secondary_hue, neutral_hue=neutral_hue, spacing_size=spacing_size, radius_size=radius_size, text_size=text_size, font=font, font_mono=font_mono, ) self.name = "soft" super().set( # Colors background_fill_primary="*neutral_50", slider_color="*primary_500", slider_color_dark="*primary_600", # Shadows shadow_drop="0 1px 4px 0 rgb(0 0 0 / 0.1)", shadow_drop_lg="0 2px 5px 0 rgb(0 0 0 / 0.2)", # Block Labels block_background_fill="white", block_label_padding="*spacing_sm *spacing_md", block_label_background_fill="*primary_100", block_label_background_fill_dark="*primary_600", block_label_radius="*radius_md", block_label_text_size="*text_md", block_label_text_weight="600", block_label_text_color="*primary_500", block_label_text_color_dark="white", block_title_radius="*block_label_radius", block_title_padding="*block_label_padding", block_title_background_fill="*block_label_background_fill", block_title_text_weight="600", block_title_text_color="*primary_500", block_title_text_color_dark="white", block_label_margin="*spacing_md", # Inputs input_background_fill="white", input_border_color="*neutral_100", input_shadow="*shadow_drop", input_shadow_focus="*shadow_drop_lg", checkbox_shadow="none", # Buttons shadow_spread="6px", button_primary_shadow="*shadow_drop_lg", button_primary_shadow_hover="*shadow_drop_lg", button_primary_shadow_active="*shadow_inset", button_secondary_shadow="*shadow_drop_lg", button_secondary_shadow_hover="*shadow_drop_lg", button_secondary_shadow_active="*shadow_inset", checkbox_label_shadow="*shadow_drop_lg", button_primary_background_fill="*primary_500", button_primary_background_fill_hover="*primary_400", button_primary_background_fill_hover_dark="*primary_500", button_primary_text_color="white", button_secondary_background_fill="white", button_secondary_background_fill_hover="*neutral_100", button_secondary_background_fill_hover_dark="*primary_500", button_secondary_text_color="*neutral_800", button_cancel_background_fill="*button_secondary_background_fill", button_cancel_background_fill_hover="*button_secondary_background_fill_hover", button_cancel_background_fill_hover_dark="*button_secondary_background_fill_hover", button_cancel_text_color="*button_secondary_text_color", checkbox_label_background_fill_selected="*primary_500", checkbox_label_background_fill_selected_dark="*primary_600", checkbox_border_width="1px", checkbox_border_color="*neutral_100", checkbox_border_color_dark="*neutral_600", checkbox_background_color_selected="*primary_600", checkbox_background_color_selected_dark="*primary_700", checkbox_border_color_focus="*primary_500", checkbox_border_color_focus_dark="*primary_600", checkbox_border_color_selected="*primary_600", checkbox_border_color_selected_dark="*primary_700", checkbox_label_text_color_selected="white", # Borders block_border_width="0px", panel_border_width="0px", ) custom_css = \ """ /* Hide sort buttons at gr.DataFrame */ .sort-button { display: none !important; } """ with gr.Blocks(theme=Soft(), css=custom_css) as demo: gr.HTML(description) with gr.Row(): with gr.Column(scale=1, variant="panel"): source = gr.Textbox( label="Input sentence", placeholder="Write a sentence to parse", show_label=False, lines=1, max_lines=5, autofocus=True ) submit = gr.Button("Submit", variant="primary") with gr.Column(scale=1, variant="panel"): dataset = gr.Dataset(components=[gr.Textbox(visible=False)], label="Input examples", samples=[ ["Thomassen er på vei til sin neste gjerning."], ["På toppen av dette kom de metodiske utfordringer."], ["Berntsen har påtatt seg en både viktig og vanskelig oppgave."], ["Ikke bare har det vært et problem, som han selv skriver i forordet, å bli klok på Borten."], ["Statsministeren i Norges første brede og varige borgerlige koalisjonsregjering etterlot seg timelange radiointervjuer med tidligere Dagsnytt-redaktør Per Bøhn og 70-80 stappfulle esker med usorterte papirer på loft og i kjeller hjemme på gården i Flå."] ] ) with gr.Column(scale=1, variant="panel"): #gr.Label("", show_label=False, container=False) table = gr.DataFrame([[""] * 42 for _ in range(8)], headers=[""] * 42, interactive=False, datatype="markdown") dependency_plot = gr.Plot(None, container=False) source.submit( fn=parse, inputs=[source], outputs=[dependency_plot, table], queue=True ) submit.click( fn=parse, inputs=[source], outputs=[dependency_plot, table], queue=True ) dataset.click( fn=lambda text: text[0], inputs=[dataset], outputs=[source] ).then( fn=parse, inputs=[source], outputs=[dependency_plot, table], queue=True ) demo.queue(max_size=32) demo.launch()