Spaces:
Sleeping
Sleeping
two models
Browse files- bill.py → One_model.py +0 -0
- billPdf.py +0 -216
- logs/conversation_logs.json +48 -0
- pages/Two_models.py +306 -0
bill.py → One_model.py
RENAMED
File without changes
|
billPdf.py
DELETED
@@ -1,216 +0,0 @@
|
|
1 |
-
#!/usr/bin/env -S poetry run python
|
2 |
-
|
3 |
-
import os
|
4 |
-
import json
|
5 |
-
#import pdfplumber
|
6 |
-
import streamlit as st
|
7 |
-
from openai import OpenAI
|
8 |
-
|
9 |
-
client = OpenAI()
|
10 |
-
|
11 |
-
def load_user_data(user_id):
|
12 |
-
file_path = os.path.join("data", "user_data", f"user_data_{user_id}.json")
|
13 |
-
if not os.path.exists(file_path):
|
14 |
-
return {}
|
15 |
-
with open(file_path, "r") as file:
|
16 |
-
return json.load(file)
|
17 |
-
|
18 |
-
def parse_pdf_to_json(pdf_path):
|
19 |
-
user_id = {}
|
20 |
-
serie_factura = {}
|
21 |
-
data_factura = {}
|
22 |
-
costuri = {}
|
23 |
-
with pdfplumber.open(pdf_path, ) as pdf:
|
24 |
-
for page in pdf.pages:
|
25 |
-
text = page.extract_text()
|
26 |
-
if text:
|
27 |
-
lines = text.split('\n')
|
28 |
-
|
29 |
-
# Process each line and look for specific categories
|
30 |
-
for line in lines:
|
31 |
-
# Check for 'Data emiterii facturii'
|
32 |
-
if 'Data facturii' in line:
|
33 |
-
date = line.split()[-1]
|
34 |
-
data_factura['Data factura'] = date
|
35 |
-
|
36 |
-
# Check for 'Serie factură'
|
37 |
-
if 'rul facturii:' in line:
|
38 |
-
serie = line.split()[-1]
|
39 |
-
serie_factura['Serie numar'] = serie
|
40 |
-
|
41 |
-
# Check for 'Cont client'
|
42 |
-
if 'Cont client' in line:
|
43 |
-
cont = line.split()[-1]
|
44 |
-
user_id['Cont client'] = cont
|
45 |
-
|
46 |
-
# Check for 'Valoare facturată fără TVA'
|
47 |
-
if 'Sold precedent' in line:
|
48 |
-
value = line.split()[-2].replace(',', '.') # Extract and convert to float
|
49 |
-
costuri['Sold precedent'] = value
|
50 |
-
|
51 |
-
# Check for 'Total bază de impozitare TVA'
|
52 |
-
elif 'din sold precedent' in line:
|
53 |
-
value = line.split()[-2].replace(',', '.') # Extract and convert to float
|
54 |
-
costuri['Total platit din sold precedent'] = value
|
55 |
-
|
56 |
-
# Check for 'TVA'
|
57 |
-
elif 'TVA' in line and '%' in line:
|
58 |
-
value = line.split()[-2].replace(',', '.') # Extract and convert to float
|
59 |
-
costuri['TVA'] = value
|
60 |
-
|
61 |
-
# Check for 'Dobânzi penalizatoare'
|
62 |
-
elif 'Abonamente' in line:
|
63 |
-
value = line.split()[-2].replace(',', '.') # Extract and convert to float
|
64 |
-
costuri['Abonamente si extraopiuni'] = value
|
65 |
-
|
66 |
-
# Check for 'TOTAL DE PLATĂ FACTURĂ CURENTĂ'
|
67 |
-
elif 'Total factura curenta fara TVA' in line:
|
68 |
-
value = float(line.split()[-2].replace(',', '.')) # Extract and convert to float
|
69 |
-
costuri['Total factura curenta fara TVA'] = value
|
70 |
-
|
71 |
-
# Check for 'Sold Cont Contract'
|
72 |
-
elif 'Servicii utilizate' in line:
|
73 |
-
value = line.split()[-2].replace(',', '.') # Extract and convert to float
|
74 |
-
costuri['Servicii utilizate'] = value
|
75 |
-
|
76 |
-
# Check for 'Compensatii'
|
77 |
-
elif 'Rate terminal' in line:
|
78 |
-
value = float(line.split()[-2].replace(',', '.')) # Extract and convert to float
|
79 |
-
costuri['Rate terminal'] = value
|
80 |
-
|
81 |
-
# Check for 'TVA 19,00%'
|
82 |
-
elif 'TVA 19,00%' in line:
|
83 |
-
value = float(line.split()[-2].replace(',', '.')) # Extract and convert to float
|
84 |
-
costuri['TVA'] = value
|
85 |
-
|
86 |
-
# Check for 'Compensatii'
|
87 |
-
elif 'Total factura curenta' in line:
|
88 |
-
value = float(line.split()[-2].replace(',', '.')) # Extract and convert to float
|
89 |
-
costuri['Total factura curenta'] = value
|
90 |
-
|
91 |
-
return costuri
|
92 |
-
|
93 |
-
def check_related_keys(question, user_id):
|
94 |
-
user_data = load_user_data(user_id)
|
95 |
-
bill_keys = set()
|
96 |
-
for bill in user_data.get("bills", []):
|
97 |
-
bill_keys.update(bill.keys())
|
98 |
-
return [key for key in bill_keys if key.lower() in question.lower()]
|
99 |
-
|
100 |
-
def process_query(query, user_id):
|
101 |
-
user_data = load_user_data(user_id)
|
102 |
-
bill_info = user_data.get("bills", [])
|
103 |
-
related_keys = check_related_keys(query, user_id)
|
104 |
-
related_keys_str = ", ".join(related_keys) if related_keys else "N/A"
|
105 |
-
|
106 |
-
if related_keys_str != "N/A":
|
107 |
-
context = (
|
108 |
-
f"Citeste informatiile despre costrurile in lei facturate din dictionar: {bill_info} "
|
109 |
-
f"si raspunde la intrebarea: '{query}' dar numai cu info legate de: {related_keys_str}"
|
110 |
-
)
|
111 |
-
else:
|
112 |
-
context = (
|
113 |
-
f"Citeste informatiile despre costrurile in lei facturate din dictionar: {bill_info} "
|
114 |
-
f"si raspunde la intrebarea: '{query}' dar numai cu info legate de factura"
|
115 |
-
)
|
116 |
-
|
117 |
-
max_input_length = 550
|
118 |
-
st.write(f"Context:\n{context}")
|
119 |
-
st.write(f"Context size: {len(context)} characters")
|
120 |
-
|
121 |
-
if len(context) > max_input_length:
|
122 |
-
st.warning("Prea multe caractere în context, solicitarea nu va fi trimisă.")
|
123 |
-
return None
|
124 |
-
|
125 |
-
return context
|
126 |
-
|
127 |
-
def main():
|
128 |
-
|
129 |
-
st.title("Telecom Bill Chat with LLM Agent")
|
130 |
-
|
131 |
-
if "user_id" not in st.session_state:
|
132 |
-
st.session_state.user_id = None
|
133 |
-
|
134 |
-
user_id = st.sidebar.text_input("Introdu numărul de telefon:")
|
135 |
-
if user_id and user_id != st.session_state.user_id:
|
136 |
-
data = load_user_data(user_id)
|
137 |
-
if data:
|
138 |
-
st.session_state.user_id = user_id
|
139 |
-
st.success("Utilizator găsit!")
|
140 |
-
else:
|
141 |
-
st.warning("Nu am găsit date pentru acest ID. Încărcați o factură PDF la nevoie.")
|
142 |
-
st.session_state.user_id = user_id
|
143 |
-
|
144 |
-
uploaded_file = st.file_uploader("Încarcă factura PDF", type="pdf")
|
145 |
-
if uploaded_file and st.session_state.user_id:
|
146 |
-
bill_data = parse_pdf_to_json(uploaded_file)
|
147 |
-
existing_data = load_user_data(st.session_state.user_id)
|
148 |
-
if "bills" not in existing_data:
|
149 |
-
existing_data["bills"] = []
|
150 |
-
existing_data["bills"].append(bill_data)
|
151 |
-
file_path = os.path.join("data", "user_data", f"user_data_{st.session_state['user_id']}.json")
|
152 |
-
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
153 |
-
with open(file_path, "w") as file:
|
154 |
-
json.dump(existing_data, file)
|
155 |
-
st.success("Factura a fost încărcată și salvată cu succes!")
|
156 |
-
|
157 |
-
if st.session_state.user_id:
|
158 |
-
data = load_user_data(st.session_state.user_id)
|
159 |
-
st.write(f"Phone Number: {st.session_state.user_id}")
|
160 |
-
st.write("Facturi existente:")
|
161 |
-
for bill in data.get("bills", []):
|
162 |
-
st.write(bill)
|
163 |
-
else:
|
164 |
-
st.info("Introduceți un ID și/sau încărcați o factură PDF pentru a continua.")
|
165 |
-
|
166 |
-
# Initialize conversation in the session state
|
167 |
-
# "context_prompt_added" indicates whether we've added the specialized "bill info" context yet.
|
168 |
-
if "messages" not in st.session_state:
|
169 |
-
st.session_state["messages"] = [
|
170 |
-
{"role": "assistant", "content": "Cu ce te pot ajuta?"}
|
171 |
-
]
|
172 |
-
if "context_prompt_added" not in st.session_state:
|
173 |
-
st.session_state.context_prompt_added = False
|
174 |
-
|
175 |
-
st.write("---")
|
176 |
-
st.subheader("Chat")
|
177 |
-
|
178 |
-
for msg in st.session_state["messages"]:
|
179 |
-
st.chat_message(msg["role"]).write(msg["content"])
|
180 |
-
|
181 |
-
if prompt := st.chat_input("Introduceți întrebarea aici:"):
|
182 |
-
if not st.session_state.user_id:
|
183 |
-
st.error("Trebuie să introduceți un număr de telefon valid sau să încărcați date.")
|
184 |
-
return
|
185 |
-
|
186 |
-
# If the context prompt hasn't been added yet, build & inject it once;
|
187 |
-
# otherwise, just add the user's raw question.
|
188 |
-
if not st.session_state.context_prompt_added:
|
189 |
-
final_prompt = process_query(prompt, st.session_state["user_id"])
|
190 |
-
if final_prompt is None:
|
191 |
-
st.stop()
|
192 |
-
st.session_state["messages"].append({"role": "user", "content": final_prompt})
|
193 |
-
st.session_state.context_prompt_added = True
|
194 |
-
else:
|
195 |
-
st.session_state["messages"].append({"role": "user", "content": prompt})
|
196 |
-
|
197 |
-
# Display the latest user message in the chat
|
198 |
-
st.chat_message("user").write(st.session_state["messages"][-1]["content"])
|
199 |
-
|
200 |
-
# Now call GPT-4 with the entire conversation
|
201 |
-
completion = client.chat.completions.create(
|
202 |
-
model="gpt-4",
|
203 |
-
messages=st.session_state["messages"]
|
204 |
-
)
|
205 |
-
response_text = completion.choices[0].message.content.strip()
|
206 |
-
|
207 |
-
st.session_state["messages"].append({"role": "assistant", "content": response_text})
|
208 |
-
st.chat_message("assistant").write(response_text)
|
209 |
-
|
210 |
-
if hasattr(completion, "usage"):
|
211 |
-
st.write("Prompt tokens:", completion.usage.prompt_tokens)
|
212 |
-
st.write("Completion tokens:", completion.usage.completion_tokens)
|
213 |
-
st.write("Total tokens:", completion.usage.total_tokens)
|
214 |
-
|
215 |
-
if __name__ == "__main__":
|
216 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logs/conversation_logs.json
CHANGED
@@ -58,5 +58,53 @@
|
|
58 |
"total_tokens": 1229
|
59 |
},
|
60 |
"cost": 0.018435
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
}
|
62 |
]
|
|
|
58 |
"total_tokens": 1229
|
59 |
},
|
60 |
"cost": 0.018435
|
61 |
+
},
|
62 |
+
{
|
63 |
+
"timestamp": "2025-01-16T10:54:40.132782",
|
64 |
+
"user_id": "0724077190",
|
65 |
+
"user_query": "cat am de plata",
|
66 |
+
"assistant_response": "Pentru a te ajuta, am nevoie de mai multe informa\u021bii despre contextul pl\u0103\u021bii. Este vorba despre o factur\u0103, un credit, un serviciu de telecomunica\u021bii sau altceva? Te rog s\u0103-mi oferi detalii suplimentare pentru a putea oferi un r\u0103spuns corect.",
|
67 |
+
"tokens": {
|
68 |
+
"prompt_tokens": 22,
|
69 |
+
"completion_tokens": 69,
|
70 |
+
"total_tokens": 91
|
71 |
+
},
|
72 |
+
"cost": 0.001365
|
73 |
+
},
|
74 |
+
{
|
75 |
+
"timestamp": "2025-01-16T11:50:17.716138",
|
76 |
+
"user_id": "0724077190",
|
77 |
+
"user_query": "nu e cam mult?",
|
78 |
+
"assistant_response": "Analiz\u00e2nd informa\u021biile furnizate privind factura, observ\u0103m urm\u0103toarele detalii:\n\n1. **Total factur\u0103 curent\u0103:** 104,93 lei\n2. **TVA 19%:** 16,75 lei\n3. **Total rate terminal:** 5.445,53 lei\n4. **Abonamente \u0219i extraop\u021biuni:** 91,17 lei\n5. **Reduceri:** -2,99 lei\n6. **Rest plat\u0103:** -5.073,74 lei\n7. **Sold precedent:** -5.073,74 lei\n8. **Total de plat\u0103:** 476,72 lei\n\nPrincipalul factor care face suma facturii mare este valoarea considerabil\u0103 pentru \"Rate terminal\", care este 5.445,53 lei. Aceasta este o sum\u0103 semnificativ\u0103 \u00een totalul facturii, dar se pare c\u0103 a fost acoperit\u0103 par\u021bial de ajust\u0103rile \u0219i soldul precedent, rezult\u00e2nd \u00eentr-un \"Total de plat\u0103\" de 476,72 lei.\n\nDac\u0103 aceast\u0103 sum\u0103 vi se pare mare, ar putea fi util s\u0103 reevalua\u021bi planurile tarifare \u0219i ratele terminalului pentru o reducere poten\u021bial\u0103 a costurilor viitoare. De asemenea, verificarea detaliat\u0103 a sumelor pentru terminale \u0219i beneficiile sau reducerile aplicate poate oferi o perspectiv\u0103 mai clar\u0103 asupra factorilor care contribuie la totalul de plat\u0103.",
|
79 |
+
"tokens": {
|
80 |
+
"prompt_tokens": 835,
|
81 |
+
"completion_tokens": 313,
|
82 |
+
"total_tokens": 1148
|
83 |
+
},
|
84 |
+
"cost": 0.03444
|
85 |
+
},
|
86 |
+
{
|
87 |
+
"timestamp": "2025-01-16T11:50:17.730726",
|
88 |
+
"user_id": "0724077190",
|
89 |
+
"user_query": "nu e cam mult?",
|
90 |
+
"assistant_response": "Factura are un total de plata de 476.72 lei, iar costurile suplimentare se ridic\u0103 la 5445.53 lei. De asemenea, exist\u0103 o serie de reduceri \u0219i corec\u021bii la costurile utilizatorilor care afecteaz\u0103 suma total\u0103. Chiar dac\u0103 sumele par semnificative, acestea sunt influen\u021bate de diverse categorii de costuri, inclusiv ratele terminale \u0219i abonamentele. A\u0219adar, \u00een contextul totalului de plat\u0103, s-ar putea spune c\u0103 suma este relativ mare, dar trebuie analizat\u0103 \u00een func\u021bie de serviciile \u0219i produsele incluse.",
|
91 |
+
"tokens": {
|
92 |
+
"prompt_tokens": 835,
|
93 |
+
"completion_tokens": 133,
|
94 |
+
"total_tokens": 968
|
95 |
+
},
|
96 |
+
"cost": 0.014519999999999998
|
97 |
+
},
|
98 |
+
{
|
99 |
+
"timestamp": "2025-01-16T11:53:39.957975",
|
100 |
+
"user_id": "0724077190",
|
101 |
+
"user_query": "dar pe umere care costa mai mult?",
|
102 |
+
"assistant_response": "Pe baza informa\u021biilor furnizate \u00een factur\u0103:\n\n1. **Total Rate Terminal:** 5.445,53 lei\n2. **Abonamente \u0219i Extraop\u021biuni:** 91,17 lei (incluz\u00e2nd costuri \u0219i reduceri)\n\nDin aceste date, reiese c\u0103 **ratele terminale**, cu o valoare semnificativ\u0103 de 5.445,53 lei, cost\u0103 mult mai mult comparativ cu **abonamentele \u0219i extraop\u021biunile** care totalizeaz\u0103 91,17 lei (dup\u0103 aplicarea reducerilor).\n\nA\u0219adar, costul pe ratele terminale este semnificativ mai mare dec\u00e2t costurile pentru abonamente \u0219i extraop\u021biuni.",
|
103 |
+
"tokens": {
|
104 |
+
"prompt_tokens": 1300,
|
105 |
+
"completion_tokens": 152,
|
106 |
+
"total_tokens": 1452
|
107 |
+
},
|
108 |
+
"cost": 0.021779999999999997
|
109 |
}
|
110 |
]
|
pages/Two_models.py
ADDED
@@ -0,0 +1,306 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env -S poetry run python
|
2 |
+
|
3 |
+
import os
|
4 |
+
import json
|
5 |
+
import streamlit as st
|
6 |
+
from openai import OpenAI
|
7 |
+
from dotenv import load_dotenv
|
8 |
+
|
9 |
+
# Load environment variables from .env file
|
10 |
+
load_dotenv()
|
11 |
+
|
12 |
+
# Get the OpenAI API key from environment variables
|
13 |
+
api_key = os.getenv("OPENAI_API_KEY")
|
14 |
+
if not api_key:
|
15 |
+
raise ValueError("The OPENAI_API_KEY environment variable is not set.")
|
16 |
+
|
17 |
+
client = OpenAI()
|
18 |
+
|
19 |
+
|
20 |
+
def load_user_data(user_id):
|
21 |
+
file_path = os.path.join(os.getcwd(), "data", "user_data", f"user_data_{user_id}.json")
|
22 |
+
#st.write(f"Loading user data from: {file_path}")
|
23 |
+
#st.write(f"Current working directory: {os.getcwd()}")
|
24 |
+
|
25 |
+
#Verify if the file exists
|
26 |
+
if not os.path.exists(file_path):
|
27 |
+
#st.write("File does not exist.")
|
28 |
+
return {}
|
29 |
+
|
30 |
+
try:
|
31 |
+
with open(file_path, "r") as file:
|
32 |
+
data = json.load(file)
|
33 |
+
#st.write(f"Loaded data: {data}")
|
34 |
+
return data
|
35 |
+
except json.JSONDecodeError:
|
36 |
+
st.write("Error decoding JSON.")
|
37 |
+
return {}
|
38 |
+
except Exception as e:
|
39 |
+
st.write(f"An error occurred: {e}")
|
40 |
+
return {}
|
41 |
+
|
42 |
+
|
43 |
+
def save_user_data(user_id, data):
|
44 |
+
file_path = os.path.join("data", "user_data", f"user_data_{user_id}.json")
|
45 |
+
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
46 |
+
with open(file_path, "w") as file:
|
47 |
+
json.dump(data, file)
|
48 |
+
|
49 |
+
def parseBill(data):
|
50 |
+
billDate = data.get("billDate")
|
51 |
+
billNo = data.get("billNo")
|
52 |
+
amountDue = data.get("amountDue")
|
53 |
+
extraCharge = data.get("extraCharge")
|
54 |
+
taxItems = data.get("taxItem", [])
|
55 |
+
subscribers = data.get("subscribers", [])
|
56 |
+
|
57 |
+
totalBillCosts = [{"categorie": t.get("cat"), "amount": t.get("amt")} for t in taxItems]
|
58 |
+
subscriberCosts = []
|
59 |
+
categories = set()
|
60 |
+
names = set()
|
61 |
+
|
62 |
+
for sub in subscribers:
|
63 |
+
logicalResource = sub.get("logicalResource")
|
64 |
+
billSummaryItems = sub.get("billSummaryItem", [])
|
65 |
+
for item in billSummaryItems:
|
66 |
+
try:
|
67 |
+
categories.add(item["cat"]),
|
68 |
+
names.add(item["name"])
|
69 |
+
except KeyError:
|
70 |
+
continue
|
71 |
+
|
72 |
+
subscriberCosts.append({
|
73 |
+
"Numar telefon": logicalResource,
|
74 |
+
"Categorie cost": item["cat"],
|
75 |
+
"Cost": item["name"],
|
76 |
+
"Valoare": item["amt"]
|
77 |
+
})
|
78 |
+
|
79 |
+
|
80 |
+
return {
|
81 |
+
"Data factura": billDate,
|
82 |
+
"Serie numar factura": billNo,
|
83 |
+
"De plata": amountDue,
|
84 |
+
"Costuri suplimentare": extraCharge,
|
85 |
+
"Total plata factura": totalBillCosts,
|
86 |
+
"Costuri utilizatori": subscriberCosts,
|
87 |
+
"Categorii": list(categories),
|
88 |
+
"Costuri": list(names)
|
89 |
+
}
|
90 |
+
|
91 |
+
def check_related_keys(question, user_id):
|
92 |
+
user_data = load_user_data(user_id)
|
93 |
+
categories = set()
|
94 |
+
for bill in user_data.get("bills", []):
|
95 |
+
categories.update(bill.get("Categorii", []))
|
96 |
+
st.write(f"Categorii: {categories}")
|
97 |
+
return [category for category in categories if category.lower() in question.lower()]
|
98 |
+
|
99 |
+
def process_query(query, user_id, model_name):
|
100 |
+
user_data = load_user_data(user_id)
|
101 |
+
bill_info = user_data.get("bills", [])
|
102 |
+
related_keys = check_related_keys(query, user_id)
|
103 |
+
related_keys_str = ", ".join(related_keys) if related_keys else "N/A"
|
104 |
+
|
105 |
+
if related_keys_str != "N/A":
|
106 |
+
context = (
|
107 |
+
f"Citeste informatiile despre costurile in lei facturate din json: {bill_info} "
|
108 |
+
f"si raspunde la intrebarea: '{query}' dar numai cu info legate de: {related_keys_str}"
|
109 |
+
)
|
110 |
+
else:
|
111 |
+
context = (
|
112 |
+
f"Citeste informatiile despre costrurile in lei facturate din json: {bill_info} "
|
113 |
+
f"si raspunde la intrebarea: '{query}' dar numai cu info legate de factura"
|
114 |
+
)
|
115 |
+
|
116 |
+
max_input_length = 5550
|
117 |
+
#st.write(f"Context:\n{context}")
|
118 |
+
st.write(f"Context size: {len(context)} characters")
|
119 |
+
|
120 |
+
if len(context) > max_input_length:
|
121 |
+
st.warning("Prea multe caractere în context, solicitarea nu va fi trimisă.")
|
122 |
+
return None
|
123 |
+
|
124 |
+
# Update this part to run the chosen model
|
125 |
+
if model_name == "gpt-4o-mini":
|
126 |
+
# Code to run model 4o mini
|
127 |
+
st.write("Running model GPT-4o-mini")
|
128 |
+
elif model_name == "gpt-4o":
|
129 |
+
# Code to run model 4o
|
130 |
+
st.write("Running model GPT-4o")
|
131 |
+
|
132 |
+
return context
|
133 |
+
|
134 |
+
# import the datetime class from the datetime module
|
135 |
+
from datetime import datetime
|
136 |
+
def log_conversation(user_id, user_query, assistant_response, tokens, cost):
|
137 |
+
log_entry = {
|
138 |
+
"timestamp": datetime.now().isoformat(),
|
139 |
+
"user_id": user_id,
|
140 |
+
"user_query": user_query,
|
141 |
+
"assistant_response": assistant_response,
|
142 |
+
"tokens": tokens,
|
143 |
+
"cost": cost
|
144 |
+
}
|
145 |
+
log_file_path = os.path.join("logs", "conversation_logs.json")
|
146 |
+
os.makedirs(os.path.dirname(log_file_path), exist_ok=True)
|
147 |
+
if os.path.exists(log_file_path):
|
148 |
+
with open(log_file_path, "r") as log_file:
|
149 |
+
logs = json.load(log_file)
|
150 |
+
else:
|
151 |
+
logs = []
|
152 |
+
logs.append(log_entry)
|
153 |
+
with open(log_file_path, "w") as log_file:
|
154 |
+
json.dump(logs, log_file, indent=4)
|
155 |
+
|
156 |
+
|
157 |
+
def main():
|
158 |
+
st.title("Bill info LLM Agent (OpenAI)")
|
159 |
+
st.image("https://miro.medium.com/v2/resize:fit:100/format:webp/1*NfE0G4nEj4xX7Z_8dSx83g.png")
|
160 |
+
|
161 |
+
# Create a sidebar menu to choose between models
|
162 |
+
model_name = st.sidebar.selectbox("Choose OpenAI Model", ["gpt-4o-mini", "gpt-4o"])
|
163 |
+
if "user_id" not in st.session_state:
|
164 |
+
st.session_state.user_id = None
|
165 |
+
|
166 |
+
user_id = st.sidebar.text_input("Introdu numărul de telefon:", placeholder="Incearca 0724077190")
|
167 |
+
|
168 |
+
# use a predefined user_id for testing
|
169 |
+
|
170 |
+
|
171 |
+
|
172 |
+
# display the user data if the user_id is set
|
173 |
+
#st.write(f"User ID: {user_id}")
|
174 |
+
|
175 |
+
st.session_state.user_data = None
|
176 |
+
|
177 |
+
if user_id and user_id != st.session_state.user_id:
|
178 |
+
data = load_user_data(user_id)
|
179 |
+
st.session_state.user_data = data
|
180 |
+
if data:
|
181 |
+
st.session_state.user_id = user_id
|
182 |
+
st.success("Utilizator găsit!")
|
183 |
+
st.write(f"Numar telefon: {st.session_state.user_id}")
|
184 |
+
st.session_state.user_data = data
|
185 |
+
|
186 |
+
else:
|
187 |
+
st.warning("Nu am găsit date pentru acest ID.")
|
188 |
+
st.warning("Încărcați o factură json.")
|
189 |
+
st.session_state.user_id = user_id
|
190 |
+
st.session_state.user_data = None
|
191 |
+
|
192 |
+
# If the user has no data yet Show the upload (st.file_uploader...) dialog ,
|
193 |
+
# 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
|
194 |
+
if st.session_state.user_data:
|
195 |
+
st.write("Facturi existente (extras):")
|
196 |
+
for bill in st.session_state.user_data.get("bills", []):
|
197 |
+
st.write({
|
198 |
+
"Data factura": bill.get("Data factura"),
|
199 |
+
"Serie numar factura": bill.get("Serie numar factura"),
|
200 |
+
"De plata": bill.get("De plata"),
|
201 |
+
"Costuri suplimentare": bill.get("Costuri suplimentare")
|
202 |
+
})
|
203 |
+
if not st.session_state.user_data:
|
204 |
+
uploaded_file = st.file_uploader("Incarca factura", type="json")
|
205 |
+
if uploaded_file and st.session_state.user_id:
|
206 |
+
bill_data = json.load(uploaded_file)
|
207 |
+
parsed_bill = parseBill(bill_data)
|
208 |
+
existing_data = load_user_data(st.session_state.user_id)
|
209 |
+
|
210 |
+
# Check if the billNo already exists in the existing data
|
211 |
+
existing_bill_nos = [bill.get("Data factura") for bill in existing_data.get("bills", [])]
|
212 |
+
if parsed_bill.get("Data factura") in existing_bill_nos:
|
213 |
+
st.warning("Factură existentă.")
|
214 |
+
else:
|
215 |
+
if "bills" not in existing_data:
|
216 |
+
existing_data["bills"] = []
|
217 |
+
existing_data["bills"].append(parsed_bill)
|
218 |
+
save_user_data(st.session_state.user_id, existing_data)
|
219 |
+
st.success("Factura a fost încărcată și salvată cu succes!")
|
220 |
+
|
221 |
+
|
222 |
+
|
223 |
+
# Initialize conversation in the session state
|
224 |
+
# "context_prompt_added" indicates whether we've added the specialized "bill info" context yet.
|
225 |
+
if "messages" not in st.session_state:
|
226 |
+
st.session_state["messages"] = [
|
227 |
+
{"role": "assistant", "content": "Cu ce te pot ajuta?"}
|
228 |
+
]
|
229 |
+
if "context_prompt_added" not in st.session_state:
|
230 |
+
st.session_state.context_prompt_added = False
|
231 |
+
|
232 |
+
st.write("---")
|
233 |
+
st.subheader("Chat")
|
234 |
+
|
235 |
+
|
236 |
+
for msg in st.session_state["messages"]:
|
237 |
+
st.chat_message(msg["role"]).write(msg["content"])
|
238 |
+
|
239 |
+
if prompt := st.chat_input("Introduceți întrebarea aici:"):
|
240 |
+
if not st.session_state.user_id:
|
241 |
+
st.error("Trebuie să introduci un număr de telefon valid sau să încarci date.")
|
242 |
+
return
|
243 |
+
|
244 |
+
# If the context prompt hasn't been added yet, build & inject it once;
|
245 |
+
# otherwise, just add the user's raw question.
|
246 |
+
if not st.session_state.context_prompt_added:
|
247 |
+
final_prompt = process_query(prompt, st.session_state["user_id"], "gpt-4o")
|
248 |
+
if final_prompt is None:
|
249 |
+
st.stop()
|
250 |
+
st.session_state["messages"].append({"role": "user", "content": final_prompt})
|
251 |
+
st.session_state.context_prompt_added = True
|
252 |
+
else:
|
253 |
+
st.session_state["messages"].append({"role": "user", "content": prompt})
|
254 |
+
|
255 |
+
# Display the latest user message in the chat
|
256 |
+
st.chat_message("user").write(st.session_state["messages"][-1]["content"])
|
257 |
+
|
258 |
+
# Display the related keys
|
259 |
+
related_keys = check_related_keys(prompt, st.session_state["user_id"])
|
260 |
+
st.write("Focus pe entitatile:", related_keys)
|
261 |
+
|
262 |
+
# Call GPT-4o model
|
263 |
+
completion_4o = client.chat.completions.create(
|
264 |
+
model="gpt-4o",
|
265 |
+
messages=st.session_state["messages"]
|
266 |
+
)
|
267 |
+
response_text_4o = completion_4o.choices[0].message.content.strip()
|
268 |
+
|
269 |
+
# Call GPT-4o-mini model
|
270 |
+
completion_4o_mini = client.chat.completions.create(
|
271 |
+
model="gpt-4o-mini",
|
272 |
+
messages=st.session_state["messages"]
|
273 |
+
)
|
274 |
+
response_text_4o_mini = completion_4o_mini.choices[0].message.content.strip()
|
275 |
+
|
276 |
+
st.session_state["messages"].append({"role": "assistant", "content": response_text_4o})
|
277 |
+
st.session_state["messages"].append({"role": "assistant", "content": response_text_4o_mini})
|
278 |
+
|
279 |
+
col1, col2 = st.columns(2)
|
280 |
+
with col1:
|
281 |
+
st.subheader("GPT-4o")
|
282 |
+
st.chat_message("assistant").write(response_text_4o)
|
283 |
+
# Log the conversation for GPT-4o
|
284 |
+
if hasattr(completion_4o, "usage"):
|
285 |
+
prompt_tokens_4o = completion_4o.usage.prompt_tokens
|
286 |
+
completion_tokens_4o = completion_4o.usage.completion_tokens
|
287 |
+
total_tokens_4o = completion_4o.usage.total_tokens
|
288 |
+
st.write("GPT-4o Prompt tokens:", prompt_tokens_4o)
|
289 |
+
st.write("GPT-4o Completion tokens:", completion_tokens_4o)
|
290 |
+
st.write("GPT-4o Total tokens:", total_tokens_4o)
|
291 |
+
with col2:
|
292 |
+
st.subheader("GPT-4o-mini")
|
293 |
+
st.chat_message("assistant").write(response_text_4o_mini)
|
294 |
+
if hasattr(completion_4o_mini, "usage"):
|
295 |
+
prompt_tokens_4o_mini = completion_4o_mini.usage.prompt_tokens
|
296 |
+
completion_tokens_4o_mini = completion_4o_mini.usage.completion_tokens
|
297 |
+
total_tokens_4o_mini = completion_4o_mini.usage.total_tokens
|
298 |
+
st.write("GPT-4o-mini Prompt tokens:", prompt_tokens_4o_mini)
|
299 |
+
st.write("GPT-4o-mini Completion tokens:", completion_tokens_4o_mini)
|
300 |
+
st.write("GPT-4o-mini Total tokens:", total_tokens_4o_mini)
|
301 |
+
|
302 |
+
|
303 |
+
|
304 |
+
|
305 |
+
if __name__ == "__main__":
|
306 |
+
main()
|