afb6d5185d
Feature/lesser table caching refactor hybrid * chore: Remove unused duplicate main.py from shared pipeline * fix: Correct crosswalk paths in aarete_derived.py * chore: Remove unused documentation files from fieldExtraction * docs: Add documentation files to documentation folder * docs: Update README with uv setup, expanded project structure, and branching conventions * docs: Add uv installation steps with Ubuntu/WSL emphasis * Enable prompt caching for all remaining LLM calls - Add _INSTRUCTION() functions for: EXHIBIT_HEADER, EXHIBIT_LINKAGE, EXHIBIT_TITLE_MATCH, DATE_FIX, DERIVED_TERM_DATE, CHECK_PROVIDER_NAME_MATCH, SPECIAL_CASE_ASSIGNMENT - Update all invoke_claude() calls in saas and clover pipelines to use cache=True with corresponding _INSTRUCTION() functions - Add new instructions to get_cacheable_instructions() for cache warming - Update tests for new instruction functions Functions now using caching: - prompt_exhibit_level - prompt_exhibit_lesser (EXHIBIT_LEVEL_LESSER_OF) - prompt_fee_schedule_breakout - prompt_grouper_breakout - prompt_special_case_assignment - prompt_exhibit_linkage - prompt_exhibit_header - prompt_smart_chunked (ONE_TO_ONE templates) - prompt_date_fix - prompt_derived_term_date - prompt_exhibit_title_match - provider_name_match_check 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Reorder * feat: Add bcbs_promise client pipeline with OFFSET_TERM extraction - Add new bcbs_promise client with HSC-based OFFSET_TERM field extraction - Extract full paragraph text of offset/recoupment provisions from contracts - Derive OFFSET_INDICATOR (Y/N) from OFFSET_TERM presence - Fix reorder_columns to preserve extra columns not in COLUMN_ORDER - Update QC/QA output path to outputs/qc_qa/ * fix: Update dev deps and test assertions for QC/QA output path - Add pytest/pytest-mock to dev dependencies for mypy type checking - Update test assertions to expect outputs/qc_qa instead of qa_qc_output * style: Apply black formatting to prompt_templates.py * Merge main, move scripts * Archive some scripts * update py version * remove .py version file * Remove ASCII characters * Restore testbed code * restore tracking * Update testbed metrics * Enable prompt caching for CODE_LAST_CHECK, FILL_BILL_TYPE, DUAL_LOB_CHECK, and GROUPER_BREAKOUT - Add CODE_LAST_CHECK_INSTRUCTION() for service specificity classification - Add FILL_BILL_TYPE_INSTRUCTION() for bill type code determination - Add DUAL_LOB_CHECK_INSTRUCTION() for Medicare/Medicaid classification - Update code_funcs.py to use caching for CODE_LAST_CHECK, FILL_BILL_TYPE, GROUPER_BREAKOUT - Update postprocessing_funcs.py to use caching for DUAL_LOB_CHECK - Add new instructions to get_cacheable_instructions() for cache warming - Add unit tests for new instruction functions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fix postprocessing_funcs to remove invalid columns * Merge branch 'main' into feature/lesser-table-caching-refactor-hybrid * Revert prompt caching changes from aed1b73c * update formatting * Update imports Approved-by: Sha Brown Approved-by: Praneel Panchigar
272 lines
8.2 KiB
Python
272 lines
8.2 KiB
Python
import json
|
|
|
|
import boto3
|
|
from langchain.prompts import PromptTemplate
|
|
from langchain.embeddings.bedrock import BedrockEmbeddings
|
|
from langchain.llms.bedrock import Bedrock
|
|
from langchain_community.vectorstores import Chroma
|
|
from constants import (
|
|
CHROMA_SETTINGS,
|
|
EMBEDDING_MODEL_NAME,
|
|
PERSIST_DIRECTORY,
|
|
MODEL_ID,
|
|
MODEL_BASENAME,
|
|
SOURCE_DIRECTORY,
|
|
USER_LIST,
|
|
)
|
|
from langchain.chains import RetrievalQA
|
|
|
|
import streamlit as st
|
|
from streamlit_extras.add_vertical_space import add_vertical_space
|
|
import os
|
|
import pandas as pd
|
|
import util
|
|
|
|
REDIRECT_URI = "https://doczydev.aarete.com:8502"
|
|
user_list = USER_LIST
|
|
|
|
st.set_page_config(layout="wide")
|
|
# Sidebar contents
|
|
with st.sidebar:
|
|
st.title("Doczy.AI ™")
|
|
st.markdown(
|
|
"""
|
|
## About
|
|
This app extracts data from contracts
|
|
|
|
"""
|
|
)
|
|
add_vertical_space(15)
|
|
# st.write("Doczy")
|
|
|
|
util.setup_page(REDIRECT_URI)
|
|
_, c1 = st.columns([5, 1])
|
|
c1.write(f"User: **{st.session_state.user_info['displayName']}**")
|
|
|
|
if st.session_state.user_info["mail"] in user_list:
|
|
|
|
fields = pd.read_csv(
|
|
"contract_fields.csv", encoding="unicode_escape", skipinitialspace=True
|
|
)
|
|
fields = fields[fields["PRIORITY"] == "A"]
|
|
fields = fields.drop_duplicates(subset="Field Name", keep="first").sort_values(
|
|
"Field Name"
|
|
)
|
|
fields["Interrogation Question?"] = fields["Interrogation Question?"].fillna(" ")
|
|
field_prompt_mapping = dict(
|
|
zip(fields["Field Name"], fields["Interrogation Question?"])
|
|
)
|
|
|
|
def file_selector(folder_path=SOURCE_DIRECTORY):
|
|
filenames = os.listdir(folder_path)
|
|
selected_filename = st.selectbox(
|
|
"Select a file", filenames, label_visibility="collapsed"
|
|
)
|
|
# return os.path.join(folder_path, selected_filename)
|
|
return selected_filename
|
|
|
|
file_row = st.columns([0.2, 0.7, 0.1])
|
|
with file_row[0]:
|
|
st.write("**Contract Name**")
|
|
with file_row[1]:
|
|
# file_name = st.text_input("**Contract Name**", label_visibility = "collapsed")
|
|
file_name = file_selector()
|
|
|
|
# lob_row = st.columns([0.2, 0.7, 0.1])
|
|
# with lob_row[0]:
|
|
# st.write("**LOB**")
|
|
# with lob_row[1]:
|
|
# lob = st.selectbox('LOB',('Medicare', 'Medicaid'), label_visibility = "collapsed")
|
|
|
|
llm_row = st.columns([0.2, 0.7, 0.1])
|
|
with llm_row[0]:
|
|
st.write("**Langauge Model**")
|
|
with llm_row[1]:
|
|
llm_selected = st.selectbox(
|
|
"Langauge Model",
|
|
(
|
|
"Claude 2",
|
|
"Claude Instant",
|
|
"Llama 2 Chat 13B",
|
|
"Llama 2 Chat 70B",
|
|
"Titan Text Express",
|
|
),
|
|
label_visibility="collapsed",
|
|
)
|
|
|
|
page_list = []
|
|
with open(os.path.join(SOURCE_DIRECTORY, file_name), "r") as infile:
|
|
text = infile.read()
|
|
page_count = text.count("Start of Page No. = ")
|
|
for page in range(page_count + 1):
|
|
file_path = "SOURCE_DOCUMENTS\\" + f"{file_name[:-4]}_page{page}.txt"
|
|
dict_with_pages = {"source": {"$eq": file_path}}
|
|
page_list.append(dict_with_pages)
|
|
|
|
# AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID')
|
|
# AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY')
|
|
# AWS_SESSION_TOKEN=os.getenv('AWS_SESSION_TOKEN')
|
|
|
|
# Setup bedrock
|
|
bedrock_runtime = boto3.client(
|
|
service_name="bedrock-runtime", region_name="us-east-1"
|
|
)
|
|
|
|
embeddings = BedrockEmbeddings(
|
|
client=bedrock_runtime,
|
|
model_id="amazon.titan-embed-text-v1",
|
|
)
|
|
DB = Chroma(
|
|
persist_directory=PERSIST_DIRECTORY,
|
|
embedding_function=embeddings,
|
|
client_settings=CHROMA_SETTINGS,
|
|
)
|
|
RETRIEVER = DB.as_retriever(search_kwargs={"filter": {"$or": page_list}, "k": 4})
|
|
|
|
# if "LLM" not in st.session_state:
|
|
if llm_selected == "Titan Text Express":
|
|
LLM = Bedrock(
|
|
model_id="amazon.titan-text-express-v1",
|
|
client=bedrock_runtime,
|
|
model_kwargs={
|
|
"maxTokenCount": 512,
|
|
"stopSequences": [],
|
|
"temperature": 0,
|
|
"topP": 1,
|
|
},
|
|
)
|
|
elif llm_selected == "Llama 2 Chat 70B":
|
|
LLM = Bedrock(
|
|
model_id="meta.llama2-70b-chat-v1",
|
|
client=bedrock_runtime,
|
|
model_kwargs={
|
|
"max_gen_len": 512,
|
|
"temperature": 0,
|
|
# "topP": 0.9,
|
|
},
|
|
)
|
|
elif llm_selected == "Llama 2 Chat 13B":
|
|
LLM = Bedrock(
|
|
model_id="meta.llama2-13b-chat-v1",
|
|
client=bedrock_runtime,
|
|
model_kwargs={
|
|
"max_gen_len": 512,
|
|
"temperature": 0,
|
|
# "topP": 0.9,
|
|
},
|
|
)
|
|
elif llm_selected == "Claude Instant":
|
|
LLM = Bedrock(
|
|
model_id="anthropic.claude-instant-v1",
|
|
client=bedrock_runtime,
|
|
model_kwargs={
|
|
# "max_tokens_to_sample": 512,
|
|
"temperature": 0,
|
|
# "topP": 0.9,
|
|
},
|
|
)
|
|
elif llm_selected == "Claude 2":
|
|
LLM = Bedrock(
|
|
model_id="anthropic.claude-v2:1",
|
|
client=bedrock_runtime,
|
|
model_kwargs={
|
|
# "max_tokens_to_sample": 512,
|
|
"temperature": 0,
|
|
# "topP": 0.9,
|
|
},
|
|
)
|
|
st.session_state["LLM"] = LLM
|
|
|
|
template = """
|
|
|
|
Use the following pieces of context to answer the question at the end. If you don't know the answer,\
|
|
just say that you don't know, don't try to make up an answer.
|
|
|
|
{context}
|
|
|
|
Question: {question}
|
|
Answer:"""
|
|
prompt = PromptTemplate(input_variables=["context", "question"], template=template)
|
|
|
|
QA = RetrievalQA.from_chain_type(
|
|
llm=LLM,
|
|
chain_type="stuff",
|
|
retriever=RETRIEVER,
|
|
return_source_documents=True,
|
|
chain_type_kwargs={"prompt": prompt},
|
|
)
|
|
|
|
# query = "In which state or states is the Contract applicable? Answer in one or two words. State name: "
|
|
# response = QA({"query":query})
|
|
# st.write(query)
|
|
# st.write(response['result'])
|
|
# st.write("-----------")
|
|
# st.write(response)
|
|
|
|
# clicked = st.button("Show Results")
|
|
df = pd.DataFrame(
|
|
columns=[
|
|
"Contract Name",
|
|
"Field Name",
|
|
"Snippet",
|
|
"Page Number",
|
|
"Confidence Level",
|
|
"Field Extracted Value",
|
|
"Imputed Value",
|
|
]
|
|
)
|
|
field_list = list(field_prompt_mapping.keys())
|
|
query_list = [field_prompt_mapping[x] for x in field_list]
|
|
score_list = [
|
|
DB.similarity_search_with_relevance_scores(
|
|
query, k=4, filter={"$or": page_list}
|
|
)
|
|
for query in query_list
|
|
]
|
|
confidence_list = []
|
|
for score in score_list:
|
|
confidence_list.append(max(d[1] for d in score))
|
|
# st.write(confidence_list)
|
|
|
|
if st.button("Show Results"):
|
|
response_list = [QA({"query": query}) for query in query_list]
|
|
answer_list = [response["result"] for response in response_list]
|
|
doc_list = [response["source_documents"] for response in response_list]
|
|
snippet_list = [str(doc[0].page_content) for doc in doc_list]
|
|
page_no_list = [
|
|
int(str(doc[0].metadata["source"]).rsplit("_page")[1].replace(".txt", ""))
|
|
+ 1
|
|
for doc in doc_list
|
|
]
|
|
|
|
df["Field Name"] = field_list
|
|
df["Contract Name"] = file_name
|
|
df["Snippet"] = snippet_list
|
|
df["Page Number"] = page_no_list
|
|
df["Confidence Level"] = confidence_list
|
|
df["Field Extracted Value"] = answer_list
|
|
df.to_csv("temp2.csv", index=False)
|
|
|
|
df2 = pd.read_csv("temp2.csv")
|
|
df2["Imputed Value"] = ""
|
|
edited_df = st.data_editor(df2)
|
|
|
|
@st.cache_data
|
|
def convert_df(df):
|
|
return df.to_csv(index=False).encode("utf-8")
|
|
|
|
csv = convert_df(edited_df)
|
|
|
|
buttons = st.columns(3)
|
|
with buttons[0]:
|
|
st.button("Save All Imputations")
|
|
with buttons[1]:
|
|
st.download_button(
|
|
"Download Table", csv, "file.csv", "text/csv", key="download-csv"
|
|
)
|
|
with buttons[2]:
|
|
st.button("Kickoff Database Integration")
|
|
|
|
else:
|
|
st.write("Access Denied")
|