├── .github └── dependabot.yml ├── .gitignore ├── LICENSE ├── README.md ├── api ├── Dockerfile ├── Pipfile ├── Pipfile.lock ├── app.py ├── boot.sh ├── model_setup.py ├── plugin_watcher.py └── utils │ ├── abort.py │ ├── aio.py │ ├── article_download.py │ ├── cancel.py │ ├── pdf.py │ ├── request.py │ ├── semantic.py │ └── sentence.py ├── cli ├── __init__.py ├── config.py ├── docker_interface.py ├── exceptions.py ├── git_interface.py ├── plugins.py ├── schema.py └── utils.py ├── configure.py ├── debug_plugin ├── Pipfile ├── Pipfile.lock ├── metric.py ├── model_setup.py ├── summarizer.py └── sw-plugin-config.yaml ├── demo-files ├── example.jsonl ├── generated.txt ├── references.txt └── viewer-example.json ├── docker └── Dockerfile.plugin ├── docs ├── .gitignore ├── README.md ├── babel.config.js ├── docs │ ├── api-documentation.md │ ├── configuration.md │ ├── deployment.md │ ├── setup_quickstart.md │ ├── usage.md │ └── writing-a-plugin.md ├── docusaurus.config.js ├── package-lock.json ├── package.json ├── sidebars.js ├── src │ ├── css │ │ └── custom.css │ └── pages │ │ └── index.module.css └── static │ ├── .nojekyll │ ├── evaluation_input.gif │ ├── evaluation_plotter.gif │ ├── evaluation_scores.gif │ ├── evaluation_visualization.gif │ ├── img │ └── favicon.png │ ├── summarize_input.gif │ ├── summarize_pdf_extract.gif │ └── summarize_usage.gif ├── frontend ├── .dockerignore ├── .eslintrc.yml ├── .prettierrc.json ├── Dockerfile ├── Dockerfile.dev ├── nginx.conf ├── package-lock.json ├── package.json ├── postcss.config.js ├── public │ ├── index.html │ └── static │ │ └── example.jsonl ├── src │ ├── App.js │ ├── api.js │ ├── components │ │ ├── About.js │ │ ├── CompareTable.js │ │ ├── Evaluate.js │ │ ├── Model.js │ │ ├── OneHypRef.js │ │ ├── Result.js │ │ ├── Saved.js │ │ ├── ScoreWorkbench.js │ │ ├── Settings.js │ │ ├── Summarize.js │ │ ├── Upload.js │ │ └── utils │ │ │ ├── Arguments.js │ │ │ ├── Badge.js │ │ │ ├── Button.js │ │ │ ├── Card.js │ │ │ ├── ChooseFile.js │ │ │ ├── Container.js │ │ │ ├── Disclosure.js │ │ │ ├── Error.js │ │ │ ├── Form.js │ │ │ ├── FuzzySearch.js │ │ │ ├── Icons.js │ │ │ ├── Layout.js │ │ │ ├── Loading.js │ │ │ ├── Markup.js │ │ │ ├── Modal.js │ │ │ ├── Pagination.js │ │ │ ├── Radio.js │ │ │ ├── Range.js │ │ │ ├── Spinner.js │ │ │ ├── Table.js │ │ │ ├── Tabs.js │ │ │ ├── Text.js │ │ │ ├── Toggle.js │ │ │ └── Tooltip.js │ ├── config.js │ ├── contexts │ │ ├── DragContext.js │ │ ├── HoverContext.js │ │ ├── MetricsContext.js │ │ ├── SettingsContext.js │ │ └── SummarizersContext.js │ ├── css │ │ └── App.css │ ├── hooks │ │ ├── abortController.js │ │ ├── calculations.js │ │ ├── list.js │ │ ├── markup.js │ │ └── plugins.js │ ├── index.css │ ├── index.js │ ├── request.js │ └── utils │ │ ├── color.js │ │ ├── common.js │ │ ├── export.js │ │ ├── flatScores.js │ │ ├── markup.js │ │ ├── python.js │ │ ├── readFile.js │ │ └── saved.js └── tailwind.config.js ├── grobid ├── Dockerfile └── config.yaml ├── jsonl_converter.py ├── metrics ├── bartscore │ ├── Pipfile │ ├── Pipfile.lock │ ├── bart_score.py │ ├── metric.py │ ├── model_setup.py │ └── sw-plugin-config.yaml ├── bertscore │ ├── Pipfile │ ├── Pipfile.lock │ ├── metric.py │ ├── model_setup.py │ └── sw-plugin-config.yaml ├── bleu │ ├── Pipfile │ ├── Pipfile.lock │ ├── metric │ │ ├── __init__.py │ │ └── bleu.py │ ├── model_setup.py │ └── sw-plugin-config.yaml ├── bleurt │ ├── Pipfile │ ├── Pipfile.lock │ ├── metric.py │ ├── model_setup.py │ └── sw-plugin-config.yaml ├── cider │ ├── Pipfile │ ├── Pipfile.lock │ ├── license.txt │ ├── metric │ │ ├── __init__.py │ │ ├── cider.py │ │ └── cider_scorer.py │ ├── model_setup.py │ └── sw-plugin-config.yaml ├── greedy_matching │ ├── Pipfile │ ├── Pipfile.lock │ ├── metric.py │ ├── model_setup.py │ └── sw-plugin-config.yaml ├── meteor │ ├── Pipfile │ ├── Pipfile.lock │ ├── license.txt │ ├── metric │ │ ├── __init__.py │ │ ├── data │ │ │ └── paraphrase-en.gz │ │ ├── meteor-1.5.jar │ │ └── meteor.py │ ├── model_setup.py │ └── sw-plugin-config.yaml ├── moverscore │ ├── Pipfile │ ├── Pipfile.lock │ ├── metric.py │ ├── model_setup.py │ └── sw-plugin-config.yaml ├── rouge │ ├── Pipfile │ ├── Pipfile.lock │ ├── metric.py │ ├── model_setup.py │ └── sw-plugin-config.yaml ├── sbert │ ├── Pipfile │ ├── Pipfile.lock │ ├── metric.py │ ├── model_setup.py │ └── sw-plugin-config.yaml └── spacy_similarity │ ├── Pipfile │ ├── Pipfile.lock │ ├── metric.py │ ├── model_setup.py │ └── sw-plugin-config.yaml ├── other ├── interesting_findings.md └── static │ ├── BLEURT_BERTScore.png │ ├── MoverScore_BERTScore.png │ ├── SBERT_BERTScore.png │ ├── T5-11B_T5-3B.png │ ├── example_low-BARTScore_medium-BERTScore.png │ ├── example_medium-BARTScore_low-BERTScore.png │ ├── select_BLEURT_BARTScore.png │ ├── select_BLEU_BERTScore.png │ ├── semantic_lexical_variance.png │ └── spacy_similarity_BERTScore.png ├── plugin_config └── .gitkeep ├── plugin_server ├── app.py ├── application.py ├── argument_models.py ├── dev.boot.sh ├── errors.py ├── manager │ ├── request.py │ └── websocket.py ├── metric_factory.py ├── requirements.txt ├── summarizer_factory.py ├── utils │ ├── aio.py │ ├── cache.py │ ├── event.py │ ├── pipe.py │ └── thread.py └── workers.py ├── requirements.txt ├── schema └── .gitignore ├── summarizers ├── aosumm │ ├── Pipfile │ ├── Pipfile.lock │ ├── model_setup.py │ ├── summarizer │ │ ├── __init__.py │ │ ├── summarizer.py │ │ └── transformer_summarizer.py │ └── sw-plugin-config.yaml ├── argpagerank │ ├── Pipfile │ ├── Pipfile.lock │ ├── argument.py │ ├── model_setup.py │ ├── summarizer.py │ └── sw-plugin-config.yaml ├── bertsum │ ├── Pipfile │ ├── Pipfile.lock │ ├── model_setup.py │ ├── summarizer │ │ ├── BertParent.py │ │ ├── ClusterFeatures.py │ │ ├── ModelProcessor.py │ │ └── __init__.py │ └── sw-plugin-config.yaml ├── biasedtextrank │ ├── Pipfile │ ├── Pipfile.lock │ ├── model_setup.py │ ├── summarizer.py │ └── sw-plugin-config.yaml ├── brio │ ├── Pipfile │ ├── Pipfile.lock │ ├── model_setup.py │ ├── summarizer │ │ ├── __init__.py │ │ ├── summarizer.py │ │ └── transformer_summarizer.py │ └── sw-plugin-config.yaml ├── cliffsum │ ├── Pipfile │ ├── Pipfile.lock │ ├── model_setup.py │ ├── summarizer │ │ ├── __init__.py │ │ ├── download.py │ │ ├── summarizer.py │ │ └── transformer_summarizer.py │ └── sw-plugin-config.yaml ├── conclugen │ ├── Pipfile │ ├── Pipfile.lock │ ├── model_setup.py │ ├── summarizer │ │ ├── __init__.py │ │ ├── download.py │ │ ├── summarizer.py │ │ └── transformer_summarizer.py │ └── sw-plugin-config.yaml ├── coopsum │ ├── Pipfile │ ├── Pipfile.lock │ ├── model_setup.py │ ├── summarizer │ │ ├── __init__.py │ │ └── coop │ │ │ ├── config │ │ │ ├── bimeanvae │ │ │ │ ├── amzn.jsonnet │ │ │ │ └── yelp.jsonnet │ │ │ ├── optimus │ │ │ │ ├── amzn.jsonnet │ │ │ │ └── yelp.jsonnet │ │ │ └── utils.libsonnet │ │ │ ├── coop │ │ │ ├── __init__.py │ │ │ ├── models │ │ │ │ ├── __init__.py │ │ │ │ ├── base.py │ │ │ │ ├── bimeanvae.py │ │ │ │ ├── optimus.py │ │ │ │ └── util.py │ │ │ ├── reader.py │ │ │ ├── search.py │ │ │ ├── tokenizer.py │ │ │ ├── util.py │ │ │ └── vae.py │ │ │ └── scripts │ │ │ ├── get_summ.py │ │ │ ├── preprocess.py │ │ │ └── spm_train.py │ └── sw-plugin-config.yaml ├── featuresum │ ├── Pipfile │ ├── Pipfile.lock │ ├── model_setup.py │ ├── summarizer │ │ ├── __init__.py │ │ └── scores │ │ │ ├── __init__.py │ │ │ ├── average_lexical_connectivity.py │ │ │ ├── content_words_ratio.py │ │ │ ├── length.py │ │ │ ├── position.py │ │ │ ├── rank.py │ │ │ ├── special_tokens.py │ │ │ ├── tfidf.py │ │ │ ├── util.py │ │ │ └── word_overlap.py │ └── sw-plugin-config.yaml ├── gsum │ ├── Pipfile │ ├── Pipfile.lock │ ├── guided_summarization │ │ └── bart │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── fairseq │ │ │ ├── __init__.py │ │ │ ├── benchmark │ │ │ │ ├── __init__.py │ │ │ │ ├── dummy_lm.py │ │ │ │ ├── dummy_masked_lm.py │ │ │ │ └── dummy_model.py │ │ │ ├── binarizer.py │ │ │ ├── bleu.py │ │ │ ├── checkpoint_utils.py │ │ │ ├── clib │ │ │ │ ├── libbleu │ │ │ │ │ ├── libbleu.cpp │ │ │ │ │ └── module.cpp │ │ │ │ ├── libnat │ │ │ │ │ └── edit_dist.cpp │ │ │ │ └── libnat_cuda │ │ │ │ │ ├── binding.cpp │ │ │ │ │ ├── edit_dist.cu │ │ │ │ │ └── edit_dist.h │ │ │ ├── criterions │ │ │ │ ├── __init__.py │ │ │ │ ├── adaptive_loss.py │ │ │ │ ├── binary_cross_entropy.py │ │ │ │ ├── composite_loss.py │ │ │ │ ├── cross_entropy.py │ │ │ │ ├── fairseq_criterion.py │ │ │ │ ├── guided_label_smoothed_cross_entropy.py │ │ │ │ ├── label_smoothed_cross_entropy.py │ │ │ │ ├── label_smoothed_cross_entropy_with_alignment.py │ │ │ │ ├── legacy_masked_lm.py │ │ │ │ ├── masked_lm.py │ │ │ │ ├── nat_loss.py │ │ │ │ ├── sentence_prediction.py │ │ │ │ └── sentence_ranking.py │ │ │ ├── data │ │ │ │ ├── __init__.py │ │ │ │ ├── append_token_dataset.py │ │ │ │ ├── audio │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── raw_audio_dataset.py │ │ │ │ ├── backtranslation_dataset.py │ │ │ │ ├── base_wrapper_dataset.py │ │ │ │ ├── colorize_dataset.py │ │ │ │ ├── concat_dataset.py │ │ │ │ ├── concat_sentences_dataset.py │ │ │ │ ├── data_utils.py │ │ │ │ ├── data_utils_fast.cpp │ │ │ │ ├── data_utils_fast.pyx │ │ │ │ ├── denoising_dataset.py │ │ │ │ ├── dictionary.py │ │ │ │ ├── encoders │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── byte_bpe.py │ │ │ │ │ ├── byte_utils.py │ │ │ │ │ ├── bytes.py │ │ │ │ │ ├── characters.py │ │ │ │ │ ├── fastbpe.py │ │ │ │ │ ├── gpt2_bpe.py │ │ │ │ │ ├── gpt2_bpe_utils.py │ │ │ │ │ ├── hf_bert_bpe.py │ │ │ │ │ ├── hf_byte_bpe.py │ │ │ │ │ ├── moses_tokenizer.py │ │ │ │ │ ├── nltk_tokenizer.py │ │ │ │ │ ├── sentencepiece_bpe.py │ │ │ │ │ ├── space_tokenizer.py │ │ │ │ │ ├── subword_nmt_bpe.py │ │ │ │ │ └── utils.py │ │ │ │ ├── fairseq_dataset.py │ │ │ │ ├── guided_language_pair_dataset.py │ │ │ │ ├── id_dataset.py │ │ │ │ ├── indexed_dataset.py │ │ │ │ ├── iterators.py │ │ │ │ ├── language_pair_dataset.py │ │ │ │ ├── legacy │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── block_pair_dataset.py │ │ │ │ │ ├── masked_lm_dataset.py │ │ │ │ │ └── masked_lm_dictionary.py │ │ │ │ ├── list_dataset.py │ │ │ │ ├── lm_context_window_dataset.py │ │ │ │ ├── lru_cache_dataset.py │ │ │ │ ├── mask_tokens_dataset.py │ │ │ │ ├── monolingual_dataset.py │ │ │ │ ├── multi_corpus_sampled_dataset.py │ │ │ │ ├── nested_dictionary_dataset.py │ │ │ │ ├── noising.py │ │ │ │ ├── num_samples_dataset.py │ │ │ │ ├── numel_dataset.py │ │ │ │ ├── offset_tokens_dataset.py │ │ │ │ ├── pad_dataset.py │ │ │ │ ├── plasma_utils.py │ │ │ │ ├── prepend_dataset.py │ │ │ │ ├── prepend_token_dataset.py │ │ │ │ ├── raw_label_dataset.py │ │ │ │ ├── replace_dataset.py │ │ │ │ ├── resampling_dataset.py │ │ │ │ ├── roll_dataset.py │ │ │ │ ├── round_robin_zip_datasets.py │ │ │ │ ├── sharded_dataset.py │ │ │ │ ├── sort_dataset.py │ │ │ │ ├── strip_token_dataset.py │ │ │ │ ├── subsample_dataset.py │ │ │ │ ├── token_block_dataset.py │ │ │ │ ├── token_block_utils_fast.cpp │ │ │ │ ├── token_block_utils_fast.pyx │ │ │ │ ├── transform_eos_dataset.py │ │ │ │ ├── transform_eos_lang_pair_dataset.py │ │ │ │ └── truncate_dataset.py │ │ │ ├── distributed_utils.py │ │ │ ├── file_io.py │ │ │ ├── file_utils.py │ │ │ ├── hub_utils.py │ │ │ ├── incremental_decoding_utils.py │ │ │ ├── iterative_refinement_generator.py │ │ │ ├── legacy_distributed_data_parallel.py │ │ │ ├── logging │ │ │ │ ├── __init__.py │ │ │ │ ├── meters.py │ │ │ │ ├── metrics.py │ │ │ │ └── progress_bar.py │ │ │ ├── model_parallel │ │ │ │ ├── __init__.py │ │ │ │ ├── criterions │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── vocab_parallel_cross_entropy.py │ │ │ │ ├── megatron_trainer.py │ │ │ │ ├── models │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── transformer.py │ │ │ │ │ └── transformer_lm.py │ │ │ │ └── modules │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── multihead_attention.py │ │ │ │ │ └── transformer_layer.py │ │ │ ├── models │ │ │ │ ├── __init__.py │ │ │ │ ├── bart │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── guided_hub_interface.py │ │ │ │ │ ├── guided_model.py │ │ │ │ │ ├── hub_interface.py │ │ │ │ │ └── model.py │ │ │ │ ├── composite_encoder.py │ │ │ │ ├── distributed_fairseq_model.py │ │ │ │ ├── fairseq_decoder.py │ │ │ │ ├── fairseq_encoder.py │ │ │ │ ├── fairseq_incremental_decoder.py │ │ │ │ ├── fairseq_model.py │ │ │ │ ├── fconv.py │ │ │ │ ├── fconv_lm.py │ │ │ │ ├── fconv_self_att.py │ │ │ │ ├── guided_transformer.py │ │ │ │ ├── huggingface │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── hf_gpt2.py │ │ │ │ ├── lightconv.py │ │ │ │ ├── lightconv_lm.py │ │ │ │ ├── lstm.py │ │ │ │ ├── lstm_lm.py │ │ │ │ ├── masked_lm.py │ │ │ │ ├── model_utils.py │ │ │ │ ├── multilingual_transformer.py │ │ │ │ ├── nat │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── cmlm_transformer.py │ │ │ │ │ ├── fairseq_nat_model.py │ │ │ │ │ ├── insertion_transformer.py │ │ │ │ │ ├── iterative_nonautoregressive_transformer.py │ │ │ │ │ ├── levenshtein_transformer.py │ │ │ │ │ ├── levenshtein_utils.py │ │ │ │ │ ├── nat_crf_transformer.py │ │ │ │ │ ├── nonautoregressive_ensembles.py │ │ │ │ │ └── nonautoregressive_transformer.py │ │ │ │ ├── roberta │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── alignment_utils.py │ │ │ │ │ ├── hub_interface.py │ │ │ │ │ ├── model.py │ │ │ │ │ ├── model_camembert.py │ │ │ │ │ └── model_xlmr.py │ │ │ │ ├── transformer.py │ │ │ │ ├── transformer_from_pretrained_xlm.py │ │ │ │ ├── transformer_lm.py │ │ │ │ └── wav2vec.py │ │ │ ├── modules │ │ │ │ ├── __init__.py │ │ │ │ ├── adaptive_input.py │ │ │ │ ├── adaptive_softmax.py │ │ │ │ ├── beamable_mm.py │ │ │ │ ├── character_token_embedder.py │ │ │ │ ├── conv_tbc.py │ │ │ │ ├── cross_entropy.py │ │ │ │ ├── cuda_utils.cu │ │ │ │ ├── downsampled_multihead_attention.py │ │ │ │ ├── dynamic_convolution.py │ │ │ │ ├── dynamic_crf_layer.py │ │ │ │ ├── dynamicconv_layer │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── cuda_function_gen.py │ │ │ │ │ ├── dynamicconv_cuda.cpp │ │ │ │ │ ├── dynamicconv_cuda.cuh │ │ │ │ │ ├── dynamicconv_cuda_kernel.cu │ │ │ │ │ ├── dynamicconv_layer.py │ │ │ │ │ ├── dynamiconv_cpu.cpp │ │ │ │ │ └── setup.py │ │ │ │ ├── fp32_group_norm.py │ │ │ │ ├── gelu.py │ │ │ │ ├── grad_multiply.py │ │ │ │ ├── guided_transformer_layer.py │ │ │ │ ├── gumbel_vector_quantizer.py │ │ │ │ ├── kmeans_vector_quantizer.py │ │ │ │ ├── layer_norm.py │ │ │ │ ├── learned_positional_embedding.py │ │ │ │ ├── lightconv_layer │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── cuda_function_gen.py │ │ │ │ │ ├── lightconv_cuda.cpp │ │ │ │ │ ├── lightconv_cuda.cuh │ │ │ │ │ ├── lightconv_cuda_kernel.cu │ │ │ │ │ ├── lightconv_layer.py │ │ │ │ │ └── setup.py │ │ │ │ ├── lightweight_convolution.py │ │ │ │ ├── linearized_convolution.py │ │ │ │ ├── multihead_attention.py │ │ │ │ ├── positional_embedding.py │ │ │ │ ├── scalar_bias.py │ │ │ │ ├── sinusoidal_positional_embedding.py │ │ │ │ ├── sparse_multihead_attention.py │ │ │ │ ├── sparse_transformer_sentence_encoder.py │ │ │ │ ├── sparse_transformer_sentence_encoder_layer.py │ │ │ │ ├── transformer_layer.py │ │ │ │ ├── transformer_sentence_encoder.py │ │ │ │ ├── transformer_sentence_encoder_layer.py │ │ │ │ ├── unfold.py │ │ │ │ └── vggblock.py │ │ │ ├── nan_detector.py │ │ │ ├── optim │ │ │ │ ├── __init__.py │ │ │ │ ├── adadelta.py │ │ │ │ ├── adafactor.py │ │ │ │ ├── adagrad.py │ │ │ │ ├── adam.py │ │ │ │ ├── adamax.py │ │ │ │ ├── bmuf.py │ │ │ │ ├── fairseq_optimizer.py │ │ │ │ ├── fp16_optimizer.py │ │ │ │ ├── fused_adam.py │ │ │ │ ├── fused_lamb.py │ │ │ │ ├── lr_scheduler │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── cosine_lr_scheduler.py │ │ │ │ │ ├── fairseq_lr_scheduler.py │ │ │ │ │ ├── fixed_schedule.py │ │ │ │ │ ├── inverse_square_root_schedule.py │ │ │ │ │ ├── polynomial_decay_schedule.py │ │ │ │ │ ├── reduce_lr_on_plateau.py │ │ │ │ │ ├── tri_stage_lr_scheduler.py │ │ │ │ │ └── triangular_lr_scheduler.py │ │ │ │ ├── nag.py │ │ │ │ └── sgd.py │ │ │ ├── options.py │ │ │ ├── pdb.py │ │ │ ├── registry.py │ │ │ ├── search.py │ │ │ ├── sequence_generator.py │ │ │ ├── sequence_scorer.py │ │ │ ├── tasks │ │ │ │ ├── __init__.py │ │ │ │ ├── audio_pretraining.py │ │ │ │ ├── cross_lingual_lm.py │ │ │ │ ├── denoising.py │ │ │ │ ├── fairseq_task.py │ │ │ │ ├── guided_translation.py │ │ │ │ ├── language_modeling.py │ │ │ │ ├── legacy_masked_lm.py │ │ │ │ ├── masked_lm.py │ │ │ │ ├── multilingual_denoising.py │ │ │ │ ├── multilingual_masked_lm.py │ │ │ │ ├── multilingual_translation.py │ │ │ │ ├── semisupervised_translation.py │ │ │ │ ├── sentence_prediction.py │ │ │ │ ├── sentence_ranking.py │ │ │ │ ├── translation.py │ │ │ │ ├── translation_from_pretrained_bart.py │ │ │ │ ├── translation_from_pretrained_xlm.py │ │ │ │ └── translation_lev.py │ │ │ ├── tokenizer.py │ │ │ ├── trainer.py │ │ │ └── utils.py │ │ │ ├── fairseq_cli │ │ │ ├── __init__.py │ │ │ ├── generate.py │ │ │ ├── guided_preprocess.py │ │ │ ├── interactive.py │ │ │ ├── preprocess.py │ │ │ ├── score.py │ │ │ ├── train.py │ │ │ └── validate.py │ │ │ ├── generate.py │ │ │ ├── hubconf.py │ │ │ ├── interactive.py │ │ │ ├── preprocess.py │ │ │ ├── requirements.txt │ │ │ ├── score.py │ │ │ ├── setup.py │ │ │ ├── test.py │ │ │ ├── train.py │ │ │ ├── validate.py │ │ │ ├── z_bin.sh │ │ │ ├── z_bpe.sh │ │ │ ├── z_test.py │ │ │ ├── z_test.sh │ │ │ └── z_train.sh │ ├── model_setup.py │ ├── summarizer.py │ └── sw-plugin-config.yaml ├── lobart │ ├── Pipfile │ ├── Pipfile.lock │ ├── localattn.py │ ├── model_setup.py │ ├── summarizer.py │ └── sw-plugin-config.yaml ├── longformer2roberta │ ├── Pipfile │ ├── Pipfile.lock │ ├── model_setup.py │ ├── summarizer │ │ └── __init__.py │ └── sw-plugin-config.yaml ├── neuralsum │ ├── Pipfile │ ├── Pipfile.lock │ ├── model_setup.py │ ├── summarizer │ │ ├── __init__.py │ │ ├── summarizer.py │ │ └── transformer_summarizer.py │ └── sw-plugin-config.yaml ├── newspaper3k │ ├── Pipfile │ ├── Pipfile.lock │ ├── model_setup.py │ ├── summarizer │ │ └── __init__.py │ └── sw-plugin-config.yaml ├── pmisum │ ├── Pipfile │ ├── Pipfile.lock │ ├── gpt2.py │ ├── model_setup.py │ ├── summarizer.py │ └── sw-plugin-config.yaml ├── positionrank │ ├── model_setup.py │ ├── requirements.txt │ ├── summarizer.py │ └── sw-plugin-config.yaml ├── schnitsum │ ├── Pipfile │ ├── Pipfile.lock │ ├── model_setup.py │ ├── summarizer.py │ └── sw-plugin-config.yaml ├── textrank │ ├── model_setup.py │ ├── requirements.txt │ ├── summarizer.py │ └── sw-plugin-config.yaml └── topicrank │ ├── model_setup.py │ ├── requirements.txt │ ├── summarizer.py │ └── sw-plugin-config.yaml ├── summary-workbench.py ├── sw-config.yaml ├── templates ├── docker │ ├── api.yaml │ ├── frontend.yaml │ ├── grobid.yaml │ └── mongo.yaml └── kubernetes │ ├── basic │ ├── api.yaml │ ├── frontend.yaml │ ├── grobid.yaml │ ├── ingress.yaml │ ├── mongodb.yaml │ └── proxy.yaml │ ├── plugin.yaml │ ├── token_secrets.yaml │ └── volumes.yaml └── version.json /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: [] # No automatic version updates because there is no CI to verify them. 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright © 2021 Dominik Schwabe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to whom the 10 | Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included 13 | in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 21 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Summary Workbench 2 | 3 | Unifying the application and evaluation of text summarization models. [[Paper](https://arxiv.org/pdf/2210.09587.pdf)] [[Documentation](https://webis-de.github.io/summary-workbench/)] [[Live Demo](https://tldr.demo.webis.de)] 4 | >Accepted at EMNLP 2022 (Demo track). :tada: :tada: 5 | 6 | #### :loudspeaker: Updates (3-12-2022) 7 | 1. Integrated 2 new models and their variants (6 in total): [BRIO](https://arxiv.org/abs/2203.16804) trained on news, [Schnitsum](https://github.com/sobamchan/schnitsum) trained on scholary documents. 8 | 2. Integrated [contrastive search](https://huggingface.co/blog/introducing-csearch) for more fluent summaries. User can now toggle between regular and contrasitve search for supported models. 9 | 2. Improvements to the UI responsiveness on smaller devices. 10 | 11 | ## Summarize 12 | 13 | ### Create a Request 14 | 15 | ![Create a Request](docs/static/summarize_input.gif) 16 | 17 | ### Inspect the Results 18 | 19 | ![Inspect the Results](docs/static/summarize_usage.gif) 20 | 21 | # Evaluate 22 | 23 | ### Create a Request 24 | 25 | ![Create a Request](docs/static/evaluation_input.gif) 26 | 27 | ### Inspect the Results 28 | 29 | #### Scores 30 | 31 | ![Scores](docs/static/evaluation_scores.gif) 32 | 33 | #### Visualize Text Examples 34 | 35 | ![Visualize Text Examples](docs/static/evaluation_visualization.gif) 36 | 37 | #### Plot Scores against each other 38 | 39 | ![Plot Scores against each other](docs/static/evaluation_plotter.gif) 40 | -------------------------------------------------------------------------------- /api/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10 2 | 3 | COPY . /app 4 | WORKDIR /app 5 | RUN pip install pipenv && pipenv install --system 6 | RUN python model_setup.py 7 | 8 | CMD uvicorn app:app --host 0.0.0.0 --port 5000 9 | 10 | 11 | # vi: ft=dockerfile 12 | -------------------------------------------------------------------------------- /api/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | doc2json = {git = "https://github.com/allenai/s2orc-doc2json"} 8 | newspaper3k = "*" 9 | nltk = "*" 10 | beautifulsoup4 = "*" 11 | boto3 = "*" 12 | aiohttp = "*" 13 | lxml = "*" 14 | python-multipart = "*" 15 | python-magic = "*" 16 | latex2mathml = "*" 17 | fastapi = "*" 18 | uvicorn = "*" 19 | spacy = "*" 20 | pymongo = {extras = ["srv"], version = "*"} 21 | 22 | [dev-packages] 23 | 24 | [requires] 25 | python_version = "3.11" 26 | -------------------------------------------------------------------------------- /api/boot.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | export VIRTUAL_ENV=/root/.venv 4 | 5 | VENV_EXISTS=false 6 | if [[ -r $VIRTUAL_ENV/pyvenv.cfg ]]; then 7 | VENV_PYTHON_VERSION=$(sed -n '/^version/ s/[^0-9.]//g p' $VIRTUAL_ENV/pyvenv.cfg) 8 | GLOBAL_PYTHON_VERSION=$(python --version | sed -n 's/[^0-9.]//g p') 9 | if [[ $VENV_PYTHON_VERSION == $GLOBAL_PYTHON_VERSION ]]; then 10 | VENV_EXISTS=true 11 | fi 12 | fi 13 | 14 | if [[ $VENV_EXISTS == "false" ]]; then 15 | echo "no valid virtualenv found, creating ..." 16 | rm -rf $VIRTUAL_ENV 17 | python -m venv $VIRTUAL_ENV || exit 1 18 | fi 19 | 20 | source $VIRTUAL_ENV/bin/activate || exit 1 21 | 22 | cd /app || exit 1 23 | 24 | pip install pipenv || exit 1 25 | pipenv install || exit 1 26 | 27 | python model_setup.py 28 | uvicorn app:app --app-dir /app --host 0.0.0.0 --port 5000 --reload 29 | -------------------------------------------------------------------------------- /api/model_setup.py: -------------------------------------------------------------------------------- 1 | import nltk 2 | import spacy 3 | 4 | SPACY_MODEL = "en_core_web_md" 5 | 6 | 7 | def setup(): 8 | spacy.cli.download(SPACY_MODEL) 9 | nltk.download("punkt") 10 | 11 | 12 | if __name__ == "__main__": 13 | setup() 14 | -------------------------------------------------------------------------------- /api/utils/abort.py: -------------------------------------------------------------------------------- 1 | from sys import stderr 2 | 3 | 4 | def aborter(message): 5 | if not hasattr(aborter, "was_aborted"): 6 | aborter.was_aborted = True 7 | print(message, file=stderr) 8 | exit(1) 9 | -------------------------------------------------------------------------------- /api/utils/aio.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from functools import wraps 3 | 4 | 5 | async def wait_first(coros): 6 | futures = [asyncio.ensure_future(c) for c in coros] 7 | try: 8 | done, _ = await asyncio.wait(futures, return_when=asyncio.FIRST_COMPLETED) 9 | done_future = done.pop() 10 | done_coro = coros[futures.index(done_future)] 11 | return done_future.result(), done_coro 12 | finally: 13 | for future in futures: 14 | future.cancel() 15 | for future in futures: 16 | try: 17 | await future 18 | except: 19 | pass 20 | 21 | 22 | async def until_true(func, interval=1): 23 | while not await func(): 24 | await asyncio.sleep(interval) 25 | 26 | 27 | async def finish_or_condition(wrapped_coro, condition_func, interval=1): 28 | result, coro = await wait_first( 29 | [wrapped_coro, until_true(condition_func, interval=interval)] 30 | ) 31 | if coro is wrapped_coro: 32 | return result 33 | return None 34 | 35 | 36 | def to_future(func): 37 | @wraps(func) 38 | def wrapper(*args, **kwargs): 39 | return asyncio.ensure_future(func(*args, **kwargs)) 40 | 41 | return wrapper 42 | 43 | 44 | def to_threaded(func): 45 | @wraps(func) 46 | async def wrapper(*args, **kwargs): 47 | return await asyncio.to_thread(func, *args, **kwargs) 48 | 49 | return wrapper 50 | -------------------------------------------------------------------------------- /api/utils/article_download.py: -------------------------------------------------------------------------------- 1 | from newspaper import Article 2 | from utils.aio import to_threaded 3 | 4 | 5 | @to_threaded 6 | def download_article(url): 7 | article = Article(url) 8 | article.download() 9 | article.parse() 10 | return {"text": article.text, "title": article.title} 11 | -------------------------------------------------------------------------------- /api/utils/cancel.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | 3 | from fastapi import Response 4 | from utils.aio import finish_or_condition 5 | 6 | 7 | def disconnect_checker(request): 8 | async def checker(): 9 | return await request.is_disconnected() 10 | 11 | return checker 12 | 13 | 14 | def cancel_on_disconnect(func): 15 | @wraps(func) 16 | async def wrapper(request, *args, **kwargs): 17 | result = await finish_or_condition( 18 | func(request, *args, **kwargs), disconnect_checker(request) 19 | ) 20 | if result is None: 21 | return Response(status_code=204) 22 | return result 23 | 24 | return wrapper 25 | -------------------------------------------------------------------------------- /api/utils/request.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | import aiohttp 4 | from utils.aio import wait_first 5 | 6 | 7 | async def _fetch(session, parse_as_json=True, **kwargs): 8 | async with session.request(**kwargs) as response: 9 | if parse_as_json: 10 | return await response.json() 11 | return await response.text() 12 | 13 | 14 | timeout = aiohttp.ClientTimeout(total=None, connect=10, sock_connect=10, sock_read=None) 15 | 16 | 17 | async def request(request_data, cancel_event=None, return_exceptions=True): 18 | async with aiohttp.ClientSession(timeout=timeout) as session: 19 | requests = [] 20 | for data in request_data: 21 | data = data.copy() 22 | method = data.get("method") 23 | if method is None: 24 | data["method"] = "GET" if data.get("json") is None else "POST" 25 | requests.append(_fetch(session, **data)) 26 | gather_coro = asyncio.gather(*requests, return_exceptions=return_exceptions) 27 | coros = [gather_coro] 28 | if cancel_event is not None: 29 | coros.append(cancel_event.wait()) 30 | result, coro = await wait_first(coros) 31 | if coro is gather_coro: 32 | return result 33 | raise asyncio.CancelledError() 34 | -------------------------------------------------------------------------------- /api/utils/semantic.py: -------------------------------------------------------------------------------- 1 | import spacy 2 | from model_setup import SPACY_MODEL 3 | from utils.aio import to_threaded 4 | 5 | 6 | class SemanticSimilarity: 7 | def __init__(self): 8 | self.nlp = spacy.load(SPACY_MODEL) 9 | 10 | def _get_sentences(self, text): 11 | if isinstance(text, str): 12 | text = [s for s in self.nlp(text).sents if any(t.is_alpha for t in s)] 13 | else: 14 | text = [self.nlp(s) for s in text] 15 | return text 16 | 17 | def evaluate(self, document, summary): 18 | document_sents = self._get_sentences(document) 19 | summary_sents = self._get_sentences(summary) 20 | return { 21 | "documentSentences": [doc_sent.text_with_ws for doc_sent in document_sents], 22 | "summarySentences": [sum_sent.text_with_ws for sum_sent in summary_sents], 23 | "scores": [ 24 | [doc_sent.similarity(sum_sent) for doc_sent in document_sents] 25 | for sum_sent in summary_sents 26 | ], 27 | } 28 | 29 | 30 | evaluator = SemanticSimilarity() 31 | 32 | 33 | @to_threaded 34 | def semantic_similarity(sentences, summary): 35 | return evaluator.evaluate(sentences, summary) 36 | -------------------------------------------------------------------------------- /api/utils/sentence.py: -------------------------------------------------------------------------------- 1 | import nltk 2 | from utils.aio import to_threaded 3 | 4 | 5 | @to_threaded 6 | def sentence_split(text): 7 | return nltk.sent_tokenize(text) 8 | -------------------------------------------------------------------------------- /cli/config.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | DEPLOY_PATH = Path("./deploy") 4 | KUBERNETES_TEMPLATES_PATH = Path("./templates/kubernetes") 5 | DOCKER_TEMPLATES_PATH = Path("./templates/docker") 6 | PLUGIN_CONFIG_PATH = Path("./plugin_config/plugin_config.json") 7 | DOCKER_COMPOSE_YAML_PATH = Path("./docker-compose.yaml") 8 | PLUGIN_DOCKERFILE_PATH = Path("./docker/Dockerfile.plugin") 9 | CONTAINER_PLUGIN_FILES_PATH = Path("/summary_workbench_plugin_files") 10 | CONTAINER_PLUGIN_SERVER_PATH = Path("/summary_workbench_plugin_server") 11 | DEV_BOOT_PATH = CONTAINER_PLUGIN_SERVER_PATH / "dev.boot.sh" 12 | PLUGIN_SERVER_PATH = Path("./plugin_server").absolute() 13 | REMOTE_PLUGIN_FOLDER = Path("~/.summary_workbench_plugins").expanduser() 14 | REQUIRED_FILE_GROUPS = [{"Pipfile.lock", "Pipfile", "requirements.txt"}] 15 | SCHEMA_FOLDER = Path("./schema") 16 | DEFAULT_CONFIG = "sw-config.yaml" 17 | DEFAULT_PLUGIN_CONFIG = "sw-plugin-config.yaml" 18 | DEFAULTS = {} 19 | 20 | SETUP_SERVER_FILES_DOCKER_FILE = f""" 21 | WORKDIR {CONTAINER_PLUGIN_SERVER_PATH} 22 | COPY . . 23 | RUN pip install -r requirements.txt 24 | """ 25 | 26 | SETUP_PLUGIN_FILES_DOCKER_FILE = f""" 27 | {{environment}} 28 | WORKDIR {CONTAINER_PLUGIN_FILES_PATH} 29 | COPY . . 30 | RUN if [ -f Pipfile.lock -o -f Pipfile ]; then pip install pipenv && pipenv install --system; else pip install -r requirements.txt; fi 31 | RUN python model_setup.py 32 | WORKDIR {CONTAINER_PLUGIN_FILES_PATH} 33 | CMD ["uvicorn", "--app-dir", "/summary_workbench_plugin_server", "app:app", "--host", "0.0.0.0", "--port", "5000"] 34 | """ 35 | -------------------------------------------------------------------------------- /cli/git_interface.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import git 4 | import giturlparse 5 | from termcolor import colored 6 | 7 | from .config import REMOTE_PLUGIN_FOLDER 8 | from .exceptions import InvalidGitLinkError 9 | 10 | 11 | def is_github_link(source): 12 | return giturlparse.validate(source) 13 | 14 | 15 | def from_github(source): 16 | p = giturlparse.parse(source) 17 | plugin_name = f"{p.platform}---{p.owner}---{p.repo}---{p.branch}" 18 | plugin_path = REMOTE_PLUGIN_FOLDER / plugin_name 19 | if not plugin_path.exists(): 20 | print(f"cloning {colored(source, 'green')} to {colored(plugin_path, 'green')}") 21 | print("if a login prompt shows up, the url might be wrong") 22 | plugin_path.parent.mkdir(exist_ok=True, parents=True) 23 | git.Repo.clone_from(source, plugin_path) 24 | return plugin_path, p.owner 25 | 26 | def pull(path): 27 | print(f"pulling {colored(path, 'green')}") 28 | git.Repo(path).remotes[0].pull() 29 | 30 | def resolve_source(source): 31 | if isinstance(source, Path): 32 | return source.absolute(), None 33 | if not is_github_link(source): 34 | raise InvalidGitLinkError("the link is invalid", source) 35 | return from_github(source) 36 | -------------------------------------------------------------------------------- /configure.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from cli import main 4 | 5 | if __name__ == "__main__": 6 | main() 7 | -------------------------------------------------------------------------------- /debug_plugin/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | ipython = "*" 10 | ipdb = "*" 11 | 12 | [requires] 13 | python_version = "3.10" 14 | -------------------------------------------------------------------------------- /debug_plugin/model_setup.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webis-de/summary-workbench/c17d28b89bb43f43f99abc061c3c8a5c4eaf1971/debug_plugin/model_setup.py -------------------------------------------------------------------------------- /debug_plugin/sw-plugin-config.yaml: -------------------------------------------------------------------------------- 1 | version: "1.0" 2 | name: Debug 3 | metadata: {} 4 | -------------------------------------------------------------------------------- /docker/Dockerfile.plugin: -------------------------------------------------------------------------------- 1 | ARG python_version=3.10 2 | 3 | FROM python:${python_version} 4 | 5 | RUN apt update && apt --yes install default-jre-headless 6 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /docs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/docs/api-documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Api Documentation 3 | sidebar_position: 5 4 | --- 5 | 6 | The api documentation can be found under `/api/docs` (e.g. ) and `/api/redoc` (e.g. ). 7 | 8 | The script `summary-workbench.py`, which can be found in the root of the repository, can be used to access the application from the commandline. 9 | It can also be imported in python files to build applications based on Summary Workbench. 10 | -------------------------------------------------------------------------------- /docs/docs/deployment.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Deployment 3 | sidebar_position: 4 4 | --- 5 | 6 | The deployment of the application is not generic and is tailored to our custom Kubernetes cluster. 7 | 8 | 1. Add the following to your plugin `sw-config.yaml`: 9 | 10 | ```yaml title=sw-config.yaml 11 | docker_username: 12 | deploy: 13 | host: 14 | resources: # this option can be omitted 15 | requests: 16 | cpu: "500m" 17 | limits: 18 | cpu: "4000m" 19 | ``` 20 | 21 | :::note 22 | 23 | The `resources` field is exactly like described in . 24 | It is used to limit the deployed containers. 25 | 26 | ::: 27 | 28 | 2. Login to your Dockerhub account. 29 | 3. Build the necessary images and push them to dockerhub with `./configure.py build --all` and `./configure.py push --all`. 30 | 4. Run `./configure.py gen-kubernetes` to generate the deployment files under `deploy/`. 31 | 5. Use `kubectl` to deploy the application (e.g. `kubectl apply -f`). 32 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "^2.2.0", 18 | "@docusaurus/preset-classic": "^2.2.0" 19 | }, 20 | "browserslist": { 21 | "production": [ 22 | ">0.5%", 23 | "not dead", 24 | "not op_mini all" 25 | ], 26 | "development": [ 27 | "last 1 chrome version", 28 | "last 1 firefox version", 29 | "last 1 safari version" 30 | ] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /docs/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | // @ts-check 13 | 14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 15 | const sidebars = { 16 | // By default, Docusaurus generates a sidebar from the docs folder structure 17 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], 18 | 19 | // But you can create a sidebar manually 20 | /* 21 | tutorialSidebar: [ 22 | { 23 | type: 'category', 24 | label: 'Tutorial', 25 | items: ['hello'], 26 | }, 27 | ], 28 | */ 29 | }; 30 | 31 | module.exports = sidebars; 32 | -------------------------------------------------------------------------------- /docs/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #2e8555; 10 | --ifm-color-primary-dark: #29784c; 11 | --ifm-color-primary-darker: #277148; 12 | --ifm-color-primary-darkest: #205d3b; 13 | --ifm-color-primary-light: #33925d; 14 | --ifm-color-primary-lighter: #359962; 15 | --ifm-color-primary-lightest: #3cad6e; 16 | --ifm-code-font-size: 95%; 17 | } 18 | 19 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 20 | [data-theme='dark'] { 21 | --ifm-color-primary: #25c2a0; 22 | --ifm-color-primary-dark: #21af90; 23 | --ifm-color-primary-darker: #1fa588; 24 | --ifm-color-primary-darkest: #1a8870; 25 | --ifm-color-primary-light: #29d5b0; 26 | --ifm-color-primary-lighter: #32d8b4; 27 | --ifm-color-primary-lightest: #4fddbf; 28 | } 29 | 30 | .docusaurus-highlight-code-line { 31 | background-color: rgba(0, 0, 0, 0.1); 32 | display: block; 33 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 34 | padding: 0 var(--ifm-pre-padding); 35 | } 36 | 37 | [data-theme='dark'] .docusaurus-highlight-code-line { 38 | background-color: rgba(0, 0, 0, 0.3); 39 | } 40 | -------------------------------------------------------------------------------- /docs/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /docs/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webis-de/summary-workbench/c17d28b89bb43f43f99abc061c3c8a5c4eaf1971/docs/static/.nojekyll -------------------------------------------------------------------------------- /docs/static/evaluation_input.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webis-de/summary-workbench/c17d28b89bb43f43f99abc061c3c8a5c4eaf1971/docs/static/evaluation_input.gif -------------------------------------------------------------------------------- /docs/static/evaluation_plotter.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webis-de/summary-workbench/c17d28b89bb43f43f99abc061c3c8a5c4eaf1971/docs/static/evaluation_plotter.gif -------------------------------------------------------------------------------- /docs/static/evaluation_scores.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webis-de/summary-workbench/c17d28b89bb43f43f99abc061c3c8a5c4eaf1971/docs/static/evaluation_scores.gif -------------------------------------------------------------------------------- /docs/static/evaluation_visualization.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webis-de/summary-workbench/c17d28b89bb43f43f99abc061c3c8a5c4eaf1971/docs/static/evaluation_visualization.gif -------------------------------------------------------------------------------- /docs/static/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webis-de/summary-workbench/c17d28b89bb43f43f99abc061c3c8a5c4eaf1971/docs/static/img/favicon.png -------------------------------------------------------------------------------- /docs/static/summarize_input.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webis-de/summary-workbench/c17d28b89bb43f43f99abc061c3c8a5c4eaf1971/docs/static/summarize_input.gif -------------------------------------------------------------------------------- /docs/static/summarize_pdf_extract.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webis-de/summary-workbench/c17d28b89bb43f43f99abc061c3c8a5c4eaf1971/docs/static/summarize_pdf_extract.gif -------------------------------------------------------------------------------- /docs/static/summarize_usage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webis-de/summary-workbench/c17d28b89bb43f43f99abc061c3c8a5c4eaf1971/docs/static/summarize_usage.gif -------------------------------------------------------------------------------- /frontend/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /frontend/.eslintrc.yml: -------------------------------------------------------------------------------- 1 | env: 2 | browser: true 3 | node: true 4 | extends: 5 | - airbnb 6 | - prettier 7 | - eslint:recommended 8 | - plugin:react/recommended 9 | - plugin:react-hooks/recommended 10 | parser: "@babel/eslint-parser" 11 | parserOptions: 12 | requireConfigFile: false 13 | ecmaFeatures: 14 | jsx: true 15 | ecmaVersion: 12 16 | sourceType: module 17 | babelOptions: 18 | presets: 19 | - "@babel/preset-react" 20 | settings: 21 | react: 22 | version: detect 23 | plugins: 24 | - prettier 25 | - react 26 | - react-hooks 27 | - simple-import-sort 28 | - import 29 | - promise 30 | rules: 31 | react/function-component-definition: 0 32 | react/jsx-curly-brace-presence: 0 33 | react/react-in-jsx-scope: 0 34 | react/jsx-filename-extension: 0 35 | react/no-array-index-key: 0 36 | react/prop-types: 0 37 | import/prefer-default-export: 0 38 | react/button-has-type: 0 39 | no-underscore-dangle: 0 40 | react/jsx-props-no-spreading: 0 41 | no-plusplus: 0 42 | react/display-name: 0 43 | jsx-a11y/anchor-has-content: 0 44 | jsx-a11y/control-has-associated-label: 0 45 | jsx-a11y/label-has-associated-control: 0 46 | jsx-a11y/no-autofocus: 0 47 | jsx-a11y/click-events-have-key-events: 0 48 | jsx-a11y/no-static-element-interactions: 0 49 | no-constant-condition: 0 50 | import/no-extraneous-dependencies: 0 51 | no-restricted-syntax: 0 52 | max-classes-per-file: 0 53 | no-unused-vars: "warn" 54 | -------------------------------------------------------------------------------- /frontend/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "printWidth": 100 4 | } 5 | -------------------------------------------------------------------------------- /frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:19-slim as build-stage 2 | 3 | WORKDIR /app 4 | COPY . /app/ 5 | RUN npm ci 6 | RUN npm run build 7 | 8 | FROM nginx:alpine 9 | COPY ./nginx.conf /etc/nginx/nginx.conf 10 | COPY --from=build-stage /app/build /srv/static 11 | -------------------------------------------------------------------------------- /frontend/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM node:19-slim 2 | -------------------------------------------------------------------------------- /frontend/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | 3 | events { 4 | worker_connections 1024; 5 | } 6 | 7 | http { 8 | server { 9 | listen 80; 10 | 11 | include /etc/nginx/mime.types; 12 | default_type application/octet-stream; 13 | 14 | charset utf-8; 15 | charset_types *; 16 | gzip on; 17 | gzip_types *; 18 | sendfile on; 19 | 20 | root /srv/static; 21 | try_files $uri /index.html; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /frontend/src/api.js: -------------------------------------------------------------------------------- 1 | import { get, post, wrappedFetch } from "./request"; 2 | 3 | const getMetricsRequest = () => get("/api/metrics"); 4 | const getSummarizersRequest = () => get("/api/summarizers"); 5 | 6 | const evaluateRequest = (metrics, references, hypotheses, abortController) => 7 | post("/api/evaluate", { metrics, references, hypotheses }, { abortController }); 8 | 9 | const summarizeRequest = (documents, summarizers, ratio, bulk, abortController) => { 10 | const data = { documents, summarizers, ratio, add_metadata: true }; 11 | if (!bulk) { 12 | data.split_sentences = true; 13 | } 14 | return post("/api/summarize", data, { abortController }); 15 | }; 16 | 17 | const pdfExtractRequest = async (pdf) => { 18 | const res = await wrappedFetch("/api/pdf/extract", { 19 | method: "POST", 20 | body: pdf, 21 | }); 22 | if (res.ok) { 23 | const data = await res.json(); 24 | if (data.error) throw new Error(data.error); 25 | return data; 26 | } 27 | throw new Error(`request failed with status ${res.status}`); 28 | }; 29 | 30 | const semanticRequest = async (sentences, summary) => 31 | post("/api/semantic_similarity", { sentences, summary }); 32 | 33 | const feedbackRequest = (summarizer, summary, reference, url, feedback) => { 34 | let json = { summarizer, summary, reference, feedback }; 35 | if (url !== null) json = { url, ...json }; 36 | return post("/api/feedback", json); 37 | }; 38 | 39 | export { 40 | getMetricsRequest, 41 | getSummarizersRequest, 42 | evaluateRequest, 43 | summarizeRequest, 44 | pdfExtractRequest, 45 | feedbackRequest, 46 | semanticRequest, 47 | }; 48 | -------------------------------------------------------------------------------- /frontend/src/components/OneHypRef.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | 3 | import { Textarea } from "./utils/Form"; 4 | import { FlexResponsive } from "./utils/Layout"; 5 | 6 | const TextField = ({ value, setValue, placeholder }) => ( 7 |