georgeek commited on
Commit
f7f78a2
·
1 Parent(s): 6e102d9

first_sync_test

Browse files
Files changed (2) hide show
  1. README.md +78 -0
  2. bill.py +216 -0
README.md ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Llm Bill Chat
3
+ emoji: 🥸🧮
4
+ colorFrom: indigo
5
+ colorTo: red
6
+ sdk: streamlit
7
+ sdk_version: 1.41.1
8
+ app_file: bill.py
9
+ pinned: false
10
+ license: apache-2.0
11
+ short_description: 'LLM app '
12
+ ---
13
+
14
+ # LLM Bill Chat App
15
+
16
+ This project is a proof of concept for a chat application utilizing a Large Language Model (LLM) to assist users with their telecom billing inquiries. The application is built using Python and Streamlit, providing an interactive web interface for users to engage with.
17
+
18
+ ## Features
19
+
20
+ - Maintain chat conversation context
21
+ - Allow users to query their billing information
22
+ - Compare the last four bills and provide insights
23
+ - Respond exclusively with the user's own billing information
24
+ - Save user information and conversation history
25
+
26
+ ## Project Structure
27
+
28
+ ```
29
+ llm-bill-chat-app
30
+ ├── src
31
+ │ ├── chat
32
+ │ │ ├── __init__.py # Package initialization for chat module
33
+ │ │ ├── context.py # Manages conversation context
34
+ │ │ ├── bill_comparison.py # Compares user bills
35
+ │ │ ├── user_info.py # Handles user-specific information
36
+ │ │ └── conversation.py # Manages conversation flow
37
+ │ └── utils
38
+ │ └── __init__.py # Package
39
+ ├── bill.py # Main entry point for the Streamlit app
40
+ initialization for utils module
41
+ ├── requirements.txt # Project dependencies
42
+ └── README.md # Project documentation
43
+ ```
44
+
45
+ ## Installation
46
+
47
+ 1. Clone the repository:
48
+ ```
49
+ git remote add origin https://github.com/serbantica/llm-bill-chat.git
50
+ cd llm-chat-app
51
+ ```
52
+
53
+ 2. Create and activate a virtual environment (Windows example):
54
+ ```
55
+ python -m venv .venv .venv\Scrips\activate
56
+ ```
57
+
58
+ 3. Install the required dependencies:
59
+ ```
60
+ pip install -r requirements.txt
61
+ ```
62
+
63
+ ## Usage
64
+
65
+ To run the application, execute the following command:
66
+ ```
67
+ streamlit run bill.py
68
+ ```
69
+
70
+ Open your web browser and navigate to `http://localhost:8501` to interact with the chat application.
71
+
72
+ ## Contributing
73
+
74
+ Contributions are welcome! Please feel free to submit a pull request or open an issue for any suggestions or improvements.
75
+
76
+ ## License
77
+
78
+ This project is licensed under the MIT License. See the LICENSE file for more details.
bill.py ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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()