Ashok75 commited on
Commit
428ef01
·
verified ·
1 Parent(s): 60ebff8

Upload 28 files

Browse files
CODE_OF_CONDUCT.md ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ # Microsoft Open Source Code of Conduct
2
+
3
+ This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
4
+
5
+ Resources:
6
+
7
+ - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
8
+ - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
9
+ - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
LICENSE ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Microsoft.
2
+ Copyright (c) Microsoft Corporation.
3
+
4
+ MIT License
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
README.md ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ license_link: https://huggingface.co/microsoft/Phi-4-reasoning/resolve/main/LICENSE
4
+ language:
5
+ - en
6
+ base_model:
7
+ - microsoft/phi-4
8
+ pipeline_tag: text-generation
9
+ tags:
10
+ - phi
11
+ - nlp
12
+ - math
13
+ - code
14
+ - chat
15
+ - conversational
16
+ - reasoning
17
+ inference:
18
+ parameters:
19
+ temperature: 0
20
+ widget:
21
+ - messages:
22
+ - role: user
23
+ content: What is the derivative of x^2?
24
+ library_name: transformers
25
+ ---
26
+
27
+ # Phi-4-reasoning Model Card
28
+
29
+ [Phi-4-reasoning Technical Report](https://huggingface.co/papers/2504.21318)
30
+
31
+ ## Model Summary
32
+
33
+ | | |
34
+ |-------------------------|-------------------------------------------------------------------------------|
35
+ | **Developers** | Microsoft Research |
36
+ | **Description** | Phi-4-reasoning is a state-of-the-art open-weight reasoning model finetuned from Phi-4 using supervised fine-tuning on a dataset of chain-of-thought traces and reinforcement learning. The supervised fine-tuning dataset includes a blend of synthetic prompts and high-quality filtered data from public domain websites, focused on math, science, and coding skills as well as alignment data for safety and Responsible AI. The goal of this approach was to ensure that small capable models were trained with data focused on high quality and advanced reasoning. |
37
+ | **Architecture** | Base model same as previously released Phi-4, 14B parameters, dense decoder-only Transformer model |
38
+ | **Inputs** | Text, best suited for prompts in the chat format |
39
+ | **Context length** | 32k tokens |
40
+ | **GPUs** | 32 H100-80G |
41
+ | **Training time** | 2.5 days |
42
+ | **Training data** | 16B tokens, ~8.3B unique tokens |
43
+ | **Outputs** | Generated text in response to the input. Model responses have two sections, namely, a reasoning chain-of-thought block followed by a summarization block |
44
+ | **Dates** | January 2025 – April 2025 |
45
+ | **Status** | Static model trained on an offline dataset with cutoff dates of March 2025 and earlier for publicly available data |
46
+ | **Release date** | April 30, 2025 |
47
+ | **License** | MIT |
48
+
49
+ ## Intended Use
50
+
51
+ | | |
52
+ |-------------------------------|-------------------------------------------------------------------------|
53
+ | **Primary Use Cases** | Our model is designed to accelerate research on language models, for use as a building block for generative AI powered features. It provides uses for general purpose AI systems and applications (primarily in English) which require:<br><br>1. Memory/compute constrained environments.<br>2. Latency bound scenarios.<br>3. Reasoning and logic. |
54
+ | **Out-of-Scope Use Cases** | This model is designed and tested for math reasoning only. Our models are not specifically designed or evaluated for all downstream purposes. Developers should consider common limitations of language models as they select use cases, and evaluate and mitigate for accuracy, safety, and fairness before using within a specific downstream use case, particularly for high-risk scenarios. Developers should be aware of and adhere to applicable laws or regulations (including privacy, trade compliance laws, etc.) that are relevant to their use case, including the model’s focus on English. Review the Responsible AI Considerations section below for further guidance when choosing a use case. Nothing contained in this Model Card should be interpreted as or deemed a restriction or modification to the license the model is released under. |
55
+
56
+ ## Usage
57
+
58
+ > [!IMPORTANT]
59
+ > To fully take advantage of the model's capabilities, inference must use `temperature=0.8`, `top_k=50`, `top_p=0.95`, and `do_sample=True`. For more complex queries, set `max_new_tokens=32768` to allow for longer chain-of-thought (CoT).
60
+
61
+ ### Input Formats
62
+
63
+ Given the nature of the training data, **always use** ChatML template with the **following system prompt** for inference:
64
+
65
+ ```bash
66
+ <|im_start|>system<|im_sep|>
67
+ You are Phi, a language model trained by Microsoft to help users. Your role as an assistant involves thoroughly exploring questions through a systematic thinking process before providing the final precise and accurate solutions. This requires engaging in a comprehensive cycle of analysis, summarizing, exploration, reassessment, reflection, backtracing, and iteration to develop well-considered thinking process. Please structure your response into two main sections: Thought and Solution using the specified format: <think> {Thought section} </think> {Solution section}. In the Thought section, detail your reasoning process in steps. Each step should include detailed considerations such as analysing questions, summarizing relevant findings, brainstorming new ideas, verifying the accuracy of the current steps, refining any errors, and revisiting previous steps. In the Solution section, based on various attempts, explorations, and reflections from the Thought section, systematically present the final solution that you deem correct. The Solution section should be logical, accurate, and concise and detail necessary steps needed to reach the conclusion. Now, try to solve the following question through the above guidelines:<|im_end|>
68
+ <|im_start|>user<|im_sep|>
69
+ What is the derivative of x^2?<|im_end|>
70
+ <|im_start|>assistant<|im_sep|>
71
+ ```
72
+
73
+ ### With `transformers`
74
+
75
+ ```python
76
+ from transformers import AutoTokenizer, AutoModelForCausalLM
77
+
78
+ tokenizer = AutoTokenizer.from_pretrained("microsoft/Phi-4-reasoning")
79
+ model = AutoModelForCausalLM.from_pretrained("microsoft/Phi-4-reasoning", device_map="auto", torch_dtype="auto")
80
+
81
+ messages = [
82
+ {"role": "system", "content": "You are Phi, a language model trained by Microsoft to help users. Your role as an assistant involves thoroughly exploring questions through a systematic thinking process before providing the final precise and accurate solutions. This requires engaging in a comprehensive cycle of analysis, summarizing, exploration, reassessment, reflection, backtracing, and iteration to develop well-considered thinking process. Please structure your response into two main sections: Thought and Solution using the specified format: <think> {Thought section} </think> {Solution section}. In the Thought section, detail your reasoning process in steps. Each step should include detailed considerations such as analysing questions, summarizing relevant findings, brainstorming new ideas, verifying the accuracy of the current steps, refining any errors, and revisiting previous steps. In the Solution section, based on various attempts, explorations, and reflections from the Thought section, systematically present the final solution that you deem correct. The Solution section should be logical, accurate, and concise and detail necessary steps needed to reach the conclusion. Now, try to solve the following question through the above guidelines:"},
83
+ {"role": "user", "content": "What is the derivative of x^2?"},
84
+ ]
85
+ inputs = tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors="pt")
86
+
87
+ outputs = model.generate(
88
+ inputs.to(model.device),
89
+ max_new_tokens=4096,
90
+ temperature=0.8,
91
+ top_k=50,
92
+ top_p=0.95,
93
+ do_sample=True,
94
+ )
95
+ print(tokenizer.decode(outputs[0]))
96
+ ```
97
+
98
+ ### With `vllm`
99
+
100
+ ```bash
101
+ vllm serve microsoft/Phi-4-reasoning --enable-reasoning --reasoning-parser deepseek_r1
102
+ ```
103
+
104
+ *Phi-4-reasoning is also supported out-of-the-box by Ollama, llama.cpp, and any Phi-4 compatible framework.*
105
+
106
+ ## Data Overview
107
+
108
+ ### Training Datasets
109
+
110
+ Our training data is a mixture of Q&A, chat format data in math, science, and coding. The chat prompts are sourced from filtered high-quality web data and optionally rewritten and processed through a synthetic data generation pipeline. We further include data to improve truthfulness and safety.
111
+
112
+ ### Benchmark Datasets
113
+
114
+ We evaluated Phi-4-reasoning using the open-source [Eureka](https://github.com/microsoft/eureka-ml-insights) evaluation suite and our own internal benchmarks to understand the model's capabilities. More specifically, we evaluate our model on:
115
+
116
+ Reasoning tasks:
117
+
118
+ * **AIME 2025, 2024, 2023, and 2022:** Math olympiad questions.
119
+
120
+ * **GPQA-Diamond:** Complex, graduate-level science questions.
121
+
122
+ * **OmniMath:** Collection of over 4000 olympiad-level math problems with human annotation.
123
+
124
+ * **LiveCodeBench:** Code generation benchmark gathered from competitive coding contests.
125
+
126
+ * **3SAT (3-literal Satisfiability Problem) and TSP (Traveling Salesman Problem):** Algorithmic problem solving.
127
+
128
+ * **BA Calendar:** Planning.
129
+
130
+ * **Maze and SpatialMap:** Spatial understanding.
131
+
132
+ General-purpose benchmarks:
133
+
134
+ * **Kitab:** Information retrieval.
135
+
136
+ * **IFEval and ArenaHard:** Instruction following.
137
+
138
+ * **PhiBench:** Internal benchmark.
139
+
140
+ * **FlenQA:** Impact of prompt length on model performance.
141
+
142
+ * **HumanEvalPlus:** Functional code generation.
143
+
144
+ * **MMLU-Pro:** Popular aggregated dataset for multitask language understanding.
145
+
146
+ ## Safety
147
+
148
+ ### Approach
149
+
150
+ Phi-4-reasoning has adopted a robust safety post-training approach via supervised fine-tuning (SFT). This approach leverages a variety of both open-source and in-house generated synthetic prompts, with LLM-generated responses that adhere to rigorous Microsoft safety guidelines, e.g., User Understanding and Clarity, Security and Ethical Guidelines, Limitations, Disclaimers and Knowledge Scope, Handling Complex and Sensitive Topics, Safety and Respectful Engagement, Confidentiality of Guidelines and Confidentiality of Chain-of-Thoughts.
151
+
152
+ ### Safety Evaluation and Red-Teaming
153
+
154
+ Prior to release, Phi-4-reasoning followed a multi-faceted evaluation approach. Quantitative evaluation was conducted with multiple open-source safety benchmarks and in-house tools utilizing adversarial conversation simulation. For qualitative safety evaluation, we collaborated with the independent AI Red Team (AIRT) at Microsoft to assess safety risks posed by Phi-4-reasoning in both average and adversarial user scenarios. In the average user scenario, AIRT emulated typical single-turn and multi-turn interactions to identify potentially risky behaviors. The adversarial user scenario tested a wide range of techniques aimed at intentionally subverting the model's safety training including grounded-ness, jailbreaks, harmful content like hate and unfairness, violence, sexual content, or self-harm, and copyright violations for protected material. We further evaluate models on Toxigen, a benchmark designed to measure bias and toxicity targeted towards minority groups.
155
+
156
+ Please refer to the technical report for more details on safety alignment.
157
+
158
+ ## Model Quality
159
+
160
+ At the high-level overview of the model quality on representative benchmarks. For the tables below, higher numbers indicate better performance:
161
+
162
+ | | AIME 24 | AIME 25 | OmniMath | GPQA-D | LiveCodeBench (8/1/24–2/1/25) |
163
+ |-----------------------------|-------------|-------------|-------------|------------|-------------------------------|
164
+ | Phi-4-reasoning | 75.3 | 62.9 | 76.6 | 65.8 | 53.8 |
165
+ | Phi-4-reasoning-plus | 81.3 | 78.0 | 81.9 | 68.9 | 53.1 |
166
+ | OpenThinker2-32B | 58.0 | 58.0 | — | 64.1 | — |
167
+ | QwQ 32B | 79.5 | 65.8 | — | 59.5 | 63.4 |
168
+ | EXAONE-Deep-32B | 72.1 | 65.8 | — | 66.1 | 59.5 |
169
+ | DeepSeek-R1-Distill-70B | 69.3 | 51.5 | 63.4 | 66.2 | 57.5 |
170
+ | DeepSeek-R1 | 78.7 | 70.4 | 85.0 | 73.0 | 62.8 |
171
+ | o1-mini | 63.6 | 54.8 | — | 60.0 | 53.8 |
172
+ | o1 | 74.6 | 75.3 | 67.5 | 76.7 | 71.0 |
173
+ | o3-mini | 88.0 | 78.0 | 74.6 | 77.7 | 69.5 |
174
+ | Claude-3.7-Sonnet | 55.3 | 58.7 | 54.6 | 76.8 | — |
175
+ | Gemini-2.5-Pro | 92.0 | 86.7 | 61.1 | 84.0 | 69.2 |
176
+
177
+ | | Phi-4 | Phi-4-reasoning | Phi-4-reasoning-plus | o3-mini | GPT-4o |
178
+ |----------------------------------------|-------|------------------|-------------------|---------|--------|
179
+ | FlenQA [3K-token subset] | 82.0 | 97.7 | 97.9 | 96.8 | 90.8 |
180
+ | IFEval Strict | 62.3 | 83.4 | 84.9 | 91.5 | 81.8 |
181
+ | ArenaHard | 68.1 | 73.3 | 79.0 | 81.9 | 75.6 |
182
+ | HumanEvalPlus | 83.5 | 92.9 | 92.3 | 94.0| 88.0 |
183
+ | MMLUPro | 71.5 | 74.3 | 76.0 | 79.4 | 73.0 |
184
+ | Kitab<br><small>No Context - Precision<br>With Context - Precision<br>No Context - Recall<br>With Context - Recall</small> | <br>19.3<br>88.5<br>8.2<br>68.1 | <br>23.2<br>91.5<br>4.9<br>74.8 | <br>27.6<br>93.6<br>6.3<br>75.4 | <br>37.9<br>94.0<br>4.2<br>76.1 | <br>53.7<br>84.7<br>20.3<br>69.2 |
185
+ | Toxigen Discriminative<br><small>Toxic category<br>Neutral category</small> | <br>72.6<br>90.0 | <br>86.7<br>84.7 | <br>77.3<br>90.5 | <br>85.4<br>88.7 | <br>87.6<br>85.1 |
186
+ | PhiBench 2.21 | 58.2 | 70.6 | 74.2 | 78.0| 72.4 |
187
+
188
+ Overall, Phi-4-reasoning, with only 14B parameters, performs well across a wide range of reasoning tasks, outperforming significantly larger open-weight models such as DeepSeek-R1 distilled 70B model and approaching the performance levels of full DeepSeek R1 model. We also test the models on multiple new reasoning benchmarks for algorithmic problem solving and planning, including 3SAT, TSP, and BA-Calendar. These new tasks are nominally out-of-domain for the models as the training process did not intentionally target these skills, but the models still show strong generalization to these tasks. Furthermore, when evaluating performance against standard general abilities benchmarks such as instruction following or non-reasoning tasks, we find that our new models improve significantly from Phi-4, despite the post-training being focused on reasoning skills in specific domains.
189
+
190
+ ## Responsible AI Considerations
191
+
192
+ Like other language models, Phi-4-reasoning can potentially behave in ways that are unfair, unreliable, or offensive. Some of the limiting behaviors to be aware of include:
193
+
194
+ * **Quality of Service:** The model is trained primarily on English text. Languages other than English will experience worse performance. English language varieties with less representation in the training data might experience worse performance than standard American English. Phi-4-reasoning is not intended to support multilingual use.
195
+
196
+ * **Representation of Harms & Perpetuation of Stereotypes:** These models can over- or under-represent groups of people, erase representation of some groups, or reinforce demeaning or negative stereotypes. Despite safety post-training, these limitations may still be present due to differing levels of representation of different groups or prevalence of examples of negative stereotypes in training data that reflect real-world patterns and societal biases.
197
+
198
+ * **Inappropriate or Offensive Content:** These models may produce other types of inappropriate or offensive content, which may make it inappropriate to deploy for sensitive contexts without additional mitigations that are specific to the use case.
199
+
200
+ * **Information Reliability:** Language models can generate nonsensical content or fabricate content that might sound reasonable but is inaccurate or outdated.
201
+
202
+ * **Election Information Reliability:** The model has an elevated defect rate when responding to election-critical queries, which may result in incorrect or unauthoritative election critical information being presented. We are working to improve the model's performance in this area. Users should verify information related to elections with the election authority in their region.
203
+
204
+ * **Limited Scope for Code:** Majority of Phi-4-reasoning training data is based in Python and uses common packages such as `typing`, `math`, `random`, `collections`, `datetime`, `itertools`. If the model generates Python scripts that utilize other packages or scripts in other languages, we strongly recommend users manually verify all API uses.
205
+
206
+ Developers should apply responsible AI best practices and are responsible for ensuring that a specific use case complies with relevant laws and regulations (e.g. privacy, trade, etc.). Using safety services like [Azure AI Content Safety](https://azure.microsoft.com/en-us/products/ai-services/ai-content-safety) that have advanced guardrails is highly recommended. Important areas for consideration include:
207
+
208
+ * **Allocation:** Models may not be suitable for scenarios that could have consequential impact on legal status or the allocation of resources or life opportunities (ex: housing, employment, credit, etc.) without further assessments and additional debiasing techniques.
209
+
210
+ * **High-Risk Scenarios:** Developers should assess suitability of using models in high-risk scenarios where unfair, unreliable or offensive outputs might be extremely costly or lead to harm. This includes providing advice in sensitive or expert domains where accuracy and reliability are critical (ex: legal or health advice). Additional safeguards should be implemented at the application level according to the deployment context.
211
+
212
+ * **Misinformation:** Models may produce inaccurate information. Developers should follow transparency best practices and inform end-users they are interacting with an AI system. At the application level, developers can build feedback mechanisms and pipelines to ground responses in use-case specific, contextual information, a technique known as Retrieval Augmented Generation (RAG).
213
+
214
+ * **Generation of Harmful Content:** Developers should assess outputs for their context and use available safety classifiers or custom solutions appropriate for their use case.
215
+
216
+ * **Misuse:** Other forms of misuse such as fraud, spam, or malware production may be possible, and developers should ensure that their applications do not violate applicable laws and regulations.
217
+
218
+ * **Data Summary:** https://huggingface.co/microsoft/Phi-4-reasoning/blob/main/data_summary_card.md
SECURITY.md ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!-- BEGIN MICROSOFT SECURITY.MD V0.0.9 BLOCK -->
2
+
3
+ ## Security
4
+
5
+ Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin).
6
+
7
+ If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below.
8
+
9
+ ## Reporting Security Issues
10
+
11
+ **Please do not report security vulnerabilities through public GitHub issues.**
12
+
13
+ Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report).
14
+
15
+ If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp).
16
+
17
+ You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
18
+
19
+ Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
20
+
21
+ * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
22
+ * Full paths of source file(s) related to the manifestation of the issue
23
+ * The location of the affected source code (tag/branch/commit or direct URL)
24
+ * Any special configuration required to reproduce the issue
25
+ * Step-by-step instructions to reproduce the issue
26
+ * Proof-of-concept or exploit code (if possible)
27
+ * Impact of the issue, including how an attacker might exploit the issue
28
+
29
+ This information will help us triage your report more quickly.
30
+
31
+ If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs.
32
+
33
+ ## Preferred Languages
34
+
35
+ We prefer all communications to be in English.
36
+
37
+ ## Policy
38
+
39
+ Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd).
40
+
41
+ <!-- END MICROSOFT SECURITY.MD BLOCK -->
added_tokens.json ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "<|endoftext|>": 32000,
3
+ "<|assistant|>": 32001,
4
+ "<|placeholder1|>": 32002,
5
+ "<|placeholder2|>": 32003,
6
+ "<|placeholder3|>": 32004,
7
+ "<|placeholder4|>": 32005,
8
+ "<|system|>": 32006,
9
+ "<|end|>": 32007,
10
+ "<|placeholder5|>": 32008,
11
+ "<|placeholder6|>": 32009,
12
+ "<|user|>": 32010
13
+ }
auth.html ADDED
@@ -0,0 +1,1510 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-bs-theme="dark">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>GAKR AI - Login/Signup</title>
7
+ <link rel="stylesheet" href="https://cdn.replit.com/agent/bootstrap-agent-dark-theme.min.css">
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
9
+ <style>
10
+ :root {
11
+ --gakr-blue: #4285F4;
12
+ --gakr-blue-dark: #1a73e8;
13
+ --gakr-blue-light: #e8f0fe;
14
+ --gakr-grey-text: #5f6368;
15
+ --gakr-grey-hover-bg: rgba(95, 99, 104, 0.1);
16
+ }
17
+
18
+ :root[data-bs-theme="dark"] {
19
+ --gakr-blue: #8ab4f8;
20
+ --gakr-blue-dark: #669df6;
21
+ --gakr-blue-light: rgba(138, 180, 248, 0.1);
22
+ --gakr-grey-text: #bdc1c6;
23
+ --gakr-grey-hover-bg: rgba(189, 193, 198, 0.1);
24
+ }
25
+
26
+ @import url('https://fonts.googleapis.com/css?family=Poppins:400,500,600,700&display=swap');
27
+
28
+ * {
29
+ margin: 0;
30
+ padding: 0;
31
+ box-sizing: border-box;
32
+ font-family: 'Poppins', sans-serif;
33
+ }
34
+
35
+ html, body {
36
+ display: flex;
37
+ flex-direction: column;
38
+ align-items: center;
39
+ height: 100%;
40
+ width: 100%;
41
+ background: -webkit-linear-gradient(left, #003366,#004080,#0059b3 , #0073e6);
42
+ color: var(--bs-body-color);
43
+ padding-top: 5vh;
44
+ padding-bottom: 5vh;
45
+ min-height: 100vh;
46
+ /* Allow space for modal overlay */
47
+ position: relative;
48
+ }
49
+
50
+ ::selection {
51
+ background: var(--gakr-blue);
52
+ color: #fff;
53
+ }
54
+
55
+ .wrapper {
56
+ overflow: hidden;
57
+ max-width: 440px;
58
+ width: 90%;
59
+ background: var(--bs-body-bg);
60
+ box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.5);
61
+ padding: 30px;
62
+ border-radius: 15px;
63
+ margin-top: 10px;
64
+ margin-bottom: 10px;
65
+ z-index: 10; /* Ensure wrapper is below modal but above background */
66
+ }
67
+
68
+ .wrapper .title-text {
69
+ display: flex;
70
+ width: 200%;
71
+ }
72
+
73
+ .wrapper .title {
74
+ width: 50%;
75
+ font-size: 35px;
76
+ font-weight: 600;
77
+ text-align: center;
78
+ transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
79
+ color: var(--bs-body-color);
80
+ }
81
+
82
+ .wrapper .slide-controls {
83
+ position: relative;
84
+ display: flex;
85
+ height: 50px;
86
+ width: 100%;
87
+ overflow: hidden;
88
+ margin: 30px 0 10px 0;
89
+ justify-content: space-between;
90
+ border: 1px solid var(--bs-border-color);
91
+ border-radius: 15px;
92
+ }
93
+
94
+ .slide-controls .slide {
95
+ height: 100%;
96
+ width: 100%;
97
+ color: var(--bs-body-color);
98
+ font-size: 18px;
99
+ font-weight: 500;
100
+ text-align: center;
101
+ line-height: 48px;
102
+ cursor: pointer;
103
+ z-index: 1;
104
+ transition: all 0.6s ease;
105
+ }
106
+
107
+ .slide-controls label.signup{
108
+ color: var(--bs-body-color);
109
+ }
110
+
111
+ .slide-controls .slider-tab {
112
+ position: absolute;
113
+ height: 100%;
114
+ width: 50%;
115
+ left: 0;
116
+ z-index: 0;
117
+ border-radius: 15px;
118
+ background: var(--gakr-blue);
119
+ transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
120
+ }
121
+
122
+ input[type="radio"]{
123
+ display: none;
124
+ }
125
+
126
+ #signup:checked ~ .slider-tab{
127
+ left: 50%;
128
+ }
129
+ #signup:checked ~ label.signup{
130
+ color: #fff;
131
+ cursor: default;
132
+ user-select: none;
133
+ }
134
+ #signup:checked ~ label.login{
135
+ color: var(--bs-body-color);
136
+ }
137
+ #login:checked ~ label.signup{
138
+ color: var(--bs-body-color);
139
+ }
140
+ #login:checked ~ label.login{
141
+ color: #fff;
142
+ cursor: default;
143
+ user-select: none;
144
+ }
145
+
146
+ .wrapper .form-container {
147
+ width: 100%;
148
+ overflow: hidden;
149
+ }
150
+
151
+ .form-container .form-inner {
152
+ display: flex;
153
+ width: 200%;
154
+ }
155
+
156
+ .form-container .form-inner form {
157
+ width: 50%;
158
+ transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
159
+ }
160
+
161
+ .form-inner form .field {
162
+ height: 50px;
163
+ width: 100%;
164
+ margin-top: 20px;
165
+ position: relative;
166
+ }
167
+
168
+ .form-inner form .field:has(+ .field.btn) {
169
+ margin-bottom: 25px;
170
+ }
171
+
172
+ .form-inner form .field input{
173
+ height: 100%;
174
+ width: 100%;
175
+ outline: none;
176
+ padding-left: 15px;
177
+ border-radius: 15px;
178
+ border: 1px solid var(--bs-border-color);
179
+ border-bottom-width: 2px;
180
+ font-size: 17px;
181
+ transition: all 0.3s ease;
182
+ color: var(--bs-body-color);
183
+ background-color: var(--bs-body-bg);
184
+ }
185
+
186
+ .form-inner form .field input.error {
187
+ border-color: red;
188
+ }
189
+
190
+ .form-inner form .field input::placeholder{
191
+ color: var(--bs-secondary-color);
192
+ transition: all 0.3s ease;
193
+ }
194
+
195
+ form .field input:focus::placeholder{
196
+ color: var(--gakr-blue);
197
+ }
198
+
199
+ .form-inner form .pass-link{
200
+ margin-top: 5px;
201
+ font-size: 0.9rem;
202
+ text-align: right; /* Align to right */
203
+ }
204
+
205
+ .form-inner form .signup-link{
206
+ text-align: center;
207
+ margin-top: 30px;
208
+ font-size: 0.9rem;
209
+ }
210
+
211
+ .form-inner form .pass-link a,
212
+ .form-inner form .signup-link a{
213
+ color: var(--gakr-blue);
214
+ text-decoration: none;
215
+ transition: color 0.2s ease-in-out;
216
+ }
217
+
218
+ .form-inner form .pass-link a:hover,
219
+ form-inner form .signup-link a:hover{
220
+ text-decoration: underline;
221
+ color: var(--gakr-blue-dark);
222
+ }
223
+
224
+ form .btn {
225
+ height: 50px;
226
+ width: 100%;
227
+ margin-top: 20px;
228
+ border-radius: 15px;
229
+ position: relative;
230
+ overflow: hidden;
231
+ background: none;
232
+ transition: none;
233
+ }
234
+
235
+ form .btn input[type="submit"] {
236
+ height: 100%;
237
+ width: 100%;
238
+ z-index: 1;
239
+ position: relative;
240
+ background: var(--gakr-blue);
241
+ border: none;
242
+ color: #fff;
243
+ padding: 0;
244
+ border-radius: 15px;
245
+ font-size: 20px;
246
+ font-weight: 500;
247
+ cursor: pointer;
248
+ transition: background-color 0.2s ease-in-out, transform 0.1s ease-in-out;
249
+ display: flex;
250
+ align-items: center;
251
+ justify-content: center;
252
+ }
253
+
254
+ form .btn input[type="submit"]:hover {
255
+ background-color: var(--gakr-blue-dark);
256
+ }
257
+
258
+ form .btn input[type="submit"]:active {
259
+ transform: scale(0.98);
260
+ transition: background-color 0s, transform 0.1s;
261
+ }
262
+
263
+ .error-message {
264
+ color: red;
265
+ font-size: 0.8rem;
266
+ margin-top: 4px;
267
+ position: absolute;
268
+ bottom: -18px;
269
+ left: 15px;
270
+ white-space: nowrap;
271
+ }
272
+
273
+ .form-message {
274
+ text-align: center;
275
+ margin-top: 20px;
276
+ font-size: 0.9rem;
277
+ min-height: 1.2em;
278
+ }
279
+
280
+ .form-message.success {
281
+ color: green;
282
+ }
283
+
284
+ .form-message.error {
285
+ color: red;
286
+ }
287
+
288
+ .loading-spinner {
289
+ display: inline-block;
290
+ width: 18px;
291
+ height: 18px;
292
+ border: 3px solid rgba(255, 255, 255, .3);
293
+ border-radius: 50%;
294
+ border-top-color: #fff;
295
+ animation: spin 1s ease-in-out infinite;
296
+ margin-left: 10px;
297
+ vertical-align: middle;
298
+ }
299
+
300
+ @keyframes spin {
301
+ to { -webkit-transform: rotate(360deg); }
302
+ }
303
+
304
+ .form-inner form .btn input[type="submit"]:disabled {
305
+ opacity: 0.7;
306
+ cursor: not-allowed;
307
+ position: relative;
308
+ }
309
+
310
+ /* --- Modal Styles --- */
311
+ .modal-overlay {
312
+ position: fixed;
313
+ top: 0;
314
+ left: 0;
315
+ width: 100%;
316
+ height: 100%;
317
+ background-color: rgba(0, 0, 0, 0.6); /* Semi-transparent black overlay */
318
+ display: flex;
319
+ justify-content: center;
320
+ align-items: center;
321
+ z-index: 100; /* On top of everything */
322
+ visibility: hidden; /* Hidden by default */
323
+ opacity: 0;
324
+ transition: visibility 0s, opacity 0.3s ease-in-out;
325
+ }
326
+
327
+ .modal-overlay.show {
328
+ visibility: visible;
329
+ opacity: 1;
330
+ }
331
+
332
+ .modal-content {
333
+ background-color: var(--bs-body-bg);
334
+ padding: 30px;
335
+ border-radius: 15px;
336
+ box-shadow: 0px 5px 20px rgba(0, 0, 0, 0.7);
337
+ text-align: center;
338
+ max-width: 350px;
339
+ width: 80%;
340
+ transform: translateY(-20px); /* Slight animation on show */
341
+ transition: transform 0.3s ease-in-out;
342
+ }
343
+
344
+ .modal-overlay.show .modal-content {
345
+ transform: translateY(0);
346
+ }
347
+
348
+ .modal-message {
349
+ font-size: 1.1rem;
350
+ margin-bottom: 25px;
351
+ color: var(--bs-body-color);
352
+ }
353
+ .modal-message p {
354
+ margin-bottom: 10px; /* Spacing for paragraphs in modal message */
355
+ line-height: 1.4;
356
+ }
357
+ .modal-message strong {
358
+ font-size: 1.2rem; /* Heading for attempts message */
359
+ display: block;
360
+ margin-bottom: 15px;
361
+ }
362
+ .modal-message ul {
363
+ list-style: none;
364
+ padding: 0;
365
+ margin-bottom: 20px;
366
+ }
367
+ .modal-message ul li {
368
+ margin-bottom: 8px;
369
+ font-size: 1rem;
370
+ }
371
+
372
+
373
+ .modal-button {
374
+ background-color: var(--gakr-blue);
375
+ color: #fff;
376
+ border: none;
377
+ padding: 10px 25px;
378
+ border-radius: 10px;
379
+ font-size: 1rem;
380
+ cursor: pointer;
381
+ transition: background-color 0.2s ease-in-out, transform 0.1s ease-in-out;
382
+ }
383
+
384
+ .modal-button:hover {
385
+ background-color: var(--gakr-blue-dark);
386
+ }
387
+
388
+ .modal-button:active {
389
+ transform: scale(0.98);
390
+ }
391
+
392
+ .modal-buttons-stacked {
393
+ display: flex;
394
+ flex-direction: column; /* Stack buttons vertically */
395
+ gap: 10px;
396
+ margin-top: 20px;
397
+ }
398
+
399
+ .modal-buttons-stacked .modal-button {
400
+ width: 100%; /* Make buttons full width */
401
+ }
402
+
403
+ .modal-buttons-inline {
404
+ display: flex;
405
+ justify-content: space-between; /* Space them out */
406
+ gap: 15px;
407
+ margin-top: 20px;
408
+ }
409
+ .modal-buttons-inline .modal-button {
410
+ flex-grow: 1; /* Allow buttons to grow to fill space */
411
+ max-width: none; /* Override max-width from .modal-buttons */
412
+ }
413
+
414
+
415
+ /* Eye icon styles */
416
+ .field .toggle-password {
417
+ position: absolute;
418
+ right: 15px;
419
+ top: 50%;
420
+ transform: translateY(-50%);
421
+ cursor: pointer;
422
+ color: var(--bs-secondary-color);
423
+ font-size: 0.9rem;
424
+ }
425
+
426
+ .field .toggle-password:hover {
427
+ color: var(--gakr-blue);
428
+ }
429
+
430
+ /* Added styles for Forgot/Reset Modals */
431
+ .modal-form {
432
+ text-align: left;
433
+ margin-top: 20px;
434
+ }
435
+ .modal-form .field {
436
+ margin-bottom: 15px;
437
+ height: auto; /* Override default field height */
438
+ }
439
+ .modal-form label {
440
+ display: block;
441
+ margin-bottom: 5px;
442
+ font-size: 0.9rem;
443
+ color: var(--bs-body-color);
444
+ }
445
+ .modal-form input[type="text"],
446
+ .modal-form input[type="email"],
447
+ .modal-form input[type="password"] {
448
+ width: 100%;
449
+ padding: 10px 15px;
450
+ border: 1px solid var(--bs-border-color);
451
+ border-radius: 10px;
452
+ background-color: var(--bs-body-bg);
453
+ color: var(--bs-body-color);
454
+ box-sizing: border-box; /* Crucial for consistent width */
455
+ }
456
+ .modal-form .error-message {
457
+ position: static; /* Remove absolute positioning */
458
+ margin-top: 5px;
459
+ margin-left: 0;
460
+ white-space: normal; /* Allow message to wrap */
461
+ }
462
+ .modal-buttons {
463
+ display: flex;
464
+ justify-content: center;
465
+ gap: 15px;
466
+ margin-top: 20px;
467
+ }
468
+ .modal-buttons button {
469
+ width: 100%;
470
+ max-width: 150px;
471
+ }
472
+ </style>
473
+ </head>
474
+ <body>
475
+
476
+ <div class="wrapper">
477
+ <div class="title-text">
478
+ <div class="title login">Login Form</div>
479
+ <div class="title signup">Signup Form</div>
480
+ </div>
481
+ <div class="form-container">
482
+ <div class="slide-controls">
483
+ <input type="radio" name="slide" id="login" checked>
484
+ <input type="radio" name="slide" id="signup">
485
+ <label for="login" class="slide login">Login</label>
486
+ <label for="signup" class="slide signup">Signup</label>
487
+ <div class="slider-tab"></div>
488
+ </div>
489
+ <div class="form-inner">
490
+ <form action="#" class="login" id="loginForm">
491
+ <div class="field">
492
+ <input type="text" placeholder="Username or Email" required id="loginUsernameOrEmail" name="username_or_email">
493
+ <div class="error-message" id="loginUsernameOrEmailError"></div>
494
+ </div>
495
+ <div class="field">
496
+ <input type="password" placeholder="Password" required id="loginPassword" name="password">
497
+ <span class="toggle-password fa-solid fa-eye-slash"></span>
498
+ <div class="error-message" id="loginPasswordError"></div>
499
+ </div>
500
+ <div class="pass-link"><a href="#" id="forgotPasswordLink">Forgot password?</a></div>
501
+ <div class="field btn">
502
+ <input type="submit" value="Login">
503
+ </div>
504
+ <div class="signup-link">Not a member? <a href="">Signup now</a></div>
505
+ </form>
506
+ <form action="#" class="signup" id="signupForm">
507
+ <div class="field">
508
+ <input type="text" placeholder="Name" required id="signupName" name="name">
509
+ <div class="error-message" id="signupNameError"></div>
510
+ </div>
511
+ <div class="field">
512
+ <input type="text" placeholder="Email Address" required id="signupEmail" name="email">
513
+ <div class="error-message" id="signupEmailError"></div>
514
+ </div>
515
+ <div class="field">
516
+ <input type="password" placeholder="Password" required id="signupPassword" name="password">
517
+ <span class="toggle-password fa-solid fa-eye-slash"></span>
518
+ <div class="error-message" id="signupPasswordError"></div>
519
+ </div>
520
+ <div class="field">
521
+ <input type="password" placeholder="Confirm password" required id="signupConfirmPassword" name="confirm_password">
522
+ <span class="toggle-password fa-solid fa-eye-slash"></span>
523
+ <div class="error-message" id="signupConfirmPasswordError"></div>
524
+ </div>
525
+ <div class="field btn">
526
+ <input type="submit" value="Signup">
527
+ </div>
528
+ </form>
529
+ </div>
530
+ </div>
531
+ <div class="form-message" id="generalFormMessage"></div>
532
+ </div>
533
+
534
+ <div class="modal-overlay" id="customModal">
535
+ <div class="modal-content">
536
+ <div class="modal-message" id="modalMessage"></div>
537
+ <div id="modalButtonsContainer" class="modal-buttons">
538
+ </div>
539
+ </div>
540
+ </div>
541
+
542
+ <div class="modal-overlay" id="forgotPasswordRequestModal">
543
+ <div class="modal-content">
544
+ <p class="modal-message" id="forgotModalMessage">Enter your registered email to request a password reset.</p>
545
+
546
+ <form id="forgotPasswordRequestForm" class="modal-form">
547
+ <div class="field">
548
+ <label for="forgotEmail">Email:</label>
549
+ <input type="email" id="forgotEmail" name="email" required>
550
+ <div class="error-message" id="forgotEmailError"></div>
551
+ </div>
552
+ <div class="modal-buttons-stacked">
553
+ <button type="submit" class="modal-button" id="forgotSubmitButton">Request OTP</button>
554
+ <button type="button" class="modal-button" id="forgotCancelButton" style="background-color: var(--bs-secondary-color);">Cancel</button>
555
+ </div>
556
+ </form>
557
+
558
+ <div id="otpSection" style="display: none;">
559
+ <form id="verifyOtpForm" class="modal-form">
560
+ <div class="field">
561
+ <label for="otpInput">Enter OTP:</label>
562
+ <input type="text" id="otpInput" name="otp" required maxlength="6" pattern="\d{6}" title="Please enter a 6-digit OTP">
563
+ <div class="error-message" id="otpError"></div>
564
+ </div>
565
+ <div class="modal-buttons-stacked">
566
+ <button type="submit" class="modal-button" id="verifyOtpButton">Verify OTP</button>
567
+ <button type="button" class="modal-button" id="resendOtpButton" style="background-color: var(--bs-secondary-color);">Send Again</button>
568
+ <button type="button" class="modal-button" id="otpCancelButton" style="background-color: var(--bs-secondary-color);">Cancel</button>
569
+ </div>
570
+ </form>
571
+ </div>
572
+ </div>
573
+ </div>
574
+
575
+ <div class="modal-overlay" id="resetPasswordModal">
576
+ <div class="modal-content">
577
+ <p class="modal-message">Enter your new password.</p>
578
+ <form id="resetPasswordForm" class="modal-form">
579
+ <div class="field">
580
+ <label for="resetEmail">Email:</label>
581
+ <input type="email" id="resetEmail" name="email" required readonly style="background-color: var(--bs-secondary-bg); cursor: not-allowed;">
582
+ <div class="error-message" id="resetEmailError"></div>
583
+ </div>
584
+ <div class="field">
585
+ <label for="newPassword">New Password:</label>
586
+ <input type="password" id="newPassword" name="new_password" required>
587
+ <span class="toggle-password fa-solid fa-eye-slash"></span>
588
+ <div class="error-message" id="newPasswordError"></div>
589
+ </div>
590
+ <div class="field">
591
+ <label for="confirmNewPassword">Confirm New Password:</label>
592
+ <input type="password" id="confirmNewPassword" name="confirm_new_password" required>
593
+ <span class="toggle-password fa-solid fa-eye-slash"></span>
594
+ <div class="error-message" id="confirmNewPasswordError"></div>
595
+ </div>
596
+ <div class="modal-buttons-stacked">
597
+ <button type="submit" class="modal-button" id="resetSubmitButton">Reset Password</button>
598
+ <button type="button" class="modal-button" id="resetCancelButton" style="background-color: var(--bs-secondary-color);">Cancel</button>
599
+ </div>
600
+ </form>
601
+ </div>
602
+ </div>
603
+
604
+ <script>
605
+ document.addEventListener('DOMContentLoaded', function() {
606
+ const loginText = document.querySelector(".title-text .login");
607
+ const loginFormDiv = document.querySelector("form.login");
608
+ const signupLink = document.querySelector("form .signup-link a");
609
+ const loginRadio = document.getElementById('login');
610
+ const signupRadio = document.getElementById('signup');
611
+
612
+ const loginForm = document.getElementById('loginForm');
613
+ const signupForm = document.getElementById('signupForm');
614
+
615
+ // Existing form inputs and error displays
616
+ const loginUsernameOrEmailInput = document.getElementById('loginUsernameOrEmail');
617
+ const loginPasswordInput = document.getElementById('loginPassword');
618
+ const loginSubmitButton = loginForm ? loginForm.querySelector('input[type="submit"]') : null;
619
+
620
+ const signupNameInput = document.getElementById('signupName');
621
+ const signupEmailInput = document.getElementById('signupEmail');
622
+ const signupPasswordInput = document.getElementById('signupPassword');
623
+ const signupConfirmPasswordInput = document.getElementById('signupConfirmPassword');
624
+ const signupSubmitButton = signupForm ? signupForm.querySelector('input[type="submit"]') : null;
625
+
626
+ const loginUsernameOrEmailError = document.getElementById('loginUsernameOrEmailError');
627
+ const loginPasswordError = document.getElementById('loginPasswordError');
628
+
629
+ const signupNameError = document.getElementById('signupNameError');
630
+ const signupEmailError = document.getElementById('signupEmailError');
631
+ const signupPasswordError = document.getElementById('signupPasswordError');
632
+ const signupConfirmPasswordError = document.getElementById('signupConfirmPasswordError');
633
+
634
+ const generalFormMessage = document.getElementById('generalFormMessage');
635
+
636
+ // Modal elements (main alert modal)
637
+ const customModal = document.getElementById('customModal');
638
+ const modalMessageDiv = document.getElementById('modalMessage');
639
+ const modalButtonsContainer = document.getElementById('modalButtonsContainer');
640
+
641
+ // Forgot Password Request & OTP Modals elements
642
+ const forgotPasswordLink = document.getElementById('forgotPasswordLink');
643
+ const forgotPasswordRequestModal = document.getElementById('forgotPasswordRequestModal'); // The main overlay for forgot/otp
644
+ const forgotModalMessage = document.getElementById('forgotModalMessage'); // Message within the forgot/otp modal
645
+
646
+ // Elements for Step 1: Request Email
647
+ const forgotPasswordRequestForm = document.getElementById('forgotPasswordRequestForm');
648
+ const forgotEmailInput = document.getElementById('forgotEmail');
649
+ const forgotEmailError = document.getElementById('forgotEmailError');
650
+ const forgotSubmitButton = document.getElementById('forgotSubmitButton');
651
+ const forgotCancelButton = document.getElementById('forgotCancelButton');
652
+
653
+ // Elements for Step 2: Enter OTP
654
+ const otpSection = document.getElementById('otpSection'); // The container for OTP input and buttons
655
+ const verifyOtpForm = document.getElementById('verifyOtpForm');
656
+ const otpInput = document.getElementById('otpInput');
657
+ const otpError = document.getElementById('otpError');
658
+ const verifyOtpButton = document.getElementById('verifyOtpButton');
659
+ const resendOtpButton = document.getElementById('resendOtpButton');
660
+ const otpCancelButton = document.getElementById('otpCancelButton');
661
+
662
+ // Reset Password Modal elements (Step 3: New Password)
663
+ const resetPasswordModal = document.getElementById('resetPasswordModal');
664
+ const resetPasswordForm = document.getElementById('resetPasswordForm');
665
+ const resetEmailInput = document.getElementById('resetEmail'); // Hidden but read-only email field
666
+ const newPasswordInput = document.getElementById('newPassword');
667
+ const confirmNewPasswordInput = document.getElementById('confirmNewPassword');
668
+ const resetEmailError = document.getElementById('resetEmailError'); // Error for reset email (should be rare)
669
+ const newPasswordError = document.getElementById('newPasswordError');
670
+ const confirmNewPasswordError = document.getElementById('confirmNewPasswordError');
671
+ const resetSubmitButton = document.getElementById('resetSubmitButton');
672
+ const resetCancelButton = document.getElementById('resetCancelButton');
673
+
674
+ // New: Login Attempt Counter
675
+ let loginAttempts = 0;
676
+ const MAX_LOGIN_ATTEMPTS = 3;
677
+
678
+ // Stores the email across the forgot password/OTP/reset flow
679
+ let currentForgotEmail = '';
680
+
681
+ // Function to clear all error messages and input error classes
682
+ function clearErrors(formType = 'all') {
683
+ if (formType === 'all' || formType === 'main') {
684
+ document.querySelectorAll('.error-message').forEach(el => el.textContent = '');
685
+ document.querySelectorAll('.form-inner form .field input').forEach(el => el.classList.remove('error'));
686
+ if (generalFormMessage) {
687
+ generalFormMessage.textContent = '';
688
+ generalFormMessage.className = 'form-message';
689
+ }
690
+ }
691
+ if (formType === 'all' || formType === 'forgot') {
692
+ if (forgotEmailError) forgotEmailError.textContent = '';
693
+ if (forgotEmailInput) forgotEmailInput.classList.remove('error');
694
+ // Also clear the general forgotModalMessage when resetting for new flow
695
+ if (forgotModalMessage) forgotModalMessage.innerHTML = 'Enter your registered email to request a password reset.';
696
+ }
697
+ if (formType === 'all' || formType === 'otp') {
698
+ if (otpError) otpError.textContent = '';
699
+ if (otpInput) otpInput.classList.remove('error');
700
+ }
701
+ if (formType === 'all' || formType === 'reset') {
702
+ if (resetEmailError) resetEmailError.textContent = '';
703
+ if (newPasswordError) newPasswordError.textContent = '';
704
+ if (confirmNewPasswordError) confirmNewPasswordError.textContent = '';
705
+ if (resetEmailInput) resetEmailInput.classList.remove('error');
706
+ if (newPasswordInput) newPasswordInput.classList.remove('error');
707
+ if (confirmNewPasswordInput) confirmNewPasswordInput.classList.remove('error');
708
+ }
709
+ }
710
+
711
+ // Function to display general form-level message (below the forms)
712
+ function displayFormMessage(message, type = 'info') {
713
+ if (generalFormMessage) {
714
+ generalFormMessage.textContent = message;
715
+ generalFormMessage.className = 'form-message ' + type;
716
+ }
717
+ }
718
+
719
+ // Universal modal display function
720
+ function showCustomModal(modalElement) {
721
+ modalElement.classList.add('show');
722
+ }
723
+
724
+ // Universal modal hide function
725
+ function hideCustomModal(modalElement) {
726
+ modalElement.classList.remove('show');
727
+ clearErrors(); // Clear all errors when any modal is hidden
728
+ }
729
+
730
+ // Function to show a flexible custom modal (for general alerts/info)
731
+ function showFlexibleModal(messageHTML, buttonsConfig, callback = null) {
732
+ modalMessageDiv.innerHTML = messageHTML;
733
+ modalButtonsContainer.innerHTML = ''; // Clear previous buttons
734
+
735
+ if (buttonsConfig && buttonsConfig.length > 0) {
736
+ const isInline = buttonsConfig.every(btn => !btn.newline);
737
+ modalButtonsContainer.className = isInline ? 'modal-buttons-inline' : 'modal-buttons-stacked';
738
+
739
+ buttonsConfig.forEach(btnConf => {
740
+ const button = document.createElement('button');
741
+ button.className = 'modal-button';
742
+ button.textContent = btnConf.text;
743
+ if (btnConf.style) {
744
+ button.style = btnConf.style;
745
+ }
746
+ button.onclick = function() {
747
+ hideCustomModal(customModal);
748
+ if (btnConf.action && typeof btnConf.action === 'function') {
749
+ btnConf.action();
750
+ }
751
+ if (callback && typeof callback === 'function') {
752
+ callback();
753
+ }
754
+ };
755
+ modalButtonsContainer.appendChild(button);
756
+ });
757
+ } else {
758
+ const okButton = document.createElement('button');
759
+ okButton.className = 'modal-button';
760
+ okButton.textContent = 'OK';
761
+ okButton.onclick = function() {
762
+ hideCustomModal(customModal);
763
+ if (callback && typeof callback === 'function') {
764
+ callback();
765
+ }
766
+ };
767
+ modalButtonsContainer.classList.remove('modal-buttons-inline', 'modal-buttons-stacked');
768
+ modalButtonsContainer.classList.add('modal-buttons');
769
+ modalButtonsContainer.appendChild(okButton);
770
+ }
771
+ showCustomModal(customModal);
772
+ }
773
+
774
+ // Slide logic
775
+ function slideToSignup() {
776
+ if (loginFormDiv && loginText) {
777
+ loginFormDiv.style.marginLeft = "-50%";
778
+ loginText.style.marginLeft = "-50%";
779
+ clearErrors('main');
780
+ }
781
+ }
782
+
783
+ function slideToLogin() {
784
+ if (loginFormDiv && loginText) {
785
+ loginFormDiv.style.marginLeft = "0%";
786
+ loginText.style.marginLeft = "0%";
787
+ clearErrors('main');
788
+ }
789
+ }
790
+
791
+ // Event listeners for radio buttons and signup link
792
+ if (signupRadio) {
793
+ signupRadio.addEventListener('change', function() {
794
+ if (this.checked) {
795
+ slideToSignup();
796
+ }
797
+ });
798
+ }
799
+
800
+ if (loginRadio) {
801
+ loginRadio.addEventListener('change', function() {
802
+ if (this.checked) {
803
+ slideToLogin();
804
+ }
805
+ });
806
+ }
807
+
808
+ if (signupLink && signupRadio) {
809
+ signupLink.onclick = ((e) => {
810
+ e.preventDefault();
811
+ signupRadio.checked = true;
812
+ slideToSignup();
813
+ });
814
+ }
815
+
816
+ // Handle URL parameter for initial tab
817
+ const urlParams = new URLSearchParams(window.location.search);
818
+ const showSignup = urlParams.get('signup');
819
+
820
+ if (showSignup === 'true') {
821
+ if (signupRadio) {
822
+ signupRadio.checked = true;
823
+ slideToSignup();
824
+ }
825
+ } else {
826
+ if (loginRadio) {
827
+ loginRadio.checked = true;
828
+ slideToLogin();
829
+ }
830
+ }
831
+
832
+ // Function to toggle password visibility
833
+ function setupPasswordToggle(passwordInput, toggleIcon) {
834
+ if (passwordInput && toggleIcon) {
835
+ toggleIcon.addEventListener('click', function() {
836
+ const type = passwordInput.getAttribute('type') === 'password' ? 'text' : 'password';
837
+ passwordInput.setAttribute('type', type);
838
+ this.classList.toggle('fa-eye');
839
+ this.classList.toggle('fa-eye-slash');
840
+ });
841
+ }
842
+ }
843
+
844
+ // Setup password toggles for all password fields
845
+ setupPasswordToggle(loginPasswordInput, document.querySelector('#loginForm .toggle-password'));
846
+ setupPasswordToggle(signupPasswordInput, document.querySelector('#signupForm #signupPassword + .toggle-password'));
847
+ setupPasswordToggle(signupConfirmPasswordInput, document.querySelector('#signupForm #signupConfirmPassword + .toggle-password'));
848
+ setupPasswordToggle(newPasswordInput, document.querySelector('#resetPasswordForm #newPassword + .toggle-password'));
849
+ setupPasswordToggle(confirmNewPasswordInput, document.querySelector('#resetPasswordForm #confirmNewPassword + .toggle-password'));
850
+
851
+
852
+ // Login Form Submission Handler
853
+ if (loginForm) {
854
+ loginForm.addEventListener('submit', function(event) {
855
+ event.preventDefault();
856
+ clearErrors('main'); // Clear main form errors
857
+
858
+ // Only proceed if login attempts are below limit
859
+ if (loginAttempts >= MAX_LOGIN_ATTEMPTS) {
860
+ showLoginAttemptsLimitModal();
861
+ return; // Stop submission
862
+ }
863
+
864
+ let isValid = true;
865
+
866
+ const usernameOrEmail = loginUsernameOrEmailInput.value.trim();
867
+ const password = loginPasswordInput.value.trim();
868
+
869
+ if (usernameOrEmail === '') {
870
+ isValid = false;
871
+ loginUsernameOrEmailError.textContent = 'Username or Email is required';
872
+ loginUsernameOrEmailInput.classList.add('error');
873
+ }
874
+ if (password === '') {
875
+ isValid = false;
876
+ loginPasswordError.textContent = 'Password is required';
877
+ loginPasswordInput.classList.add('error');
878
+ }
879
+
880
+ if (isValid) {
881
+ submitFormData(event.target, loginSubmitButton, '/api/login', 'main');
882
+ }
883
+ });
884
+ }
885
+
886
+ // Signup Form Submission Handler
887
+ if (signupForm) {
888
+ signupForm.addEventListener('submit', function(event) {
889
+ event.preventDefault();
890
+ clearErrors('main');
891
+
892
+ let isValid = true;
893
+
894
+ const name = signupNameInput.value.trim();
895
+ const email = signupEmailInput.value.trim();
896
+ const password = signupPasswordInput.value.trim();
897
+ const confirmPassword = signupConfirmPasswordInput.value.trim();
898
+
899
+ if (name === '') {
900
+ isValid = false;
901
+ signupNameError.textContent = 'Name is required';
902
+ signupNameInput.classList.add('error');
903
+ }
904
+
905
+ if (email === '') {
906
+ isValid = false;
907
+ signupEmailError.textContent = 'Email is required';
908
+ signupEmailInput.classList.add('error');
909
+ } else if (!isValidEmail(email)) {
910
+ isValid = false;
911
+ signupEmailError.textContent = 'Enter a valid email address';
912
+ signupEmailInput.classList.add('error');
913
+ }
914
+
915
+ if (password === '') {
916
+ isValid = false;
917
+ signupPasswordError.textContent = 'Password is required';
918
+ signupPasswordInput.classList.add('error');
919
+ } else if (password.length < 6) {
920
+ isValid = false;
921
+ signupPasswordError.textContent = 'Password must be at least 6 characters';
922
+ signupPasswordInput.classList.add('error');
923
+ }
924
+
925
+ if (confirmPassword === '') {
926
+ isValid = false;
927
+ signupConfirmPasswordError.textContent = 'Confirm password is required';
928
+ signupConfirmPasswordInput.classList.add('error');
929
+ } else if (password !== confirmPassword) {
930
+ isValid = false;
931
+ signupConfirmPasswordError.textContent = 'Passwords do not match';
932
+ signupConfirmPasswordInput.classList.add('error');
933
+ signupPasswordInput.classList.add('error');
934
+ }
935
+
936
+ if (isValid) {
937
+ submitFormData(event.target, signupSubmitButton, '/api/signup', 'main');
938
+ }
939
+ });
940
+ }
941
+
942
+ // Forgot Password Link Handler (Initial Click)
943
+ if (forgotPasswordLink) {
944
+ forgotPasswordLink.addEventListener('click', function(event) {
945
+ event.preventDefault();
946
+ clearErrors('forgot'); // Clear errors for email request
947
+ clearErrors('otp'); // Clear errors for OTP section
948
+
949
+ forgotPasswordRequestForm.reset(); // Clear email input
950
+ otpInput.value = ''; // Clear OTP input
951
+ // Reset the general message for the forgot password modal
952
+ forgotModalMessage.innerHTML = 'Enter your registered email to request a password reset.';
953
+
954
+ // Show the email request form and hide the OTP section
955
+ forgotPasswordRequestForm.style.display = 'block';
956
+ if (otpSection) otpSection.style.display = 'none'; // Ensure OTP section is hidden initially
957
+
958
+ showCustomModal(forgotPasswordRequestModal);
959
+ });
960
+ }
961
+
962
+ // Step 1: Forgot Password Request Form Submission Handler (Requests OTP)
963
+ if (forgotPasswordRequestForm) {
964
+ forgotPasswordRequestForm.addEventListener('submit', async function(event) {
965
+ event.preventDefault();
966
+ clearErrors('forgot'); // Clear errors specific to the email request form
967
+
968
+ let isValid = true;
969
+ const email = forgotEmailInput.value.trim();
970
+
971
+ if (email === '') {
972
+ isValid = false;
973
+ forgotEmailError.textContent = 'Email is required.';
974
+ forgotEmailInput.classList.add('error');
975
+ } else if (!isValidEmail(email)) {
976
+ isValid = false;
977
+ forgotEmailError.textContent = 'Enter a valid email address.';
978
+ forgotEmailInput.classList.add('error');
979
+ }
980
+
981
+ if (isValid) {
982
+ forgotSubmitButton.disabled = true;
983
+ forgotSubmitButton.innerHTML = '<span class="loading-spinner"></span> Sending OTP...';
984
+
985
+ try {
986
+ const response = await fetch('/api/forgot_password_request', {
987
+ method: 'POST',
988
+ headers: { 'Content-Type': 'application/json' },
989
+ body: JSON.stringify({ email: email })
990
+ });
991
+
992
+ const result = await response.json();
993
+
994
+ if (response.ok) {
995
+ currentForgotEmail = email; // Store email for subsequent steps
996
+
997
+ // Transition to OTP input section
998
+ forgotPasswordRequestForm.style.display = 'none';
999
+ if (otpSection) otpSection.style.display = 'block';
1000
+ otpInput.value = ''; // Clear previous OTP in case of resend
1001
+
1002
+ // Update message for OTP section
1003
+ let messageText = `<p>An OTP has been sent to <strong>${email}</strong>. Please enter it below.</p>`;
1004
+ // Note: Do not display simulated OTP in production. This is for testing convenience.
1005
+ // if (result.simulated_otp) {
1006
+ // messageText += `<p><strong>SIMULATED OTP (FOR TESTING):</strong> ${result.simulated_otp}</p>`;
1007
+ // }
1008
+ forgotModalMessage.innerHTML = messageText;
1009
+
1010
+ } else {
1011
+ displayModalFormErrors(result, forgotPasswordRequestForm, 'forgot');
1012
+ }
1013
+ } catch (error) {
1014
+ console.error('Fetch error:', error);
1015
+ showFlexibleModal('<p>Network error. Could not request OTP. Please try again.</p>', [{ text: 'OK' }]);
1016
+ } finally {
1017
+ forgotSubmitButton.disabled = false;
1018
+ forgotSubmitButton.innerHTML = 'Request OTP';
1019
+ }
1020
+ }
1021
+ });
1022
+ }
1023
+
1024
+ // Step 2: OTP Verification Form Submission Handler
1025
+ if (verifyOtpForm) {
1026
+ verifyOtpForm.addEventListener('submit', async function(event) {
1027
+ event.preventDefault();
1028
+ clearErrors('otp'); // Clear errors specific to the OTP verification form
1029
+
1030
+ let isValid = true;
1031
+ const otp = otpInput.value.trim();
1032
+
1033
+ if (otp === '') {
1034
+ isValid = false;
1035
+ otpError.textContent = 'OTP is required.';
1036
+ otpInput.classList.add('error');
1037
+ } else if (!/^\d{6}$/.test(otp)) { // Ensure it's 6 digits
1038
+ isValid = false;
1039
+ otpError.textContent = 'OTP must be a 6-digit number.';
1040
+ otpInput.classList.add('error');
1041
+ }
1042
+
1043
+ if (isValid) {
1044
+ verifyOtpButton.disabled = true;
1045
+ verifyOtpButton.innerHTML = '<span class="loading-spinner"></span> Verifying...';
1046
+
1047
+ try {
1048
+ const response = await fetch('/api/verify_otp', {
1049
+ method: 'POST',
1050
+ headers: { 'Content-Type': 'application/json' },
1051
+ body: JSON.stringify({ email: currentForgotEmail, otp: otp })
1052
+ });
1053
+
1054
+ const result = await response.json();
1055
+
1056
+ if (response.ok) {
1057
+ hideCustomModal(forgotPasswordRequestModal); // Hide the OTP modal
1058
+ resetEmailInput.value = currentForgotEmail; // Pre-fill email in new password form
1059
+ newPasswordInput.value = ''; // Clear new password field
1060
+ confirmNewPasswordInput.value = ''; // Clear confirm new password field
1061
+ showCustomModal(resetPasswordModal); // Show the new password modal
1062
+
1063
+ } else {
1064
+ otpError.textContent = result.message || 'OTP verification failed.';
1065
+ otpInput.classList.add('error');
1066
+ otpInput.classList.add('error'); // Ensure input field is marked
1067
+ // Update the main message area of the OTP modal if a general message is returned
1068
+ if (result.message) {
1069
+ forgotModalMessage.innerHTML = `<p>${result.message}</p>`;
1070
+ }
1071
+ }
1072
+ } catch (error) {
1073
+ console.error('Fetch error:', error);
1074
+ showFlexibleModal('<p>Network error. Could not verify OTP. Please try again.</p>', [{ text: 'OK' }]);
1075
+ } finally {
1076
+ verifyOtpButton.disabled = false;
1077
+ verifyOtpButton.innerHTML = 'Verify OTP';
1078
+ }
1079
+ }
1080
+ });
1081
+ }
1082
+
1083
+ // Step 2: Resend OTP Button Handler
1084
+ if (resendOtpButton) {
1085
+ resendOtpButton.addEventListener('click', async function(event) {
1086
+ event.preventDefault();
1087
+ clearErrors('otp'); // Clear any previous OTP errors
1088
+ resendOtpButton.disabled = true;
1089
+ resendOtpButton.innerHTML = '<span class="loading-spinner"></span> Resending...';
1090
+
1091
+ try {
1092
+ const response = await fetch('/api/forgot_password_request', {
1093
+ method: 'POST',
1094
+ headers: { 'Content-Type': 'application/json' },
1095
+ body: JSON.stringify({ email: currentForgotEmail })
1096
+ });
1097
+
1098
+ const result = await response.json();
1099
+
1100
+ if (response.ok) {
1101
+ let messageText = `<p>New OTP sent to <strong>${currentForgotEmail}</strong>. Please enter it below.</p>`;
1102
+ // if (result.simulated_otp) { // Again, remove in production
1103
+ // messageText += `<p><strong>SIMULATED OTP (FOR TESTING):</strong> ${result.simulated_otp}</p>`;
1104
+ // }
1105
+ forgotModalMessage.innerHTML = messageText;
1106
+ otpInput.value = ''; // Clear previous OTP
1107
+ otpInput.classList.remove('error'); // Clear error state
1108
+ otpError.textContent = ''; // Clear error message
1109
+ } else {
1110
+ // If there's an error on resend, display it in the OTP section's error message
1111
+ otpError.textContent = result.message || 'Failed to resend OTP.';
1112
+ otpInput.classList.add('error'); // Potentially highlight OTP input again
1113
+ if (result.message) {
1114
+ forgotModalMessage.innerHTML = `<p>${result.message}</p>`;
1115
+ }
1116
+ }
1117
+ } catch (error) {
1118
+ console.error('Fetch error:', error);
1119
+ showFlexibleModal('<p>Network error. Could not resend OTP. Please try again.</p>', [{ text: 'OK' }]);
1120
+ } finally {
1121
+ resendOtpButton.disabled = false;
1122
+ resendOtpButton.innerHTML = 'Send Again';
1123
+ }
1124
+ });
1125
+ }
1126
+
1127
+ // Handlers for Cancel buttons in the forgot/reset modals
1128
+ if (forgotCancelButton) {
1129
+ forgotCancelButton.addEventListener('click', function() {
1130
+ hideCustomModal(forgotPasswordRequestModal);
1131
+ forgotPasswordRequestForm.reset();
1132
+ currentForgotEmail = ''; // Clear stored email
1133
+ clearErrors('forgot'); // Ensure all forgot-related errors are cleared
1134
+ });
1135
+ }
1136
+
1137
+ if (otpCancelButton) {
1138
+ otpCancelButton.addEventListener('click', function() {
1139
+ hideCustomModal(forgotPasswordRequestModal);
1140
+ verifyOtpForm.reset();
1141
+ currentForgotEmail = ''; // Clear stored email
1142
+ clearErrors('otp'); // Ensure all OTP-related errors are cleared
1143
+ });
1144
+ }
1145
+
1146
+ if (resetCancelButton) {
1147
+ resetCancelButton.addEventListener('click', function() {
1148
+ hideCustomModal(resetPasswordModal);
1149
+ resetPasswordForm.reset();
1150
+ currentForgotEmail = ''; // Clear stored email
1151
+ clearErrors('reset'); // Ensure all reset-related errors are cleared
1152
+ });
1153
+ }
1154
+
1155
+ // Step 3: Reset Password Form Submission Handler
1156
+ if (resetPasswordForm) {
1157
+ resetPasswordForm.addEventListener('submit', async function(event) {
1158
+ event.preventDefault();
1159
+ clearErrors('reset'); // Clear errors specific to the reset password form
1160
+
1161
+ let isValid = true;
1162
+ const email = resetEmailInput.value.trim(); // This should be currentForgotEmail
1163
+ const newPassword = newPasswordInput.value.trim();
1164
+ const confirmNewPassword = confirmNewPasswordInput.value.trim();
1165
+
1166
+
1167
+ if (email === '') { // Should be pre-filled, but a final check
1168
+ isValid = false;
1169
+ resetEmailError.textContent = 'Email is required.';
1170
+ resetEmailInput.classList.add('error');
1171
+ } else if (!isValidEmail(email)) {
1172
+ isValid = false;
1173
+ resetEmailError.textContent = 'Invalid email format.'; // Should not happen if pre-filled correctly
1174
+ resetEmailInput.classList.add('error');
1175
+ }
1176
+
1177
+ if (newPassword === '') {
1178
+ isValid = false;
1179
+ newPasswordError.textContent = 'New password is required.';
1180
+ newPasswordInput.classList.add('error');
1181
+ } else if (newPassword.length < 6) {
1182
+ isValid = false;
1183
+ newPasswordError.textContent = 'New password must be at least 6 characters.';
1184
+ newPasswordInput.classList.add('error');
1185
+ }
1186
+
1187
+ if (confirmNewPassword === '') {
1188
+ isValid = false;
1189
+ confirmNewPasswordError.textContent = 'Confirm new password is required.';
1190
+ confirmNewPasswordInput.classList.add('error');
1191
+ } else if (newPassword !== confirmNewPassword) {
1192
+ isValid = false;
1193
+ confirmNewPasswordError.textContent = 'Passwords do not match.';
1194
+ confirmNewPasswordInput.classList.add('error');
1195
+ newPasswordInput.classList.add('error'); // Highlight both if they don't match
1196
+ }
1197
+
1198
+
1199
+ if (isValid) {
1200
+ resetSubmitButton.disabled = true;
1201
+ resetSubmitButton.innerHTML = '<span class="loading-spinner"></span> Resetting...';
1202
+
1203
+ try {
1204
+ const response = await fetch('/api/reset_password', {
1205
+ method: 'POST',
1206
+ headers: { 'Content-Type': 'application/json' },
1207
+ body: JSON.stringify({ email: email, new_password: newPassword }) // Ensure correct variable name for new password
1208
+ });
1209
+
1210
+ const result = await response.json();
1211
+
1212
+ if (response.ok) {
1213
+ hideCustomModal(resetPasswordModal);
1214
+ resetPasswordForm.reset();
1215
+ currentForgotEmail = ''; // Clear stored email after successful reset
1216
+ showFlexibleModal(
1217
+ '<p>Password reset successful. You can now login with your new password.</p>',
1218
+ [{ text: 'OK', action: () => {
1219
+ loginRadio.checked = true;
1220
+ slideToLogin();
1221
+ }}]
1222
+ );
1223
+ } else {
1224
+ // Display error messages from the backend
1225
+ displayModalFormErrors(result, resetPasswordForm, 'reset');
1226
+ if (result.message && (result.message.includes('OTP not verified') || result.message.includes('not initiated'))) {
1227
+ showFlexibleModal(
1228
+ `<p>${result.message}</p><p>Please start the password reset process again.</p>`,
1229
+ [{ text: 'Start Over', action: () => {
1230
+ hideCustomModal(resetPasswordModal);
1231
+ forgotPasswordLink.click(); // Re-open the initial forgot password modal
1232
+ }}],
1233
+ null // No general callback
1234
+ );
1235
+ }
1236
+ }
1237
+ } catch (error) {
1238
+ console.error('Fetch error:', error);
1239
+ showFlexibleModal('<p>Network error. Could not reset password. Please try again.</p>', [{ text: 'OK' }]);
1240
+ } finally {
1241
+ resetSubmitButton.disabled = false;
1242
+ resetSubmitButton.innerHTML = 'Reset Password';
1243
+ }
1244
+ }
1245
+ });
1246
+ }
1247
+
1248
+ // This function handles displaying errors from the backend for the main login/signup forms
1249
+ function handleMainFormErrors(result, formElement, endpoint) {
1250
+ if (endpoint === '/api/login') {
1251
+ if (result.message && (result.message.includes('Account not found') || result.message.includes('Invalid credentials'))) {
1252
+ showFlexibleModal(
1253
+ `<p>${result.message}</p><p>If you are not registered, please sign up.</p>`,
1254
+ [{ text: 'Sign Up', action: () => {
1255
+ signupRadio.checked = true;
1256
+ slideToSignup();
1257
+ }}, { text: 'OK', style: 'background-color: var(--bs-secondary-color);' }]
1258
+ );
1259
+ }
1260
+ } else if (endpoint === '/api/signup') {
1261
+ if (result.message && (result.message.includes('Email already registered') || result.message.includes('Username already taken'))) {
1262
+ showFlexibleModal(
1263
+ `<p>${result.message}</p><p>If you are already registered, please log in.</p>`,
1264
+ [{ text: 'Login', action: () => {
1265
+ loginRadio.checked = true;
1266
+ slideToLogin();
1267
+ }}, { text: 'OK', style: 'background-color: var(--bs-secondary-color);' }]
1268
+ );
1269
+ }
1270
+ }
1271
+ if (result.errors) {
1272
+ for (const field in result.errors) {
1273
+ let inputElement, errorElement;
1274
+
1275
+ if (formElement.id === 'loginForm' && field === 'username_or_email') {
1276
+ inputElement = loginUsernameOrEmailInput;
1277
+ errorElement = loginUsernameOrEmailError;
1278
+ } else if (formElement.id === 'signupForm' && field === 'name') {
1279
+ inputElement = signupNameInput;
1280
+ errorElement = signupNameError;
1281
+ }
1282
+ else {
1283
+ // This handles password errors for login and email/password for signup
1284
+ // Need to be careful with field names. Example: if backend returns 'password' for signup.
1285
+ inputElement = formElement.querySelector(`[name="${field}"]`);
1286
+ // This assumes predictable error element IDs based on input IDs
1287
+ const inputId = inputElement ? inputElement.id : null;
1288
+ errorElement = inputId ? document.getElementById(`${inputId}Error`) : null;
1289
+ }
1290
+
1291
+ if (errorElement) errorElement.textContent = result.errors[field];
1292
+ if (inputElement) inputElement.classList.add('error');
1293
+ }
1294
+ }
1295
+ }
1296
+
1297
+ // Generic function for handling errors in modals (forgot, otp, reset)
1298
+ function displayModalFormErrors(result, formElement, formContext) {
1299
+ // Clear errors specific to the current modal's form context
1300
+ clearErrors(formContext);
1301
+
1302
+ if (result.errors) {
1303
+ for (const field in result.errors) {
1304
+ // This is robust: Find the input element by its 'name' attribute
1305
+ const inputElement = formElement.querySelector(`[name="${field}"]`);
1306
+ // Then, find its associated error message element by looking for a sibling with the ID pattern
1307
+ const errorElement = inputElement ? document.getElementById(`${inputElement.id}Error`) : null;
1308
+
1309
+ if (errorElement) errorElement.textContent = result.errors[field];
1310
+ if (inputElement) inputElement.classList.add('error');
1311
+ }
1312
+ }
1313
+ // Update the main message area of the modal if a general message is returned
1314
+ const targetModalMessage = formElement.closest('.modal-content').querySelector('.modal-message');
1315
+ if (targetModalMessage && result.message) {
1316
+ targetModalMessage.innerHTML = `<p>${result.message}</p>`;
1317
+ }
1318
+ }
1319
+
1320
+ // Main data submission function (unchanged for main forms, adapted for modals)
1321
+ async function submitFormData(formElement, submitButton, endpoint, formContext) {
1322
+ if (!formElement || !submitButton || !endpoint) {
1323
+ console.error("submitFormData called with missing arguments.");
1324
+ return;
1325
+ }
1326
+
1327
+ const formData = new FormData(formElement);
1328
+ const data = {};
1329
+ for (let [key, value] of formData.entries()) {
1330
+ data[key] = value;
1331
+ }
1332
+
1333
+ submitButton.disabled = true;
1334
+ if (formContext === 'main') {
1335
+ displayFormMessage("Submitting...", 'info');
1336
+ } else {
1337
+ // For modal buttons, update their innerHTML directly
1338
+ submitButton.innerHTML = '<span class="loading-spinner"></span> Submitting...';
1339
+ }
1340
+
1341
+ try {
1342
+ const response = await fetch(endpoint, {
1343
+ method: 'POST',
1344
+ headers: {
1345
+ 'Content-Type': 'application/json',
1346
+ },
1347
+ body: JSON.stringify(data),
1348
+ });
1349
+
1350
+ const result = await response.json();
1351
+
1352
+ if (response.ok) {
1353
+ if (formContext === 'main') {
1354
+ clearErrors('main');
1355
+ formElement.reset();
1356
+ displayFormMessage('', 'info'); // Clear "Submitting..." message
1357
+ loginAttempts = 0; // Reset login attempts on successful login
1358
+
1359
+ if (endpoint === '/api/signup') {
1360
+ showFlexibleModal(
1361
+ '<p>Registration successful! Please log in.</p>',
1362
+ [{ text: 'OK', action: () => {
1363
+ loginRadio.checked = true;
1364
+ slideToLogin();
1365
+ const url = new URL(window.location);
1366
+ url.searchParams.delete('signup'); // Clean up URL
1367
+ window.history.replaceState({}, '', url.toString());
1368
+ }}]
1369
+ );
1370
+ } else if (endpoint === '/api/login') {
1371
+ showFlexibleModal(
1372
+ '<p>Login successful!</p>',
1373
+ [{
1374
+ text: 'OK',
1375
+ action: () => {
1376
+ // Mark user as logged in so chat.html knows
1377
+ localStorage.setItem('gakr_is_logged_in', 'true');
1378
+ alert('Welcome! You are logged in.');
1379
+ // Optional: remove any guest flag if you had one
1380
+ // localStorage.removeItem('gakr_is_guest');
1381
+
1382
+ // Redirect back to chat interface
1383
+ window.location.href = 'chat.html';
1384
+ }
1385
+ }]
1386
+ );
1387
+ }
1388
+
1389
+ }
1390
+ // For forgot/otp/reset, successful handling is now done directly in their respective event listeners
1391
+ // so no generic handling here for those contexts.
1392
+ } else { // Server returned an error (response.ok is false)
1393
+ if (formContext === 'main') {
1394
+ if (endpoint === '/api/login' && result.message && result.message.includes('Invalid credentials')) {
1395
+ loginAttempts++;
1396
+ loginPasswordInput.classList.add('error');
1397
+ loginPasswordError.textContent = result.message;
1398
+
1399
+ if (loginAttempts >= MAX_LOGIN_ATTEMPTS) {
1400
+ showLoginAttemptsLimitModal();
1401
+ loginPasswordInput.value = ''; // Clear password after max attempts
1402
+ } else {
1403
+ displayFormMessage(`Incorrect password. You have ${MAX_LOGIN_ATTEMPTS - loginAttempts} attempts left.`, 'error');
1404
+ }
1405
+ } else {
1406
+ displayFormMessage(result.message || 'An error occurred.', 'error');
1407
+ handleMainFormErrors(result, formElement, endpoint);
1408
+ }
1409
+ } else if (formContext === 'forgot') {
1410
+ displayModalFormErrors(result, formElement, 'forgot');
1411
+ } else if (formContext === 'otp') {
1412
+ displayModalFormErrors(result, formElement, 'otp');
1413
+ } else if (formContext === 'reset') {
1414
+ displayModalFormErrors(result, formElement, 'reset');
1415
+ }
1416
+ }
1417
+ } catch (error) {
1418
+ console.error('Fetch error:', error);
1419
+ if (formContext === 'main') {
1420
+ displayFormMessage('Network error. Please try again.', 'error');
1421
+ } else {
1422
+ showFlexibleModal(
1423
+ '<p>Network error. Please check your internet connection and try again.</p>',
1424
+ [{ text: 'OK' }]
1425
+ );
1426
+ }
1427
+ } finally {
1428
+ submitButton.disabled = false;
1429
+ // Reset button text specifically for modal forms if not handled by success/error flows above
1430
+ if (formContext === 'forgot') {
1431
+ submitButton.innerHTML = 'Request OTP';
1432
+ } else if (formContext === 'otp') {
1433
+ submitButton.innerHTML = 'Verify OTP';
1434
+ } else if (formContext === 'reset') {
1435
+ submitButton.innerHTML = 'Reset Password';
1436
+ }
1437
+ }
1438
+ }
1439
+
1440
+
1441
+ function showLoginAttemptsLimitModal() {
1442
+ const messageHtml = `
1443
+ <strong>You have made three incorrect login attempts.</strong>
1444
+ <ul>
1445
+ <li>1. If you are not registered, please <a href="#" id="modalSignupLink">sign up now</a>.</li>
1446
+ <li>2. If you are already registered, please <a href="#" id="modalLoginLink">login now</a> with correct credentials.</li>
1447
+ <li>3. If you have forgotten your password, click <a href="#" id="modalForgotPasswordLink">forgot password</a> to reset it.</li>
1448
+ </ul>
1449
+ `;
1450
+
1451
+ const buttons = [
1452
+ { text: 'Login', action: () => {
1453
+ loginRadio.checked = true;
1454
+ slideToLogin();
1455
+ loginAttempts = 0;
1456
+ loginPasswordInput.value = '';
1457
+ }},
1458
+ { text: 'Signup', action: () => {
1459
+ signupRadio.checked = true;
1460
+ slideToSignup();
1461
+ loginAttempts = 0;
1462
+ loginPasswordInput.value = '';
1463
+ }}
1464
+ ];
1465
+
1466
+ showFlexibleModal(messageHtml, buttons, () => {
1467
+ const modalSignupLink = document.getElementById('modalSignupLink');
1468
+ const modalLoginLink = document.getElementById('modalLoginLink');
1469
+ const modalForgotPasswordLink = document.getElementById('modalForgotPasswordLink');
1470
+
1471
+ if (modalSignupLink) {
1472
+ modalSignupLink.onclick = (e) => {
1473
+ e.preventDefault();
1474
+ hideCustomModal(customModal);
1475
+ signupRadio.checked = true;
1476
+ slideToSignup();
1477
+ loginAttempts = 0;
1478
+ loginPasswordInput.value = '';
1479
+ };
1480
+ }
1481
+ if (modalLoginLink) {
1482
+ modalLoginLink.onclick = (e) => {
1483
+ e.preventDefault();
1484
+ hideCustomModal(customModal);
1485
+ loginRadio.checked = true;
1486
+ slideToLogin();
1487
+ loginAttempts = 0;
1488
+ loginPasswordInput.value = '';
1489
+ };
1490
+ }
1491
+ if (modalForgotPasswordLink) {
1492
+ modalForgotPasswordLink.onclick = (e) => {
1493
+ e.preventDefault();
1494
+ hideCustomModal(customModal);
1495
+ forgotPasswordLink.click();
1496
+ loginAttempts = 0;
1497
+ loginPasswordInput.value = '';
1498
+ };
1499
+ }
1500
+ });
1501
+ }
1502
+
1503
+ function isValidEmail(email) {
1504
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1505
+ return emailRegex.test(email);
1506
+ }
1507
+ });
1508
+ </script>
1509
+ </body>
1510
+ </html>
chat.html ADDED
@@ -0,0 +1,1177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-bs-theme="dark">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>GAKR AI - Chat</title>
7
+ <link rel="stylesheet" href="https://cdn.replit.com/agent/bootstrap-agent-dark-theme.min.css">
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
9
+ <link rel="stylesheet" href="style.css">
10
+ <style>
11
+ /* --- Define CSS Variables (Copy from homepage or ensure in style.css) --- */
12
+ :root {
13
+ /* Default light mode colors (adjust as needed) */
14
+ --gakr-blue: #4285F4;
15
+ --gakr-blue-dark: #1a73e8; /* A slightly darker shade for active states */
16
+ --gakr-blue-light: #e8f0fe; /* A light shade for backgrounds */
17
+ --gakr-grey-text: #5f6368;
18
+ --gakr-grey-hover-bg: rgba(95, 99, 104, 0.1); /* Subtle hover for grey icons */
19
+ }
20
+
21
+ :root[data-bs-theme="dark"] {
22
+ /* Dark mode overrides (adjust based on your bootstrap dark theme vars) */
23
+ --gakr-blue: #8ab4f8; /* Lighter blue for dark mode */
24
+ --gakr-blue-dark: #669df6;
25
+ --gakr-blue-light: rgba(138, 180, 248, 0.1); /* Light blue background with transparency */
26
+ --gakr-grey-text: #bdc1c6;
27
+ --gakr-grey-hover-bg: rgba(189, 193, 198, 0.1);
28
+ /* Ensure --bs-body-bg, --bs-body-color, --bs-border-color etc. are correctly set by bootstrap theme */
29
+ }
30
+
31
+ /* --- Base Chat Layout --- */
32
+ .gemini-chat-layout {
33
+ display: flex;
34
+ flex-direction: column;
35
+ height: 100vh;
36
+ width: 100%;
37
+ background-color: var(--bs-body-bg); /* Use BS var */
38
+ color: var(--bs-body-color); /* Use BS var */
39
+ }
40
+
41
+ .gemini-chat-header {
42
+ padding: 0.75rem 1.5rem;
43
+ display: flex;
44
+ justify-content: space-between;
45
+ align-items: center;
46
+ border-bottom: 1px solid var(--bs-border-color);
47
+ height: 64px;
48
+ }
49
+
50
+ .gemini-logo-area {
51
+ display: flex;
52
+ align-items: center;
53
+ gap: 0.75rem;
54
+ }
55
+
56
+ .gemini-brand-logo {
57
+ width: 24px;
58
+ height: 24px;
59
+ display: flex;
60
+ align-items: center;
61
+ justify-content: center;
62
+ }
63
+
64
+ .gemini-brand-text {
65
+ font-size: 1.25rem;
66
+ font-weight: 500;
67
+ color: var(--gakr-blue);
68
+ }
69
+
70
+ .gemini-chat-wrapper {
71
+ flex: 1;
72
+ overflow: hidden;
73
+ position: relative;
74
+ display: flex;
75
+ flex-direction: column;
76
+ }
77
+
78
+ .gemini-chat-container {
79
+ flex: 1;
80
+ overflow-y: auto;
81
+ padding: 1rem;
82
+ display: flex;
83
+ flex-direction: column;
84
+ gap: 1.5rem;
85
+ width: 100%;
86
+ max-width: 800px;
87
+ margin: 0 auto;
88
+ }
89
+
90
+ .gemini-prompt-area {
91
+ padding: 1rem;
92
+ border-top: 1px solid var(--bs-border-color);
93
+ width: 100%;
94
+ max-width: 800px;
95
+ margin: 0 auto;
96
+ /* --- MODIFIED CSS: Add relative positioning for options container --- */
97
+ position: relative;
98
+ /* --- END MODIFIED CSS --- */
99
+ }
100
+
101
+ .gemini-message {
102
+ display: flex;
103
+ flex-direction: column;
104
+ gap: 0.5rem;
105
+ max-width: 90%;
106
+ }
107
+
108
+ .gemini-message-user {
109
+ align-self: flex-end;
110
+ }
111
+
112
+ .gemini-message-ai {
113
+ align-self: flex-start;
114
+ }
115
+
116
+ .gemini-message-header {
117
+ display: flex;
118
+ align-items: center;
119
+ gap: 0.5rem;
120
+ font-size: 0.85rem;
121
+ color: var(--bs-secondary-color);
122
+ }
123
+
124
+ .gemini-message-avatar {
125
+ width: 24px;
126
+ height: 24px;
127
+ border-radius: 50%;
128
+ display: flex;
129
+ align-items: center;
130
+ justify-content: center;
131
+ font-size: 0.75rem;
132
+ }
133
+
134
+ .gemini-message-content {
135
+ padding: 1rem;
136
+ border-radius: 12px;
137
+ line-height: 1.5;
138
+ /* Ensure text wrapping */
139
+ white-space: pre-wrap;
140
+ word-break: break-word;
141
+ }
142
+
143
+ .gemini-message-user .gemini-message-content {
144
+ background-color: var(--bs-tertiary-bg);
145
+ border-top-right-radius: 4px;
146
+ }
147
+
148
+ .gemini-message-ai .gemini-message-content {
149
+ background-color: rgba(66, 133, 244, 0.1);
150
+ border-top-left-radius: 4px;
151
+ }
152
+
153
+ .gemini-message-user .gemini-message-avatar {
154
+ background-color: var(--bs-tertiary-bg);
155
+ }
156
+
157
+ .gemini-message-ai .gemini-message-avatar {
158
+ background-color: rgba(66, 133, 244, 0.2);
159
+ color: var(--gakr-blue);
160
+ }
161
+
162
+ /* --- MODIFIED CSS FOR INPUT/TEXTAREA AND ACTIONS --- */
163
+ .gemini-input-container {
164
+ position: relative; /* Keep relative */
165
+ border-radius: 24px;
166
+ border: 1px solid var(--bs-border-color);
167
+ background: var(--bs-body-bg);
168
+ padding: 0.75rem 1rem; /* Padding around content */
169
+ display: flex;
170
+ /* --- MODIFIED: align-items to flex-end for textarea --- */
171
+ align-items: flex-end;
172
+ /* --- END MODIFIED --- */
173
+ box-shadow: 0 1px 5px rgba(0,0,0,0.1);
174
+ /* Added focus transition from homepage */
175
+ transition: border-color 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
176
+ flex-wrap: wrap; /* Allow items to wrap when attachments are added */
177
+ gap: 0.5rem; /* Space between attachments and input */
178
+ }
179
+
180
+ /* Style when the container or its children are focused */
181
+ .gemini-input-container:focus-within {
182
+ border-color: var(--gakr-blue);
183
+ box-shadow: 0 1px 8px rgba(66, 133, 244, 0.2);
184
+ background: var(--bs-body-bg);
185
+ }
186
+
187
+ /* Style the textarea (#userInput) */
188
+ .gemini-input { /* Targets the textarea with id="userInput" */
189
+ flex: 1; /* Textarea takes available space */
190
+ border: none;
191
+ background: transparent;
192
+ /* --- MODIFIED: Padding adjustment for textarea (removed initial padding to be controlled by JS) --- */
193
+ padding: 0; /* Changed from 0.5rem 0.5rem */
194
+ /* Removed fixed height: height: 24px; from previous input styles */
195
+ /* Removed fixed max-height: 120px; from chat page styles */
196
+ outline: none;
197
+ color: var(--bs-body-color);
198
+ font-size: 1rem; /* Keep font size */
199
+ resize: none; /* Prevent manual resizing */
200
+ overflow-y: hidden; /* JS manages this */
201
+ line-height: 1.4; /* Adjust line height as needed */
202
+ /* Min/Max height calculation (adjust if 1rem font size differs) */
203
+ min-height: calc(1rem * 1.4); /* Approx height of 1 line */
204
+ max-height: calc(1rem * 1.4 * 5); /* Approx max height (5 lines) */
205
+ box-sizing: border-box; /* Include padding/border in element size */
206
+ white-space: pre-wrap; /* Ensure text wraps */
207
+ word-break: break-word; /* Break long words */
208
+ vertical-align: bottom; /* Align baseline to bottom */
209
+ transition: height 0.3s ease-in-out; /* Animate height change */
210
+ }
211
+
212
+ .gemini-input:focus {
213
+ outline: none;
214
+ }
215
+
216
+
217
+ .gemini-input-actions {
218
+ display: flex;
219
+ /* --- MODIFIED: align-items to flex-end for textarea --- */
220
+ align-items: flex-end;
221
+ /* --- ADDED: padding-bottom to align icons --- */
222
+ padding-bottom: 0px; /* Changed from 0.5rem, controlled by JS */
223
+ /* --- END MODIFIED --- */
224
+ gap: 0.5rem;
225
+ font-size: 1.25rem;
226
+ flex-shrink: 0; /* Prevent shrinking */
227
+ }
228
+
229
+ /* Reusing .gemini-action-button styles for new '+' button */
230
+ .gemini-action-button {
231
+ width: 36px;
232
+ height: 36px;
233
+ border-radius: 50%;
234
+ display: flex;
235
+ align-items: center;
236
+ justify-content: center;
237
+ cursor: pointer;
238
+ color: var(--bs-secondary-color); /* Use BS var */
239
+ /* Added hover/active transitions from homepage */
240
+ transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
241
+ }
242
+
243
+ .gemini-action-button:hover {
244
+ background-color: var(--bs-tertiary-bg); /* Use BS var */
245
+ color: var(--bs-body-color); /* Darken icon color slightly on hover */
246
+ }
247
+
248
+ .gemini-action-button:active {
249
+ transform: scale(0.95); /* Slight press effect */
250
+ background-color: var(--bs-tertiary-bg); /* Keep hover bg on active */
251
+ color: var(--bs-body-color);
252
+ transition: background-color 0s, transform 0.1s; /* Make feedback immediate */
253
+ }
254
+
255
+ .gemini-submit-button {
256
+ color: var(--gakr-blue); /* Use CSS var */
257
+ }
258
+
259
+ /* Specific styles for submit button hover/active */
260
+ .gemini-submit-button:hover {
261
+ background-color: var(--gakr-blue-light); /* Light blue background on hover */
262
+ color: var(--gakr-blue-dark); /* Darker blue icon on hover */
263
+ }
264
+
265
+ .gemini-submit-button:active {
266
+ transform: scale(0.95); /* Slight press effect */
267
+ background-color: var(--gakr-blue-light);
268
+ color: var(--gakr-blue-dark);
269
+ transition: background-color 0s, transform 0.1s;
270
+ }
271
+
272
+ .gemini-submit-button.disabled {
273
+ opacity: 0.5;
274
+ cursor: default;
275
+ /* Ensure transitions are off when disabled to avoid weird states */
276
+ transition: none;
277
+ }
278
+
279
+ .gemini-submit-button.disabled:hover {
280
+ background-color: transparent;
281
+ color: var(--gakr-blue); /* Keep original color when disabled */
282
+ }
283
+ /* --- END MODIFIED CSS --- */
284
+
285
+
286
+ .gemini-typing {
287
+ display: inline-flex;
288
+ align-items: center;
289
+ gap: 4px;
290
+ padding: 0.75rem 1rem;
291
+ border-radius: 12px;
292
+ background-color: rgba(66, 133, 244, 0.1);
293
+ margin-bottom: 1rem;
294
+ align-self: flex-end;
295
+ }
296
+
297
+ .gemini-typing-dot {
298
+ width: 6px;
299
+ height: 6px;
300
+ border-radius: 50%;
301
+ background-color: var(--gakr-blue);
302
+ animation: typing 1.3s infinite ease-in-out;
303
+ }
304
+
305
+ .gemini-typing-dot:nth-child(1) { animation-delay: 0s; }
306
+ .gemini-typing-dot:nth-child(2) { animation-delay: 0.2s; }
307
+ .gemini-typing-dot:nth-child(3) { animation-delay: 0.4s; }
308
+
309
+ .gemini-thinking-toggle {
310
+ background: none;
311
+ border: none;
312
+ color: var(--gakr-blue);
313
+ cursor: pointer;
314
+ font-size: 0.9rem;
315
+ padding: 0;
316
+ margin-bottom: 0.5rem;
317
+ text-decoration: underline;
318
+ }
319
+
320
+ .gemini-thinking-content {
321
+ display: block;
322
+ margin-top: 0.5rem;
323
+ padding: 0.5rem;
324
+ background-color: rgba(138, 180, 248, 0.05);
325
+ border-left: 3px solid var(--gakr-blue);
326
+ font-style: italic;
327
+ }
328
+
329
+ .gemini-welcome {
330
+ text-align: center;
331
+ max-width: 600px;
332
+ margin: 4rem auto;
333
+ }
334
+
335
+ .gemini-welcome-icon {
336
+ font-size: 3rem;
337
+ margin-bottom: 1.5rem;
338
+ color: var(--gakr-blue);
339
+ }
340
+
341
+ .gemini-welcome-title {
342
+ font-size: 2rem;
343
+ margin-bottom: 1rem;
344
+ font-weight: 500;
345
+ }
346
+
347
+ /* Login button styling (from homepage) */
348
+ .gemini-login-button {
349
+ color: var(--gakr-blue);
350
+ text-decoration: none;
351
+ background: transparent;
352
+ border: 1px solid var(--gakr-blue);
353
+ padding: 0.5rem 1rem;
354
+ border-radius: 50px;
355
+ font-size: 0.9rem;
356
+ transition: background-color 0.2s;
357
+ }
358
+
359
+ .gemini-login-button:hover {
360
+ background-color: rgba(66, 133, 244, 0.1);
361
+ }
362
+
363
+ /* Login prompt modal (keep existing) */
364
+ .gemini-login-prompt {
365
+ position: fixed;
366
+ top: 0;
367
+ left: 0;
368
+ width: 100%;
369
+ height: 100%;
370
+ background-color: rgba(0, 0, 0, 0.5);
371
+ display: flex;
372
+ align-items: center;
373
+ justify-content: center;
374
+ z-index: 1000;
375
+ opacity: 0;
376
+ visibility: hidden;
377
+ transition: opacity 0.3s, visibility 0.3s;
378
+ }
379
+
380
+ .gemini-login-prompt.show {
381
+ opacity: 1;
382
+ visibility: visible;
383
+ }
384
+
385
+ .gemini-login-prompt-content {
386
+ background-color: var(--bs-body-bg);
387
+ border-radius: 12px;
388
+ padding: 1.5rem;
389
+ max-width: 400px;
390
+ width: 90%;
391
+ }
392
+
393
+ .gemini-login-prompt-title {
394
+ font-size: 1.25rem;
395
+ font-weight: 500;
396
+ margin-bottom: 1rem;
397
+ color: var(--gakr-blue);
398
+ }
399
+
400
+ .gemini-login-prompt-buttons {
401
+ display: flex;
402
+ flex-direction: column;
403
+ gap: 0.75rem;
404
+ margin-top: 1.5rem;
405
+ }
406
+
407
+ .gemini-button-primary {
408
+ background-color: var(--gakr-blue);
409
+ color: white;
410
+ border: none;
411
+ padding: 0.75rem 1rem;
412
+ border-radius: 50px;
413
+ font-size: 0.9rem;
414
+ text-align: center;
415
+ text-decoration: none;
416
+ cursor: pointer;
417
+ transition: background-color 0.2s;
418
+ }
419
+
420
+ .gemini-button-primary:hover {
421
+ background-color: #3b78e7;
422
+ }
423
+
424
+ .gemini-button-secondary {
425
+ background-color: transparent;
426
+ color: var(--gakr-blue);
427
+ border: 1px solid var(--gakr-blue);
428
+ padding: 0.75rem 1rem;
429
+ border-radius: 50px;
430
+ font-size: 0.9rem;
431
+ text-align: center;
432
+ text-decoration: none;
433
+ cursor: pointer;
434
+ transition: background-color 0.2s;
435
+ }
436
+
437
+ .gemini-button-secondary:hover {
438
+ background-color: rgba(66, 133, 244, 0.1);
439
+ }
440
+
441
+ .gemini-button-text {
442
+ background-color: transparent;
443
+ color: var(--bs-body-color);
444
+ border: none;
445
+ padding: 0.75rem 1rem;
446
+ border-radius: 50px;
447
+ font-size: 0.9rem;
448
+ text-align: center;
449
+ text-decoration: none;
450
+ cursor: pointer;
451
+ transition: background-color 0.2s;
452
+ }
453
+
454
+ .gemini-button-text:hover {
455
+ background-color: var(--bs-tertiary-bg);
456
+ }
457
+
458
+ /* --- NEW CSS for Attachment Options --- */
459
+ .attachment-options-container {
460
+ position: absolute;
461
+ bottom: 100%; /* Position above the input bar */
462
+ left: 1rem; /* Align with left edge padding of prompt area */
463
+ transform: translateY(-10px); /* Slight initial lift */
464
+ z-index: 10; /* Ensure it's above chat messages */
465
+
466
+ background-color: var(--bs-body-bg);
467
+ border: 1px solid var(--bs-border-color);
468
+ border-radius: 15px;
469
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
470
+ padding: 15px;
471
+
472
+ display: flex;
473
+ gap: 20px;
474
+ justify-content: flex-start; /* Align options to the left */
475
+ min-width: max-content;
476
+
477
+ opacity: 0;
478
+ visibility: hidden;
479
+ pointer-events: none; /* Prevent clicks when hidden */
480
+
481
+ transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out, visibility 0s linear 0.3s; /* Animate opacity/transform, hide visibility after */
482
+ }
483
+
484
+ /* State when visible */
485
+ .attachment-options-container.visible {
486
+ opacity: 1;
487
+ visibility: visible;
488
+ pointer-events: auto; /* Allow clicks when visible */
489
+ transform: translateY(-20px); /* Slight slide up effect */
490
+ transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out, visibility 0s linear 0s; /* Show visibility immediately */
491
+ }
492
+
493
+ .attachment-option {
494
+ display: flex;
495
+ flex-direction: column;
496
+ align-items: center;
497
+ cursor: pointer;
498
+ color: var(--bs-body-color);
499
+ font-size: 0.8rem;
500
+ gap: 5px;
501
+ transition: color 0.2s ease-in-out;
502
+ }
503
+
504
+ .attachment-option:hover {
505
+ color: var(--gakr-blue);
506
+ }
507
+
508
+ .attachment-icon {
509
+ width: 48px;
510
+ height: 48px;
511
+ border-radius: 50%;
512
+ background-color: var(--bs-tertiary-bg);
513
+ display: flex;
514
+ align-items: center;
515
+ justify-content: center;
516
+ font-size: 1.2rem;
517
+ transition: background-color 0.2s ease-in-out;
518
+ }
519
+
520
+ .attachment-option:hover .attachment-icon {
521
+ background-color: var(--gakr-blue-light);
522
+ }
523
+
524
+ .attachment-text {
525
+ text-align: center;
526
+ }
527
+
528
+ /* Styles for attached file chips */
529
+ .attached-file-preview-container {
530
+ display: flex;
531
+ flex-wrap: wrap;
532
+ gap: 8px;
533
+ /* Removed padding-bottom from here, handled by JS setting padding-top on textarea */
534
+ width: 100%; /* Take full width of parent container */
535
+ }
536
+
537
+ .attached-file-chip {
538
+ display: flex;
539
+ align-items: center;
540
+ background-color: var(--bs-tertiary-bg);
541
+ border-radius: 16px;
542
+ padding: 4px 8px;
543
+ font-size: 0.85rem;
544
+ color: var(--bs-body-color);
545
+ max-width: 150px; /* Limit chip width */
546
+ overflow: hidden;
547
+ text-overflow: ellipsis;
548
+ white-space: nowrap;
549
+ position: relative; /* For the close button */
550
+ }
551
+
552
+ .attached-file-chip .file-name {
553
+ flex-grow: 1;
554
+ overflow: hidden;
555
+ text-overflow: ellipsis;
556
+ white-space: nowrap;
557
+ padding-right: 8px; /* Space for close button */
558
+ }
559
+
560
+ .attached-file-chip .remove-file {
561
+ cursor: pointer;
562
+ margin-left: 4px;
563
+ color: var(--bs-secondary-color);
564
+ }
565
+
566
+ .attached-file-chip .remove-file:hover {
567
+ color: var(--bs-body-color);
568
+ }
569
+
570
+ .attached-file-chip img {
571
+ width: 24px;
572
+ height: 24px;
573
+ border-radius: 4px;
574
+ object-fit: cover;
575
+ margin-right: 8px;
576
+ }
577
+
578
+ /* Hide file inputs */
579
+ .hidden-file-input {
580
+ display: none;
581
+ }
582
+
583
+
584
+ /* --- END NEW CSS --- */
585
+
586
+ </style>
587
+ </head>
588
+ <body>
589
+ <div class="gemini-chat-layout">
590
+ <header class="gemini-chat-header">
591
+ <div class="gemini-logo-area">
592
+ <a href="/" class="gemini-brand-logo">
593
+ <i class="fas fa-robot" style="color: var(--gakr-blue);"></i>
594
+ </a>
595
+ <a href="/" class="gemini-brand-text" style="text-decoration: none;">GAKR AI</a>
596
+ </div>
597
+
598
+ <div class="gemini-nav-controls">
599
+ <a href="auth.html" class="gemini-login-button">Sign in / Register</a>
600
+ </div>
601
+ </header>
602
+
603
+ <div class="gemini-chat-wrapper">
604
+ <div class="gemini-chat-container" id="chatContainer">
605
+ <div class="gemini-welcome" id="welcomeMessage">
606
+ <div class="gemini-welcome-icon">
607
+ <i class="fas fa-robot"></i>
608
+ </div>
609
+ <h1 class="gemini-welcome-title">How can I help you today?</h1>
610
+ <p>I'm GAKR AI, your AI assistant. Ask me anything!</p>
611
+ </div>
612
+
613
+ <div class="gemini-message gemini-message-ai d-none" id="initialMessage">
614
+ <div class="gemini-message-header">
615
+ <div class="gemini-message-avatar">
616
+ <i class="fas fa-robot"></i>
617
+ </div>
618
+ <span>GAKR AI</span>
619
+ </div>
620
+ <div class="gemini-message-content">Hello! I'm GAKR AI How can I help you today?
621
+ </div>
622
+ </div>
623
+
624
+ <div class="gemini-typing d-none" id="typingIndicator">
625
+ <div class="gemini-typing-dot"></div>
626
+ <div class="gemini-typing-dot"></div>
627
+ <div class="gemini-typing-dot"></div>
628
+ </div>
629
+ </div>
630
+
631
+ <div class="gemini-prompt-area">
632
+ <div class="gemini-input-container" id="inputContainer">
633
+ <div class="gemini-action-button" id="addButton">
634
+ <i class="fas fa-plus"></i>
635
+ </div>
636
+ <div class="attached-file-preview-container" id="attachedFilePreviewContainer">
637
+ </div>
638
+ <textarea class="gemini-input" id="userInput" placeholder="Message GAKR AI..." rows="1"></textarea>
639
+
640
+ <div class="gemini-input-actions">
641
+ <div class="gemini-action-button">
642
+ <i class="fas fa-microphone"></i>
643
+ </div>
644
+ <div class="gemini-action-button gemini-submit-button disabled" id="submitButton">
645
+ <i class="fas fa-arrow-right"></i>
646
+ </div>
647
+ </div>
648
+ </div>
649
+
650
+ <div class="attachment-options-container">
651
+ <div class="attachment-option" id="cameraOption">
652
+ <div class="attachment-icon"><i class="fas fa-camera"></i></div>
653
+ <div class="attachment-text">Camera</div>
654
+ <input type="file" id="cameraInput" class="hidden-file-input" accept="image/*" capture="camera">
655
+ </div>
656
+ <div class="attachment-option" id="galleryOption">
657
+ <div class="attachment-icon"><i class="fas fa-image"></i></div>
658
+ <div class="attachment-text">Gallery</div>
659
+ <input type="file" id="galleryInput" class="hidden-file-input" accept="image/*">
660
+ </div>
661
+ <div class="attachment-option" id="filesOption">
662
+ <div class="attachment-icon"><i class="fas fa-paperclip"></i></div>
663
+ <div class="attachment-text">Files</div>
664
+ <input type="file" id="filesInput" class="hidden-file-input" accept="*/*">
665
+ </div>
666
+ <div class="attachment-option" id="driveOption">
667
+ <div class="attachment-icon"><i class="fab fa-google-drive"></i></div>
668
+ <div class="attachment-text">Drive</div>
669
+ </div>
670
+ </div>
671
+ </div>
672
+ </div>
673
+
674
+ <div class="gemini-login-prompt" id="loginPrompt">
675
+ <div class="gemini-login-prompt-content">
676
+ <div class="gemini-login-prompt-title">Continue with GAKR AI</div>
677
+ <p>You've had 5 conversations with GAKR AI. Would you like to create an account to save your history?</p>
678
+ <div class="gemini-login-prompt-buttons">
679
+ <a href="login.html" class="gemini-button-primary">Sign in</a>
680
+ <a href="/login?signup=true" class="gemini-button-secondary">Create account</a>
681
+ <button type="button" class="gemini-button-text" id="continueGuest">Continue as guest</button>
682
+ </div>
683
+ </div>
684
+ </div>
685
+ </div>
686
+ <script>
687
+ document.addEventListener('DOMContentLoaded', function() {
688
+ const chatContainer = document.getElementById('chatContainer');
689
+ const userInput = document.getElementById('userInput'); // textarea
690
+ const submitButton = document.getElementById('submitButton');
691
+ const typingIndicator = document.getElementById('typingIndicator');
692
+ const welcomeMessage = document.getElementById('welcomeMessage');
693
+ const initialMessage = document.getElementById('initialMessage');
694
+ const loginPrompt = document.getElementById('loginPrompt');
695
+ const continueGuest = document.getElementById('continueGuest');
696
+
697
+ const addButton = document.getElementById('addButton');
698
+ const attachmentOptionsContainer = document.querySelector('.attachment-options-container');
699
+
700
+ const cameraInput = document.getElementById('cameraInput');
701
+ const galleryInput = document.getElementById('galleryInput');
702
+ const filesInput = document.getElementById('filesInput');
703
+ const attachedFilePreviewContainer = document.getElementById('attachedFilePreviewContainer');
704
+ const inputContainer = document.getElementById('inputContainer');
705
+
706
+ let messageCount = 0;
707
+ let attachedFiles = [];
708
+
709
+ // Simple login flag (set in auth flow: localStorage.setItem("gakr_is_logged_in","true"))
710
+ const isLoggedIn = localStorage.getItem("gakr_is_logged_in") === "true";
711
+
712
+ // --- Textarea Auto-Grow & Scroll Logic (original, kept) ---
713
+ const style = getComputedStyle(userInput);
714
+ const lineHeight = parseFloat(style.lineHeight);
715
+ const minHeight = lineHeight;
716
+ const maxHeight = lineHeight * 5;
717
+
718
+ const initialInputContainerPaddingTop = parseFloat(getComputedStyle(inputContainer).paddingTop);
719
+ const initialInputContainerPaddingBottom = parseFloat(getComputedStyle(inputContainer).paddingBottom);
720
+ const initialActionButtonsHeight = document.querySelector('.gemini-input-actions').offsetHeight;
721
+
722
+ function autoGrowTextarea() {
723
+ if (!userInput) {
724
+ console.error("autoGrowTextarea called but userInput element not found.");
725
+ return;
726
+ }
727
+
728
+ userInput.style.height = 'auto';
729
+ const scrollHeight = userInput.scrollHeight;
730
+
731
+ let newTextareaHeight = Math.min(Math.max(scrollHeight, minHeight), maxHeight);
732
+ userInput.style.height = newTextareaHeight + 'px';
733
+
734
+ if (attachedFiles.length > 0) {
735
+ userInput.style.paddingTop = '0px';
736
+ userInput.style.paddingBottom = '0px';
737
+ inputContainer.style.alignItems = 'flex-start';
738
+ } else {
739
+ userInput.style.paddingTop = '0px';
740
+ userInput.style.paddingBottom = '0px';
741
+ inputContainer.style.alignItems = 'flex-end';
742
+ }
743
+
744
+ if (scrollHeight > maxHeight) {
745
+ userInput.style.overflowY = 'auto';
746
+ } else {
747
+ userInput.style.overflowY = 'hidden';
748
+ }
749
+
750
+ // Prompt is required; files alone cannot send
751
+ if (userInput.value.trim().length > 0) {
752
+ submitButton.classList.remove('disabled');
753
+ } else {
754
+ submitButton.classList.add('disabled');
755
+ }
756
+ }
757
+
758
+ userInput.addEventListener('input', autoGrowTextarea);
759
+ setTimeout(autoGrowTextarea, 0);
760
+ // --- End Textarea Auto-Grow & Scroll Logic ---
761
+
762
+
763
+ // Handle Enter key for sending (Shift+Enter = newline)
764
+ userInput.addEventListener('keydown', function(event) {
765
+ if (event.key === 'Enter' && !event.shiftKey && !submitButton.classList.contains('disabled')) {
766
+ event.preventDefault();
767
+ sendMessage();
768
+ }
769
+ });
770
+
771
+ // Handle submit button click
772
+ submitButton.addEventListener('click', function() {
773
+ if (!this.classList.contains('disabled')) {
774
+ sendMessage();
775
+ }
776
+ });
777
+
778
+ // Existing “continue as guest” modal
779
+ continueGuest.addEventListener('click', function() {
780
+ loginPrompt.classList.remove('show');
781
+ });
782
+
783
+ // --- Handle Add Button Click ---
784
+ addButton.addEventListener('click', function(event) {
785
+ event.stopPropagation();
786
+ attachmentOptionsContainer.classList.toggle('visible');
787
+ });
788
+
789
+ // --- Attachment Options ---
790
+ document.getElementById('cameraOption').addEventListener('click', function() {
791
+ cameraInput.click();
792
+ attachmentOptionsContainer.classList.remove('visible');
793
+ });
794
+
795
+ document.getElementById('galleryOption').addEventListener('click', function() {
796
+ galleryInput.click();
797
+ attachmentOptionsContainer.classList.remove('visible');
798
+ });
799
+
800
+ document.getElementById('filesOption').addEventListener('click', function() {
801
+ filesInput.click();
802
+ attachmentOptionsContainer.classList.remove('visible');
803
+ });
804
+
805
+ document.getElementById('driveOption').addEventListener('click', function() {
806
+ alert("Google Drive integration requires backend setup and Google API access.");
807
+ attachmentOptionsContainer.classList.remove('visible');
808
+ });
809
+
810
+ // --- File input change (camera/gallery/files) ---
811
+ [cameraInput, galleryInput, filesInput].forEach(input => {
812
+ input.addEventListener('change', function(event) {
813
+ const files = event.target.files;
814
+ if (files.length > 0) {
815
+ for (let i = 0; i < files.length; i++) {
816
+ attachedFiles.push(files[i]);
817
+ addFileChip(files[i]);
818
+ }
819
+ autoGrowTextarea();
820
+ userInput.focus();
821
+ }
822
+ event.target.value = '';
823
+ });
824
+ });
825
+
826
+ function addFileChip(file) {
827
+ const chip = document.createElement('div');
828
+ chip.className = 'attached-file-chip';
829
+ chip.setAttribute('data-filename', file.name);
830
+
831
+ let previewElement;
832
+ if (file.type.startsWith('image/')) {
833
+ const img = document.createElement('img');
834
+ img.src = URL.createObjectURL(file);
835
+ img.alt = file.name;
836
+ img.onload = () => URL.revokeObjectURL(img.src);
837
+ previewElement = img;
838
+ } else {
839
+ const icon = document.createElement('i');
840
+ icon.className = 'fas fa-paperclip';
841
+ previewElement = icon;
842
+ }
843
+
844
+ const fileNameSpan = document.createElement('span');
845
+ fileNameSpan.className = 'file-name';
846
+ fileNameSpan.textContent = file.name;
847
+
848
+ const removeButton = document.createElement('span');
849
+ removeButton.className = 'remove-file';
850
+ removeButton.innerHTML = '&times;';
851
+ removeButton.addEventListener('click', function() {
852
+ removeFileChip(file.name);
853
+ });
854
+
855
+ chip.appendChild(previewElement);
856
+ chip.appendChild(fileNameSpan);
857
+ chip.appendChild(removeButton);
858
+
859
+ attachedFilePreviewContainer.appendChild(chip);
860
+ autoGrowTextarea();
861
+ }
862
+
863
+ function removeFileChip(filename) {
864
+ const chipToRemove = attachedFilePreviewContainer.querySelector(`[data-filename="${filename}"]`);
865
+ if (chipToRemove) {
866
+ attachedFilePreviewContainer.removeChild(chipToRemove);
867
+ attachedFiles = attachedFiles.filter(file => file.name !== filename);
868
+ autoGrowTextarea();
869
+ }
870
+ }
871
+
872
+ // Hide attachment options when clicking outside
873
+ document.addEventListener('click', function(event) {
874
+ const isClickInsideAddButton = addButton && addButton.contains(event.target);
875
+ const isClickInsideOptions = attachmentOptionsContainer && attachmentOptionsContainer.contains(event.target);
876
+
877
+ if (attachmentOptionsContainer && attachmentOptionsContainer.classList.contains('visible') && !isClickInsideAddButton && !isClickInsideOptions) {
878
+ attachmentOptionsContainer.classList.remove('visible');
879
+ }
880
+ });
881
+
882
+ if (attachmentOptionsContainer) {
883
+ attachmentOptionsContainer.addEventListener('click', function(event){
884
+ event.stopPropagation();
885
+ });
886
+ }
887
+
888
+ // --- Centered login reminder mini-window ---
889
+ let inlineLoginPromptShown = false;
890
+ function maybeShowInlineLoginPrompt() {
891
+ if (isLoggedIn) return;
892
+ if (inlineLoginPromptShown) return;
893
+ if (messageCount % 5 !== 0) return;
894
+
895
+ inlineLoginPromptShown = true;
896
+
897
+ const overlay = document.createElement('div');
898
+ overlay.style.position = 'fixed';
899
+ overlay.style.inset = '0';
900
+ overlay.style.backgroundColor = 'rgba(0,0,0,0.35)';
901
+ overlay.style.display = 'flex';
902
+ overlay.style.alignItems = 'center';
903
+ overlay.style.justifyContent = 'center';
904
+ overlay.style.zIndex = '9999';
905
+
906
+ const card = document.createElement('div');
907
+ card.style.width = '100%';
908
+ card.style.maxWidth = '420px';
909
+ card.style.borderRadius = '16px';
910
+ card.style.backgroundColor = 'var(--bs-body-bg)';
911
+ card.style.border = '1px solid var(--bs-border-color)';
912
+ card.style.boxShadow = '0 16px 40px rgba(0,0,0,0.25)';
913
+ card.style.padding = '1.25rem 1.5rem';
914
+ card.style.display = 'flex';
915
+ card.style.flexDirection = 'column';
916
+ card.style.gap = '0.75rem';
917
+
918
+ const title = document.createElement('div');
919
+ title.textContent = "Save your conversations";
920
+ title.style.fontWeight = '600';
921
+ title.style.fontSize = '1rem';
922
+
923
+ const text = document.createElement('div');
924
+ text.textContent = "Log in to keep your chat history across devices. If you continue as a guest, your conversations may be lost later.";
925
+ text.style.fontSize = '0.9rem';
926
+ text.style.opacity = '0.9';
927
+
928
+ const buttonRow = document.createElement('div');
929
+ buttonRow.style.display = 'flex';
930
+ buttonRow.style.gap = '0.75rem';
931
+ buttonRow.style.marginTop = '0.5rem';
932
+
933
+ const skipBtn = document.createElement('button');
934
+ skipBtn.textContent = "Continue without login";
935
+ skipBtn.style.flex = '1';
936
+ skipBtn.style.borderRadius = '999px';
937
+ skipBtn.style.border = '1px solid var(--bs-border-color)';
938
+ skipBtn.style.backgroundColor = 'transparent';
939
+ skipBtn.style.color = 'var(--bs-body-color)';
940
+ skipBtn.style.padding = '0.5rem 0.75rem';
941
+ skipBtn.style.fontSize = '0.85rem';
942
+ skipBtn.style.cursor = 'pointer';
943
+
944
+ const loginBtn = document.createElement('button');
945
+ loginBtn.textContent = "Log in to save chats";
946
+ loginBtn.style.flex = '1';
947
+ loginBtn.style.borderRadius = '999px';
948
+ loginBtn.style.border = 'none';
949
+ loginBtn.style.backgroundColor = 'var(--gakr-blue)';
950
+ loginBtn.style.color = '#fff';
951
+ loginBtn.style.padding = '0.5rem 0.75rem';
952
+ loginBtn.style.fontSize = '0.85rem';
953
+ loginBtn.style.cursor = 'pointer';
954
+
955
+ skipBtn.addEventListener('click', function () {
956
+ overlay.remove();
957
+ });
958
+
959
+ loginBtn.addEventListener('click', function () {
960
+ window.location.href = 'auth.html';
961
+ });
962
+
963
+ buttonRow.appendChild(skipBtn);
964
+ buttonRow.appendChild(loginBtn);
965
+ card.appendChild(title);
966
+ card.appendChild(text);
967
+ card.appendChild(buttonRow);
968
+ overlay.appendChild(card);
969
+ document.body.appendChild(overlay);
970
+ }
971
+
972
+ // --- sendMessage: prompt required, files optional, generic error ---
973
+ async function sendMessage() {
974
+ const text = userInput.value.trim();
975
+
976
+ // Prompt is required; files alone cannot be sent
977
+ if (!text) {
978
+ return;
979
+ }
980
+
981
+ if (!welcomeMessage.classList.contains('d-none')) {
982
+ welcomeMessage.classList.add('d-none');
983
+ }
984
+
985
+ if (initialMessage.classList.contains('d-none')) {
986
+ initialMessage.classList.remove('d-none');
987
+ }
988
+
989
+ const filesSnapshot = [...attachedFiles];
990
+
991
+ let userMessageContent = text;
992
+ if (filesSnapshot.length > 0) {
993
+ const fileNames = filesSnapshot.map(f => f.name).join(', ');
994
+ userMessageContent += ` (Attached: ${fileNames})`;
995
+ }
996
+ addMessage(userMessageContent, 'user');
997
+ messageCount++;
998
+
999
+ userInput.value = '';
1000
+ attachedFiles = [];
1001
+ attachedFilePreviewContainer.innerHTML = '';
1002
+ autoGrowTextarea();
1003
+ userInput.dispatchEvent(new Event('input', { bubbles: true }));
1004
+
1005
+ typingIndicator.classList.remove('d-none');
1006
+ scrollToBottom();
1007
+
1008
+ const formData = new FormData();
1009
+ formData.append('api_key', 'gakr-ai-2025-secret');
1010
+ formData.append('prompt', text);
1011
+
1012
+ if (filesSnapshot.length > 0) {
1013
+ filesSnapshot.forEach((file) => {
1014
+ formData.append('files', file);
1015
+ });
1016
+ }
1017
+
1018
+ try {
1019
+ const response = await fetch('/api/analyze', {
1020
+ method: 'POST',
1021
+ body: formData
1022
+ });
1023
+
1024
+ if (!response.ok) {
1025
+ typingIndicator.classList.add('d-none');
1026
+ addMessage('There was a server issue. Please try again later.', 'ai');
1027
+ return;
1028
+ }
1029
+
1030
+ // Create AI message div first
1031
+ const aiMessageDiv = addMessage('', 'ai', true); // true for streaming
1032
+ let accumulatedText = '';
1033
+
1034
+ const reader = response.body.getReader();
1035
+ const decoder = new TextDecoder();
1036
+
1037
+ while (true) {
1038
+ const { done, value } = await reader.read();
1039
+ if (done) break;
1040
+ const chunk = decoder.decode(value, { stream: true });
1041
+ accumulatedText += chunk;
1042
+ updateAIMessage(aiMessageDiv, accumulatedText);
1043
+ }
1044
+
1045
+ if (messageCount > 0 && messageCount % 5 === 0) {
1046
+ maybeShowInlineLoginPrompt();
1047
+ }
1048
+ } catch (error) {
1049
+ console.error('Fetch Error:', error);
1050
+ typingIndicator.classList.add('d-none');
1051
+ addMessage('There was a server issue. Please try again later.', 'ai');
1052
+ } finally {
1053
+ if (typingIndicator) {
1054
+ typingIndicator.classList.add('d-none');
1055
+ }
1056
+ scrollToBottom();
1057
+ }
1058
+ }
1059
+
1060
+ function addMessage(text, type, streaming = false) {
1061
+ const messageDiv = document.createElement('div');
1062
+ messageDiv.className = `gemini-message gemini-message-${type}`;
1063
+
1064
+ const headerDiv = document.createElement('div');
1065
+ headerDiv.className = 'gemini-message-header';
1066
+
1067
+ const avatarDiv = document.createElement('div');
1068
+ avatarDiv.className = 'gemini-message-avatar';
1069
+
1070
+ const avatarIcon = document.createElement('i');
1071
+ avatarIcon.className = type === 'user' ? 'fas fa-user' : 'fas fa-robot';
1072
+
1073
+ const nameSpan = document.createElement('span');
1074
+ nameSpan.textContent = type === 'user' ? 'You' : 'GAKR AI';
1075
+
1076
+ const contentDiv = document.createElement('div');
1077
+ contentDiv.className = 'gemini-message-content';
1078
+ if (!streaming) {
1079
+ if (type === 'ai') {
1080
+ updateAIMessage(contentDiv, text);
1081
+ } else {
1082
+ contentDiv.textContent = text;
1083
+ }
1084
+ }
1085
+
1086
+ avatarDiv.appendChild(avatarIcon);
1087
+ headerDiv.appendChild(avatarDiv);
1088
+ headerDiv.appendChild(nameSpan);
1089
+
1090
+ messageDiv.appendChild(headerDiv);
1091
+ messageDiv.appendChild(contentDiv);
1092
+
1093
+ chatContainer.appendChild(messageDiv);
1094
+
1095
+ if (!streaming) {
1096
+ scrollToBottom();
1097
+ }
1098
+
1099
+ return streaming ? contentDiv : null;
1100
+ }
1101
+
1102
+ function updateAIMessage(contentDiv, text) {
1103
+ // Clear previous content
1104
+ contentDiv.innerHTML = '';
1105
+
1106
+ const thinkStart = text.indexOf('<think>');
1107
+ const thinkEnd = text.indexOf('</think>');
1108
+ if (thinkStart !== -1 && thinkEnd !== -1 && thinkEnd > thinkStart) {
1109
+ const thinking = text.substring(thinkStart + 7, thinkEnd).trim();
1110
+ const solution = text.substring(thinkEnd + 8).trim();
1111
+
1112
+ // Create toggle button
1113
+ const toggleButton = document.createElement('button');
1114
+ toggleButton.className = 'gemini-thinking-toggle';
1115
+ toggleButton.textContent = 'hide thinking';
1116
+ toggleButton.onclick = function() {
1117
+ const thinkingDiv = this.nextElementSibling;
1118
+ if (thinkingDiv.style.display === 'none') {
1119
+ thinkingDiv.style.display = 'block';
1120
+ this.textContent = 'hide thinking';
1121
+ } else {
1122
+ thinkingDiv.style.display = 'none';
1123
+ this.textContent = 'show thinking';
1124
+ }
1125
+ };
1126
+
1127
+ // Create thinking content div
1128
+ const thinkingDiv = document.createElement('div');
1129
+ thinkingDiv.className = 'gemini-thinking-content';
1130
+ thinkingDiv.textContent = thinking;
1131
+
1132
+ contentDiv.appendChild(toggleButton);
1133
+ contentDiv.appendChild(thinkingDiv);
1134
+ contentDiv.appendChild(document.createTextNode(solution));
1135
+ } else {
1136
+ contentDiv.textContent = text;
1137
+ }
1138
+ }
1139
+
1140
+ function scrollToBottom() {
1141
+ setTimeout(() => {
1142
+ if (chatContainer) {
1143
+ chatContainer.scrollTop = chatContainer.scrollHeight;
1144
+ }
1145
+ }, 0);
1146
+ }
1147
+
1148
+ // Initialize from URL ?q=...
1149
+ const urlParams = new URLSearchParams(window.location.search);
1150
+ const initialQuery = urlParams.get('q');
1151
+
1152
+ if (initialQuery) {
1153
+ userInput.value = initialQuery;
1154
+ userInput.dispatchEvent(new Event('input', { bubbles: true }));
1155
+
1156
+ setTimeout(function() {
1157
+ if (submitButton && !submitButton.classList.contains('disabled')) {
1158
+ submitButton.click();
1159
+ }
1160
+ }, 100);
1161
+ } else {
1162
+ if (userInput) {
1163
+ if (userInput.value.trim().length === 0 && attachedFiles.length === 0) {
1164
+ if (submitButton) submitButton.classList.add('disabled');
1165
+ } else {
1166
+ if (submitButton) submitButton.classList.remove('disabled');
1167
+ }
1168
+ autoGrowTextarea();
1169
+ }
1170
+ }
1171
+ });
1172
+ </script>
1173
+
1174
+
1175
+
1176
+ </body>
1177
+ </html>
config.json ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "Phi3ForCausalLM"
4
+ ],
5
+ "attention_bias": false,
6
+ "attention_dropout": 0.0,
7
+ "bos_token_id": 100257,
8
+ "embd_pdrop": 0.0,
9
+ "eos_token_id": 100265,
10
+ "hidden_act": "silu",
11
+ "hidden_size": 5120,
12
+ "initializer_range": 0.02,
13
+ "intermediate_size": 17920,
14
+ "max_position_embeddings": 32768,
15
+ "model_type": "phi3",
16
+ "num_attention_heads": 40,
17
+ "num_hidden_layers": 40,
18
+ "num_key_value_heads": 10,
19
+ "original_max_position_embeddings": 32768,
20
+ "pad_token_id": 100349,
21
+ "partial_rotary_factor": 1.0,
22
+ "resid_pdrop": 0.0,
23
+ "rms_norm_eps": 1e-05,
24
+ "rope_scaling": null,
25
+ "rope_theta": 500000,
26
+ "sliding_window": null,
27
+ "tie_word_embeddings": false,
28
+ "torch_dtype": "bfloat16",
29
+ "transformers_version": "4.51.1",
30
+ "use_cache": true,
31
+ "vocab_size": 100352
32
+ }
data_summary_card.md ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ # Data Summary for microsoft_Phi-4-reasoning
4
+
5
+
6
+
7
+
8
+
9
+ ## 1. General information
10
+
11
+ **1.0.1 Version of the Summary:** 1.0
12
+
13
+
14
+
15
+ **1.0.2 Last update:** 24-Nov-2025
16
+
17
+
18
+
19
+ ## 1.1 Model Developer Identification
20
+
21
+ **1.1.1 Model Developer name and contact details:** Microsoft Corporation at One Microsoft Way, Redmond, WA 98052. Tel: 425-882-8080
22
+
23
+
24
+
25
+ ## 1.2 Model Identification
26
+
27
+ **1.2.1 Versioned model name(s):** Phi-4-reasoning
28
+
29
+
30
+
31
+ **1.2.2 Model release date:** 30-Apr-2025
32
+
33
+
34
+
35
+ ## 1.3 Overall training data size and characteristics
36
+
37
+ ### 1.3.1 Size of dataset and characteristics
38
+
39
+ **1.3.1.A Text training data size:** 1 billion to 1 trillion tokens
40
+
41
+
42
+
43
+
44
+
45
+ **1.3.1.B Text training data content:** Prompts sourced from publicly available websites, existing datasets, and licensed collections, augmented with synthetically generated problems; responses generated using o3-mini including chain-of-thought traces; includes STEM, coding, logical puzzles, and safety/Responsible AI alignment data
46
+
47
+
48
+
49
+ **1.3.1.C Image training data size:** Not applicable. Images are not part of the training data
50
+
51
+
52
+
53
+ **1.3.1.D Image training data content:** Not applicable
54
+
55
+
56
+
57
+ **1.3.1.E Audio training data size:** Not applicable. Audio data is not part of the training data
58
+
59
+
60
+
61
+ **1.3.1.F Audio training data content:** Not applicable
62
+
63
+
64
+
65
+ **1.3.1.G Video training data size:** Not applicable. Video data is not part of the training data
66
+
67
+
68
+
69
+ **1.3.1.H Video training data content:** Not applicable
70
+
71
+
72
+
73
+ **1.3.1.I Other training data size:** Not applicable
74
+
75
+
76
+
77
+ **1.3.1.J Other training data content:** Not applicable
78
+
79
+
80
+
81
+ **1.3.2 Latest date of data acquisition/collection for model training:** 31-Mar-2025
82
+
83
+
84
+
85
+ **1.3.3 Is data collection ongoing to update the model with new data collection after deployment?** No
86
+
87
+
88
+
89
+ **1.3.4 Date the training dataset was first used to train the model:** 01-Jan-2025
90
+
91
+
92
+
93
+ **1.3.5 Rationale or purpose of data selection:** Datasets were curated to emphasize complex multi-step reasoning and verifiable solutions across STEM, coding, and safety, selecting prompts at the boundary of base model capabilities. Synthetic problems and teacher-generated reasoning traces were used to distill structured chain-of-thought and promote concise, checkable answers, supporting robust reasoning performance and generalization to broader tasks
94
+
95
+
96
+
97
+ ## 2. List of data sources
98
+
99
+ ### 2.1 Publicly available datasets
100
+
101
+ **2.1.1 Have you used publicly available datasets to train the model?** Yes
102
+
103
+
104
+
105
+ ## 2.2 Private non-publicly available datasets obtained from third parties
106
+
107
+ ### 2.2.1 Datasets commercially licensed by rights holders or their representatives
108
+
109
+ **2.2.1.A Have you concluded transactional commercial licensing agreement(s) with rights holder(s) or with their representatives?** Yes
110
+
111
+
112
+
113
+ ### 2.2.2 Private datasets obtained from other third-parties
114
+
115
+ **2.2.2.A Have you obtained private datasets from third parties that are not licensed as described in Section 2.2.1, such as data obtained from providers of private databases, or data intermediaries?** This information cannot be provided due to unavailability of the underlying data (e.g., loss, corruption, or other access limitations)
116
+
117
+
118
+
119
+ ## 2.3 Personal Information
120
+
121
+ **2.3.1 Was personal data used to train the model?** Microsoft follows all relevant laws and regulations pertaining to personal information
122
+
123
+
124
+
125
+ ## 2.4 Synthetic data
126
+
127
+ **2.4.1 Was any synthetic AI-generated data used to train the model?** Yes
128
+
129
+
130
+
131
+ ## 3. Data processing aspects
132
+
133
+ ### 3.1 Respect of reservation of rights from text and data mining exception or limitation
134
+
135
+ **3.1.1 Does this dataset include any data protected by copyright, trademark, or patent?** Microsoft follows all required regulations and laws for processing data protected by copyright, trademark, or patent
136
+
137
+
138
+
139
+ ## 3.2 Other information
140
+
141
+ **3.2.1 Does the dataset include information about consumer groups without revealing individual consumer identities?** Microsoft follows all required regulations and laws for protecting consumer identities
142
+
143
+
144
+
145
+ **3.2.2 Was the dataset cleaned or modified before model training?** Yes
146
+
147
+
148
+
149
+
file_pipeline.py ADDED
@@ -0,0 +1,397 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ file_pipeline.py
4
+
5
+ Universal file pipeline for GAKR AI.
6
+
7
+ Responsibilities:
8
+ - Create dataupload/ folder structure (if not present)
9
+ - Save uploaded files to disk
10
+ - Detect file type
11
+ - Run type-specific extractors
12
+ - Return structured, text-friendly context for Phi-3
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import os
18
+ import mimetypes
19
+ from datetime import datetime
20
+ from typing import List, Dict, Any
21
+
22
+ import pandas as pd
23
+ import pdfplumber
24
+ import docx
25
+ import fitz # PyMuPDF
26
+ from PIL import Image
27
+ import pytesseract
28
+ import whisper
29
+ import ffmpeg
30
+ from fastapi import UploadFile
31
+
32
+ # ============================================================
33
+ # PATHS & FOLDERS
34
+ # ============================================================
35
+
36
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
37
+ UPLOAD_ROOT = os.path.join(BASE_DIR, "dataupload")
38
+
39
+ FOLDERS = {
40
+ "image": "images",
41
+ "video": "videos",
42
+ "audio": "audio",
43
+ "document": "documents",
44
+ "tabular": "tabular",
45
+ "other": "other",
46
+ }
47
+
48
+
49
+ def ensure_folders() -> None:
50
+ """
51
+ Ensure base upload folder and all subfolders exist.
52
+ """
53
+ os.makedirs(UPLOAD_ROOT, exist_ok=True)
54
+ for sub in FOLDERS.values():
55
+ os.makedirs(os.path.join(UPLOAD_ROOT, sub), exist_ok=True)
56
+
57
+
58
+ ensure_folders()
59
+
60
+ # ============================================================
61
+ # TYPE DETECTION & PATHS
62
+ # ============================================================
63
+
64
+
65
+ def detect_kind(filename: str, content_type: str | None) -> str:
66
+ """
67
+ Decide logical kind: tabular, document, image, audio, video, other.
68
+ """
69
+ ext = os.path.splitext(filename)[1].lower()
70
+
71
+ if ext in [".csv", ".xlsx", ".xls", ".json"]:
72
+ return "tabular"
73
+ if ext in [".pdf", ".txt"]:
74
+ return "document"
75
+ if ext in [".docx"]:
76
+ return "document"
77
+ if ext in [".png", ".jpg", ".jpeg", ".webp", ".bmp"]:
78
+ return "image"
79
+ if ext in [".mp3", ".wav", ".m4a"]:
80
+ return "audio"
81
+ if ext in [".mp4", ".mkv", ".mov", ".avi"]:
82
+ return "video"
83
+
84
+ if content_type:
85
+ if content_type.startswith("image/"):
86
+ return "image"
87
+ if content_type.startswith("audio/"):
88
+ return "audio"
89
+ if content_type.startswith("video/"):
90
+ return "video"
91
+
92
+ return "other"
93
+
94
+
95
+ def make_target_path(kind: str, filename: str) -> str:
96
+ """
97
+ Build a safe, timestamped filepath under dataupload/{sub}/.
98
+ """
99
+ sub = FOLDERS.get(kind, "other")
100
+ safe_name = os.path.basename(filename)
101
+ timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S_%f")
102
+ final_name = f"{timestamp}_{safe_name}"
103
+ return os.path.join(UPLOAD_ROOT, sub, final_name)
104
+
105
+
106
+ # ============================================================
107
+ # MAIN MULTI-FILE ENTRY POINT
108
+ # ============================================================
109
+
110
+
111
+ async def process_files(files: List[UploadFile]) -> Dict[str, Any]:
112
+ """
113
+ Save all files, run analyses, and return structured context.
114
+
115
+ Output example:
116
+ {
117
+ "files": [
118
+ {
119
+ "original_name": "...",
120
+ "stored_path": "dataupload/documents/...",
121
+ "kind": "document",
122
+ "summary": { ... }
123
+ },
124
+ ...
125
+ ]
126
+ }
127
+ """
128
+ ensure_folders()
129
+ results: List[Dict[str, Any]] = []
130
+
131
+ for uf in files:
132
+ try:
133
+ kind = detect_kind(uf.filename, uf.content_type)
134
+ target_path = make_target_path(kind, uf.filename)
135
+
136
+ # Save file to disk
137
+ try:
138
+ with open(target_path, "wb") as out:
139
+ data = await uf.read()
140
+ out.write(data)
141
+ except Exception as save_err:
142
+ results.append(
143
+ {
144
+ "original_name": uf.filename,
145
+ "stored_path": None,
146
+ "kind": kind,
147
+ "summary": {
148
+ "error": f"Failed to save file: {save_err}"
149
+ },
150
+ }
151
+ )
152
+ continue
153
+
154
+ # Analyze by type
155
+ try:
156
+ summary = analyze_file(target_path, kind)
157
+ except Exception as analyze_err:
158
+ summary = {
159
+ "error": f"Unexpected error in analysis: {analyze_err}"
160
+ }
161
+
162
+ results.append(
163
+ {
164
+ "original_name": uf.filename,
165
+ "stored_path": os.path.relpath(target_path, BASE_DIR),
166
+ "kind": kind,
167
+ "summary": summary,
168
+ }
169
+ )
170
+
171
+ except Exception as outer_err:
172
+ results.append(
173
+ {
174
+ "original_name": getattr(uf, "filename", "unknown"),
175
+ "stored_path": None,
176
+ "kind": "unknown",
177
+ "summary": {
178
+ "error": f"Fatal error while handling file: {outer_err}"
179
+ },
180
+ }
181
+ )
182
+
183
+ return {"files": results}
184
+
185
+
186
+ # ============================================================
187
+ # TYPE-SPECIFIC ANALYSIS
188
+ # ============================================================
189
+
190
+
191
+ def analyze_file(path: str, kind: str) -> Dict[str, Any]:
192
+ if kind == "tabular":
193
+ return analyze_tabular(path)
194
+ if kind == "document":
195
+ return analyze_document(path)
196
+ if kind == "image":
197
+ return analyze_image(path)
198
+ if kind == "audio":
199
+ return analyze_audio(path)
200
+ if kind == "video":
201
+ return analyze_video(path)
202
+ return {"type": "other", "note": "Unsupported or unknown file type"}
203
+
204
+
205
+ # ---------- TABULAR: CSV / Excel / JSON ----------
206
+
207
+
208
+ def analyze_tabular(path: str) -> Dict[str, Any]:
209
+ ext = os.path.splitext(path)[1].lower()
210
+ df = None
211
+
212
+ try:
213
+ if ext == ".csv":
214
+ df = pd.read_csv(path)
215
+ elif ext in [".xlsx", ".xls"]:
216
+ df = pd.read_excel(path)
217
+ elif ext == ".json":
218
+ df = pd.read_json(path)
219
+ else:
220
+ return {
221
+ "type": "tabular",
222
+ "error": f"Unsupported tabular format: {ext}",
223
+ }
224
+ except Exception as e:
225
+ return {"type": "tabular", "error": f"Failed to load table: {e}"}
226
+
227
+ summary: Dict[str, Any] = {
228
+ "type": "tabular",
229
+ "rows": int(df.shape[0]),
230
+ "columns": [str(c) for c in df.columns],
231
+ }
232
+
233
+ try:
234
+ summary["missing_values"] = df.isna().sum().to_dict()
235
+ except Exception as e:
236
+ summary["missing_values_error"] = str(e)
237
+
238
+ try:
239
+ summary["numeric_stats"] = df.describe(include="number").to_dict()
240
+ except Exception:
241
+ summary["numeric_stats"] = {}
242
+
243
+ return summary
244
+
245
+
246
+ # ---------- DOCUMENTS: PDF / DOCX / TXT ----------
247
+
248
+
249
+ def analyze_document(path: str) -> Dict[str, Any]:
250
+ ext = os.path.splitext(path)[1].lower()
251
+ text = ""
252
+
253
+ try:
254
+ if ext == ".pdf":
255
+ # First try pdfplumber
256
+ try:
257
+ with pdfplumber.open(path) as pdf:
258
+ pages = []
259
+ for page in pdf.pages[:10]:
260
+ t = page.extract_text()
261
+ if t:
262
+ pages.append(t)
263
+ text = "\n".join(pages)
264
+ except Exception:
265
+ # Fallback to PyMuPDF
266
+ doc = fitz.open(path)
267
+ chunks = []
268
+ for page in doc[:10]:
269
+ chunks.append(page.get_text())
270
+ text = "\n".join(chunks)
271
+ elif ext == ".docx":
272
+ d = docx.Document(path)
273
+ paras = [p.text for p in d.paragraphs if p.text.strip()]
274
+ text = "\n".join(paras)
275
+ else: # .txt or unknown plain-text
276
+ with open(path, "r", encoding="utf-8", errors="ignore") as f:
277
+ text = f.read()
278
+ except Exception as e:
279
+ return {
280
+ "type": "document",
281
+ "error": f"Failed to extract document text: {e}",
282
+ }
283
+
284
+ short = text[:4000]
285
+ return {
286
+ "type": "document",
287
+ "char_count": len(text),
288
+ "preview": short,
289
+ }
290
+
291
+
292
+ # ---------- IMAGES ----------
293
+
294
+
295
+ def analyze_image(path: str) -> Dict[str, Any]:
296
+ try:
297
+ img = Image.open(path)
298
+ except Exception as e:
299
+ return {"type": "image", "error": f"Failed to open image: {e}"}
300
+
301
+ try:
302
+ text = pytesseract.image_to_string(img)
303
+ except Exception as e:
304
+ text = ""
305
+ ocr_error = str(e)
306
+ else:
307
+ ocr_error = None
308
+
309
+ short = text[:2000]
310
+
311
+ result: Dict[str, Any] = {
312
+ "type": "image",
313
+ "size": {"width": img.width, "height": img.height},
314
+ "ocr_preview": short,
315
+ }
316
+ if ocr_error:
317
+ result["ocr_error"] = ocr_error
318
+
319
+ return result
320
+
321
+
322
+ # ---------- AUDIO (Whisper) ----------
323
+
324
+ _whisper_model = None
325
+
326
+
327
+ def get_whisper_model():
328
+ global _whisper_model
329
+ if _whisper_model is None:
330
+ try:
331
+ _whisper_model = whisper.load_model("small")
332
+ except Exception as e:
333
+ raise RuntimeError(f"Failed to load Whisper model: {e}")
334
+ return _whisper_model
335
+
336
+
337
+ def analyze_audio(path: str) -> Dict[str, Any]:
338
+ try:
339
+ model = get_whisper_model()
340
+ except Exception as e:
341
+ return {"type": "audio", "error": str(e)}
342
+
343
+ try:
344
+ result = model.transcribe(path)
345
+ except Exception as e:
346
+ return {"type": "audio", "error": f"Whisper transcription failed: {e}"}
347
+
348
+ text = result.get("text", "") or ""
349
+ short = text[:4000]
350
+ duration = None
351
+ try:
352
+ if result.get("segments"):
353
+ duration = result["segments"][-1].get("end", None)
354
+ except Exception:
355
+ duration = None
356
+
357
+ return {
358
+ "type": "audio",
359
+ "duration_sec": duration,
360
+ "transcript_preview": short,
361
+ }
362
+
363
+
364
+ # ---------- VIDEO (audio extraction + Whisper) ----------
365
+
366
+
367
+ def analyze_video(path: str) -> Dict[str, Any]:
368
+ audio_path = path + ".tmp_audio.wav"
369
+ audio_summary: Dict[str, Any]
370
+
371
+ try:
372
+ (
373
+ ffmpeg
374
+ .input(path)
375
+ .output(audio_path, ac=1, ar=16000)
376
+ .overwrite_output()
377
+ .run(quiet=True)
378
+ )
379
+ except Exception as e:
380
+ return {
381
+ "type": "video",
382
+ "error": f"Failed to extract audio from video: {e}",
383
+ }
384
+
385
+ try:
386
+ audio_summary = analyze_audio(audio_path)
387
+ finally:
388
+ try:
389
+ if os.path.exists(audio_path):
390
+ os.remove(audio_path)
391
+ except Exception:
392
+ pass
393
+
394
+ return {
395
+ "type": "video",
396
+ "audio_analysis": audio_summary,
397
+ }
generate.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # generate.py
2
+ import torch
3
+ from transformers import TextIteratorStreamer
4
+ from load_model import get_model
5
+ import threading
6
+
7
+ def generate_response(
8
+ user_prompt: str,
9
+ system_prompt: str = "You are a helpful AI assistant.",
10
+ max_tokens: int = 32768,
11
+ stream: bool = False,
12
+ ) -> str:
13
+ """Generate response using ALREADY LOADED model"""
14
+ model, tokenizer = get_model() # Fast - no loading!
15
+
16
+ messages = [
17
+ {"role": "system", "content": system_prompt},
18
+ {"role": "user", "content": user_prompt},
19
+ ]
20
+
21
+ input_ids = tokenizer.apply_chat_template(
22
+ messages,
23
+ add_generation_prompt=True,
24
+ return_tensors="pt",
25
+ ).to(model.device)
26
+
27
+ attention_mask = (input_ids != tokenizer.pad_token_id).long()
28
+
29
+ gen_kwargs = dict(
30
+ input_ids=input_ids,
31
+ attention_mask=attention_mask,
32
+ max_new_tokens=max_tokens,
33
+ pad_token_id=tokenizer.eos_token_id,
34
+ use_cache=False,
35
+ do_sample=True,
36
+ temperature=0.8,
37
+ top_k=50,
38
+ top_p=0.95,
39
+ )
40
+
41
+ if stream:
42
+ streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
43
+ gen_kwargs["streamer"] = streamer
44
+ thread = threading.Thread(target=model.generate, kwargs=gen_kwargs)
45
+ thread.start()
46
+ for text in streamer:
47
+ yield text
48
+ else:
49
+ with torch.no_grad():
50
+ outputs = model.generate(**gen_kwargs)
51
+
52
+ return tokenizer.decode(
53
+ outputs[0][input_ids.shape[1]:],
54
+ skip_special_tokens=True,
55
+ ).strip()
generation_config.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "bos_token_id": 100257,
4
+ "do_sample": true,
5
+ "eos_token_id": 100265,
6
+ "pad_token_id": 100349,
7
+ "temperature": 0.8,
8
+ "top_k": 50,
9
+ "top_p": 0.95,
10
+ "transformers_version": "4.51.1"
11
+ }
load_model.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # load_model.py
2
+ import torch
3
+ import os
4
+ from transformers import AutoModelForCausalLM, AutoTokenizer
5
+
6
+ _model = None
7
+ _tokenizer = None
8
+
9
+ def init_model(model_dir: str = "."):
10
+ """Call this ONCE at startup to load model into memory"""
11
+ global _model, _tokenizer
12
+
13
+ if _model is not None:
14
+ print("✅ Model already loaded!")
15
+ return _model, _tokenizer
16
+
17
+ device = "cuda" if torch.cuda.is_available() else "cpu"
18
+ if device == "cpu":
19
+ torch.set_num_threads(os.cpu_count())
20
+
21
+ print("\n" + "=" * 70)
22
+ print(f"Loading model from local files on {device.upper()}...")
23
+ print("=" * 70)
24
+
25
+ _model = AutoModelForCausalLM.from_pretrained(
26
+ model_dir,
27
+ device_map=device,
28
+ torch_dtype="auto",
29
+ trust_remote_code=True,
30
+ local_files_only=True,
31
+ )
32
+
33
+ _tokenizer = AutoTokenizer.from_pretrained(
34
+ model_dir,
35
+ local_files_only=True,
36
+ )
37
+
38
+ print(f"✅ Model loaded! ({sum(p.numel() for p in _model.parameters()) / 1e9:.1f}B params)")
39
+ print("=" * 70 + "\n")
40
+
41
+ return _model, _tokenizer
42
+
43
+ def get_model():
44
+ """Get the already-loaded model (fast)"""
45
+ global _model, _tokenizer
46
+ if _model is None:
47
+ raise RuntimeError("Model not initialized! Call init_model() first.")
48
+ return _model, _tokenizer
model-00001-of-00006.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ec6a22fa7bdb16f42be8ce5c08b36bacc0f3f865f2879cd364bb384413ada6b6
3
+ size 4933656472
model-00002-of-00006.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2fb9cce7c157ae2383bb65c129be64bff0f9d39a2fc0692bae478d14ea0e3232
3
+ size 4954690712
model-00003-of-00006.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ab647f44ed3e8dda48e9004dad07cfdca3ce12e463b4e9f78c2421813100083c
3
+ size 4902241352
model-00004-of-00006.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d8a37c74492fcacc8b6ae4329b0391002e597df169d7b4c1e9024dc99b3f1a7d
3
+ size 4771169120
model-00005-of-00006.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ad7007b5aee76abab4561f757c586106d9ad934d0de9eec75f0d6211df6b2574
3
+ size 4771169120
model-00006-of-00006.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:09f8f3d34d1610761ada4b6671f2a1971ed924dc636b964ebe26fb84efbdc3ce
3
+ size 4986116216
model.safetensors.index.json ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "metadata": {
3
+ "total_size": 29319014400
4
+ },
5
+ "weight_map": {
6
+ "lm_head.weight": "model-00006-of-00006.safetensors",
7
+ "model.embed_tokens.weight": "model-00001-of-00006.safetensors",
8
+ "model.layers.0.input_layernorm.weight": "model-00001-of-00006.safetensors",
9
+ "model.layers.0.mlp.down_proj.weight": "model-00001-of-00006.safetensors",
10
+ "model.layers.0.mlp.gate_up_proj.weight": "model-00001-of-00006.safetensors",
11
+ "model.layers.0.post_attention_layernorm.weight": "model-00001-of-00006.safetensors",
12
+ "model.layers.0.self_attn.o_proj.weight": "model-00001-of-00006.safetensors",
13
+ "model.layers.0.self_attn.qkv_proj.weight": "model-00001-of-00006.safetensors",
14
+ "model.layers.1.input_layernorm.weight": "model-00001-of-00006.safetensors",
15
+ "model.layers.1.mlp.down_proj.weight": "model-00001-of-00006.safetensors",
16
+ "model.layers.1.mlp.gate_up_proj.weight": "model-00001-of-00006.safetensors",
17
+ "model.layers.1.post_attention_layernorm.weight": "model-00001-of-00006.safetensors",
18
+ "model.layers.1.self_attn.o_proj.weight": "model-00001-of-00006.safetensors",
19
+ "model.layers.1.self_attn.qkv_proj.weight": "model-00001-of-00006.safetensors",
20
+ "model.layers.10.input_layernorm.weight": "model-00002-of-00006.safetensors",
21
+ "model.layers.10.mlp.down_proj.weight": "model-00002-of-00006.safetensors",
22
+ "model.layers.10.mlp.gate_up_proj.weight": "model-00002-of-00006.safetensors",
23
+ "model.layers.10.post_attention_layernorm.weight": "model-00002-of-00006.safetensors",
24
+ "model.layers.10.self_attn.o_proj.weight": "model-00002-of-00006.safetensors",
25
+ "model.layers.10.self_attn.qkv_proj.weight": "model-00002-of-00006.safetensors",
26
+ "model.layers.11.input_layernorm.weight": "model-00002-of-00006.safetensors",
27
+ "model.layers.11.mlp.down_proj.weight": "model-00002-of-00006.safetensors",
28
+ "model.layers.11.mlp.gate_up_proj.weight": "model-00002-of-00006.safetensors",
29
+ "model.layers.11.post_attention_layernorm.weight": "model-00002-of-00006.safetensors",
30
+ "model.layers.11.self_attn.o_proj.weight": "model-00002-of-00006.safetensors",
31
+ "model.layers.11.self_attn.qkv_proj.weight": "model-00002-of-00006.safetensors",
32
+ "model.layers.12.input_layernorm.weight": "model-00002-of-00006.safetensors",
33
+ "model.layers.12.mlp.down_proj.weight": "model-00002-of-00006.safetensors",
34
+ "model.layers.12.mlp.gate_up_proj.weight": "model-00002-of-00006.safetensors",
35
+ "model.layers.12.post_attention_layernorm.weight": "model-00002-of-00006.safetensors",
36
+ "model.layers.12.self_attn.o_proj.weight": "model-00002-of-00006.safetensors",
37
+ "model.layers.12.self_attn.qkv_proj.weight": "model-00002-of-00006.safetensors",
38
+ "model.layers.13.input_layernorm.weight": "model-00003-of-00006.safetensors",
39
+ "model.layers.13.mlp.down_proj.weight": "model-00003-of-00006.safetensors",
40
+ "model.layers.13.mlp.gate_up_proj.weight": "model-00003-of-00006.safetensors",
41
+ "model.layers.13.post_attention_layernorm.weight": "model-00003-of-00006.safetensors",
42
+ "model.layers.13.self_attn.o_proj.weight": "model-00003-of-00006.safetensors",
43
+ "model.layers.13.self_attn.qkv_proj.weight": "model-00003-of-00006.safetensors",
44
+ "model.layers.14.input_layernorm.weight": "model-00003-of-00006.safetensors",
45
+ "model.layers.14.mlp.down_proj.weight": "model-00003-of-00006.safetensors",
46
+ "model.layers.14.mlp.gate_up_proj.weight": "model-00003-of-00006.safetensors",
47
+ "model.layers.14.post_attention_layernorm.weight": "model-00003-of-00006.safetensors",
48
+ "model.layers.14.self_attn.o_proj.weight": "model-00003-of-00006.safetensors",
49
+ "model.layers.14.self_attn.qkv_proj.weight": "model-00003-of-00006.safetensors",
50
+ "model.layers.15.input_layernorm.weight": "model-00003-of-00006.safetensors",
51
+ "model.layers.15.mlp.down_proj.weight": "model-00003-of-00006.safetensors",
52
+ "model.layers.15.mlp.gate_up_proj.weight": "model-00003-of-00006.safetensors",
53
+ "model.layers.15.post_attention_layernorm.weight": "model-00003-of-00006.safetensors",
54
+ "model.layers.15.self_attn.o_proj.weight": "model-00003-of-00006.safetensors",
55
+ "model.layers.15.self_attn.qkv_proj.weight": "model-00003-of-00006.safetensors",
56
+ "model.layers.16.input_layernorm.weight": "model-00003-of-00006.safetensors",
57
+ "model.layers.16.mlp.down_proj.weight": "model-00003-of-00006.safetensors",
58
+ "model.layers.16.mlp.gate_up_proj.weight": "model-00003-of-00006.safetensors",
59
+ "model.layers.16.post_attention_layernorm.weight": "model-00003-of-00006.safetensors",
60
+ "model.layers.16.self_attn.o_proj.weight": "model-00003-of-00006.safetensors",
61
+ "model.layers.16.self_attn.qkv_proj.weight": "model-00003-of-00006.safetensors",
62
+ "model.layers.17.input_layernorm.weight": "model-00003-of-00006.safetensors",
63
+ "model.layers.17.mlp.down_proj.weight": "model-00003-of-00006.safetensors",
64
+ "model.layers.17.mlp.gate_up_proj.weight": "model-00003-of-00006.safetensors",
65
+ "model.layers.17.post_attention_layernorm.weight": "model-00003-of-00006.safetensors",
66
+ "model.layers.17.self_attn.o_proj.weight": "model-00003-of-00006.safetensors",
67
+ "model.layers.17.self_attn.qkv_proj.weight": "model-00003-of-00006.safetensors",
68
+ "model.layers.18.input_layernorm.weight": "model-00003-of-00006.safetensors",
69
+ "model.layers.18.mlp.down_proj.weight": "model-00003-of-00006.safetensors",
70
+ "model.layers.18.mlp.gate_up_proj.weight": "model-00003-of-00006.safetensors",
71
+ "model.layers.18.post_attention_layernorm.weight": "model-00003-of-00006.safetensors",
72
+ "model.layers.18.self_attn.o_proj.weight": "model-00003-of-00006.safetensors",
73
+ "model.layers.18.self_attn.qkv_proj.weight": "model-00003-of-00006.safetensors",
74
+ "model.layers.19.input_layernorm.weight": "model-00003-of-00006.safetensors",
75
+ "model.layers.19.mlp.down_proj.weight": "model-00003-of-00006.safetensors",
76
+ "model.layers.19.mlp.gate_up_proj.weight": "model-00003-of-00006.safetensors",
77
+ "model.layers.19.post_attention_layernorm.weight": "model-00003-of-00006.safetensors",
78
+ "model.layers.19.self_attn.o_proj.weight": "model-00003-of-00006.safetensors",
79
+ "model.layers.19.self_attn.qkv_proj.weight": "model-00003-of-00006.safetensors",
80
+ "model.layers.2.input_layernorm.weight": "model-00001-of-00006.safetensors",
81
+ "model.layers.2.mlp.down_proj.weight": "model-00001-of-00006.safetensors",
82
+ "model.layers.2.mlp.gate_up_proj.weight": "model-00001-of-00006.safetensors",
83
+ "model.layers.2.post_attention_layernorm.weight": "model-00001-of-00006.safetensors",
84
+ "model.layers.2.self_attn.o_proj.weight": "model-00001-of-00006.safetensors",
85
+ "model.layers.2.self_attn.qkv_proj.weight": "model-00001-of-00006.safetensors",
86
+ "model.layers.20.input_layernorm.weight": "model-00004-of-00006.safetensors",
87
+ "model.layers.20.mlp.down_proj.weight": "model-00004-of-00006.safetensors",
88
+ "model.layers.20.mlp.gate_up_proj.weight": "model-00004-of-00006.safetensors",
89
+ "model.layers.20.post_attention_layernorm.weight": "model-00004-of-00006.safetensors",
90
+ "model.layers.20.self_attn.o_proj.weight": "model-00003-of-00006.safetensors",
91
+ "model.layers.20.self_attn.qkv_proj.weight": "model-00003-of-00006.safetensors",
92
+ "model.layers.21.input_layernorm.weight": "model-00004-of-00006.safetensors",
93
+ "model.layers.21.mlp.down_proj.weight": "model-00004-of-00006.safetensors",
94
+ "model.layers.21.mlp.gate_up_proj.weight": "model-00004-of-00006.safetensors",
95
+ "model.layers.21.post_attention_layernorm.weight": "model-00004-of-00006.safetensors",
96
+ "model.layers.21.self_attn.o_proj.weight": "model-00004-of-00006.safetensors",
97
+ "model.layers.21.self_attn.qkv_proj.weight": "model-00004-of-00006.safetensors",
98
+ "model.layers.22.input_layernorm.weight": "model-00004-of-00006.safetensors",
99
+ "model.layers.22.mlp.down_proj.weight": "model-00004-of-00006.safetensors",
100
+ "model.layers.22.mlp.gate_up_proj.weight": "model-00004-of-00006.safetensors",
101
+ "model.layers.22.post_attention_layernorm.weight": "model-00004-of-00006.safetensors",
102
+ "model.layers.22.self_attn.o_proj.weight": "model-00004-of-00006.safetensors",
103
+ "model.layers.22.self_attn.qkv_proj.weight": "model-00004-of-00006.safetensors",
104
+ "model.layers.23.input_layernorm.weight": "model-00004-of-00006.safetensors",
105
+ "model.layers.23.mlp.down_proj.weight": "model-00004-of-00006.safetensors",
106
+ "model.layers.23.mlp.gate_up_proj.weight": "model-00004-of-00006.safetensors",
107
+ "model.layers.23.post_attention_layernorm.weight": "model-00004-of-00006.safetensors",
108
+ "model.layers.23.self_attn.o_proj.weight": "model-00004-of-00006.safetensors",
109
+ "model.layers.23.self_attn.qkv_proj.weight": "model-00004-of-00006.safetensors",
110
+ "model.layers.24.input_layernorm.weight": "model-00004-of-00006.safetensors",
111
+ "model.layers.24.mlp.down_proj.weight": "model-00004-of-00006.safetensors",
112
+ "model.layers.24.mlp.gate_up_proj.weight": "model-00004-of-00006.safetensors",
113
+ "model.layers.24.post_attention_layernorm.weight": "model-00004-of-00006.safetensors",
114
+ "model.layers.24.self_attn.o_proj.weight": "model-00004-of-00006.safetensors",
115
+ "model.layers.24.self_attn.qkv_proj.weight": "model-00004-of-00006.safetensors",
116
+ "model.layers.25.input_layernorm.weight": "model-00004-of-00006.safetensors",
117
+ "model.layers.25.mlp.down_proj.weight": "model-00004-of-00006.safetensors",
118
+ "model.layers.25.mlp.gate_up_proj.weight": "model-00004-of-00006.safetensors",
119
+ "model.layers.25.post_attention_layernorm.weight": "model-00004-of-00006.safetensors",
120
+ "model.layers.25.self_attn.o_proj.weight": "model-00004-of-00006.safetensors",
121
+ "model.layers.25.self_attn.qkv_proj.weight": "model-00004-of-00006.safetensors",
122
+ "model.layers.26.input_layernorm.weight": "model-00004-of-00006.safetensors",
123
+ "model.layers.26.mlp.down_proj.weight": "model-00004-of-00006.safetensors",
124
+ "model.layers.26.mlp.gate_up_proj.weight": "model-00004-of-00006.safetensors",
125
+ "model.layers.26.post_attention_layernorm.weight": "model-00004-of-00006.safetensors",
126
+ "model.layers.26.self_attn.o_proj.weight": "model-00004-of-00006.safetensors",
127
+ "model.layers.26.self_attn.qkv_proj.weight": "model-00004-of-00006.safetensors",
128
+ "model.layers.27.input_layernorm.weight": "model-00005-of-00006.safetensors",
129
+ "model.layers.27.mlp.down_proj.weight": "model-00005-of-00006.safetensors",
130
+ "model.layers.27.mlp.gate_up_proj.weight": "model-00005-of-00006.safetensors",
131
+ "model.layers.27.post_attention_layernorm.weight": "model-00005-of-00006.safetensors",
132
+ "model.layers.27.self_attn.o_proj.weight": "model-00004-of-00006.safetensors",
133
+ "model.layers.27.self_attn.qkv_proj.weight": "model-00004-of-00006.safetensors",
134
+ "model.layers.28.input_layernorm.weight": "model-00005-of-00006.safetensors",
135
+ "model.layers.28.mlp.down_proj.weight": "model-00005-of-00006.safetensors",
136
+ "model.layers.28.mlp.gate_up_proj.weight": "model-00005-of-00006.safetensors",
137
+ "model.layers.28.post_attention_layernorm.weight": "model-00005-of-00006.safetensors",
138
+ "model.layers.28.self_attn.o_proj.weight": "model-00005-of-00006.safetensors",
139
+ "model.layers.28.self_attn.qkv_proj.weight": "model-00005-of-00006.safetensors",
140
+ "model.layers.29.input_layernorm.weight": "model-00005-of-00006.safetensors",
141
+ "model.layers.29.mlp.down_proj.weight": "model-00005-of-00006.safetensors",
142
+ "model.layers.29.mlp.gate_up_proj.weight": "model-00005-of-00006.safetensors",
143
+ "model.layers.29.post_attention_layernorm.weight": "model-00005-of-00006.safetensors",
144
+ "model.layers.29.self_attn.o_proj.weight": "model-00005-of-00006.safetensors",
145
+ "model.layers.29.self_attn.qkv_proj.weight": "model-00005-of-00006.safetensors",
146
+ "model.layers.3.input_layernorm.weight": "model-00001-of-00006.safetensors",
147
+ "model.layers.3.mlp.down_proj.weight": "model-00001-of-00006.safetensors",
148
+ "model.layers.3.mlp.gate_up_proj.weight": "model-00001-of-00006.safetensors",
149
+ "model.layers.3.post_attention_layernorm.weight": "model-00001-of-00006.safetensors",
150
+ "model.layers.3.self_attn.o_proj.weight": "model-00001-of-00006.safetensors",
151
+ "model.layers.3.self_attn.qkv_proj.weight": "model-00001-of-00006.safetensors",
152
+ "model.layers.30.input_layernorm.weight": "model-00005-of-00006.safetensors",
153
+ "model.layers.30.mlp.down_proj.weight": "model-00005-of-00006.safetensors",
154
+ "model.layers.30.mlp.gate_up_proj.weight": "model-00005-of-00006.safetensors",
155
+ "model.layers.30.post_attention_layernorm.weight": "model-00005-of-00006.safetensors",
156
+ "model.layers.30.self_attn.o_proj.weight": "model-00005-of-00006.safetensors",
157
+ "model.layers.30.self_attn.qkv_proj.weight": "model-00005-of-00006.safetensors",
158
+ "model.layers.31.input_layernorm.weight": "model-00005-of-00006.safetensors",
159
+ "model.layers.31.mlp.down_proj.weight": "model-00005-of-00006.safetensors",
160
+ "model.layers.31.mlp.gate_up_proj.weight": "model-00005-of-00006.safetensors",
161
+ "model.layers.31.post_attention_layernorm.weight": "model-00005-of-00006.safetensors",
162
+ "model.layers.31.self_attn.o_proj.weight": "model-00005-of-00006.safetensors",
163
+ "model.layers.31.self_attn.qkv_proj.weight": "model-00005-of-00006.safetensors",
164
+ "model.layers.32.input_layernorm.weight": "model-00005-of-00006.safetensors",
165
+ "model.layers.32.mlp.down_proj.weight": "model-00005-of-00006.safetensors",
166
+ "model.layers.32.mlp.gate_up_proj.weight": "model-00005-of-00006.safetensors",
167
+ "model.layers.32.post_attention_layernorm.weight": "model-00005-of-00006.safetensors",
168
+ "model.layers.32.self_attn.o_proj.weight": "model-00005-of-00006.safetensors",
169
+ "model.layers.32.self_attn.qkv_proj.weight": "model-00005-of-00006.safetensors",
170
+ "model.layers.33.input_layernorm.weight": "model-00005-of-00006.safetensors",
171
+ "model.layers.33.mlp.down_proj.weight": "model-00005-of-00006.safetensors",
172
+ "model.layers.33.mlp.gate_up_proj.weight": "model-00005-of-00006.safetensors",
173
+ "model.layers.33.post_attention_layernorm.weight": "model-00005-of-00006.safetensors",
174
+ "model.layers.33.self_attn.o_proj.weight": "model-00005-of-00006.safetensors",
175
+ "model.layers.33.self_attn.qkv_proj.weight": "model-00005-of-00006.safetensors",
176
+ "model.layers.34.input_layernorm.weight": "model-00006-of-00006.safetensors",
177
+ "model.layers.34.mlp.down_proj.weight": "model-00006-of-00006.safetensors",
178
+ "model.layers.34.mlp.gate_up_proj.weight": "model-00006-of-00006.safetensors",
179
+ "model.layers.34.post_attention_layernorm.weight": "model-00006-of-00006.safetensors",
180
+ "model.layers.34.self_attn.o_proj.weight": "model-00005-of-00006.safetensors",
181
+ "model.layers.34.self_attn.qkv_proj.weight": "model-00005-of-00006.safetensors",
182
+ "model.layers.35.input_layernorm.weight": "model-00006-of-00006.safetensors",
183
+ "model.layers.35.mlp.down_proj.weight": "model-00006-of-00006.safetensors",
184
+ "model.layers.35.mlp.gate_up_proj.weight": "model-00006-of-00006.safetensors",
185
+ "model.layers.35.post_attention_layernorm.weight": "model-00006-of-00006.safetensors",
186
+ "model.layers.35.self_attn.o_proj.weight": "model-00006-of-00006.safetensors",
187
+ "model.layers.35.self_attn.qkv_proj.weight": "model-00006-of-00006.safetensors",
188
+ "model.layers.36.input_layernorm.weight": "model-00006-of-00006.safetensors",
189
+ "model.layers.36.mlp.down_proj.weight": "model-00006-of-00006.safetensors",
190
+ "model.layers.36.mlp.gate_up_proj.weight": "model-00006-of-00006.safetensors",
191
+ "model.layers.36.post_attention_layernorm.weight": "model-00006-of-00006.safetensors",
192
+ "model.layers.36.self_attn.o_proj.weight": "model-00006-of-00006.safetensors",
193
+ "model.layers.36.self_attn.qkv_proj.weight": "model-00006-of-00006.safetensors",
194
+ "model.layers.37.input_layernorm.weight": "model-00006-of-00006.safetensors",
195
+ "model.layers.37.mlp.down_proj.weight": "model-00006-of-00006.safetensors",
196
+ "model.layers.37.mlp.gate_up_proj.weight": "model-00006-of-00006.safetensors",
197
+ "model.layers.37.post_attention_layernorm.weight": "model-00006-of-00006.safetensors",
198
+ "model.layers.37.self_attn.o_proj.weight": "model-00006-of-00006.safetensors",
199
+ "model.layers.37.self_attn.qkv_proj.weight": "model-00006-of-00006.safetensors",
200
+ "model.layers.38.input_layernorm.weight": "model-00006-of-00006.safetensors",
201
+ "model.layers.38.mlp.down_proj.weight": "model-00006-of-00006.safetensors",
202
+ "model.layers.38.mlp.gate_up_proj.weight": "model-00006-of-00006.safetensors",
203
+ "model.layers.38.post_attention_layernorm.weight": "model-00006-of-00006.safetensors",
204
+ "model.layers.38.self_attn.o_proj.weight": "model-00006-of-00006.safetensors",
205
+ "model.layers.38.self_attn.qkv_proj.weight": "model-00006-of-00006.safetensors",
206
+ "model.layers.39.input_layernorm.weight": "model-00006-of-00006.safetensors",
207
+ "model.layers.39.mlp.down_proj.weight": "model-00006-of-00006.safetensors",
208
+ "model.layers.39.mlp.gate_up_proj.weight": "model-00006-of-00006.safetensors",
209
+ "model.layers.39.post_attention_layernorm.weight": "model-00006-of-00006.safetensors",
210
+ "model.layers.39.self_attn.o_proj.weight": "model-00006-of-00006.safetensors",
211
+ "model.layers.39.self_attn.qkv_proj.weight": "model-00006-of-00006.safetensors",
212
+ "model.layers.4.input_layernorm.weight": "model-00001-of-00006.safetensors",
213
+ "model.layers.4.mlp.down_proj.weight": "model-00001-of-00006.safetensors",
214
+ "model.layers.4.mlp.gate_up_proj.weight": "model-00001-of-00006.safetensors",
215
+ "model.layers.4.post_attention_layernorm.weight": "model-00001-of-00006.safetensors",
216
+ "model.layers.4.self_attn.o_proj.weight": "model-00001-of-00006.safetensors",
217
+ "model.layers.4.self_attn.qkv_proj.weight": "model-00001-of-00006.safetensors",
218
+ "model.layers.5.input_layernorm.weight": "model-00002-of-00006.safetensors",
219
+ "model.layers.5.mlp.down_proj.weight": "model-00002-of-00006.safetensors",
220
+ "model.layers.5.mlp.gate_up_proj.weight": "model-00001-of-00006.safetensors",
221
+ "model.layers.5.post_attention_layernorm.weight": "model-00002-of-00006.safetensors",
222
+ "model.layers.5.self_attn.o_proj.weight": "model-00001-of-00006.safetensors",
223
+ "model.layers.5.self_attn.qkv_proj.weight": "model-00001-of-00006.safetensors",
224
+ "model.layers.6.input_layernorm.weight": "model-00002-of-00006.safetensors",
225
+ "model.layers.6.mlp.down_proj.weight": "model-00002-of-00006.safetensors",
226
+ "model.layers.6.mlp.gate_up_proj.weight": "model-00002-of-00006.safetensors",
227
+ "model.layers.6.post_attention_layernorm.weight": "model-00002-of-00006.safetensors",
228
+ "model.layers.6.self_attn.o_proj.weight": "model-00002-of-00006.safetensors",
229
+ "model.layers.6.self_attn.qkv_proj.weight": "model-00002-of-00006.safetensors",
230
+ "model.layers.7.input_layernorm.weight": "model-00002-of-00006.safetensors",
231
+ "model.layers.7.mlp.down_proj.weight": "model-00002-of-00006.safetensors",
232
+ "model.layers.7.mlp.gate_up_proj.weight": "model-00002-of-00006.safetensors",
233
+ "model.layers.7.post_attention_layernorm.weight": "model-00002-of-00006.safetensors",
234
+ "model.layers.7.self_attn.o_proj.weight": "model-00002-of-00006.safetensors",
235
+ "model.layers.7.self_attn.qkv_proj.weight": "model-00002-of-00006.safetensors",
236
+ "model.layers.8.input_layernorm.weight": "model-00002-of-00006.safetensors",
237
+ "model.layers.8.mlp.down_proj.weight": "model-00002-of-00006.safetensors",
238
+ "model.layers.8.mlp.gate_up_proj.weight": "model-00002-of-00006.safetensors",
239
+ "model.layers.8.post_attention_layernorm.weight": "model-00002-of-00006.safetensors",
240
+ "model.layers.8.self_attn.o_proj.weight": "model-00002-of-00006.safetensors",
241
+ "model.layers.8.self_attn.qkv_proj.weight": "model-00002-of-00006.safetensors",
242
+ "model.layers.9.input_layernorm.weight": "model-00002-of-00006.safetensors",
243
+ "model.layers.9.mlp.down_proj.weight": "model-00002-of-00006.safetensors",
244
+ "model.layers.9.mlp.gate_up_proj.weight": "model-00002-of-00006.safetensors",
245
+ "model.layers.9.post_attention_layernorm.weight": "model-00002-of-00006.safetensors",
246
+ "model.layers.9.self_attn.o_proj.weight": "model-00002-of-00006.safetensors",
247
+ "model.layers.9.self_attn.qkv_proj.weight": "model-00002-of-00006.safetensors",
248
+ "model.norm.weight": "model-00006-of-00006.safetensors"
249
+ }
250
+ }
requirements.txt ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn[standard]
3
+ python-multipart
4
+
5
+ torch
6
+ transformers
7
+ accelerate
8
+ safetensors
9
+
10
+ pandas
11
+ numpy
12
+
13
+ pdfplumber
14
+ pymupdf
15
+ python-docx
16
+
17
+ Pillow
18
+ pytesseract
19
+
20
+ openai-whisper
21
+ ffmpeg-python
run.py ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ run.py - FastAPI backend + frontend server for GAKR AI
4
+
5
+ Features:
6
+ - Serves templates/chat.html at "/"
7
+ - /api/analyze:
8
+ - prompt: required (must not be empty)
9
+ - files: optional list[UploadFile] (0, 1, many; any type)
10
+ - api_key: required
11
+ - If files are present → file-analysis mode (different system prompt)
12
+ - If no files → general assistant mode
13
+ - Detailed exceptions and logs for easier debugging
14
+ """
15
+
16
+ from typing import List, Optional
17
+
18
+ import json
19
+ import traceback
20
+
21
+ from fastapi import (
22
+ FastAPI,
23
+ HTTPException,
24
+ Form,
25
+ UploadFile,
26
+ File,
27
+ Request,
28
+ )
29
+ from fastapi.middleware.cors import CORSMiddleware
30
+ from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse
31
+ from fastapi.templating import Jinja2Templates
32
+ import uvicorn
33
+
34
+ from load_model import init_model
35
+ from generate import generate_response
36
+ from file_pipeline import process_files
37
+
38
+
39
+ # ============================================================
40
+ # APP SETUP
41
+ # ============================================================
42
+
43
+ app = FastAPI(title="GAKR AI")
44
+
45
+ # Templates (chat.html lives in ./templates)
46
+ templates = Jinja2Templates(directory="templates")
47
+
48
+ # CORS (open for dev; restrict origins in production)
49
+ app.add_middleware(
50
+ CORSMiddleware,
51
+ allow_origins=["*"], # change to specific origin in production
52
+ allow_credentials=True,
53
+ allow_methods=["*"],
54
+ allow_headers=["*"],
55
+ )
56
+
57
+ # API Key
58
+ API_KEY = "gakr-ai-2025-secret"
59
+
60
+ # Load model ONCE at startup
61
+ print("🚀 Starting GAKR AI Backend...")
62
+ try:
63
+ model, tokenizer = init_model(".")
64
+ print("✅ Model initialized successfully")
65
+ except Exception as e:
66
+ print("❌ Failed to load model at startup:")
67
+ traceback.print_exc()
68
+ raise e
69
+
70
+
71
+ # ============================================================
72
+ # ROUTES
73
+ # ============================================================
74
+
75
+ @app.get("/", response_class=HTMLResponse)
76
+ async def home(request: Request):
77
+ """
78
+ Serve chat.html as homepage.
79
+ """
80
+ try:
81
+ return templates.TemplateResponse("chat.html", {"request": request})
82
+ except Exception as e:
83
+ # If template not found or other template error
84
+ traceback.print_exc()
85
+ raise HTTPException(
86
+ status_code=500,
87
+ detail=f"Failed to render chat.html: {str(e)}",
88
+ )
89
+
90
+
91
+ @app.post("/api/analyze")
92
+ async def analyze_endpoint(
93
+ prompt: str = Form(...), # required
94
+ api_key: str = Form(...),
95
+ files: Optional[List[UploadFile]] = File(None), # optional
96
+ ):
97
+ """
98
+ Main analysis endpoint.
99
+
100
+ Cases:
101
+ - prompt only (no files) → general assistant mode
102
+ - prompt + one/many files → file-analysis mode (uses file context)
103
+ """
104
+ try:
105
+ # ---------- Basic validation ----------
106
+ if api_key != API_KEY:
107
+ raise HTTPException(status_code=401, detail="Invalid API key")
108
+
109
+ if prompt is None:
110
+ raise HTTPException(status_code=400, detail="Prompt is missing")
111
+ if not prompt.strip():
112
+ raise HTTPException(status_code=400, detail="Prompt cannot be empty")
113
+
114
+ files = files or []
115
+
116
+ # ---------- Branch by presence of files ----------
117
+ if files:
118
+ # ----- CASE 1: prompt + files -----
119
+ try:
120
+ context = await process_files(files)
121
+ except Exception as extract_err:
122
+ traceback.print_exc()
123
+ raise HTTPException(
124
+ status_code=500,
125
+ detail=f"Error while processing uploaded files: {str(extract_err)}",
126
+ )
127
+
128
+ context_text = json.dumps(context, indent=2, ensure_ascii=False)
129
+ combined_user_prompt = f"""
130
+ User question:
131
+ {prompt}
132
+
133
+ Below is structured information extracted from the user's uploaded files.
134
+ The extraction was done by automated tools.
135
+
136
+ Your task:
137
+ 1. Answer the user's question as accurately as possible.
138
+ 2. Use the context when it is relevant.
139
+ 3. Highlight important patterns, risks, or opportunities.
140
+ 4. If some information is missing or uncertain, say so honestly.
141
+
142
+ Context:
143
+ {context_text}
144
+ """
145
+
146
+ system_prompt = (
147
+ "You are GAKR AI, a careful and honest analysis assistant that works with "
148
+ "structured summaries of files (tables, PDFs, documents, images, audio, video, etc.). "
149
+ "You never assume file contents beyond what the provided context states. "
150
+ "You are a reasoning-focused AI assistant. "
151
+ "Think carefully before answering. "
152
+ "Provide clear, correct answers with brief explanations when helpful. "
153
+ )
154
+
155
+ else:
156
+ # ----- CASE 2: prompt only -----
157
+ context = {"files": []} # keep structure consistent
158
+ combined_user_prompt = prompt
159
+
160
+ system_prompt = (
161
+ "You are GAKR AI, a reasoning-focused AI assistant. "
162
+ "Think carefully before answering. "
163
+ "Provide clear, correct answers with brief explanations when helpful. "
164
+ )
165
+
166
+ # ---------- Generate with Phi-4 ----------
167
+ try:
168
+ response_generator = generate_response(
169
+ user_prompt=combined_user_prompt,
170
+ system_prompt=system_prompt,
171
+ max_tokens=32768,
172
+ stream=True,
173
+ )
174
+ except Exception as gen_err:
175
+ traceback.print_exc()
176
+ raise HTTPException(
177
+ status_code=500,
178
+ detail=f"Error during model generation: {str(gen_err)}",
179
+ )
180
+
181
+ return StreamingResponse(
182
+ response_generator,
183
+ media_type="text/plain",
184
+ )
185
+
186
+ except HTTPException:
187
+ # Let FastAPI handle HTTPException as-is
188
+ raise
189
+ except Exception as e:
190
+ # Unexpected error: log full traceback and return 500
191
+ traceback.print_exc()
192
+ raise HTTPException(
193
+ status_code=500,
194
+ detail=f"Unexpected backend error: {str(e)}",
195
+ )
196
+
197
+
198
+ @app.get("/health")
199
+ async def health_check():
200
+ """
201
+ Simple health check.
202
+ """
203
+ return {"status": "healthy", "model_loaded": True}
204
+
205
+
206
+ # ============================================================
207
+ # ENTRY POINT
208
+ # ============================================================
209
+
210
+ if __name__ == "__main__":
211
+ print("\n" + "=" * 60)
212
+ print("🌐 SERVER & CHAT LOCATION")
213
+ print("=" * 60)
214
+ print("🚀 CHAT INTERFACE: http://localhost:8080")
215
+ print("📱 ALTERNATIVE URL: http://127.0.0.1:8080")
216
+ print("🔧 API DOCUMENTATION: http://localhost:8080/docs")
217
+ print("✅ CHAT.HTML SERVED: templates/chat.html")
218
+ print("📁 TEMPLATES FOLDER: ./templates/")
219
+ print("=" * 60 + "\n")
220
+
221
+ uvicorn.run(app, host="0.0.0.0", port=8080)
sample_finetune.py ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import logging
3
+
4
+ import datasets
5
+ from datasets import load_dataset
6
+ from peft import LoraConfig
7
+ import torch
8
+ import transformers
9
+ from trl import SFTTrainer
10
+ from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments, BitsAndBytesConfig
11
+
12
+ """
13
+ A simple example on using SFTTrainer and Accelerate to finetune Phi-3 models. For
14
+ a more advanced example, please follow HF alignment-handbook/scripts/run_sft.py.
15
+ This example has utilized DeepSpeed ZeRO3 offload to reduce the memory usage. The
16
+ script can be run on V100 or later generation GPUs. Here are some suggestions on
17
+ futher reducing memory consumption:
18
+ - reduce batch size
19
+ - decrease lora dimension
20
+ - restrict lora target modules
21
+ Please follow these steps to run the script:
22
+ 1. Install dependencies:
23
+ conda install -c conda-forge accelerate
24
+ pip3 install -i https://pypi.org/simple/ bitsandbytes
25
+ pip3 install peft transformers trl datasets
26
+ pip3 install deepspeed
27
+ 2. Setup accelerate and deepspeed config based on the machine used:
28
+ accelerate config
29
+ Here is a sample config for deepspeed zero3:
30
+ compute_environment: LOCAL_MACHINE
31
+ debug: false
32
+ deepspeed_config:
33
+ gradient_accumulation_steps: 1
34
+ offload_optimizer_device: none
35
+ offload_param_device: none
36
+ zero3_init_flag: true
37
+ zero3_save_16bit_model: true
38
+ zero_stage: 3
39
+ distributed_type: DEEPSPEED
40
+ downcast_bf16: 'no'
41
+ enable_cpu_affinity: false
42
+ machine_rank: 0
43
+ main_training_function: main
44
+ mixed_precision: bf16
45
+ num_machines: 1
46
+ num_processes: 4
47
+ rdzv_backend: static
48
+ same_network: true
49
+ tpu_env: []
50
+ tpu_use_cluster: false
51
+ tpu_use_sudo: false
52
+ use_cpu: false
53
+ 3. check accelerate config:
54
+ accelerate env
55
+ 4. Run the code:
56
+ accelerate launch sample_finetune.py
57
+ """
58
+
59
+ logger = logging.getLogger(__name__)
60
+
61
+
62
+ ###################
63
+ # Hyper-parameters
64
+ ###################
65
+ training_config = {
66
+ "bf16": True,
67
+ "do_eval": False,
68
+ "learning_rate": 5.0e-06,
69
+ "log_level": "info",
70
+ "logging_steps": 20,
71
+ "logging_strategy": "steps",
72
+ "lr_scheduler_type": "cosine",
73
+ "num_train_epochs": 1,
74
+ "max_steps": -1,
75
+ "output_dir": "./checkpoint_dir",
76
+ "overwrite_output_dir": True,
77
+ "per_device_eval_batch_size": 4,
78
+ "per_device_train_batch_size": 4,
79
+ "remove_unused_columns": True,
80
+ "save_steps": 100,
81
+ "save_total_limit": 1,
82
+ "seed": 0,
83
+ "gradient_checkpointing": True,
84
+ "gradient_checkpointing_kwargs":{"use_reentrant": False},
85
+ "gradient_accumulation_steps": 1,
86
+ "warmup_ratio": 0.2,
87
+ }
88
+
89
+ peft_config = {
90
+ "r": 16,
91
+ "lora_alpha": 32,
92
+ "lora_dropout": 0.05,
93
+ "bias": "none",
94
+ "task_type": "CAUSAL_LM",
95
+ "target_modules": "all-linear",
96
+ "modules_to_save": None,
97
+ }
98
+ train_conf = TrainingArguments(**training_config)
99
+ peft_conf = LoraConfig(**peft_config)
100
+
101
+
102
+ ###############
103
+ # Setup logging
104
+ ###############
105
+ logging.basicConfig(
106
+ format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
107
+ datefmt="%Y-%m-%d %H:%M:%S",
108
+ handlers=[logging.StreamHandler(sys.stdout)],
109
+ )
110
+ log_level = train_conf.get_process_log_level()
111
+ logger.setLevel(log_level)
112
+ datasets.utils.logging.set_verbosity(log_level)
113
+ transformers.utils.logging.set_verbosity(log_level)
114
+ transformers.utils.logging.enable_default_handler()
115
+ transformers.utils.logging.enable_explicit_format()
116
+
117
+ # Log on each process a small summary
118
+ logger.warning(
119
+ f"Process rank: {train_conf.local_rank}, device: {train_conf.device}, n_gpu: {train_conf.n_gpu}"
120
+ + f" distributed training: {bool(train_conf.local_rank != -1)}, 16-bits training: {train_conf.fp16}"
121
+ )
122
+ logger.info(f"Training/evaluation parameters {train_conf}")
123
+ logger.info(f"PEFT parameters {peft_conf}")
124
+
125
+
126
+ ################
127
+ # Model Loading
128
+ ################
129
+
130
+ checkpoint_path = "microsoft/Phi-3.5-mini-instruct"
131
+ model_kwargs = dict(
132
+ use_cache=False,
133
+ trust_remote_code=True,
134
+ attn_implementation="flash_attention_2", # loading the model with flash-attenstion support
135
+ torch_dtype=torch.bfloat16,
136
+ device_map=None
137
+ )
138
+ model = AutoModelForCausalLM.from_pretrained(checkpoint_path, **model_kwargs)
139
+ tokenizer = AutoTokenizer.from_pretrained(checkpoint_path)
140
+ tokenizer.model_max_length = 2048
141
+ tokenizer.pad_token = tokenizer.unk_token # use unk rather than eos token to prevent endless generation
142
+ tokenizer.pad_token_id = tokenizer.convert_tokens_to_ids(tokenizer.pad_token)
143
+ tokenizer.padding_side = 'right'
144
+
145
+
146
+ ##################
147
+ # Data Processing
148
+ ##################
149
+ def apply_chat_template(
150
+ example,
151
+ tokenizer,
152
+ ):
153
+ messages = example["messages"]
154
+ example["text"] = tokenizer.apply_chat_template(
155
+ messages, tokenize=False, add_generation_prompt=False)
156
+ return example
157
+
158
+ raw_dataset = load_dataset("HuggingFaceH4/ultrachat_200k")
159
+ train_dataset = raw_dataset["train_sft"]
160
+ test_dataset = raw_dataset["test_sft"]
161
+ column_names = list(train_dataset.features)
162
+
163
+ processed_train_dataset = train_dataset.map(
164
+ apply_chat_template,
165
+ fn_kwargs={"tokenizer": tokenizer},
166
+ num_proc=10,
167
+ remove_columns=column_names,
168
+ desc="Applying chat template to train_sft",
169
+ )
170
+
171
+ processed_test_dataset = test_dataset.map(
172
+ apply_chat_template,
173
+ fn_kwargs={"tokenizer": tokenizer},
174
+ num_proc=10,
175
+ remove_columns=column_names,
176
+ desc="Applying chat template to test_sft",
177
+ )
178
+
179
+
180
+ ###########
181
+ # Training
182
+ ###########
183
+ trainer = SFTTrainer(
184
+ model=model,
185
+ args=train_conf,
186
+ peft_config=peft_conf,
187
+ train_dataset=processed_train_dataset,
188
+ eval_dataset=processed_test_dataset,
189
+ max_seq_length=2048,
190
+ dataset_text_field="text",
191
+ tokenizer=tokenizer,
192
+ packing=True
193
+ )
194
+ train_result = trainer.train()
195
+ metrics = train_result.metrics
196
+ trainer.log_metrics("train", metrics)
197
+ trainer.save_metrics("train", metrics)
198
+ trainer.save_state()
199
+
200
+
201
+ #############
202
+ # Evaluation
203
+ #############
204
+ tokenizer.padding_side = 'left'
205
+ metrics = trainer.evaluate()
206
+ metrics["eval_samples"] = len(processed_test_dataset)
207
+ trainer.log_metrics("eval", metrics)
208
+ trainer.save_metrics("eval", metrics)
209
+
210
+
211
+ # ############
212
+ # # Save model
213
+ # ############
214
+ trainer.save_model(train_conf.output_dir)
special_tokens_map.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token": {
3
+ "content": "<|endoftext|>",
4
+ "lstrip": true,
5
+ "normalized": false,
6
+ "rstrip": true,
7
+ "single_word": false
8
+ },
9
+ "eos_token": {
10
+ "content": "<|im_end|>",
11
+ "lstrip": true,
12
+ "normalized": false,
13
+ "rstrip": true,
14
+ "single_word": false
15
+ },
16
+ "pad_token": {
17
+ "content": "<|dummy_85|>",
18
+ "lstrip": true,
19
+ "normalized": false,
20
+ "rstrip": true,
21
+ "single_word": false
22
+ }
23
+ }
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer.model ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9e556afd44213b6bd1be2b850ebbbd98f5481437a8021afaf58ee7fb1818d347
3
+ size 499723
tokenizer_config.json ADDED
@@ -0,0 +1,782 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "added_tokens_decoder": {
4
+ "100256": {
5
+ "content": "<|dummy_0|>",
6
+ "lstrip": true,
7
+ "normalized": false,
8
+ "rstrip": true,
9
+ "single_word": false,
10
+ "special": true
11
+ },
12
+ "100257": {
13
+ "content": "<|endoftext|>",
14
+ "lstrip": true,
15
+ "normalized": false,
16
+ "rstrip": true,
17
+ "single_word": false,
18
+ "special": true
19
+ },
20
+ "100258": {
21
+ "content": "<|fim_prefix|>",
22
+ "lstrip": true,
23
+ "normalized": false,
24
+ "rstrip": true,
25
+ "single_word": false,
26
+ "special": false
27
+ },
28
+ "100259": {
29
+ "content": "<|fim_middle|>",
30
+ "lstrip": true,
31
+ "normalized": false,
32
+ "rstrip": true,
33
+ "single_word": false,
34
+ "special": false
35
+ },
36
+ "100260": {
37
+ "content": "<|fim_suffix|>",
38
+ "lstrip": true,
39
+ "normalized": false,
40
+ "rstrip": true,
41
+ "single_word": false,
42
+ "special": false
43
+ },
44
+ "100261": {
45
+ "content": "<|dummy_1|>",
46
+ "lstrip": true,
47
+ "normalized": false,
48
+ "rstrip": true,
49
+ "single_word": false,
50
+ "special": true
51
+ },
52
+ "100262": {
53
+ "content": "<|dummy_2|>",
54
+ "lstrip": true,
55
+ "normalized": false,
56
+ "rstrip": true,
57
+ "single_word": false,
58
+ "special": true
59
+ },
60
+ "100263": {
61
+ "content": "<|dummy_3|>",
62
+ "lstrip": true,
63
+ "normalized": false,
64
+ "rstrip": true,
65
+ "single_word": false,
66
+ "special": true
67
+ },
68
+ "100264": {
69
+ "content": "<|im_start|>",
70
+ "lstrip": true,
71
+ "normalized": false,
72
+ "rstrip": true,
73
+ "single_word": false,
74
+ "special": true
75
+ },
76
+ "100265": {
77
+ "content": "<|im_end|>",
78
+ "lstrip": true,
79
+ "normalized": false,
80
+ "rstrip": true,
81
+ "single_word": false,
82
+ "special": true
83
+ },
84
+ "100266": {
85
+ "content": "<|im_sep|>",
86
+ "lstrip": true,
87
+ "normalized": false,
88
+ "rstrip": true,
89
+ "single_word": false,
90
+ "special": true
91
+ },
92
+ "100267": {
93
+ "content": "<|dummy_4|>",
94
+ "lstrip": true,
95
+ "normalized": false,
96
+ "rstrip": true,
97
+ "single_word": false,
98
+ "special": true
99
+ },
100
+ "100268": {
101
+ "content": "<|dummy_5|>",
102
+ "lstrip": true,
103
+ "normalized": false,
104
+ "rstrip": true,
105
+ "single_word": false,
106
+ "special": true
107
+ },
108
+ "100269": {
109
+ "content": "<|dummy_6|>",
110
+ "lstrip": true,
111
+ "normalized": false,
112
+ "rstrip": true,
113
+ "single_word": false,
114
+ "special": true
115
+ },
116
+ "100270": {
117
+ "content": "<|dummy_7|>",
118
+ "lstrip": true,
119
+ "normalized": false,
120
+ "rstrip": true,
121
+ "single_word": false,
122
+ "special": true
123
+ },
124
+ "100271": {
125
+ "content": "<|dummy_8|>",
126
+ "lstrip": true,
127
+ "normalized": false,
128
+ "rstrip": true,
129
+ "single_word": false,
130
+ "special": true
131
+ },
132
+ "100272": {
133
+ "content": "<|dummy_9|>",
134
+ "lstrip": true,
135
+ "normalized": false,
136
+ "rstrip": true,
137
+ "single_word": false,
138
+ "special": true
139
+ },
140
+ "100273": {
141
+ "content": "<|dummy_10|>",
142
+ "lstrip": true,
143
+ "normalized": false,
144
+ "rstrip": true,
145
+ "single_word": false,
146
+ "special": true
147
+ },
148
+ "100274": {
149
+ "content": "<|dummy_11|>",
150
+ "lstrip": true,
151
+ "normalized": false,
152
+ "rstrip": true,
153
+ "single_word": false,
154
+ "special": true
155
+ },
156
+ "100275": {
157
+ "content": "<|dummy_12|>",
158
+ "lstrip": true,
159
+ "normalized": false,
160
+ "rstrip": true,
161
+ "single_word": false,
162
+ "special": true
163
+ },
164
+ "100276": {
165
+ "content": "<|endofprompt|>",
166
+ "lstrip": true,
167
+ "normalized": false,
168
+ "rstrip": true,
169
+ "single_word": false,
170
+ "special": true
171
+ },
172
+ "100277": {
173
+ "content": "<|dummy_13|>",
174
+ "lstrip": true,
175
+ "normalized": false,
176
+ "rstrip": true,
177
+ "single_word": false,
178
+ "special": true
179
+ },
180
+ "100278": {
181
+ "content": "<|dummy_14|>",
182
+ "lstrip": true,
183
+ "normalized": false,
184
+ "rstrip": true,
185
+ "single_word": false,
186
+ "special": true
187
+ },
188
+ "100279": {
189
+ "content": "<|dummy_15|>",
190
+ "lstrip": true,
191
+ "normalized": false,
192
+ "rstrip": true,
193
+ "single_word": false,
194
+ "special": true
195
+ },
196
+ "100280": {
197
+ "content": "<|dummy_16|>",
198
+ "lstrip": true,
199
+ "normalized": false,
200
+ "rstrip": true,
201
+ "single_word": false,
202
+ "special": true
203
+ },
204
+ "100281": {
205
+ "content": "<|dummy_17|>",
206
+ "lstrip": true,
207
+ "normalized": false,
208
+ "rstrip": true,
209
+ "single_word": false,
210
+ "special": true
211
+ },
212
+ "100282": {
213
+ "content": "<|dummy_18|>",
214
+ "lstrip": true,
215
+ "normalized": false,
216
+ "rstrip": true,
217
+ "single_word": false,
218
+ "special": true
219
+ },
220
+ "100283": {
221
+ "content": "<|dummy_19|>",
222
+ "lstrip": true,
223
+ "normalized": false,
224
+ "rstrip": true,
225
+ "single_word": false,
226
+ "special": true
227
+ },
228
+ "100284": {
229
+ "content": "<|dummy_20|>",
230
+ "lstrip": true,
231
+ "normalized": false,
232
+ "rstrip": true,
233
+ "single_word": false,
234
+ "special": true
235
+ },
236
+ "100285": {
237
+ "content": "<|dummy_21|>",
238
+ "lstrip": true,
239
+ "normalized": false,
240
+ "rstrip": true,
241
+ "single_word": false,
242
+ "special": true
243
+ },
244
+ "100286": {
245
+ "content": "<|dummy_22|>",
246
+ "lstrip": true,
247
+ "normalized": false,
248
+ "rstrip": true,
249
+ "single_word": false,
250
+ "special": true
251
+ },
252
+ "100287": {
253
+ "content": "<|dummy_23|>",
254
+ "lstrip": true,
255
+ "normalized": false,
256
+ "rstrip": true,
257
+ "single_word": false,
258
+ "special": true
259
+ },
260
+ "100288": {
261
+ "content": "<|dummy_24|>",
262
+ "lstrip": true,
263
+ "normalized": false,
264
+ "rstrip": true,
265
+ "single_word": false,
266
+ "special": true
267
+ },
268
+ "100289": {
269
+ "content": "<|dummy_25|>",
270
+ "lstrip": true,
271
+ "normalized": false,
272
+ "rstrip": true,
273
+ "single_word": false,
274
+ "special": true
275
+ },
276
+ "100290": {
277
+ "content": "<|dummy_26|>",
278
+ "lstrip": true,
279
+ "normalized": false,
280
+ "rstrip": true,
281
+ "single_word": false,
282
+ "special": true
283
+ },
284
+ "100291": {
285
+ "content": "<|dummy_27|>",
286
+ "lstrip": true,
287
+ "normalized": false,
288
+ "rstrip": true,
289
+ "single_word": false,
290
+ "special": true
291
+ },
292
+ "100292": {
293
+ "content": "<|dummy_28|>",
294
+ "lstrip": true,
295
+ "normalized": false,
296
+ "rstrip": true,
297
+ "single_word": false,
298
+ "special": true
299
+ },
300
+ "100293": {
301
+ "content": "<|dummy_29|>",
302
+ "lstrip": true,
303
+ "normalized": false,
304
+ "rstrip": true,
305
+ "single_word": false,
306
+ "special": true
307
+ },
308
+ "100294": {
309
+ "content": "<|dummy_30|>",
310
+ "lstrip": true,
311
+ "normalized": false,
312
+ "rstrip": true,
313
+ "single_word": false,
314
+ "special": true
315
+ },
316
+ "100295": {
317
+ "content": "<|dummy_31|>",
318
+ "lstrip": true,
319
+ "normalized": false,
320
+ "rstrip": true,
321
+ "single_word": false,
322
+ "special": true
323
+ },
324
+ "100296": {
325
+ "content": "<|dummy_32|>",
326
+ "lstrip": true,
327
+ "normalized": false,
328
+ "rstrip": true,
329
+ "single_word": false,
330
+ "special": true
331
+ },
332
+ "100297": {
333
+ "content": "<|dummy_33|>",
334
+ "lstrip": true,
335
+ "normalized": false,
336
+ "rstrip": true,
337
+ "single_word": false,
338
+ "special": true
339
+ },
340
+ "100298": {
341
+ "content": "<|dummy_34|>",
342
+ "lstrip": true,
343
+ "normalized": false,
344
+ "rstrip": true,
345
+ "single_word": false,
346
+ "special": true
347
+ },
348
+ "100299": {
349
+ "content": "<|dummy_35|>",
350
+ "lstrip": true,
351
+ "normalized": false,
352
+ "rstrip": true,
353
+ "single_word": false,
354
+ "special": true
355
+ },
356
+ "100300": {
357
+ "content": "<|dummy_36|>",
358
+ "lstrip": true,
359
+ "normalized": false,
360
+ "rstrip": true,
361
+ "single_word": false,
362
+ "special": true
363
+ },
364
+ "100301": {
365
+ "content": "<|dummy_37|>",
366
+ "lstrip": true,
367
+ "normalized": false,
368
+ "rstrip": true,
369
+ "single_word": false,
370
+ "special": true
371
+ },
372
+ "100302": {
373
+ "content": "<|dummy_38|>",
374
+ "lstrip": true,
375
+ "normalized": false,
376
+ "rstrip": true,
377
+ "single_word": false,
378
+ "special": true
379
+ },
380
+ "100303": {
381
+ "content": "<|dummy_39|>",
382
+ "lstrip": true,
383
+ "normalized": false,
384
+ "rstrip": true,
385
+ "single_word": false,
386
+ "special": true
387
+ },
388
+ "100304": {
389
+ "content": "<|dummy_40|>",
390
+ "lstrip": true,
391
+ "normalized": false,
392
+ "rstrip": true,
393
+ "single_word": false,
394
+ "special": true
395
+ },
396
+ "100305": {
397
+ "content": "<|dummy_41|>",
398
+ "lstrip": true,
399
+ "normalized": false,
400
+ "rstrip": true,
401
+ "single_word": false,
402
+ "special": true
403
+ },
404
+ "100306": {
405
+ "content": "<|dummy_42|>",
406
+ "lstrip": true,
407
+ "normalized": false,
408
+ "rstrip": true,
409
+ "single_word": false,
410
+ "special": true
411
+ },
412
+ "100307": {
413
+ "content": "<|dummy_43|>",
414
+ "lstrip": true,
415
+ "normalized": false,
416
+ "rstrip": true,
417
+ "single_word": false,
418
+ "special": true
419
+ },
420
+ "100308": {
421
+ "content": "<|dummy_44|>",
422
+ "lstrip": true,
423
+ "normalized": false,
424
+ "rstrip": true,
425
+ "single_word": false,
426
+ "special": true
427
+ },
428
+ "100309": {
429
+ "content": "<|dummy_45|>",
430
+ "lstrip": true,
431
+ "normalized": false,
432
+ "rstrip": true,
433
+ "single_word": false,
434
+ "special": true
435
+ },
436
+ "100310": {
437
+ "content": "<|dummy_46|>",
438
+ "lstrip": true,
439
+ "normalized": false,
440
+ "rstrip": true,
441
+ "single_word": false,
442
+ "special": true
443
+ },
444
+ "100311": {
445
+ "content": "<|dummy_47|>",
446
+ "lstrip": true,
447
+ "normalized": false,
448
+ "rstrip": true,
449
+ "single_word": false,
450
+ "special": true
451
+ },
452
+ "100312": {
453
+ "content": "<|dummy_48|>",
454
+ "lstrip": true,
455
+ "normalized": false,
456
+ "rstrip": true,
457
+ "single_word": false,
458
+ "special": true
459
+ },
460
+ "100313": {
461
+ "content": "<|dummy_49|>",
462
+ "lstrip": true,
463
+ "normalized": false,
464
+ "rstrip": true,
465
+ "single_word": false,
466
+ "special": true
467
+ },
468
+ "100314": {
469
+ "content": "<|dummy_50|>",
470
+ "lstrip": true,
471
+ "normalized": false,
472
+ "rstrip": true,
473
+ "single_word": false,
474
+ "special": true
475
+ },
476
+ "100315": {
477
+ "content": "<|dummy_51|>",
478
+ "lstrip": true,
479
+ "normalized": false,
480
+ "rstrip": true,
481
+ "single_word": false,
482
+ "special": true
483
+ },
484
+ "100316": {
485
+ "content": "<|dummy_52|>",
486
+ "lstrip": true,
487
+ "normalized": false,
488
+ "rstrip": true,
489
+ "single_word": false,
490
+ "special": true
491
+ },
492
+ "100317": {
493
+ "content": "<|dummy_53|>",
494
+ "lstrip": true,
495
+ "normalized": false,
496
+ "rstrip": true,
497
+ "single_word": false,
498
+ "special": true
499
+ },
500
+ "100318": {
501
+ "content": "<|dummy_54|>",
502
+ "lstrip": true,
503
+ "normalized": false,
504
+ "rstrip": true,
505
+ "single_word": false,
506
+ "special": true
507
+ },
508
+ "100319": {
509
+ "content": "<|dummy_55|>",
510
+ "lstrip": true,
511
+ "normalized": false,
512
+ "rstrip": true,
513
+ "single_word": false,
514
+ "special": true
515
+ },
516
+ "100320": {
517
+ "content": "<|dummy_56|>",
518
+ "lstrip": true,
519
+ "normalized": false,
520
+ "rstrip": true,
521
+ "single_word": false,
522
+ "special": true
523
+ },
524
+ "100321": {
525
+ "content": "<|dummy_57|>",
526
+ "lstrip": true,
527
+ "normalized": false,
528
+ "rstrip": true,
529
+ "single_word": false,
530
+ "special": true
531
+ },
532
+ "100322": {
533
+ "content": "<|dummy_58|>",
534
+ "lstrip": true,
535
+ "normalized": false,
536
+ "rstrip": true,
537
+ "single_word": false,
538
+ "special": true
539
+ },
540
+ "100323": {
541
+ "content": "<|dummy_59|>",
542
+ "lstrip": true,
543
+ "normalized": false,
544
+ "rstrip": true,
545
+ "single_word": false,
546
+ "special": true
547
+ },
548
+ "100324": {
549
+ "content": "<|dummy_60|>",
550
+ "lstrip": true,
551
+ "normalized": false,
552
+ "rstrip": true,
553
+ "single_word": false,
554
+ "special": true
555
+ },
556
+ "100325": {
557
+ "content": "<|dummy_61|>",
558
+ "lstrip": true,
559
+ "normalized": false,
560
+ "rstrip": true,
561
+ "single_word": false,
562
+ "special": true
563
+ },
564
+ "100326": {
565
+ "content": "<|dummy_62|>",
566
+ "lstrip": true,
567
+ "normalized": false,
568
+ "rstrip": true,
569
+ "single_word": false,
570
+ "special": true
571
+ },
572
+ "100327": {
573
+ "content": "<|dummy_63|>",
574
+ "lstrip": true,
575
+ "normalized": false,
576
+ "rstrip": true,
577
+ "single_word": false,
578
+ "special": true
579
+ },
580
+ "100328": {
581
+ "content": "<|dummy_64|>",
582
+ "lstrip": true,
583
+ "normalized": false,
584
+ "rstrip": true,
585
+ "single_word": false,
586
+ "special": true
587
+ },
588
+ "100329": {
589
+ "content": "<|dummy_65|>",
590
+ "lstrip": true,
591
+ "normalized": false,
592
+ "rstrip": true,
593
+ "single_word": false,
594
+ "special": true
595
+ },
596
+ "100330": {
597
+ "content": "<|dummy_66|>",
598
+ "lstrip": true,
599
+ "normalized": false,
600
+ "rstrip": true,
601
+ "single_word": false,
602
+ "special": true
603
+ },
604
+ "100331": {
605
+ "content": "<|dummy_67|>",
606
+ "lstrip": true,
607
+ "normalized": false,
608
+ "rstrip": true,
609
+ "single_word": false,
610
+ "special": true
611
+ },
612
+ "100332": {
613
+ "content": "<|dummy_68|>",
614
+ "lstrip": true,
615
+ "normalized": false,
616
+ "rstrip": true,
617
+ "single_word": false,
618
+ "special": true
619
+ },
620
+ "100333": {
621
+ "content": "<|dummy_69|>",
622
+ "lstrip": true,
623
+ "normalized": false,
624
+ "rstrip": true,
625
+ "single_word": false,
626
+ "special": true
627
+ },
628
+ "100334": {
629
+ "content": "<|dummy_70|>",
630
+ "lstrip": true,
631
+ "normalized": false,
632
+ "rstrip": true,
633
+ "single_word": false,
634
+ "special": true
635
+ },
636
+ "100335": {
637
+ "content": "<|dummy_71|>",
638
+ "lstrip": true,
639
+ "normalized": false,
640
+ "rstrip": true,
641
+ "single_word": false,
642
+ "special": true
643
+ },
644
+ "100336": {
645
+ "content": "<|dummy_72|>",
646
+ "lstrip": true,
647
+ "normalized": false,
648
+ "rstrip": true,
649
+ "single_word": false,
650
+ "special": true
651
+ },
652
+ "100337": {
653
+ "content": "<|dummy_73|>",
654
+ "lstrip": true,
655
+ "normalized": false,
656
+ "rstrip": true,
657
+ "single_word": false,
658
+ "special": true
659
+ },
660
+ "100338": {
661
+ "content": "<|dummy_74|>",
662
+ "lstrip": true,
663
+ "normalized": false,
664
+ "rstrip": true,
665
+ "single_word": false,
666
+ "special": true
667
+ },
668
+ "100339": {
669
+ "content": "<|dummy_75|>",
670
+ "lstrip": true,
671
+ "normalized": false,
672
+ "rstrip": true,
673
+ "single_word": false,
674
+ "special": true
675
+ },
676
+ "100340": {
677
+ "content": "<|dummy_76|>",
678
+ "lstrip": true,
679
+ "normalized": false,
680
+ "rstrip": true,
681
+ "single_word": false,
682
+ "special": true
683
+ },
684
+ "100341": {
685
+ "content": "<|dummy_77|>",
686
+ "lstrip": true,
687
+ "normalized": false,
688
+ "rstrip": true,
689
+ "single_word": false,
690
+ "special": true
691
+ },
692
+ "100342": {
693
+ "content": "<|dummy_78|>",
694
+ "lstrip": true,
695
+ "normalized": false,
696
+ "rstrip": true,
697
+ "single_word": false,
698
+ "special": true
699
+ },
700
+ "100343": {
701
+ "content": "<|dummy_79|>",
702
+ "lstrip": true,
703
+ "normalized": false,
704
+ "rstrip": true,
705
+ "single_word": false,
706
+ "special": true
707
+ },
708
+ "100344": {
709
+ "content": "<|dummy_80|>",
710
+ "lstrip": true,
711
+ "normalized": false,
712
+ "rstrip": true,
713
+ "single_word": false,
714
+ "special": true
715
+ },
716
+ "100345": {
717
+ "content": "<|dummy_81|>",
718
+ "lstrip": true,
719
+ "normalized": false,
720
+ "rstrip": true,
721
+ "single_word": false,
722
+ "special": true
723
+ },
724
+ "100346": {
725
+ "content": "<|dummy_82|>",
726
+ "lstrip": true,
727
+ "normalized": false,
728
+ "rstrip": true,
729
+ "single_word": false,
730
+ "special": true
731
+ },
732
+ "100347": {
733
+ "content": "<|dummy_83|>",
734
+ "lstrip": true,
735
+ "normalized": false,
736
+ "rstrip": true,
737
+ "single_word": false,
738
+ "special": true
739
+ },
740
+ "100348": {
741
+ "content": "<|dummy_84|>",
742
+ "lstrip": true,
743
+ "normalized": false,
744
+ "rstrip": true,
745
+ "single_word": false,
746
+ "special": true
747
+ },
748
+ "100349": {
749
+ "content": "<|dummy_85|>",
750
+ "lstrip": true,
751
+ "normalized": false,
752
+ "rstrip": true,
753
+ "single_word": false,
754
+ "special": true
755
+ },
756
+ "100350": {
757
+ "content": "<think>",
758
+ "lstrip": true,
759
+ "normalized": false,
760
+ "rstrip": true,
761
+ "single_word": false,
762
+ "special": false
763
+ },
764
+ "100351": {
765
+ "content": "</think>",
766
+ "lstrip": true,
767
+ "normalized": false,
768
+ "rstrip": true,
769
+ "single_word": false,
770
+ "special": false
771
+ }
772
+ },
773
+ "bos_token": "<|endoftext|>",
774
+ "chat_template": "<|im_start|>system<|im_sep|>You are Phi, a language model trained by Microsoft to help users. Your role as an assistant involves thoroughly exploring questions through a systematic thinking process before providing the final precise and accurate solutions. This requires engaging in a comprehensive cycle of analysis, summarizing, exploration, reassessment, reflection, backtracing, and iteration to develop well-considered thinking process. Please structure your response into two main sections: Thought and Solution using the specified format: <think> {Thought section} </think> {Solution section}. In the Thought section, detail your reasoning process in steps. Each step should include detailed considerations such as analysing questions, summarizing relevant findings, brainstorming new ideas, verifying the accuracy of the current steps, refining any errors, and revisiting previous steps. In the Solution section, based on various attempts, explorations, and reflections from the Thought section, systematically present the final solution that you deem correct. The Solution section should be logical, accurate, and concise and detail necessary steps needed to reach the conclusion. Now, try to solve the following question through the above guidelines:<|im_end|>{% for message in messages %}{% if (message['role'] == 'user') %}{{'<|im_start|>user<|im_sep|>' + message['content'] + '<|im_end|>'}}{% elif (message['role'] == 'assistant') %}{{'<|im_start|>assistant<|im_sep|>'}}{% generation %}{{message['content'] + '<|im_end|>'}}{% endgeneration %}{% endif %}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant<|im_sep|>' }}{% endif %}",
775
+ "clean_up_tokenization_spaces": false,
776
+ "eos_token": "<|im_end|>",
777
+ "extra_special_tokens": {},
778
+ "model_max_length": 32768,
779
+ "pad_token": "<|dummy_85|>",
780
+ "padding_side": "left",
781
+ "tokenizer_class": "GPT2Tokenizer"
782
+ }
vocab.json ADDED
The diff for this file is too large to render. See raw diff