hf-llm-bill-chat / One_model.py
georgeek's picture
.
aed8428
import os
import json
import streamlit as st
from openai import OpenAI
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
# Get the OpenAI API key from environment variables
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
raise ValueError("The OPENAI_API_KEY environment variable is not set.")
client = OpenAI()
def load_user_data(user_id):
file_path = os.path.join(os.getcwd(), "data", "user_data", f"user_data_{user_id}.json")
#st.write(f"Loading user data from: {file_path}")
#st.write(f"Current working directory: {os.getcwd()}")
#Verify if the file exists
if not os.path.exists(file_path):
#st.write("File does not exist.")
return {}
try:
with open(file_path, "r") as file:
data = json.load(file)
#st.write(f"Loaded data: {data}")
return data
except json.JSONDecodeError:
st.write("Error decoding JSON.")
return {}
except Exception as e:
st.write(f"An error occurred: {e}")
return {}
def save_user_data(user_id, data):
file_path = os.path.join("data", "user_data", f"user_data_{user_id}.json")
os.makedirs(os.path.dirname(file_path), exist_ok=True)
with open(file_path, "w") as file:
json.dump(data, file)
def parseBill(data):
billDate = data.get("billDate")
billNo = data.get("billNo")
amountDue = data.get("amountDue")
extraCharge = data.get("extraCharge")
taxItems = data.get("taxItem", [])
subscribers = data.get("subscribers", [])
totalBillCosts = [{"categorie": t.get("cat"), "amount": t.get("amt")} for t in taxItems]
subscriberCosts = []
categories = set()
names = set()
for tax in taxItems:
categories.add(tax.get("cat"))
for sub in subscribers:
logicalResource = sub.get("logicalResource")
billSummaryItems = sub.get("billSummaryItem", [])
for item in billSummaryItems:
try:
categories.add(item["cat"]),
categories.add(item["name"]),
names.add(item["name"])
except KeyError:
continue
subscriberCosts.append({
"Numar telefon": logicalResource,
"Categorie cost": item["cat"],
"Cost": item["name"],
"Valoare": item["amt"]
})
#st.write(f"Costuri totale factura: {totalBillCosts}")
#st.write(f"Costuri utilizatori: {subscriberCosts}")
#st.write(f"Categorii: {categories}")
return {
"Data factura": billDate,
"Serie numar factura": billNo,
"Total de plata": amountDue,
"Costuri suplimentare": extraCharge,
"Total plata factura": totalBillCosts,
"Costuri utilizatori": subscriberCosts,
"Entities": list(categories),
"Costuri": list(names)
}
def check_related_keys2(question, user_id):
user_data = load_user_data(user_id)
categories = set()
for bill in user_data.get("bills", []):
categories.update(bill.get("Entities", []))
#st.write(f"Entities: {list(categories)}")
#st.write(f"Question: {question}"),
return [category for category in list(categories) if question in category]
def check_related_keys3(question, user_id):
user_data = load_user_data(user_id)
categories = set()
for bill in user_data.get("bills", []):
categories.update(bill.get("categories", []))
related_categories = []
for category in list(categories):
st.write(f"Entity: {category}")
if question in category:
related_categories.append(category)
return related_categories
def check_related_keys(question, user_id):
user_data = load_user_data(user_id)
entities = set()
for bill in user_data.get("bills", []):
entities.update(bill.get("Entities", []))
#st.write(f"Entities: {list(entities)}")
#st.write(f"Question: {question}")
related_entities = [entity for entity in entities if entity.lower() in question.lower()]
#st.write(f"Related Entities: {related_entities}")
return related_entities
def process_query(query, user_id, model_name):
user_data = load_user_data(user_id)
bill_info = user_data.get("bills", [])
related_keys = check_related_keys(query, user_id)
related_keys_str = ", ".join(related_keys) if related_keys else "N/A"
if related_keys_str != "N/A":
context = (
f"- Ca asistent virtual, ai acces la informatii despre costurile facturate in lei din factura.\n"
f"- Citeste informatiile despre costruri din json: \n"
f" {bill_info}\n"
f"- Raspunde la urmatoarea intrebare a clientului: :blue['{query}']\n"
f"- In special cu info legate de: :red[{related_keys_str}].\n"
f"- Pentru orice alt subiect raspunde ca nu poti oferi decat informatii despre facturi. Sugereaza ca intrebarea sa fie legata doar de factura si costuri.\n"
f"- Folosesete contextul pentru a raspunde la intrebare.\n"
f"- Daca nu ai suficiente informatii, raspunde ca nu ai suficiente informatii.\n"
f"- Raspunde pe un ton calm, prietenos si profesionist, niciodata jignitor."
)
else:
context = (
f"- Ca asistent virtual, ai acces la informatii despre costurile facturate in lei din factura.\n"
f"- Citeste informatiile despre costruri din json: \n"
f" {bill_info}\n"
f"- Raspunde la urmatoarea intrebare a clientului: :blue['{query}']\n"
f"- In special cu info legate de :red[factura].\n"
f"- Pentru orice alt subiect raspunde ca nu poti oferi decat informatii despre facturi. Sugereaza ca intrebarea sa fie legata doar de factura si costuri.\n"
f"- Folosesete contextul pentru a raspunde la intrebare.\n"
f"- Daca nu ai suficiente informatii, raspunde ca nu ai suficiente informatii.\n"
f"- Raspunde pe un ton calm, prietenos si profesionist, niciodata jignitor."
)
max_input_length = 7550
#st.write(f"Context:\n{context}")
st.write(f"Context size: {len(context)} characters")
if len(context) > max_input_length:
st.warning("Prea multe caractere în context, solicitarea nu va fi trimisă.")
return None
# Update this part to run the chosen model
if model_name == "gpt-4o-mini":
# Code to run model 4o mini
st.write("Running model GPT-4o-mini")
elif model_name == "gpt-4o":
# Code to run model 4o
st.write("Running model GPT-4o")
return context
# import the datetime class from the datetime module
from datetime import datetime
def log_conversation(user_id, user_query, assistant_response, tokens, cost):
log_entry = {
"timestamp": datetime.now().isoformat(),
"user_id": user_id,
"user_query": user_query,
"assistant_response": assistant_response,
"tokens": tokens,
"cost": cost
}
log_file_path = os.path.join("logs", "conversation_logs.json")
os.makedirs(os.path.dirname(log_file_path), exist_ok=True)
if os.path.exists(log_file_path):
with open(log_file_path, "r") as log_file:
logs = json.load(log_file)
else:
logs = []
logs.append(log_entry)
with open(log_file_path, "w") as log_file:
json.dump(logs, log_file, indent=4)
def main():
st.title("Bill info LLM Agent (OpenAI)")
st.image("https://miro.medium.com/v2/resize:fit:100/format:webp/1*NfE0G4nEj4xX7Z_8dSx83g.png")
# Create a sidebar menu to choose between models
model_name = st.sidebar.selectbox("Choose OpenAI Model", ["gpt-4o-mini", "gpt-4o"])
if "user_id" not in st.session_state:
st.session_state.user_id = None
user_id = st.sidebar.text_input("Introdu numărul de telefon:", placeholder="Incearca 0724077190")
st.session_state.user_data = None
if user_id and user_id != st.session_state.user_id:
data = load_user_data(user_id)
st.session_state.user_data = data
if data:
st.session_state.user_id = user_id
st.success("Utilizator găsit!")
st.write(f"Numar telefon: {st.session_state.user_id}")
st.session_state.user_data = data
else:
st.warning("Nu am găsit date pentru acest ID.")
st.warning("Încărcați o factură json.")
st.session_state.user_id = user_id
st.session_state.user_data = None
# If the user has no data yet Show the upload (st.file_uploader...) dialog,
# If the user has stored data in data\user_data\"user_data{user_id}.json, display the existing bills data - st.write(bill) but compacted
if st.session_state.user_data:
st.write("Facturi existente (extras):")
for bill in st.session_state.user_data.get("bills", []):
st.write({
"Data factura": bill.get("Data factura"),
"Serie numar factura": bill.get("Serie numar factura"),
"Total de plata": bill.get("Total de plata"),
"Costuri suplimentare": bill.get("Costuri suplimentare")
})
# Display entities found in user data
st.write("Entități găsite în datele utilizatorului:")
entities = set()
for bill in st.session_state.user_data.get("bills", []):
entities.update(bill.get("Entities", []))
st.write(list(entities))
if not st.session_state.user_data:
uploaded_file = st.file_uploader("Incarca factura", type="json")
if uploaded_file and st.session_state.user_id:
bill_data = json.load(uploaded_file)
parsed_bill = parseBill(bill_data)
existing_data = load_user_data(st.session_state.user_id)
# Check if the billNo already exists in the existing data
existing_bill_nos = [bill.get("Data factura") for bill in existing_data.get("bills", [])]
if parsed_bill.get("Data factura") in existing_bill_nos:
st.warning("Factură existentă.")
else:
if "bills" not in existing_data:
existing_data["bills"] = []
existing_data["bills"].append(parsed_bill)
save_user_data(st.session_state.user_id, existing_data)
st.success("Factura a fost încărcată și salvată cu succes!")
# Initialize conversation in the session state
# "context_prompt_added" indicates whether we've added the specialized "bill info" context yet.
if "messages" not in st.session_state:
st.session_state["messages"] = [
{"role": "assistant", "content": "Cu ce te pot ajuta?"}
]
if "context_prompt_added" not in st.session_state:
st.session_state.context_prompt_added = False
st.write("---")
st.subheader("Chat")
for msg in st.session_state["messages"]:
st.chat_message(msg["role"]).write(msg["content"])
if prompt := st.chat_input("Introduceți întrebarea aici:"):
if not st.session_state.user_id:
st.error("Trebuie să introduci un număr de telefon valid sau să încarci date.")
return
# If the context prompt hasn't been added yet, build & inject it once;
# otherwise, just add the user's raw question.
if not st.session_state.context_prompt_added:
final_prompt = process_query(prompt, st.session_state["user_id"], model_name)
if final_prompt is None:
st.stop()
st.session_state["messages"].append({"role": "user", "content": final_prompt})
st.session_state.context_prompt_added = True
else:
st.session_state["messages"].append({"role": "user", "content": prompt})
# Display the latest user message in the chat
st.chat_message("user").write(st.session_state["messages"][-1]["content"])
# Display the related keys
related_keys = check_related_keys(prompt, st.session_state["user_id"])
st.write("Focus pe entitatile:", related_keys)
# Now call GPT-4 with the entire conversation
completion = client.chat.completions.create(
model=model_name,
messages=st.session_state["messages"]
)
response_text = completion.choices[0].message.content.strip()
st.session_state["messages"].append({"role": "assistant", "content": response_text})
st.chat_message("assistant").write(response_text)
if hasattr(completion, "usage"):
st.write("Prompt tokens:", completion.usage.prompt_tokens)
st.write("Completion tokens:", completion.usage.completion_tokens)
st.write("Total tokens:", completion.usage.total_tokens)
# Estimate cost per conversation (find the OpenAI costs for gpt-4o and gpt-4o-mini model per token)
prompt_tokens = completion.usage.prompt_tokens
completion_tokens = completion.usage.completion_tokens
total_tokens = completion.usage.total_tokens
# Estimate cost per conversation
if model_name == "gpt-4o":
cost_per_token = 0.03 / 1000 # $0.03 per 1,000 tokens
elif model_name == "gpt-4o-mini":
cost_per_token = 0.015 / 1000 # $0.015 per 1,000 tokens
estimated_cost = total_tokens * cost_per_token
#st.write("Estimated cost:", estimated_cost)
# Log the conversation
log_conversation(
st.session_state["user_id"],
prompt,
response_text,
{
"prompt_tokens": prompt_tokens,
"completion_tokens": completion_tokens,
"total_tokens": total_tokens,
"estimated_cost": estimated_cost
},
estimated_cost
)
if __name__ == "__main__":
main()