Presentation

code
analysis
llm
Author

Nora Kristiansen, Torjørn Vatnelid

Published

April 15, 2024

This is a post with executable code.

%pip install langchain langchain-community langchain-openai unstructured openai pypdf -Uq

[notice] A new release of pip is available: 23.2.1 -> 24.0
[notice] To update, run: pip3 install --upgrade pip
Note: you may need to restart the kernel to use updated packages.
from langchain.document_loaders import PyPDFLoader, UnstructuredExcelLoader, UnstructuredWordDocumentLoader
from dotenv import load_dotenv
import os

load_dotenv()
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
PERSONAL_API_KEY = os.getenv("MY_OPEN_AI_API_KEY")
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(openai_api_key=PERSONAL_API_KEY, model="gpt-4-0125-preview")

Load test documents

Document loaders

loader_word = UnstructuredWordDocumentLoader("../../content/DPS Kvalifikasjonsgrunnlag - applikasjonsforvaltning V 2.0.docx")
loader_pdf = PyPDFLoader("../../content/Del-A-Konkurransegrunnlag-aapen-anbudskonkurranse-FOA-del-III xx.pdf")
loader_excel = UnstructuredExcelLoader("../../content/test.xlsx")

Load data from files

data_word = loader_word.load()
data_pdf = loader_pdf.load()
data_excel = loader_excel.load()
import re

def clean_text(text: str):
    # Remove excessive newlines and keep only ASCII + æøå characters.
    text = re.sub(r'\n{2,}', '\n', text)
    text = re.sub(r'[^\x00-\x7FæøåÆØÅ]+', '', text)
    # Remove empty strings
    text = "\n".join([line for line in text.split('\n') if line.strip() != ''])
    return text
from langchain_core.documents import Document
import pypdf

def process_pdf(data) -> Document:
    """
    Reads a pdf file from the stream, and returns the read text as a Document
    """
    reader = pypdf.PdfReader(data)
    text = ''
    for page_num in range(len(reader.pages)):
        text += reader.pages[page_num].extract_text()
    cleaned_text = clean_text(text)
    doc = Document(page_content=cleaned_text)
    return doc
doc = process_pdf("../../content/Del-A-Konkurransegrunnlag-aapen-anbudskonkurranse-FOA-del-III xx.pdf")
import csv
test_file_name = '../../content/test_file.csv'
with open(test_file_name, 'w', newline='') as file:
    writer = csv.writer(file)
    writer.writerow(['Column1', 'Column2', 'Column3'])
    writer.writerow(['Data1', 'Data2', 'Data3'])
from langchain_community.document_loaders import UnstructuredCSVLoader
loader_csv = UnstructuredCSVLoader("../../content/test_file.csv")
data_csv = loader_csv.load()
data_csv
[Document(page_content='\n\n\nColumn1\nColumn2\nColumn3\n\n\nData1\nData2\nData3\n\n\n', metadata={'source': '../../content/test_file.csv'})]

Chunk documents

We split the documents by tokens

from langchain_core.documents import Document
from langchain.text_splitter import TokenTextSplitter

# Take in a document and chunk it if neccessary. Splits on token length.
def split_document_by_tokens(document: list[Document], chunk_size: int, overlap: int):
    splitter = TokenTextSplitter(chunk_size=chunk_size, chunk_overlap=overlap)
    return splitter.split_documents(document)

Summarize Document(s)

Dette funker ikke lengre, null peiling hvorfor

Oppsummer ett eller flere dokumenter ved hjelp av map reduction.

1: Map hver chunk til en oppsummering av chunken

2: Reduser alle oppsummeringer til én enkelt oppsummering

summary_map_template = """Skriv en kortfattet oppsummering av følgende innhold:

{content}

OPPSUMMERING:
"""

summary_reduce_template = """Følgende er et sett med oppsummeringer:

{doc_summaries}

Lag en sammenhengende oppsumering ut fra disse.
OPPSUMMERING:"""
from langchain.chains import LLMChain, ReduceDocumentsChain, MapReduceDocumentsChain, StuffDocumentsChain
from langchain.prompts import PromptTemplate

def summarize_document(document: list[Document]):
    """
    Takes in a list of Documents and summarizes them.
    :param document: The document(s) to be summarized.
    :return: A dict of named outputs Dict[str, Any].
    """
    # Chain to generate a summary from each chunk
    map_prompt = PromptTemplate.from_template(summary_map_template)
    map_chain = LLMChain(prompt=map_prompt, llm=llm)

    # Chain to generate one cohesive summary from the summaries
    reduce_prompt = PromptTemplate.from_template(summary_reduce_template)
    reduce_chain = LLMChain(prompt=reduce_prompt, llm=llm)
    stuff_chain = StuffDocumentsChain(llm_chain=reduce_chain, document_variable_name="doc_summaries")
    reduce_docs_chain = ReduceDocumentsChain(combine_documents_chain=stuff_chain)

    # The complete map reduction chain
    map_reduce_chain = MapReduceDocumentsChain(
        llm_chain=map_chain,
        document_variable_name="content",
        reduce_documents_chain=reduce_docs_chain
    )

    splitdocs = split_document_by_tokens(document, 15000, 200)
    summary = map_reduce_chain.run(splitdocs)
    return summary
summarized_word = summarize_document(data_pdf)
/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The function `run` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.
  warn_deprecated(
summarized_word
'Statens pensjonskasse (SPK) har utlyst en åpen anbudskonkurranse for innkjøp av programvareløsninger, med mål om å inngå en rammeavtale for levering, rådgivning og vedlikehold av programvarelisenser. Konkurransen, som har et anslått kontraktsomfang på 80 til 150 millioner kroner ekskl. mva. over en periode på 2 år med mulighet for forlengelse opptil 6 år, er designet for å være transparent og rettferdig, oppfyller de nødvendige kravene i Forskrift om offentlige anskaffelser (FOA) del I og III, og overskrider EØS-terskelen. \n\nAnbudsprosessen vektlegger miljøvennlige løsninger og setter krav til leverandørenes kvalifikasjoner, økonomiske og finansielle kapasitet, samt tekniske og faglige kvalifikasjoner. Det kreves bruk av det europeiske egenerklæringsskjemaet (ESPD) for foreløpig bekreftelse på kvalifikasjoner, og leverandører må levere detaljert dokumentasjon, inkludert et signert tilbudsbrev, besvarelse på kravspesifikasjonen, og priser. Tilbudsprosedyren understreker også behovet for å beskytte konfidensiell informasjon, mens det oppfordres til å inkludere en sladdet versjon av tilbudet for innsynsforespørsler.\n\nTildelingskriteriene inneholder en balanse mellom kvalitative vurderinger og pris-/kostnadsevalueringer, med særlig fokus på nye dataplattformlisenser, kvalitet, tjenestenivå, support, og klima- og miljøhensyn. Kommunikasjon rundt konkurransen og innsendelse av tilbud skal foregå gjennom Mercell-plattformen, med en frist for tilbudslevering satt til 2. mai 2024.\n\nSPK forbeholder seg retten til å avlyse konkurransen under visse omstendigheter, med skriftlig meddelelse til alle deltakere. Dette initiativet reflekterer SPKs engasjement for rettferdige, effektive og bærekraftige anskaffelsesprosesser, samt deres overordnede mål om å fremme bærekraft i sine operasjoner.'