Upload 28 files
Browse files- CODE_OF_CONDUCT.md +9 -0
- LICENSE +22 -0
- README.md +218 -0
- SECURITY.md +41 -0
- added_tokens.json +13 -0
- auth.html +1510 -0
- chat.html +1177 -0
- config.json +32 -0
- data_summary_card.md +149 -0
- file_pipeline.py +397 -0
- generate.py +55 -0
- generation_config.json +11 -0
- load_model.py +48 -0
- model-00001-of-00006.safetensors +3 -0
- model-00002-of-00006.safetensors +3 -0
- model-00003-of-00006.safetensors +3 -0
- model-00004-of-00006.safetensors +3 -0
- model-00005-of-00006.safetensors +3 -0
- model-00006-of-00006.safetensors +3 -0
- model.safetensors.index.json +250 -0
- requirements.txt +21 -0
- run.py +221 -0
- sample_finetune.py +214 -0
- special_tokens_map.json +23 -0
- tokenizer.json +0 -0
- tokenizer.model +3 -0
- tokenizer_config.json +782 -0
- vocab.json +0 -0
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 = '×';
|
| 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
|
|
|