Yaser Abdelaziz commited on
Commit
b5acc22
·
1 Parent(s): c3adec4

Initial commit

Browse files
.gitignore ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/#use-with-ide
110
+ .pdm.toml
111
+
112
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113
+ __pypackages__/
114
+
115
+ # Celery stuff
116
+ celerybeat-schedule
117
+ celerybeat.pid
118
+
119
+ # SageMath parsed files
120
+ *.sage.py
121
+
122
+ # Environments
123
+ .env
124
+ .venv
125
+ env/
126
+ venv/
127
+ ENV/
128
+ env.bak/
129
+ venv.bak/
130
+
131
+ # Spyder project settings
132
+ .spyderproject
133
+ .spyproject
134
+
135
+ # Rope project settings
136
+ .ropeproject
137
+
138
+ # mkdocs documentation
139
+ /site
140
+
141
+ # mypy
142
+ .mypy_cache/
143
+ .dmypy.json
144
+ dmypy.json
145
+
146
+ # Pyre type checker
147
+ .pyre/
148
+
149
+ # pytype static type analyzer
150
+ .pytype/
151
+
152
+ # Cython debug symbols
153
+ cython_debug/
154
+
155
+ # PyCharm
156
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
159
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160
+ #.idea/
161
+
162
+ # Custom
163
+ montaai/
README.md CHANGED
@@ -1,13 +1,32 @@
1
- ---
2
- title: AraRAG
3
- emoji: 📈
4
- colorFrom: green
5
- colorTo: green
6
- sdk: gradio
7
- sdk_version: 4.28.3
8
- app_file: app.py
9
- pinned: false
10
- license: apache-2.0
11
- ---
12
-
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # AraRAG
2
+ This project is a chatbot application that can process Arabic PDF files. It uses the RAG model for generating responses in Arabic and Gradio for the user interface. The chatbot can also evaluate its own performance using the Cohere API.
3
+
4
+ ## Getting Started
5
+ These instructions will get you a copy of the project up and running on your local machine for development and testing purposes.
6
+
7
+ ### Prerequisites
8
+ The project requires the following Python packages:
9
+ - gradio
10
+ - PyMuPDF
11
+ - cohere
12
+
13
+ You can install these packages using pip:
14
+ ```bash
15
+ pip install -r requirements.txt
16
+ ```
17
+ ### Environment Variables
18
+ The project uses environment variables to manage sensitive information. Make sure to set these variables in your environment or in a .env file:
19
+ - `SPELLBOOK_BASE_URL`: The base URL for the Spellbook API.
20
+ - `SPELLBOOK_API_KEY`: Your Spellbook API key.
21
+ - `VECTOR_STORE_NAME`: The name of the vector store to be created.
22
+ - `OPENAI_API_KEY`: Your OpenAI API key for the RAG model.
23
+ - `COHERE_API_KEY`: Your Cohere API key for evaluating the chatbot's performance.
24
+
25
+ ## Creating a Vector Store
26
+ Before running the chatbot, you need to create a vector store using the `create_vector_store.py` script. This script uses the Spellbook API to create a vector store with the name specified in the `VECTOR_STORE_NAME` environment variable.
27
+
28
+ ## Running the Chatbot
29
+ You can run the chatbot using the `app.py` script. This script uses Gradio to create a chatbot interface. The chatbot can accept text input or a PDF file. If a PDF file is uploaded, the script will extract the text from the PDF and use it as input to the RAG model.
30
+
31
+ ## Evaluating the Chatbot
32
+ You can evaluate the chatbot's performance using the evaluate.py script. This script uses the Cohere API to evaluate the correctness of the chatbot's answers. The script reads a CSV file with a set of questions and their correct answers, uses the chatbot to answer the questions, and then evaluates the correctness of the chatbot's answers.
app.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import fitz
2
+ import gradio as gr
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv(override=True)
6
+ from rag import RAG
7
+ from utils import format_page
8
+
9
+
10
+ def add_message(history, message):
11
+ if message["files"] is not None and len(message["files"]) > 0:
12
+ file_path = message["files"][0]
13
+ history.append([file_path, ""])
14
+ else:
15
+ if message["text"] is not None:
16
+ history.append((message["text"], None))
17
+
18
+ return history
19
+
20
+
21
+ def bot(history, message, pdf_file_content):
22
+ rag = RAG()
23
+ if message["files"] is not None and len(message["files"]) > 0:
24
+ file_path = message["files"][0]
25
+ if file_path.endswith(".pdf"):
26
+ with fitz.open(file_path) as doc:
27
+ history[-1][1] = ""
28
+ for page in doc:
29
+ cont, page_content = format_page(page)
30
+ if cont:
31
+ continue
32
+ pdf_file_content += "\n\n" + page_content
33
+ history[-1][1] += "\n\n" + page_content
34
+ yield history, gr.MultimodalTextbox(value=None, interactive=False), pdf_file_content
35
+ rag.ingest_text(pdf_file_content)
36
+ else:
37
+ for answer in rag(message, stream=True):
38
+ history[-1][1] = answer
39
+ yield history, gr.MultimodalTextbox(value=None, interactive=False), pdf_file_content
40
+
41
+
42
+ with gr.Blocks() as demo:
43
+ pdf_file_content = gr.State("")
44
+
45
+ chatbot = gr.Chatbot(
46
+ [],
47
+ elem_id="chatbot",
48
+ bubble_full_width=False
49
+ )
50
+
51
+ chat_input = gr.MultimodalTextbox(interactive=True, file_types=[".pdf"], placeholder="Enter message or upload file...", show_label=False)
52
+
53
+ chat_msg = chat_input.submit(add_message, [chatbot, chat_input], chatbot)
54
+ bot_msg = chat_msg.then(bot, [chatbot, chat_input, pdf_file_content], [chatbot, chat_input, pdf_file_content], api_name="bot_response")
55
+ bot_msg.then(lambda: gr.MultimodalTextbox(interactive=True), None, [chat_input])
56
+
57
+ demo.queue()
58
+ demo.launch()
create_vector_store.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+
4
+ from dotenv import load_dotenv
5
+
6
+ load_dotenv(override=True)
7
+ from utils import call_spellbook_api
8
+
9
+
10
+ def main():
11
+ vector_store_name = os.environ.get("VECTOR_STORE_NAME")
12
+
13
+ payload = {"name": vector_store_name}
14
+
15
+ response = call_spellbook_api(endpoint="api/v1/vector-stores", payload=payload)
16
+
17
+ print(json.dumps(response, indent=2))
18
+
19
+
20
+ if __name__ == "__main__":
21
+ main()
data/Actual Budget Report 2022.pdf ADDED
Binary file (827 kB). View file
 
data/Press Release - 2022 Results (Stock Market).pdf ADDED
Binary file (518 kB). View file
 
eval/eval_results.xlsx ADDED
Binary file (8.61 kB). View file
 
eval/eval_set.csv ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ question,answer
2
+ ما هو تقرير وزارة المالية للأداء؟, تقوم وزارة المالية بإعداد تقارير دورية عن الأداء المالي للميزانية العامة للدولة، وتعزيز الشفافية والإفصاح عن السياسات والمبادرات التي تنفذها. وتطوير مستوى الإفصاح عن المبادرات والسياسات التي تقوم بتنفيذها. وتعد تقارير أداء ربع سنوية ونصف سنوية، بالإضافة إلى بيان الميزانية السنوي والأولي.
3
+ اعطيني نبذة عن مجموعة تداول ولخص لي اهم مؤشرات الاداء المالي في 2021,هو تقرير يصدر عن وزارة المالية السعودية، ويكون ذلك في نهاية العام المالي، ويضم التقرير بيانات ومؤشرات الأداء الفعلي للمالية العامة والاقتصاد خلال العام المالي المنصرم، كما يُوضح أسباب الاختلاف بين التقديرات المعتمدة في الميزانية وأدائها الفعلي. ويهدف هذا التقرير إلى دعم مبادرات الشفافية والإفصاح المالي التي تنتهجها الحكومة السعودية، بالإضافة إلى تعزيز الشفافية والإفصاح في المالية العامة، مع تقديم شرح للسياسات والمبادرات والبرامج التي تنفذها الوزارة.
4
+ ما هو صافي الربح الذي حققته مجموعة تداول السعودية في العام المالي 2022 بعد الزكاة؟, حققت مجموعة تداول السعودية صافي ربح قدره 424.6 مليون ريال سعودي في العام المالي 2022 بعد الزكاة.
5
+ ما الشركة الجديدة التي تم تأسيسها بالتعاون مع صندوق الاستثمارات العامة؟, تم تأسيس شركة سوق الكربون الطوعي الإقليمية بالتعاون مع صندوق الاستثمارات العامة.
6
+ ما هي الأهداف الرئيسية لإطلاق شركة سوق الكربون الطوعي الإقليمية؟, الأهداف الرئيسية لإطلاق شركة سوق الكربون الطوعي الإقليمية تشمل دعم الشركات والقطاعات في المنطقة لتمكينها من الوصول إلى الحياد الكربوني وضمان شراء أرصدة الكربون لتخفيض الانبعاثات الكربونية في سلاسل القيمة.
7
+ كيف تؤثر المبادرات التي أطلقتها مجموعة تداول السعودية على تحقيق أهداف رؤية المملكة 2030؟, المبادرات التي أطلقتها مجموعة تداول السعودية تساهم في تعزيز كفاءة وسيولة سوق المال السعودي، وتعزيز الأداء التشغيلي والمالي، وتوسيع قاعدة العملاء المحلية والإقليمية والعالمية، مما يدعم تحقيق أهداف رؤية المملكة 2030 لتطوير قطاع مالي متقدم يسهم في تنمية الاقتصاد الوطني.
8
+ ما هي النتائج الرئيسية للتعاون بين مجموعة تداول السعودية وسوق أبوظبي للأوراق المالية؟, نتج عن التعاون بين مجموعة تداول السعودية وسوق أبوظبي للأوراق المالية أول عملية إدراج مزدوج ومتزامن، مما يمثل مرحلة جديدة من التعاون بين السوق المالية السعودية والأسواق المالية الخليجية والدولية.
eval/eval_set_results.csv ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ question,answer,agent_answer,correctness,correctness_reason
2
+ ما هو تقرير وزارة المالية للأداء؟, تقوم وزارة المالية بإعداد تقارير دورية عن الأداء المالي للميزانية العامة للدولة، وتعزيز الشفافية والإفصاح عن السياسات والمبادرات التي تنفذها. وتطوير مستوى الإفصاح عن المبادرات والسياسات التي تقوم بتنفيذها. وتعد تقارير أداء ربع سنوية ونصف سنوية، بالإضافة إلى بيان الميزانية السنوي والأولي.,تقرير وزارة المالية للأداء هو وثيقة تصدرها وزارة المالية وتوفر نظرة عامة على أداء الوزارة على مدار عام معين. يغطي التقرير جوانب مختلفة مثل الإيرادات والإنفاق والرؤية والاستراتيجية والتحديات المستقبلية. يهدف التقرير إلى توفير تحديث شفاف للجمهور حول عمليات الوزارة وإنجازاتها.,True,"The agent's answer provides a valid explanation of what the Ministry of Finance Performance Report is, and matches the essence of the ground truth answer."
3
+ اعطيني نبذة عن مجموعة تداول ولخص لي اهم مؤشرات الاداء المالي في 2021,هو تقرير يصدر عن وزارة المالية السعودية، ويكون ذلك في نهاية العام المالي، ويضم التقرير بيانات ومؤشرات الأداء الفعلي للمالية العامة والاقتصاد خلال العام المالي المنصرم، كما يُوضح أسباب الاختلاف بين التقديرات المعتمدة في الميزانية وأدائها الفعلي. ويهدف هذا التقرير إلى دعم مبادرات الشفافية والإفصاح المالي التي تنتهجها الحكومة السعودية، بالإضافة إلى تعزيز الشفافية والإفصاح في المالية العامة، مع تقديم شرح للسياسات والمبادرات والبرامج التي تنفذها الوزارة.,"شركة تداول القابضة هي شركة مساهمة عامة مدرجة في السوق المالية السعودية (تداول). تعمل الشركة في مجال تقديم الخدمات المالية، وتمتلك عدة شركات تابعة لها تعمل في قطاعات مختلفة مرتبطة بالقطاع المالي. من أهم مؤشرات الأداء المالي للشركة لعام 2021:
4
+
5
+ 1. حققت إيرادات بلغت 1,166.1 مليون ريال سعودي، بزيادة قدرها 8.1% عن العام السابق 2020.
6
+
7
+ 2. بلغ صافي الدخل 587.7 مليون ريال سعودي، بزيادة قدرها 27.8% عن عام 2020.
8
+
9
+ 3. حققت الشركة أرباحاً قبل الفوائد والضرائب والاستهلاك والإطفاء (EBITDA) بلغت 1,071.4 مليون ريال سعودي، بزيادة قدرها 117% عن العام الماضي.
10
+
11
+ 4. بلغت أصول الشركة 7,690 مليون ريال سعودي، بينما بلغت حقوق المساهمين 4,510 مليون ريال سعودي.
12
+
13
+ هذه هي أهم مؤشرات الأداء المالي لشركة تداول القابضة خلال عام 2021 كما وردت في تقاريرهم المالية.",False,"The agent's answer doesn't match the ground truth. While the provided information is about a company named 'تداول القابضة', which is a Saudi financial services company, the ground truth mentions a report issued by the Ministry of Finance, which is different from the requested information."
14
+ ما هو صافي الربح الذي حققته مجموعة تداول السعودية في العام المالي 2022 بعد الزكاة؟, حققت مجموعة تداول السعودية صافي ربح قدره 424.6 مليون ريال سعودي في العام المالي 2022 بعد الزكاة.,424.6 مليون ريال سعودي,True,The agent's answer is identical to the ground truth.
15
+ ما الشركة الجديدة التي تم تأسيسها بالتعاون مع صندوق الاستثمارات العامة؟, تم تأسيس شركة سوق الكربون الطوعي الإقليمية بالتعاون مع صندوق الاستثمارات العامة.,تداول جروب.,False,The agent's answer 'تداول جروب' does not correspond to the ground truth answer 'شركة سوق الكربون الطوعي الإقليمية'. They don't share any key information.
16
+ ما هي الأهداف الرئيسية لإطلاق شركة سوق الكربون الطوعي الإقليمية؟, الأهداف الرئيسية لإطلاق شركة سوق الكربون الطوعي الإقليمية تشمل دعم الشركات والقطاعات في المنطقة لتمكينها م�� الوصول إلى الحياد الكربوني وضمان شراء أرصدة الكربون لتخفيض الانبعاثات الكربونية في سلاسل القيمة.,سوق الكربون الطوعي الإقليمية أطلقت بهدف تعزيز التنمية المستدامة ودعم الانتقال إلى اقتصاد أخضر في المنطقة. ستعمل السوق على إنشاء إطار موحد لتجارة الكربون، مما يسهل تخفيض الانبعاثات وتوفير فرص جديدة للاستثمار والشراكات في مجال الطاقة النظيفة. الهدف الرئيسي هو تشجيع وتسهيل مشاركة القطاع الخاص في مبادرات الحد من الكربون، والمساهمة في الجهود المبذولة لمواجهة تغير المناخ.,False,"The agent answer doesn't mention anything about supporting companies in the region or enabling them to access carbon neutrality. Also, the purchase of carbon credits to reduce emissions within the value chains is not included in the agent's response."
17
+ كيف تؤثر المبادرات التي أطلقتها مجموعة تداول السعودية على تحقيق أهداف رؤية المملكة 2030؟, المبادرات التي أطلقتها مجموعة تداول السعودية تساهم في تعزيز كفاءة وسيولة سوق المال السعودي، وتعزيز الأداء التشغيلي والمالي، وتوسيع قاعدة العملاء المحلية والإقليمية والعالمية، مما يدعم تحقيق أهداف رؤية المملكة 2030 لتطوير قطاع مالي متقدم يسهم في تنمية الاقتصاد الوطني.,تدعم مبادرات مجموعة تداول السعودية رؤية المملكة 2030 في تحفيز وتنمية سوق المال السعودي، حيث تساهم في زيادة كفاءة السوق المالية وتعزيز مكانة المملكة كمركز مالي رائد في المنطقة. كما تساعد في تطوير السوق وتوفير فرص استثمارية جديدة، مما يؤدي إلى جذب المزيد من الاستثمارات المحلية والأجنبية. بالإضافة إلى ذلك، فإن هذه المبادرات ستعمل على تحسين الأداء التشغيلي للسوق المالية السعودية وتوفير بيئة تجارية أكثر كفاءة وفعالية.,True,"The agent's answer mentions how the initiatives launched by Tadawul contribute to the vision of enhancing the efficiency of the Saudi stock market and its role as a leading financial center in the region. It also talks about the impact on operational and financial performance, which is implied in the ground truth answer."
18
+ ما هي النتائج الرئيسية للتعاون بين مجموعة تداول السعودية وسوق أبوظبي للأوراق المالية؟, نتج عن التعاون بين مجموعة تداول السعودية وسوق أبوظبي للأوراق المالية أول عملية إدراج مزدوج ومتزامن، مما يمثل مرحلة جديدة من التعاون بين السوق المالية السعودية والأسواق المالية الخليجية والدولية.,نجحت مجموعة تداول السعودية وسوق أبوظبي للأوراق المالية في تحقيق شراكة ناجحة تم على إثرها أول إدراج مزدوج لشركة سعودية في سوق أبوظبي للوراق المالية، وهي خطوة هامة نحو الأمام لتعزيز التعاون المالي بين البلدين. كما تم إدراج شركة أخرى في سوق أبوظبي أيضاً، مما سيساهم في إظهار فرص جديدة للاستثمار والتبادل التجاري.,False,"The agent's answer doesn't mention the simultaneous dual listing, which is the main outcome of the collaboration between the two companies, as stated in the ground truth answer."
evaluate.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+
4
+ import cohere
5
+ import pandas as pd
6
+ from dotenv import load_dotenv
7
+
8
+ load_dotenv(override=True)
9
+ from rag import RAG
10
+ from prompts import preamble, message
11
+
12
+
13
+ def evaluate(co, question, agent_answer, ground_truth):
14
+ response = co.chat(
15
+ model='command-r',
16
+ message=message.format(question=question, agent_answer=agent_answer, ground_truth=ground_truth),
17
+ temperature=0.0,
18
+ chat_history=[{"role": "system", "message": preamble}],
19
+ prompt_truncation='AUTO',
20
+ connectors=[]
21
+ )
22
+ json_response = json.loads(response.text[8:-4])
23
+ correctness = json_response['correctness']
24
+ correctness_reason = json_response['correctness_reason']
25
+ return correctness, correctness_reason
26
+
27
+
28
+ if __name__ == '__main__':
29
+ co = cohere.Client(os.getenv('COHERE_API_KEY'))
30
+ rag = RAG()
31
+
32
+ def df_evaluate(row):
33
+ correctness, correctness_reason = evaluate(
34
+ co,
35
+ row["question"],
36
+ row["agent_answer"],
37
+ row["answer"]
38
+ )
39
+ row["correctness"] = correctness
40
+ row["correctness_reason"] = correctness_reason
41
+ return row
42
+
43
+ eval_df = pd.read_csv("eval/eval_set.csv")
44
+ eval_df["agent_answer"] = eval_df["question"].apply(lambda x: list(rag(x))[0])
45
+ eval_df = eval_df.apply(df_evaluate, axis=1)
46
+ eval_df.to_csv("eval/eval_set_results.csv", index=False)
47
+ accuracy = eval_df["correctness"].mean()
48
+ print(f"Accuracy: {accuracy}")
ingest.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+
3
+ from utils import generate_embeddings, call_spellbook_api
4
+
5
+
6
+ def ingest_data(vector_store_name, splits):
7
+ embeddings = generate_embeddings(splits)
8
+ document_items = [
9
+ {
10
+ "embedding": embedding,
11
+ "text": split
12
+ }
13
+ for split, embedding in zip(splits, embeddings)
14
+ ]
15
+
16
+ payload = {"items": document_items}
17
+
18
+ response = call_spellbook_api(endpoint="api/v1/vector-stores/" + vector_store_name + "/documents", payload=payload)
19
+
20
+ print(json.dumps(response, indent=2))
prompts.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ prompt = """Use the following context as your learned knowledge, inside <context></context> XML tags. Notice that I extracted the following context from a pdf file.
2
+
3
+ <context>
4
+ {pdf_file_content}
5
+ </context>
6
+
7
+ When answer to user:
8
+ - If you don't know, just say that you don't know.
9
+ - If you don't know when you are not sure, ask for clarification.
10
+ - Avoid mentioning that you obtained the information from the context.
11
+ - Be concise and to the point.
12
+ - Avoid providing information that is not in the context.
13
+ - And answer according to the language of the user's question.
14
+
15
+ {message}"""
16
+
17
+ prompt_v2 = """I extracted the following Arabic text from a pdf file:
18
+
19
+ \"\"\"
20
+ {pdf_file_content}
21
+ \"\"\"
22
+
23
+ Please answer the following question in Arabic using the Arabic text above only:
24
+ {message}"""
25
+
26
+ eval_preamble = """Your role is to test AI agents. Your task consists in assessing whether a agent output correctly answers a question.
27
+ You are provided with the ground truth answer to the question. Your task is then to evaluate if the agent answer is close to the ground thruth answer.
28
+
29
+ Think step by step and consider the agent output in its entirety. Remember: you need to have a strong and sound reason to support your evaluation.
30
+ If the agent answer is correct, return True. If the agent answer is incorrect, return False along with the reason.
31
+ You must output a single JSON object with keys 'correctness' and 'correctness_reason'. Make sure you return a valid JSON object.
32
+
33
+ The question that was asked to the agent, its output, and the expected ground truth answer will be delimited with XML tags."""
34
+
35
+ eval_message = """<question>
36
+ {question}
37
+ </question>
38
+
39
+ <agent_answer>
40
+ {agent_answer}
41
+ </agent_answer>
42
+
43
+ <ground_truth>
44
+ {ground_truth}
45
+ </ground_truth>"""
rag.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ import cohere
4
+
5
+ from prompts import prompt
6
+ from ingest import ingest_data
7
+ from text_splitter import split_text_on_tokens, tokenizer
8
+ from utils import generate_embeddings, call_spellbook_api
9
+
10
+
11
+ class RAG:
12
+ vector_store_name = os.environ.get("VECTOR_STORE_NAME")
13
+
14
+ def ingest_text(self, text: str):
15
+ ingest_data(self.vector_store_name, split_text_on_tokens(text, tokenizer))
16
+
17
+ def __call__(self, message: str, stream=False):
18
+ payload = {"queryEmbedding": generate_embeddings([str(message)])[0], "k": 10}
19
+ response = call_spellbook_api(
20
+ endpoint="api/v1/vector-stores/" + self.vector_store_name + "/similarity-search", payload=payload
21
+ )
22
+ print(response)
23
+
24
+ docs = [item["text"] for item in response["data"]["items"]]
25
+
26
+ co = cohere.Client(os.getenv('COHERE_API_KEY'))
27
+ # print("Message: ", str(message))
28
+ # response = co.rerank(model="rerank-multilingual-v3.0", query=str(message), documents=docs, top_n=10, return_documents=True)
29
+ # print(response)
30
+ # docs = [doc.document.text for doc in response.results if doc.relevance_score > 0.1]
31
+
32
+ information = "\n\n".join(docs)
33
+ print(information)
34
+
35
+ answer = ""
36
+ print(prompt.format(pdf_file_content=information, message=message))
37
+ for event in co.chat_stream(
38
+ model='command-r',
39
+ message=prompt.format(pdf_file_content=information, message=message),
40
+ temperature=0.0,
41
+ chat_history=[],
42
+ prompt_truncation='AUTO',
43
+ connectors=[]
44
+ ):
45
+ if event.event_type == "text-generation":
46
+ answer += event.text
47
+
48
+ if stream:
49
+ yield answer
50
+ elif event.event_type == "stream-end":
51
+ break
52
+ if not stream:
53
+ yield answer
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio
2
+ PyMuPDF
3
+ cohere
text_splitter.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass
2
+ from typing import Callable, List
3
+
4
+ from transformers import AutoTokenizer
5
+
6
+
7
+ @dataclass
8
+ class Tokenizer:
9
+ chunk_overlap: int
10
+ tokens_per_chunk: int
11
+ decode: Callable[[List[int]], str]
12
+ encode: Callable[[str], List[int]]
13
+
14
+
15
+ def split_text_on_tokens(text: str, tokenizer: Tokenizer) -> List[str]:
16
+ """Split incoming text and return chunks using tokenizer."""
17
+ splits: list[str] = []
18
+ input_ids = tokenizer.encode(text)[1:]
19
+ start_idx = 0
20
+ cur_idx = min(start_idx + tokenizer.tokens_per_chunk, len(input_ids))
21
+ chunk_ids = input_ids[start_idx:cur_idx]
22
+ while start_idx < len(input_ids):
23
+ splits.append(tokenizer.decode(chunk_ids))
24
+ start_idx += tokenizer.tokens_per_chunk - tokenizer.chunk_overlap
25
+ cur_idx = min(start_idx + tokenizer.tokens_per_chunk, len(input_ids))
26
+ chunk_ids = input_ids[start_idx:cur_idx]
27
+ return splits
28
+
29
+
30
+ tokenizer = AutoTokenizer.from_pretrained("CohereForAI/c4ai-command-r-v01")
31
+ tokenizer = Tokenizer(
32
+ chunk_overlap=50,
33
+ tokens_per_chunk=500,
34
+ decode=tokenizer.decode,
35
+ encode=tokenizer.encode,
36
+ )
utils.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+
4
+ import cohere
5
+ # from openai import OpenAI
6
+
7
+
8
+ def generate_embeddings(texts: str):
9
+ # embedding_res = OpenAI().embeddings.create(input=text, model="text-embedding-ada-002")
10
+ # embedding = embedding_res.data[0].embedding
11
+ co = cohere.Client(os.getenv('COHERE_API_KEY'))
12
+ response = co.embed(texts=texts, input_type='classification', embedding_types=['float'], model='embed-multilingual-v3.0')
13
+ embeddings = response.embeddings.float
14
+ return embeddings
15
+
16
+
17
+ def call_spellbook_api(endpoint: str, payload: dict):
18
+ spellbook_base_url = os.environ.get("SPELLBOOK_BASE_URL")
19
+ spellbook_api_key = os.environ.get("SPELLBOOK_API_KEY")
20
+
21
+ headers = {
22
+ "accept": "application/json",
23
+ "content-type": "application/json",
24
+ "authorization": f"Bearer {spellbook_api_key}",
25
+ }
26
+ url = spellbook_base_url + endpoint if spellbook_base_url else endpoint
27
+ response = requests.request("POST", url, json=payload, headers=headers)
28
+ return response.json()
29
+
30
+
31
+ def format_page(page):
32
+ # reference (modified version for Arabic): https://stackoverflow.com/questions/78200728/how-to-avoid-pymupdf-fitz-interpreting-large-gaps-between-words-as-a-newline-c
33
+ page_content = ""
34
+ words = page.get_text("words", sort=True) # words sorted vertical, then horizontal
35
+ if len(words) == 0:
36
+ return True, page_content
37
+ line = [words[0]] # list of words in same line
38
+ for w in words[1:]:
39
+ w0 = line[-1] # get previous word
40
+ if abs(w0[3] - w[3]) <= 3: # same line (approx. same bottom coord)
41
+ line.append(w)
42
+ else: # new line starts
43
+ line.sort(key=lambda w: w[0], reverse=True) # sort words in line right-to-left
44
+ # print text of line
45
+ text = " ".join([w[4] for w in line])
46
+ page_content += text + "\n"
47
+ line = [w] # init line list again
48
+ # print last line
49
+ text = " ".join([w[4] for w in line[::-1]])
50
+ page_content += text + "\n"
51
+ page_content += chr(12) + "\n"
52
+ return False, page_content