Bmccloud22 commited on
Commit
ec8f374
·
verified ·
1 Parent(s): 8bf49b9

Deploy LaunchLLM - Production AI Training Platform

Browse files
Files changed (47) hide show
  1. .gitignore +10 -0
  2. README.md +170 -7
  3. app.py +10 -0
  4. backend_config.py +190 -0
  5. data/sample_financial_advisor_data.json +502 -0
  6. data/training_data_20251102_184524.json +0 -0
  7. data/training_data_20251102_184527.json +0 -0
  8. data/training_data_20251102_184642.json +0 -0
  9. data/training_data_20251102_185655.json +0 -0
  10. data/training_data_20251102_190750.json +0 -0
  11. data/training_data_20251102_193209.json +0 -0
  12. data/training_data_20251102_215614.json +487 -0
  13. data/training_data_20251102_220943.json +487 -0
  14. data_aggregation/__init__.py +28 -0
  15. data_aggregation/collectors.py +183 -0
  16. data_aggregation/dataset_builder.py +143 -0
  17. data_aggregation/hf_dataset_loader.py +528 -0
  18. data_aggregation/hf_dataset_loader.py.tmp.15260.1762137822282 +0 -0
  19. data_aggregation/preprocessors.py +98 -0
  20. data_aggregation/quality_validator.py +234 -0
  21. data_aggregation/synthetic_generator.py +234 -0
  22. data_aggregation/tool_use_generator.py +193 -0
  23. domain_templates/__init__.py +141 -0
  24. domain_templates/base_domain.py +93 -0
  25. domain_templates/custom_domain.py +156 -0
  26. domain_templates/education_tutor.py +221 -0
  27. domain_templates/financial_advisor.py +178 -0
  28. domain_templates/legal_advisor.py +181 -0
  29. domain_templates/medical_assistant.py +178 -0
  30. evaluation/__init__.py +43 -0
  31. evaluation/benchmark.py +368 -0
  32. evaluation/benchmark_builder.py +505 -0
  33. evaluation/certification_tests.py +500 -0
  34. evaluation/evaluator.py +347 -0
  35. evaluation/gap_analyzer.py +404 -0
  36. evaluation/intelligent_benchmark_scraper.py +474 -0
  37. evaluation/metrics.py +267 -0
  38. evaluation/training_recommender.py +505 -0
  39. financial_advisor_gui.py +2418 -0
  40. fine_tuning/__init__.py +12 -0
  41. fine_tuning/lora_trainer.py +487 -0
  42. fine_tuning/peft_trainer.py +149 -0
  43. fine_tuning/training_loop.py +274 -0
  44. model_registry.py +205 -0
  45. ollama_integration.py +145 -0
  46. requirements.txt +38 -0
  47. secure_config.py +137 -0
.gitignore ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.log
4
+ .secrets/
5
+ .gradio/
6
+ *.bin
7
+ *.safetensors
8
+ models/*
9
+ checkpoints/
10
+ wandb/
README.md CHANGED
@@ -1,14 +1,177 @@
1
  ---
2
- title: LaunchLLM
3
- emoji: 👀
4
- colorFrom: gray
5
- colorTo: red
6
  sdk: gradio
7
- sdk_version: 5.49.1
8
  app_file: app.py
9
  pinned: false
10
  license: apache-2.0
11
- short_description: No-Code Tool access OpenWeight Models, Fine-Tune, test LLM's
12
  ---
13
 
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: LaunchLLM - AI Training Lab
3
+ emoji: 🚀
4
+ colorFrom: blue
5
+ colorTo: purple
6
  sdk: gradio
7
+ sdk_version: 4.0.0
8
  app_file: app.py
9
  pinned: false
10
  license: apache-2.0
 
11
  ---
12
 
13
+ # 🚀 LaunchLLM - AURA AI Training Lab
14
+
15
+ **Professional LLM Fine-Tuning Platform for Domain Experts**
16
+
17
+ Train custom AI models for financial advisory, medical assistance, legal consultation, and more - **no coding required**.
18
+
19
+ ## 🎯 What This Does
20
+
21
+ LaunchLLM is a production-ready platform that allows you to:
22
+
23
+ - **Train Custom AI Models** - Fine-tune models like Llama, Qwen, Mistral for your specific domain
24
+ - **Generate Training Data** - AI-powered synthetic data generation using GPT-4 or Claude
25
+ - **Evaluate Performance** - Run certification exams (CFP, CFA, CPA) and custom benchmarks
26
+ - **Deploy to Production** - Cloud GPU integration and model deployment tools
27
+
28
+ ## 💡 Perfect For
29
+
30
+ - **Financial Advisors** - Train AI on CFP, CFA, tax strategy
31
+ - **Medical Professionals** - Create HIPAA-compliant medical assistants
32
+ - **Legal Firms** - Build legal research and consultation tools
33
+ - **Educational Institutions** - Develop subject-specific tutoring systems
34
+ - **Enterprises** - Custom AI for internal knowledge bases
35
+
36
+ ## 🚀 How to Use This Demo
37
+
38
+ ### 1. Configure Environment
39
+ - Navigate to **Environment** tab
40
+ - Add your HuggingFace token (get from: https://huggingface.co/settings/tokens)
41
+ - Optional: Add OpenAI or Anthropic key for synthetic data generation
42
+
43
+ ### 2. Prepare Training Data
44
+ - **Option A**: Generate synthetic data with AI
45
+ - **Option B**: Upload your own JSON data
46
+ - **Option C**: Import from Hugging Face datasets
47
+
48
+ ### 3. Train Your Model
49
+ - Select a model (e.g., Qwen 2.5 7B)
50
+ - Configure training parameters
51
+ - Click "Start Training"
52
+
53
+ ### 4. Test & Evaluate
54
+ - Chat with your trained model
55
+ - Run certification benchmarks
56
+ - Analyze knowledge gaps
57
+
58
+ ## 🏆 Key Features
59
+
60
+ ### No-Code Interface
61
+ - Gradio-based web GUI - zero programming required
62
+ - Real-time training progress monitoring
63
+ - Interactive model testing
64
+
65
+ ### Efficient Training
66
+ - LoRA (Low-Rank Adaptation) - train only 1-3% of parameters
67
+ - 4-bit quantization - run on consumer GPUs
68
+ - Cloud GPU integration (RunPod) for heavy workloads
69
+
70
+ ### Production-Ready
71
+ - Secure API key encryption
72
+ - Model versioning and registry
73
+ - Comprehensive evaluation metrics
74
+ - Knowledge gap analysis with AI recommendations
75
+
76
+ ### Multiple Domains
77
+ - Financial Advisory (CFP, CFA, tax strategy)
78
+ - Medical Assistant (diagnosis, treatment protocols)
79
+ - Legal Advisor (contract law, compliance)
80
+ - Education Tutor (subject-specific tutoring)
81
+ - Custom domains - build your own!
82
+
83
+ ## 📊 Technical Specs
84
+
85
+ - **Framework**: PyTorch, Hugging Face Transformers, PEFT
86
+ - **Training Method**: LoRA (Low-Rank Adaptation)
87
+ - **Supported Models**: Qwen, Llama, Mistral, Phi, Gemma, Mixtral
88
+ - **GPU Support**: CUDA-enabled GPUs, CPU fallback
89
+ - **Quantization**: 4-bit/8-bit for efficient training
90
+
91
+ ## 🔐 Security & Compliance
92
+
93
+ - **Encrypted API Keys** - Fernet encryption at rest
94
+ - **No Data Logging** - Your training data stays private
95
+ - **Git-Ignored Secrets** - Credentials never committed
96
+ - **HIPAA-Ready** - Suitable for healthcare applications
97
+ - **SOC 2 Compatible** - Enterprise security standards
98
+
99
+ ## 💰 Cost Efficiency
100
+
101
+ ### This Demo (Free!)
102
+ - Hugging Face Spaces provides free hosting
103
+ - Upgrade to GPU ($0.60/hour) only when training
104
+
105
+ ### Production Deployment
106
+ - **Local GPU**: One-time hardware cost
107
+ - **RunPod Cloud**: $0.44-$1.39/hour (only pay while training)
108
+ - **Model Training**: 1-4 hours for most use cases
109
+ - **Total Cost**: ~$2-10 per trained model
110
+
111
+ ## 📈 Use Cases & ROI
112
+
113
+ ### Financial Advisory Firm
114
+ - **Investment**: 10 hours training custom CFP model
115
+ - **Cost**: ~$15 (RunPod GPU)
116
+ - **Output**: AI advisor passing 85%+ on CFP exam
117
+ - **ROI**: Automate 60% of routine client questions
118
+
119
+ ### Medical Practice
120
+ - **Investment**: Custom medical Q&A model
121
+ - **Cost**: ~$20 (training + data generation)
122
+ - **Output**: HIPAA-compliant medical assistant
123
+ - **ROI**: Reduce administrative workload by 40%
124
+
125
+ ### Law Firm
126
+ - **Investment**: Legal research and contract review AI
127
+ - **Cost**: ~$25 (larger model for complex reasoning)
128
+ - **Output**: AI passing 75%+ on mock bar exam
129
+ - **ROI**: 10x faster document review
130
+
131
+ ## 🎓 Getting Started
132
+
133
+ ### For This Demo
134
+ 1. Click on the **Environment** tab above
135
+ 2. Add your HuggingFace token (required for model downloads)
136
+ 3. Navigate to **Training Data** to generate or upload data
137
+ 4. Go to **Training** tab and click "Start Training"
138
+
139
+ ### For Production Deployment
140
+ - **GitHub**: https://github.com/brennanmccloud/LaunchLLM
141
+ - **Documentation**: See CLAUDE.md in repo
142
+ - **Deploy Your Own**:
143
+ - Railway (one-click): https://railway.app
144
+ - HF Spaces (like this!): https://huggingface.co/spaces
145
+ - Local: `git clone && pip install && python financial_advisor_gui.py`
146
+
147
+ ## 🛠️ Tech Stack
148
+
149
+ - **Training**: PyTorch, Transformers, PEFT, bitsandbytes
150
+ - **Interface**: Gradio 4.0+
151
+ - **Data**: Synthetic generation via OpenAI/Anthropic APIs
152
+ - **Evaluation**: BLEU, ROUGE-L, custom metrics
153
+ - **Cloud**: RunPod integration for GPU training
154
+ - **Security**: Cryptography (Fernet), secure config management
155
+
156
+ ## 📞 Support & Resources
157
+
158
+ - **GitHub**: [brennanmccloud/LaunchLLM](https://github.com/brennanmccloud/LaunchLLM)
159
+ - **Documentation**: Comprehensive guides in repo
160
+ - **Issues**: Report bugs on GitHub Issues
161
+ - **Discussions**: GitHub Discussions for Q&A
162
+
163
+ ## 📄 License
164
+
165
+ Apache 2.0 - Free for commercial use
166
+
167
+ ---
168
+
169
+ ## 🚀 Ready to Build Your Custom AI?
170
+
171
+ Start by clicking the **Environment** tab above and adding your HuggingFace token!
172
+
173
+ **Questions?** Check the Help tab in the interface or visit our GitHub repository.
174
+
175
+ ---
176
+
177
+ **Built with ❤️ for domain experts who want custom AI without the complexity**
app.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Hugging Face Spaces deployment entry point for AURA AI Training Lab
3
+ This file is automatically used by HF Spaces to launch the application.
4
+ """
5
+
6
+ # Import and launch the main GUI
7
+ import financial_advisor_gui
8
+
9
+ # The demo.launch() call at the bottom of financial_advisor_gui.py
10
+ # will automatically run when this module is imported
backend_config.py ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Backend API Key Configuration for AURA Platform
3
+
4
+ This file stores YOUR (the platform owner's) API keys that are used
5
+ to provide managed services to end users.
6
+
7
+ Users do NOT need to provide their own API keys - everything is billed
8
+ through their AURA wallet.
9
+
10
+ SECURITY: Keep this file secure! Never commit to public repos.
11
+ Add to .gitignore immediately.
12
+ """
13
+
14
+ import os
15
+ from pathlib import Path
16
+ from dotenv import load_dotenv
17
+
18
+ # Load environment variables from .env or .env.backend
19
+ # Try .env.backend first (cleaner separation), then fall back to .env
20
+ backend_env_path = Path(".env.backend")
21
+ main_env_path = Path(".env")
22
+
23
+ if backend_env_path.exists():
24
+ try:
25
+ load_dotenv(backend_env_path)
26
+ except:
27
+ pass # If .env.backend fails, try .env
28
+
29
+ if main_env_path.exists():
30
+ try:
31
+ load_dotenv(main_env_path)
32
+ except:
33
+ pass # If .env fails, rely on system environment variables
34
+
35
+ # ============================================================================
36
+ # AURA PLATFORM API KEYS (Your Keys - Not User Keys)
37
+ # ============================================================================
38
+
39
+ class BackendConfig:
40
+ """
41
+ Platform-level API keys for managed services.
42
+ These are YOUR keys that power the platform.
43
+ """
44
+
45
+ # HuggingFace - For model downloads (FREE account)
46
+ # Get at: https://huggingface.co/settings/tokens
47
+ # Check both AURA_HUGGINGFACE_TOKEN and fallback to user's HUGGINGFACE_TOKEN
48
+ AURA_HUGGINGFACE_TOKEN = os.getenv("AURA_HUGGINGFACE_TOKEN") or os.getenv("HUGGINGFACE_TOKEN") or os.getenv("HF_TOKEN") or ""
49
+
50
+ # OpenAI - For synthetic data generation (PAID account)
51
+ # Get at: https://platform.openai.com/api-keys
52
+ # Check both AURA_OPENAI_API_KEY and fallback to user's OPENAI_API_KEY
53
+ AURA_OPENAI_API_KEY = os.getenv("AURA_OPENAI_API_KEY") or os.getenv("OPENAI_API_KEY") or ""
54
+
55
+ # Anthropic - Alternative for synthetic data (PAID account)
56
+ # Get at: https://console.anthropic.com/settings/keys
57
+ # Check both AURA_ANTHROPIC_API_KEY and fallback to user's ANTHROPIC_API_KEY
58
+ AURA_ANTHROPIC_API_KEY = os.getenv("AURA_ANTHROPIC_API_KEY") or os.getenv("ANTHROPIC_API_KEY") or ""
59
+
60
+ # RunPod - For cloud GPU infrastructure (PAID account)
61
+ # Get at: https://www.runpod.io/console/user/settings
62
+ # Check both AURA_RUNPOD_API_KEY and fallback to user's RUNPOD_API_KEY
63
+ AURA_RUNPOD_API_KEY = os.getenv("AURA_RUNPOD_API_KEY") or os.getenv("RUNPOD_API_KEY") or ""
64
+
65
+ # WandB - For experiment tracking (PAID team plan)
66
+ # Get at: https://wandb.ai/authorize
67
+ AURA_WANDB_API_KEY = os.getenv("AURA_WANDB_API_KEY", "")
68
+
69
+ # ========================================================================
70
+ # PRICING CONFIGURATION (What You Charge Users)
71
+ # ========================================================================
72
+
73
+ # Synthetic Data Pricing (per example generated)
74
+ SYNTHETIC_DATA_COST_PER_EXAMPLE = 0.10 # $0.10 per example
75
+
76
+ # Your actual costs (for margin calculation)
77
+ # Based on typical usage: ~1500 tokens per example
78
+ SYNTHETIC_DATA_ACTUAL_COST_GPT4 = 0.045 # GPT-4: ~$0.045/example
79
+ SYNTHETIC_DATA_ACTUAL_COST_GPT35 = 0.003 # GPT-3.5: ~$0.003/example
80
+ SYNTHETIC_DATA_ACTUAL_COST_CLAUDE = 0.024 # Claude Sonnet: ~$0.024/example
81
+
82
+ # Default provider for synthetic data
83
+ DEFAULT_SYNTHETIC_PROVIDER = "openai" # or "anthropic"
84
+ DEFAULT_SYNTHETIC_MODEL = "gpt-4" # or "gpt-3.5-turbo" for lower cost
85
+
86
+ # ========================================================================
87
+ # VALIDATION
88
+ # ========================================================================
89
+
90
+ @classmethod
91
+ def validate_keys(cls):
92
+ """Check which API keys are configured"""
93
+ status = {
94
+ "huggingface": bool(cls.AURA_HUGGINGFACE_TOKEN),
95
+ "openai": bool(cls.AURA_OPENAI_API_KEY),
96
+ "anthropic": bool(cls.AURA_ANTHROPIC_API_KEY),
97
+ "runpod": bool(cls.AURA_RUNPOD_API_KEY),
98
+ "wandb": bool(cls.AURA_WANDB_API_KEY)
99
+ }
100
+ return status
101
+
102
+ @classmethod
103
+ def get_missing_keys(cls):
104
+ """Return list of missing API keys"""
105
+ status = cls.validate_keys()
106
+ return [key for key, configured in status.items() if not configured]
107
+
108
+ @classmethod
109
+ def is_ready_for_production(cls):
110
+ """Check if all critical keys are configured"""
111
+ critical_keys = ["huggingface", "openai", "runpod"]
112
+ status = cls.validate_keys()
113
+ return all(status[key] for key in critical_keys)
114
+
115
+ @classmethod
116
+ def get_status_report(cls):
117
+ """Generate configuration status report"""
118
+ status = cls.validate_keys()
119
+ missing = cls.get_missing_keys()
120
+
121
+ report = "AURA Backend API Key Status\n\n"
122
+
123
+ for service, configured in status.items():
124
+ icon = "[OK]" if configured else "[MISS]"
125
+ report += f"{icon} {service.title()}: {'Configured' if configured else 'Missing'}\n"
126
+
127
+ report += f"\nReady for Production: {'Yes' if cls.is_ready_for_production() else 'No'}\n"
128
+
129
+ if missing:
130
+ report += f"\nMissing Keys: {', '.join(missing)}\n"
131
+ report += "\nAction Required:\n"
132
+ report += "1. Add missing keys to .env file\n"
133
+ report += "2. Restart the application\n"
134
+
135
+ return report
136
+
137
+
138
+ # ============================================================================
139
+ # HELPER FUNCTIONS
140
+ # ============================================================================
141
+
142
+ def get_backend_config():
143
+ """Get backend configuration instance"""
144
+ return BackendConfig()
145
+
146
+ def check_backend_status():
147
+ """Print backend configuration status"""
148
+ print(BackendConfig.get_status_report())
149
+
150
+ # ============================================================================
151
+ # USAGE INSTRUCTIONS
152
+ # ============================================================================
153
+
154
+ """
155
+ SETUP INSTRUCTIONS:
156
+
157
+ 1. Create a .env file in the project root:
158
+
159
+ AURA_HUGGINGFACE_TOKEN=hf_xxxxxxxxxxxx
160
+ AURA_OPENAI_API_KEY=sk-xxxxxxxxxxxx
161
+ AURA_ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxx
162
+ AURA_RUNPOD_API_KEY=xxxxxxxxxxxx
163
+ AURA_WANDB_API_KEY=xxxxxxxxxxxx
164
+
165
+ 2. Add .env to .gitignore:
166
+
167
+ echo ".env" >> .gitignore
168
+
169
+ 3. Verify configuration:
170
+
171
+ python backend_config.py
172
+
173
+ 4. Use in your code:
174
+
175
+ from backend_config import BackendConfig
176
+
177
+ # Use AURA's OpenAI key for synthetic data
178
+ client = OpenAI(api_key=BackendConfig.AURA_OPENAI_API_KEY)
179
+
180
+ SECURITY NOTES:
181
+ - Never hardcode API keys in this file
182
+ - Always use environment variables
183
+ - Keep .env file secure and never commit it
184
+ - Rotate keys regularly
185
+ - Use read-only tokens where possible (e.g., HuggingFace)
186
+ """
187
+
188
+ if __name__ == "__main__":
189
+ # Test configuration
190
+ check_backend_status()
data/sample_financial_advisor_data.json ADDED
@@ -0,0 +1,502 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "instruction": "HF dataset question from fingpt-finred #1",
4
+ "input": "",
5
+ "output": "HF dataset answer #1"
6
+ },
7
+ {
8
+ "instruction": "HF dataset question from fingpt-finred #2",
9
+ "input": "",
10
+ "output": "HF dataset answer #2"
11
+ },
12
+ {
13
+ "instruction": "HF dataset question from fingpt-finred #3",
14
+ "input": "",
15
+ "output": "HF dataset answer #3"
16
+ },
17
+ {
18
+ "instruction": "HF dataset question from fingpt-finred #4",
19
+ "input": "",
20
+ "output": "HF dataset answer #4"
21
+ },
22
+ {
23
+ "instruction": "HF dataset question from fingpt-finred #5",
24
+ "input": "",
25
+ "output": "HF dataset answer #5"
26
+ },
27
+ {
28
+ "instruction": "HF dataset question from fingpt-finred #6",
29
+ "input": "",
30
+ "output": "HF dataset answer #6"
31
+ },
32
+ {
33
+ "instruction": "HF dataset question from fingpt-finred #7",
34
+ "input": "",
35
+ "output": "HF dataset answer #7"
36
+ },
37
+ {
38
+ "instruction": "HF dataset question from fingpt-finred #8",
39
+ "input": "",
40
+ "output": "HF dataset answer #8"
41
+ },
42
+ {
43
+ "instruction": "HF dataset question from fingpt-finred #9",
44
+ "input": "",
45
+ "output": "HF dataset answer #9"
46
+ },
47
+ {
48
+ "instruction": "HF dataset question from fingpt-finred #10",
49
+ "input": "",
50
+ "output": "HF dataset answer #10"
51
+ },
52
+ {
53
+ "instruction": "HF dataset question from fingpt-finred #11",
54
+ "input": "",
55
+ "output": "HF dataset answer #11"
56
+ },
57
+ {
58
+ "instruction": "HF dataset question from fingpt-finred #12",
59
+ "input": "",
60
+ "output": "HF dataset answer #12"
61
+ },
62
+ {
63
+ "instruction": "HF dataset question from fingpt-finred #13",
64
+ "input": "",
65
+ "output": "HF dataset answer #13"
66
+ },
67
+ {
68
+ "instruction": "HF dataset question from fingpt-finred #14",
69
+ "input": "",
70
+ "output": "HF dataset answer #14"
71
+ },
72
+ {
73
+ "instruction": "HF dataset question from fingpt-finred #15",
74
+ "input": "",
75
+ "output": "HF dataset answer #15"
76
+ },
77
+ {
78
+ "instruction": "HF dataset question from fingpt-finred #16",
79
+ "input": "",
80
+ "output": "HF dataset answer #16"
81
+ },
82
+ {
83
+ "instruction": "HF dataset question from fingpt-finred #17",
84
+ "input": "",
85
+ "output": "HF dataset answer #17"
86
+ },
87
+ {
88
+ "instruction": "HF dataset question from fingpt-finred #18",
89
+ "input": "",
90
+ "output": "HF dataset answer #18"
91
+ },
92
+ {
93
+ "instruction": "HF dataset question from fingpt-finred #19",
94
+ "input": "",
95
+ "output": "HF dataset answer #19"
96
+ },
97
+ {
98
+ "instruction": "HF dataset question from fingpt-finred #20",
99
+ "input": "",
100
+ "output": "HF dataset answer #20"
101
+ },
102
+ {
103
+ "instruction": "HF dataset question from fingpt-finred #21",
104
+ "input": "",
105
+ "output": "HF dataset answer #21"
106
+ },
107
+ {
108
+ "instruction": "HF dataset question from fingpt-finred #22",
109
+ "input": "",
110
+ "output": "HF dataset answer #22"
111
+ },
112
+ {
113
+ "instruction": "HF dataset question from fingpt-finred #23",
114
+ "input": "",
115
+ "output": "HF dataset answer #23"
116
+ },
117
+ {
118
+ "instruction": "HF dataset question from fingpt-finred #24",
119
+ "input": "",
120
+ "output": "HF dataset answer #24"
121
+ },
122
+ {
123
+ "instruction": "HF dataset question from fingpt-finred #25",
124
+ "input": "",
125
+ "output": "HF dataset answer #25"
126
+ },
127
+ {
128
+ "instruction": "HF dataset question from fingpt-finred #26",
129
+ "input": "",
130
+ "output": "HF dataset answer #26"
131
+ },
132
+ {
133
+ "instruction": "HF dataset question from fingpt-finred #27",
134
+ "input": "",
135
+ "output": "HF dataset answer #27"
136
+ },
137
+ {
138
+ "instruction": "HF dataset question from fingpt-finred #28",
139
+ "input": "",
140
+ "output": "HF dataset answer #28"
141
+ },
142
+ {
143
+ "instruction": "HF dataset question from fingpt-finred #29",
144
+ "input": "",
145
+ "output": "HF dataset answer #29"
146
+ },
147
+ {
148
+ "instruction": "HF dataset question from fingpt-finred #30",
149
+ "input": "",
150
+ "output": "HF dataset answer #30"
151
+ },
152
+ {
153
+ "instruction": "HF dataset question from fingpt-finred #31",
154
+ "input": "",
155
+ "output": "HF dataset answer #31"
156
+ },
157
+ {
158
+ "instruction": "HF dataset question from fingpt-finred #32",
159
+ "input": "",
160
+ "output": "HF dataset answer #32"
161
+ },
162
+ {
163
+ "instruction": "HF dataset question from fingpt-finred #33",
164
+ "input": "",
165
+ "output": "HF dataset answer #33"
166
+ },
167
+ {
168
+ "instruction": "HF dataset question from fingpt-finred #34",
169
+ "input": "",
170
+ "output": "HF dataset answer #34"
171
+ },
172
+ {
173
+ "instruction": "HF dataset question from fingpt-finred #35",
174
+ "input": "",
175
+ "output": "HF dataset answer #35"
176
+ },
177
+ {
178
+ "instruction": "HF dataset question from fingpt-finred #36",
179
+ "input": "",
180
+ "output": "HF dataset answer #36"
181
+ },
182
+ {
183
+ "instruction": "HF dataset question from fingpt-finred #37",
184
+ "input": "",
185
+ "output": "HF dataset answer #37"
186
+ },
187
+ {
188
+ "instruction": "HF dataset question from fingpt-finred #38",
189
+ "input": "",
190
+ "output": "HF dataset answer #38"
191
+ },
192
+ {
193
+ "instruction": "HF dataset question from fingpt-finred #39",
194
+ "input": "",
195
+ "output": "HF dataset answer #39"
196
+ },
197
+ {
198
+ "instruction": "HF dataset question from fingpt-finred #40",
199
+ "input": "",
200
+ "output": "HF dataset answer #40"
201
+ },
202
+ {
203
+ "instruction": "HF dataset question from fingpt-finred #41",
204
+ "input": "",
205
+ "output": "HF dataset answer #41"
206
+ },
207
+ {
208
+ "instruction": "HF dataset question from fingpt-finred #42",
209
+ "input": "",
210
+ "output": "HF dataset answer #42"
211
+ },
212
+ {
213
+ "instruction": "HF dataset question from fingpt-finred #43",
214
+ "input": "",
215
+ "output": "HF dataset answer #43"
216
+ },
217
+ {
218
+ "instruction": "HF dataset question from fingpt-finred #44",
219
+ "input": "",
220
+ "output": "HF dataset answer #44"
221
+ },
222
+ {
223
+ "instruction": "HF dataset question from fingpt-finred #45",
224
+ "input": "",
225
+ "output": "HF dataset answer #45"
226
+ },
227
+ {
228
+ "instruction": "HF dataset question from fingpt-finred #46",
229
+ "input": "",
230
+ "output": "HF dataset answer #46"
231
+ },
232
+ {
233
+ "instruction": "HF dataset question from fingpt-finred #47",
234
+ "input": "",
235
+ "output": "HF dataset answer #47"
236
+ },
237
+ {
238
+ "instruction": "HF dataset question from fingpt-finred #48",
239
+ "input": "",
240
+ "output": "HF dataset answer #48"
241
+ },
242
+ {
243
+ "instruction": "HF dataset question from fingpt-finred #49",
244
+ "input": "",
245
+ "output": "HF dataset answer #49"
246
+ },
247
+ {
248
+ "instruction": "HF dataset question from fingpt-finred #50",
249
+ "input": "",
250
+ "output": "HF dataset answer #50"
251
+ },
252
+ {
253
+ "instruction": "HF dataset question from fingpt-finred #51",
254
+ "input": "",
255
+ "output": "HF dataset answer #51"
256
+ },
257
+ {
258
+ "instruction": "HF dataset question from fingpt-finred #52",
259
+ "input": "",
260
+ "output": "HF dataset answer #52"
261
+ },
262
+ {
263
+ "instruction": "HF dataset question from fingpt-finred #53",
264
+ "input": "",
265
+ "output": "HF dataset answer #53"
266
+ },
267
+ {
268
+ "instruction": "HF dataset question from fingpt-finred #54",
269
+ "input": "",
270
+ "output": "HF dataset answer #54"
271
+ },
272
+ {
273
+ "instruction": "HF dataset question from fingpt-finred #55",
274
+ "input": "",
275
+ "output": "HF dataset answer #55"
276
+ },
277
+ {
278
+ "instruction": "HF dataset question from fingpt-finred #56",
279
+ "input": "",
280
+ "output": "HF dataset answer #56"
281
+ },
282
+ {
283
+ "instruction": "HF dataset question from fingpt-finred #57",
284
+ "input": "",
285
+ "output": "HF dataset answer #57"
286
+ },
287
+ {
288
+ "instruction": "HF dataset question from fingpt-finred #58",
289
+ "input": "",
290
+ "output": "HF dataset answer #58"
291
+ },
292
+ {
293
+ "instruction": "HF dataset question from fingpt-finred #59",
294
+ "input": "",
295
+ "output": "HF dataset answer #59"
296
+ },
297
+ {
298
+ "instruction": "HF dataset question from fingpt-finred #60",
299
+ "input": "",
300
+ "output": "HF dataset answer #60"
301
+ },
302
+ {
303
+ "instruction": "HF dataset question from fingpt-finred #61",
304
+ "input": "",
305
+ "output": "HF dataset answer #61"
306
+ },
307
+ {
308
+ "instruction": "HF dataset question from fingpt-finred #62",
309
+ "input": "",
310
+ "output": "HF dataset answer #62"
311
+ },
312
+ {
313
+ "instruction": "HF dataset question from fingpt-finred #63",
314
+ "input": "",
315
+ "output": "HF dataset answer #63"
316
+ },
317
+ {
318
+ "instruction": "HF dataset question from fingpt-finred #64",
319
+ "input": "",
320
+ "output": "HF dataset answer #64"
321
+ },
322
+ {
323
+ "instruction": "HF dataset question from fingpt-finred #65",
324
+ "input": "",
325
+ "output": "HF dataset answer #65"
326
+ },
327
+ {
328
+ "instruction": "HF dataset question from fingpt-finred #66",
329
+ "input": "",
330
+ "output": "HF dataset answer #66"
331
+ },
332
+ {
333
+ "instruction": "HF dataset question from fingpt-finred #67",
334
+ "input": "",
335
+ "output": "HF dataset answer #67"
336
+ },
337
+ {
338
+ "instruction": "HF dataset question from fingpt-finred #68",
339
+ "input": "",
340
+ "output": "HF dataset answer #68"
341
+ },
342
+ {
343
+ "instruction": "HF dataset question from fingpt-finred #69",
344
+ "input": "",
345
+ "output": "HF dataset answer #69"
346
+ },
347
+ {
348
+ "instruction": "HF dataset question from fingpt-finred #70",
349
+ "input": "",
350
+ "output": "HF dataset answer #70"
351
+ },
352
+ {
353
+ "instruction": "HF dataset question from fingpt-finred #71",
354
+ "input": "",
355
+ "output": "HF dataset answer #71"
356
+ },
357
+ {
358
+ "instruction": "HF dataset question from fingpt-finred #72",
359
+ "input": "",
360
+ "output": "HF dataset answer #72"
361
+ },
362
+ {
363
+ "instruction": "HF dataset question from fingpt-finred #73",
364
+ "input": "",
365
+ "output": "HF dataset answer #73"
366
+ },
367
+ {
368
+ "instruction": "HF dataset question from fingpt-finred #74",
369
+ "input": "",
370
+ "output": "HF dataset answer #74"
371
+ },
372
+ {
373
+ "instruction": "HF dataset question from fingpt-finred #75",
374
+ "input": "",
375
+ "output": "HF dataset answer #75"
376
+ },
377
+ {
378
+ "instruction": "HF dataset question from fingpt-finred #76",
379
+ "input": "",
380
+ "output": "HF dataset answer #76"
381
+ },
382
+ {
383
+ "instruction": "HF dataset question from fingpt-finred #77",
384
+ "input": "",
385
+ "output": "HF dataset answer #77"
386
+ },
387
+ {
388
+ "instruction": "HF dataset question from fingpt-finred #78",
389
+ "input": "",
390
+ "output": "HF dataset answer #78"
391
+ },
392
+ {
393
+ "instruction": "HF dataset question from fingpt-finred #79",
394
+ "input": "",
395
+ "output": "HF dataset answer #79"
396
+ },
397
+ {
398
+ "instruction": "HF dataset question from fingpt-finred #80",
399
+ "input": "",
400
+ "output": "HF dataset answer #80"
401
+ },
402
+ {
403
+ "instruction": "HF dataset question from fingpt-finred #81",
404
+ "input": "",
405
+ "output": "HF dataset answer #81"
406
+ },
407
+ {
408
+ "instruction": "HF dataset question from fingpt-finred #82",
409
+ "input": "",
410
+ "output": "HF dataset answer #82"
411
+ },
412
+ {
413
+ "instruction": "HF dataset question from fingpt-finred #83",
414
+ "input": "",
415
+ "output": "HF dataset answer #83"
416
+ },
417
+ {
418
+ "instruction": "HF dataset question from fingpt-finred #84",
419
+ "input": "",
420
+ "output": "HF dataset answer #84"
421
+ },
422
+ {
423
+ "instruction": "HF dataset question from fingpt-finred #85",
424
+ "input": "",
425
+ "output": "HF dataset answer #85"
426
+ },
427
+ {
428
+ "instruction": "HF dataset question from fingpt-finred #86",
429
+ "input": "",
430
+ "output": "HF dataset answer #86"
431
+ },
432
+ {
433
+ "instruction": "HF dataset question from fingpt-finred #87",
434
+ "input": "",
435
+ "output": "HF dataset answer #87"
436
+ },
437
+ {
438
+ "instruction": "HF dataset question from fingpt-finred #88",
439
+ "input": "",
440
+ "output": "HF dataset answer #88"
441
+ },
442
+ {
443
+ "instruction": "HF dataset question from fingpt-finred #89",
444
+ "input": "",
445
+ "output": "HF dataset answer #89"
446
+ },
447
+ {
448
+ "instruction": "HF dataset question from fingpt-finred #90",
449
+ "input": "",
450
+ "output": "HF dataset answer #90"
451
+ },
452
+ {
453
+ "instruction": "HF dataset question from fingpt-finred #91",
454
+ "input": "",
455
+ "output": "HF dataset answer #91"
456
+ },
457
+ {
458
+ "instruction": "HF dataset question from fingpt-finred #92",
459
+ "input": "",
460
+ "output": "HF dataset answer #92"
461
+ },
462
+ {
463
+ "instruction": "HF dataset question from fingpt-finred #93",
464
+ "input": "",
465
+ "output": "HF dataset answer #93"
466
+ },
467
+ {
468
+ "instruction": "HF dataset question from fingpt-finred #94",
469
+ "input": "",
470
+ "output": "HF dataset answer #94"
471
+ },
472
+ {
473
+ "instruction": "HF dataset question from fingpt-finred #95",
474
+ "input": "",
475
+ "output": "HF dataset answer #95"
476
+ },
477
+ {
478
+ "instruction": "HF dataset question from fingpt-finred #96",
479
+ "input": "",
480
+ "output": "HF dataset answer #96"
481
+ },
482
+ {
483
+ "instruction": "HF dataset question from fingpt-finred #97",
484
+ "input": "",
485
+ "output": "HF dataset answer #97"
486
+ },
487
+ {
488
+ "instruction": "HF dataset question from fingpt-finred #98",
489
+ "input": "",
490
+ "output": "HF dataset answer #98"
491
+ },
492
+ {
493
+ "instruction": "HF dataset question from fingpt-finred #99",
494
+ "input": "",
495
+ "output": "HF dataset answer #99"
496
+ },
497
+ {
498
+ "instruction": "HF dataset question from fingpt-finred #100",
499
+ "input": "",
500
+ "output": "HF dataset answer #100"
501
+ }
502
+ ]
data/training_data_20251102_184524.json ADDED
The diff for this file is too large to render. See raw diff
 
data/training_data_20251102_184527.json ADDED
The diff for this file is too large to render. See raw diff
 
data/training_data_20251102_184642.json ADDED
The diff for this file is too large to render. See raw diff
 
data/training_data_20251102_185655.json ADDED
The diff for this file is too large to render. See raw diff
 
data/training_data_20251102_190750.json ADDED
The diff for this file is too large to render. See raw diff
 
data/training_data_20251102_193209.json ADDED
The diff for this file is too large to render. See raw diff
 
data/training_data_20251102_215614.json ADDED
@@ -0,0 +1,487 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "instruction": "Describe the process of extracting data from a database.",
4
+ "input": "",
5
+ "output": "The process of extracting data from a database begins by establishing a connection to the database. Once the connection is established, the data can be queried by writing an appropriate query. The results of the query are then retrieved from the database and stored in some local or remote data storage. This process can be automated, where the query is run periodically, or manually done by end user. Depending on the complexity of the query and the database, the process of extracting data from a database can vary."
6
+ },
7
+ {
8
+ "instruction": "Make a list of questions that should be asked during a job interview.",
9
+ "input": "",
10
+ "output": "1. What made you decide to apply for this job?\n2. What do you know about our organization?\n3. What are your career goals?\n4. What have you achieved in the past that make you the right candidate for us?\n5. What do you feel would be your major contributions to our team?\n6. What challenges have you faced in your previous roles and how did you address them?\n7. How would you describe your management style?\n8. What do you consider to be your strongest skills?\n9. How do you handle high-stress situations or tight deadlines?\n10. Do you have any questions for us?"
11
+ },
12
+ {
13
+ "instruction": "Create a song lyric that conveys a sense of nostalgia.",
14
+ "input": "",
15
+ "output": "\"Remember when we would laugh, so care-free? Life was so much simpler then, it seemed. Times have changed, yet happy memories remain. We can look back and smile, when nostalgia takes hold.\""
16
+ },
17
+ {
18
+ "instruction": "Generate a metaphor for how society views success.",
19
+ "input": "",
20
+ "output": "Success is like climbing a mountain; it requires dedication and effort, and once you reach the top you have a beautiful view of the possibilities ahead."
21
+ },
22
+ {
23
+ "instruction": "Come up with a new product to solve the given need.",
24
+ "input": "A device to alert drivers of nearby gas stations when their vehicles are low on fuel.",
25
+ "output": "An automated fuel monitor device that fits inside vehicles and detects when the vehicle is running low on fuel. The device would then alert the driver to the closest gas station with directions and fuel prices."
26
+ },
27
+ {
28
+ "instruction": "Why buy insurance?",
29
+ "input": "",
30
+ "output": "This is just an addition to base64's answer. In order to maximize your overall wealth (and wellbeing) in a long run, it is not enough to look only at the expected value (EV). In his example of always keeping $9850 or having $10000 99% of the time, EV in the second case is greater ($9900 > $9850) and if you are Bill Gates than you should not take an insurance in this case. But if your wealth is a lot less than that you should take an insurance. Take a look at Kelly criterion and utility functions. If I offer you to take 100 million dollars (no strings attached) or to take a risk to get 200 million dollars 60% of the time (and $0 40% of the time), would you take that risk? You shouldn't but Bill Gates should take that risk because that would be a very good investment for him. Utility functions can help you choose if you want an insurance or not. Maybe you want to insure your house because the value of the house is a large percentage of your wealth but on the other hand you don't need to insure your car if it is very easy for you to afford another one (but not easy to afford another house). Lets calculate what your wealth should be in order not to take this $150 insurance on a $10000 item. If you pay $150 for an insurance you have guaranteed $9850. But choosing not to take an insurance is the same as betting $9850 in order to gain $150 99% of the time. By using Kelly criterion formula fraction of the wealth needed to make this bet is: [p*(b+1)-1]/b = [0.99*(150/9850+1) -1]/ (150/9850) = 1/3. That means that if your wealth greater than $29950 you don't need an insurance. But if you want to be sure it is advised to use fractional Kelly betting (for example you could multiply fraction by 1/2) and in that case if your wealth is more than $59900 you don't need an insurance for this item."
31
+ },
32
+ {
33
+ "instruction": "Was this a good deal on a mortgage?",
34
+ "input": "",
35
+ "output": "Some part of the payment is probably also going for tax escrow, insurance payments, probably PMI if you aren't putting at least 20% down. Get a complete breakdown of the costs. Remember to budget for upkeep. And please see past discussion of why buying a home at this point in your career/life may be very, very premature."
36
+ },
37
+ {
38
+ "instruction": "Create a marketing campaign slogan for a pet grooming business.",
39
+ "input": "",
40
+ "output": "\"Treat your pet to the best - let us groom the rest!\""
41
+ },
42
+ {
43
+ "instruction": "Construct an argument to defend the following statement",
44
+ "input": "Alternative energy sources are critical to solving the climate crisis",
45
+ "output": "Alternative energy sources are absolutely vital in order to mitigate the most detrimental impacts of climate change. As the world transitions away from fossil fuels and towards more sustainable sources of energy, such as wind, solar, and hydropower, we can reduce the number of greenhouse gases in the atmosphere and prevent further warming of our planet. Additionally, using renewable and alternative energies sources will create thousands of jobs and can help to create a healthy, stable economy. Investing in alternative energy sources is a sound decision and a necessary step if we want to effectively address the climate crisis."
46
+ },
47
+ {
48
+ "instruction": "Categorize the following words into two teams.",
49
+ "input": "turtle, hedgehog, goat, fox",
50
+ "output": "Team 1: Turtle, Hedgehog\nTeam 2: Goat, Fox"
51
+ },
52
+ {
53
+ "instruction": "Explain to me how a computer virus can be contracted.",
54
+ "input": "",
55
+ "output": "Computer viruses can be contracted in various ways. Common methods include downloading files from untrustworthy websites, opening email attachments from unknown sources, and visiting websites that contain malicious code. Additionally, sharing unsecured computer systems with others can allow for the virus to spread from one system to another."
56
+ },
57
+ {
58
+ "instruction": "Taking partial capital loss purely for tax purposes",
59
+ "input": "",
60
+ "output": "Note that the rules around wash sales vary depending on where you live. For the U.S., the wash sale rules say that you cannot buy a substantially identical stock or security within 30 days (before or after) your sale. So, you could sell your stock today to lock in the capital losses. However, you would then have to wait at least 30 days before purchasing it back. If you bought it back within 30 days, you would disqualify the capital loss event. The risk, of course, is that the stock's price goes up substantially while you are waiting for the wash sale period. It's up to you to determine if the risk outweighs the benefit of locking in your capital losses. Note that this applies regardless of whether you sell SOME or ALL of the stock. Or indeed, if we are talking about securities other than stocks."
61
+ },
62
+ {
63
+ "instruction": "What is the best resource for determining a specific age-based asset allocation?",
64
+ "input": "",
65
+ "output": "Look into the asset allocations of lifecycle funds offered by a company like Vanguard. This page allows you to select your current age and find a fund based on that. You could pick a fund, like the Target Retirement 2055 Fund (ages 21-25), and examine its allocation in the Portfolio & Management tab. For this fund, the breakdown is: Then, look at the allocation of the underlying funds that comprise the lifecycle fund, in the same tab. Look at each of those funds and see what asset allocation they use, and that should give you a rough idea for an age-based allocation. For example, the Total Stock Market Index Fund page has a sector breakdown, so if you wanted to get very fine-grained with your allocation, you could. (You're probably much better off investing in the index fund, low-cost ETFs, or the lifecycle fund itself, however; it'll be much cheaper). Doing this for several lifecycle funds should be a good start. Keep in mind, however, that these funds are rebalanced as the target date approaches, so if you're following the allocation of some particular funds, you'll have to rebalance as well. If you really want an age-based allocation that you don't have to think about, invest in a lifecycle fund directly. You'll probably pay a lower expense ratio than if you invested in a whole slew of funds directory, and it's less work for someone who isn't comfortable managing their portfolio themselves. Furthermore, with Vanguard, the expense ratios are already fairly low. This is only one example of an allocation, however; your tolerance of risk, age, etc. may affect what allocation you're willing to accept. Full disclosure: Part of my Roth IRA is invested in the Target 2055 fund I used as an example above, and another part uses a similar rebalancing strategy to the one I used above, but with Admiral Share funds, which have higher minimum investments but lower expense ratios."
66
+ },
67
+ {
68
+ "instruction": "How secure is my 403(b)? Can its assets be \u201craided\u201d?",
69
+ "input": "",
70
+ "output": "I assume you get your information from somewhere where they don't report the truth. I'm sorry if mentioning Fox News offended you, it was not my intention. But the way the question is phrased suggests that you know nothing about what \"pension\" means. So let me explain. 403(b) is not a pension account. Pension account is generally a \"defined benefit\" account, whereas 403(b)/401(k) and similar - are \"defined contribution\" accounts. The difference is significant: for pensions, the employer committed on certain amount to be paid out at retirement (the defined benefit) regardless of how much the employee/employer contributed or how well the account performed. This makes such an arrangement a liability. An obligation to pay. In other words - debt. Defined contribution on the other hand doesn't create such a liability, since the employer is only committed for the match, which is paid currently. What happens to your account after the employer deposited the defined contribution (the match) - is your problem. You manage it to the best of your abilities and whatever you have there when you retire - is yours, the employer doesn't owe you anything. Here's the problem with pensions: many employers promised the defined benefit, but didn't do anything about actually having money to pay. As mentioned, such a pension is essentially a debt, and the retiree is a debt holder. What happens when employer cannot pay its debts? Employer goes bankrupt. And when bankrupt - debtors are paid only part of what they were owed, and that includes the retirees. There's no-one raiding pensions. No-one goes to the bank with a gun and demands \"give me the pension money\". What happened was that the employers just didn't fund the pensions. They promised to pay - but didn't set aside any money, or set aside not enough. Instead, they spent it on something else, and when the time came that the retirees wanted their money - they didn't have any. That's what happened in Detroit, and in many other places. 403(b) is in fact the solution to this problem. Instead of defined benefit - the employers commit on defined contribution, and after that - it's your problem, not theirs, to have enough when you're retired."
71
+ },
72
+ {
73
+ "instruction": "Why don't banks allow more control over credit/debit card charges?",
74
+ "input": "",
75
+ "output": "Credit cards and debit cards make up the bulk of the transactions in the US. Visa and Mastercard take a percentage of each credit card transaction. For the most part, this fee it built into the price of what you buy. That is, you don't generally pay extra at the grocery store if you use a credit card (gasoline purchases are a notable exception here.) If you were getting something like 2% of a third of all the retail transactions in the US, you'd probably not want to rock the boat too much either. Since there is little fraud relative to the amount of money they are taking in, and it can often be detected using statistical analysis, they don't really stand to gain that much by reducing it through these methods. Sure they can reduce the losses on the insurance they provide to the credit card consumer but they risk slowing down the money machine. These companies want avoid doing something like reducing fraud by 0.5% revenues but causing purchases with the cards drop by 1%. More security will be implemented as we can see with the (slow) introduction of chip cards in the US but only at a pace that will prevent disruption of the money machine. EMV will likely cause a large drop in CC fraud at brick-and-mortar stores but won't stop it online. You will likely see some sort of system like you describe rolled out for that eventually."
76
+ },
77
+ {
78
+ "instruction": "Given a financial statement, predict what the stock market's reaction will be.",
79
+ "input": "The company reported a 25% growth in profits year-over-year.",
80
+ "output": "The stock market would likely react positively to the company's financial statement, resulting in a rise in stock prices."
81
+ },
82
+ {
83
+ "instruction": "Write a poem about joy.",
84
+ "input": "",
85
+ "output": "Joy is in the smallest things\nThe sight of a sunset\u2019s wings\nThe song of a bird on a breeze\nThe laughter of children at ease\n\nJoy hides in the budding blooms\nLike secrets revealed in their tombs\nThe silence of a peaceful night\nThat looms beneath the stars so bright\n\nJoy is found in the dawn of day\nWhen the sun comes out to play\nIn the sparkle of the summer rain\nThat washes away every pain\n\nThis is joy, meant to be embraced\nFor it can fill any empty space\nLet it fill you up inside\nAnd cast away your tear-soaked pride."
86
+ },
87
+ {
88
+ "instruction": "Create a list of five problems associated with air pollution.",
89
+ "input": "",
90
+ "output": "1. Harmful effects on human health\n2. Impacts on natural ecosystems\n3. Costly damages to infrastructure\n4. Contribution to global warming and climate change\n5. Deterioration of air quality leading to breathing difficulties for many."
91
+ },
92
+ {
93
+ "instruction": "Given a word, construct its antonyms.",
94
+ "input": "Light",
95
+ "output": "Dark, heavy, dense"
96
+ },
97
+ {
98
+ "instruction": "If NYSE has market makers, what is the role of NYSE ARCA which is an ECN",
99
+ "input": "",
100
+ "output": "I would say it's a bit more complicated than that. Do you understand what a market maker does? An ECN (electronic communication network) is a virtual exchange that works with market makers. Using a rebate structure that works by paying for orders adding liquidity and charges a fee for removing liquidity. So liquidity is created by encouraging what are essentially limit orders, orders that are outside of the current market price and therefore not immediately executable. These orders stay in the book and are filled when the price of the security moves and triggers them. So direct answer is NYSE ARCA is where market makers do their jobs. These market makers can be floor traders or algorithmic. When you send an order through your brokerage, your broker has a number of options. Your order can be sent directly to an ECN/exchange like NYSE ARCA, sent to a market making firm like KCG Americas (formerly Knight Capital), or internalized. Internalization is when the broker uses an in house service to execute your trade. Brokerages must disclose what they do with orders. For example etrade's. https://content.etrade.com/etrade/powerpage/pdf/OrderRouting11AC6.pdf This is a good graphic showing what happens in general along with the names of some common liquidity providers. http://www.businessweek.com/articles/2012-12-20/how-your-buy-order-gets-filled"
101
+ },
102
+ {
103
+ "instruction": "Compare and contrast zero knowledge proofs with digital signatures",
104
+ "input": "",
105
+ "output": "Zero knowledge proofs are a cryptographic technique that allow one party to prove to another that they know a specific piece of information without revealing the actual information. This process typically involves a prover and a verifier. The prover proves the validity of an assertion such as a password or digital signature without revealing the information, while the verifier verifies the prover\u2019s assertion. Digital signatures, on the other hand, are essentially messages, certificates, or documents that are signed with an encrypted algorithm that can be used to verify the authenticity of the sender. Digital signatures are used to ensure data security and integrity, while zero knowledge proofs help to ensure privacy."
106
+ },
107
+ {
108
+ "instruction": "List 5 ways a machine learning model can be used to make decisions.",
109
+ "input": "",
110
+ "output": "1. Classifying data into predetermined categories.\n2. Forecasting the outcome of a given scenario.\n3. Anomaly detection in the data.\n4. Automating tedious decision-making processes.\n5. Recommending the best options to a user."
111
+ },
112
+ {
113
+ "instruction": "Suggest 3 variations of a given recipe.",
114
+ "input": "Apple Pie",
115
+ "output": "Three variations of a traditional apple pie recipe are:\n- Apple Crumble Pie - layer sliced apples and cinnamon sugar in a baking dish and top with a crumble topping of flour, butter and sugar. \n- Apple Streusel Pie - layer sliced apples with cinnamon sugar, then top with a streusel topping of butter, flour, cinnamon and sugar. \n- Apple Caramel Pie \u2013 layer sliced apples with caramel sauce and sprinkle with crumble topping."
116
+ },
117
+ {
118
+ "instruction": "Rewrite the following statement in the imperative form. \u201cPlease submit your review.\u201d",
119
+ "input": "",
120
+ "output": "Submit your review."
121
+ },
122
+ {
123
+ "instruction": "Alternatives to Intuit's PayTrust service for online bill viewing and bill payment?",
124
+ "input": "",
125
+ "output": "(Six years later...) I've used CheckFree for over 20 years, and my uncle started using it back in the early 1980s through a 300 baud modem. It has e-bills, EDI bills that you schedule yourself, and will also mail checks to people and small businesses. You can make your payments from an unlimited number of banks, can schedule multiple recurring payments for the same bill (I find that useful for when buying large/expensive items by CC: I create a different payment schedule for each), plus ad hoc payments."
126
+ },
127
+ {
128
+ "instruction": "Need to change cash to cashier's check without bank account (Just arrived to the US)",
129
+ "input": "",
130
+ "output": "A cashier's check costs money to get and is not connected to an account. You have cash. You should be able to get a bank to sell you one, even without an account. Find a bank where you would like to open an account and explain the situation. I can't guarantee that that will work, but I would expect it to do so. If not, the bank can probably suggest an alternative. You might also ask the landlord if you can do it with postal money orders. I am positive that you can buy those with cash. You might have to buy a bunch to reach your desired amount. Or perhaps a Western Union money order might be better. You also might be able to open an account with your passport and Social Security Number (SSN)."
131
+ },
132
+ {
133
+ "instruction": "Name one possible consequence of this issue.",
134
+ "input": "Issue: The widening use of smartphone technology has led to an increase in cyberbullying.",
135
+ "output": "One possible consequence of this issue is that victims of cyberbullying can become emotionally distressed and experience decreased self-esteem and more severe mental health issues."
136
+ },
137
+ {
138
+ "instruction": "Identify the most important cause of air pollution in the cities.",
139
+ "input": "",
140
+ "output": "The most important cause of air pollution in cities is emissions from vehicles, such as cars and buses."
141
+ },
142
+ {
143
+ "instruction": "Using multiple bank accounts",
144
+ "input": "",
145
+ "output": "I live near historic Concord, Massachusetts, and frequently drive past Walden Pond. I'm reminded of Henry David Thoreau's words, \"Simplify, simplify, simplify.\" In my opinion, fewer is better. 2 checkbooks? I don't see how that makes budgeting any easier. The normal set of expenses are easily kept as one bucket, one account. The savings 2&3 accounts can also be combined and tracked if you really want to think of them as separate accounts. Now, when you talk about 'Retirement' that can be in tax-wise retirement accounts, e.g. 401(k), IRA, etc. or post tax regular brokerage accounts. In our situation, the Schwab non-retirement account was able to handle emergency (as money market funds) along with vacation/rainy day, etc, in CDs of different maturities. As an old person, I remember CDs at 10% or higher, so leaving money in lower interest accounts wasn't good. Cash would go to CDs at 1-5 year maturities to maximize interest, but keep money maturing every 6-9 months. Even with the goal of simplifying, my wife and I each have a 401(k), an IRA, and a Roth IRA, I also have an inherited Roth, and I manage my teen's Roth and brokerage accounts. That's 9 accounts right there. No way to reduce it. To wrap it up, I'd go back to the first 4 you listed, and use the #4 checking attached to the broker account to be the emergency fund. Now you're at 3. Any higher granularity can be done with a spreadsheet. Think of it this way - the day you see the house you love, will you not be so willing to give up that year's vacation?"
146
+ },
147
+ {
148
+ "instruction": "Take 2 minutes for a mindfulness practice",
149
+ "input": "",
150
+ "output": "Begin by sitting comfortably and paying attention to your breath. Focus on the sensation of the air entering your nostrils and leaving your mouth. Count your inhalationbreaths from one to eight, then repeat. Do this entire process again, then focus on your entire body and release any tension that you may have. Finally, let your mind be still and peaceful, and when you're ready you may open your eyes."
151
+ },
152
+ {
153
+ "instruction": "Describe a psychological condition called \"depersonalization disorder\".",
154
+ "input": "",
155
+ "output": "Depersonalization disorder is a form of dissociative disorder in which an individual experiences a feeling of being detached from reality or themselves. People with depersonalization disorder may feel as though their thoughts and emotions are not their own, and they may have difficulty recognizing faces and voices. They may also experience distortion in the perception of time and space. Symptoms can be intermittent or continuous, and the disorder is often associated with depression, anxiety, and post-traumatic stress disorder."
156
+ },
157
+ {
158
+ "instruction": "Analyze the argument that global warming is a hoax",
159
+ "input": "",
160
+ "output": "The argument that global warming is a hoax is largely unsupported. Recent scientific findings indicate that the Earth is indeed warming at an alarming rate, and human activities are the major cause of this warming. On top of this, the only evidence that skeptics have presented to suggest that global warming is a hoax is anecdotal, and has not been verified by any scientific methodologies. Overall, the argument that global warming is a hoax is not supported by any reliable evidence."
161
+ },
162
+ {
163
+ "instruction": "Explain the term 'network latency' in one sentence.",
164
+ "input": "",
165
+ "output": "Network latency is the amount of time it takes for data to be transferred between two points on a network."
166
+ },
167
+ {
168
+ "instruction": "Decompose the following polygon into smaller polygons.",
169
+ "input": "A polygon with 5 sides",
170
+ "output": "The polygon can be decomposed into two triangles and a square."
171
+ },
172
+ {
173
+ "instruction": "Provide a general overview of the given topic.",
174
+ "input": "Topic: The birth of e-commerce",
175
+ "output": "The birth of e-commerce is an important milestone, as it introduced a new era of seamless shopping and convenience to consumers. The idea of buying items online without having to leave the comfort of home first became possible in the mid-to-late 1990s, fueled by the widespread availability of the internet. Since then, e-commerce has grown rapidly and continues to expand, with numerous advancements in technologies such as online payment processing, smartphones, and virtual/augmented reality. Nowadays, e-commerce has become an integral part of people's lives, with a variety of goods and services available through digital channels."
176
+ },
177
+ {
178
+ "instruction": "Classify this sentence as positive, negative or neutral sentiment: \"I am so tired\".",
179
+ "input": "",
180
+ "output": "Negative sentiment."
181
+ },
182
+ {
183
+ "instruction": "Classify the following sentence as verb, noun, adjective, etc.",
184
+ "input": "Nature is beautiful.",
185
+ "output": "Noun (Nature), Adjective (beautiful)."
186
+ },
187
+ {
188
+ "instruction": "Create a cryptographic code using a substitution cipher.",
189
+ "input": "",
190
+ "output": "A substitution cipher is a cryptographic code which replaces each letter in the plaintext with another letter. For example, a substitution cipher might replace each letter with the letter three positions after it in the alphabet. So, the letter \"A\" would be replaced with the letter \"D\", the letter \"B\" would be replaced with the letter \"E\", and so on."
191
+ },
192
+ {
193
+ "instruction": "Describe the symptoms of a cold",
194
+ "input": "",
195
+ "output": "The symptoms of a cold include a sore throat, a headache, a runny nose, sneezing, congestion, a cough, body aches, fatigue, and fever. Other symptoms can include chills, loss of appetite, digestive issues, and a loss of smell and taste."
196
+ },
197
+ {
198
+ "instruction": "Describe the flow of oxygen in the human body.",
199
+ "input": "",
200
+ "output": "The flow of oxygen in the human body begins with breathing. Oxygen is taken in through the lungs and enters the bloodstream. From the bloodstream, oxygen is transported to the cells throughout the body. \n\nOnce inside the cells, the oxygen combines with other molecules to create adenosine triphosphate (ATP), the energy source for all cell activity. The ATP is then used to power metabolic and physiological processes in the body. The oxygen that was used to make ATP is then released back into the bloodstream, and is eventually exhaled back out of the lungs."
201
+ },
202
+ {
203
+ "instruction": "Where can I open a Bank Account in Canadian dollars in the US?",
204
+ "input": "",
205
+ "output": "Give Harris Bank a call; they might be able to help you As of August 21, 2015, Harris bank does NOT offer Canadian dollar accounts in the U.S."
206
+ },
207
+ {
208
+ "instruction": "Are bonds really a recession proof investment?",
209
+ "input": "",
210
+ "output": "No, they are not recession proof. Assume several companies, that issued bonds in the fund, go bankrupt. Those bonds could be worthless, they could miss principle payments, or they could be restructured. All would mean a decline in value. When the economy shrinks (which is what a recession is) how does the Fed respond? By lowering interest rates. This makes current bonds more valuable as presumably they were issued at a higher rate, thus the recession proof prejudice. However, there is nothing to stop a company (in good financial shape) from issuing more bonds to pay the par value on high-interest bonds, thus refinancing their debt. Sort of like how the bank feels when one refinances the mortgage for a lower rate. The thing that troubles me the most is that rates have been low for a long time. What happens if we have a recession now? How does the Fed fix it? I am not sure exactly what the fallout would be, but it could be significant. If you are troubled, you should look for sectors that would be hurt and helped by a Trump-induced recession. Move money away from those that will be hurt. Typically aggressive growth companies are hurt (during recessions), so you may want to move money away from them. Typically established blue chip companies fare okay in a recession so you may want to move money toward them. Move some money to cash, and perhaps some towards bonds. All that being said, I'd keep some money in things like aggressive growth in case you are wrong."
211
+ },
212
+ {
213
+ "instruction": "U.S. stock sales- tax on sale for NR Canadian",
214
+ "input": "",
215
+ "output": "If you're a non resident then you owe no capital gains tax to Canada. Most banks won't let you make trades if you're a non-resident. They may not have your correct address on file so they don't realize this. This is not tax law but just OSC (or equivalent) regulations. You do have to fill out paperwork for withholding tax on OAS/CPP payments. This is something you probably already do but here's a link . It's complicated and depends on the country you live in. Of course you may owe tax in Thailand, I don't know their laws."
216
+ },
217
+ {
218
+ "instruction": "Name three things associated with the topic.",
219
+ "input": "Hiking",
220
+ "output": "Hiking is associated with walking, enjoying nature, and exploring trails."
221
+ },
222
+ {
223
+ "instruction": "Provide three example usages of the word 'adorn'.",
224
+ "input": "",
225
+ "output": "1. The bride adorned herself with the finest jewels. \n2. The house was adorned with holiday decorations. \n3. She adorned her wedding dress with a beautiful sash."
226
+ },
227
+ {
228
+ "instruction": "What is the difference between \u201cgood debt\u201d vs. \u201cbad debt\u201d?",
229
+ "input": "",
230
+ "output": "First of all debt is a technology that allows borrower to bring forward their spending; it's a financial time machine. From borrowers point of view debt is good when it increases overall economic utility. A young person wants to bring up a family but cannot afford the house. Had they waited for 30 years they would have reached the level of income and savings to buy the house for cash. By the time it might be too late to raise a family, sure they'd enjoy the house for the last 20 years of their life. But they would loose 30 years of utility - they could have enjoyed the house for 50 years! So, for a reasonable fee, they can bring the spending forward. Another young person might want to enjoy a life of luxury, using the magical debt time machine and bringing forward their future earnings. They might spend 10 years worth of future earnings on entertainment within a year and have a blast. Due to the law of diminishing marginal utility - all that utility is pretty much wasted, but they'll still will need to make sacrifices in the future. The trick is to roughly match the period of debt repayment to the economic life of the purchase. Buying a house means paying over 30 years for an asset that has an economic life of 80 years+, given that the interest fee is reasonable and the house won't loose it's value overnight that's a good debt. Buying a used car with a remaining life of 5 years and financing its with a seven years loan - is not a good idea. Buying a luxurious holiday that lasts a fortnight with 2 years of repayments, i.e. financing non-essential short term need with medium term debt is insane. The other question is could the required utility be achieved through a substitute at a lower cost without having to bring the spending forward or paying the associated fee."
231
+ },
232
+ {
233
+ "instruction": "How can you possibly lose on investments in stocks?",
234
+ "input": "",
235
+ "output": "If you're talking about a single stock, you greatly underestimate the chances of it dropping, even long-term. Check out the 12 companies that made up the first Dow Jones Industrial Average in 1896. There is probably only one you've heard of: GE. Many of the others are long gone or have since been bought up by larger companies. And remember these were 12 companies that were deemed to be the most representative of the stock market around the turn of the 20th century. Now, if you're talking about funds that hold many stocks (up to thousands), then your question is a little different. Over the long-term (25+ years), we have never experienced a period where the overall market lost value. Of course, as you recognize, the psychology of investors is a very important factor. If the stock market loses half of its value in a year (as it has done a few times), people will be inundated with bad news and proclamations of \"this time it's different!\" and explanations of why the stock market will never recover. Perhaps this may be true some day, but it never has been thus far. So based on all the evidence we have, if you hold a well-diversified fund, the chances of it going down long-term (again, meaning 25+ years) are basically zero."
236
+ },
237
+ {
238
+ "instruction": "Name the top 5 TV shows that have the highest IMDB rating?",
239
+ "input": "",
240
+ "output": "The top 5 TV shows according to IMDb rating are \"The Wire\" (9.3), \"Breaking Bad\" (9.5), \"Game of Thrones\" (9.4), \"The Sopranos\" (9.2), and \"Band of Brothers\" (9.4)."
241
+ },
242
+ {
243
+ "instruction": "Using the provided condition, construct a query in SQL.",
244
+ "input": "SELECT column FROM table WHERE condition = true",
245
+ "output": "SELECT column FROM table WHERE condition = true AND gender = 'male'"
246
+ },
247
+ {
248
+ "instruction": "Identify what type of relationship exists between two items.",
249
+ "input": "X-ray and Scan",
250
+ "output": "X-ray and Scan are both medical imaging procedures used to create a visual representation of the body. The difference between them is that an X-ray uses radiation to create the image, while a Scan uses sound waves."
251
+ },
252
+ {
253
+ "instruction": "Generate a list of 10 strategies for dealing with stress.",
254
+ "input": "",
255
+ "output": "- Identify the sources of stress in your life.\n- Develop healthy coping strategies.\n- Exercise regularly.\n- Get enough sleep.\n- Eat a healthy diet.\n- Practice deep breathing exercises.\n- Spend time with friends and family.\n- Learn to say \u201cno\u201d.\n- Take a break from the situation that\u2019s causing you stress.\n- Find ways to reduce unnecessary commitments."
256
+ },
257
+ {
258
+ "instruction": "Write a new sentence that has a simile in it.",
259
+ "input": "",
260
+ "output": "Her laughter was like a light rain shower on a hot summer day."
261
+ },
262
+ {
263
+ "instruction": "Given a news article, explain the key points and implications.",
264
+ "input": "News Article: Google is launching a new cloud-computing service.",
265
+ "output": "Google has announced the launch of a new cloud-computing service, which will offer users a wide range of data storage and computing capabilities. This new service could provide businesses with a more cost-effective way of storing and accessing large amounts of data. It could also lead to the development of new applications and services that are accessed through the cloud."
266
+ },
267
+ {
268
+ "instruction": "Come up with a riddle based on the given semantic keywords related to money.",
269
+ "input": "Hoarding, Spend, Bill",
270
+ "output": "What is always hoarded, but must be spent on bills? \nAnswer: Money."
271
+ },
272
+ {
273
+ "instruction": "Identify the main theme of the following poem:\n\n\"I'd climb a hill so high,\nThat I could touch the stars in the sky,\nAnd I'd watch the birds fly by\"",
274
+ "input": "",
275
+ "output": "The main theme of the poem is ambition and determination. The speaker is expressing a desire to reach for their dreams."
276
+ },
277
+ {
278
+ "instruction": "Generate a creative title that summarises the following article.",
279
+ "input": "A new report suggests that an increase in carbon dioxide levels has led to higher temperatures, more extreme weather events and a rapid decline in biodiversity.",
280
+ "output": "The Perils of Increased Carbon: A Rapid Climatic and Biodiversity Crisis."
281
+ },
282
+ {
283
+ "instruction": "Write an algorithm for solving an anagram.",
284
+ "input": "",
285
+ "output": "1. Get the anagram as input.\n2. Split the anagram into individual characters.\n3. Sort the characters in alphabetical order.\n4. Compare the sorted characters with words in a dictionary to form matches.\n5. Return the matches."
286
+ },
287
+ {
288
+ "instruction": "Develop a plan for the use of a new software.",
289
+ "input": "Software: Customer Service Ticket System",
290
+ "output": "A plan for the use of a customer service ticket system should include: creating user accounts and assigning access levels, training staff on the system, mapping out customer service workflows, creating customer profiles, assigning a unique ticket number for each inquiry, tracking corresponding conversations and activities, and setting up automated customer service reports."
291
+ },
292
+ {
293
+ "instruction": "Generate a list of activities for a birthday party for a 9-year-old.",
294
+ "input": "",
295
+ "output": "Here is a list of activities for a 9-year-old's birthday party:\n- Have a treasure hunt: Hide small items around the house or outside and have the children search for them. \n- Have a potato-sack race: Divide the children into two teams and let them race each other while carrying a sack of potatoes. \n- Balloon relay: Divide the group into two teams and have them compete in a balloon popping race. \n- Build castles out of blocks: Provide a variety of blocks to the children and let them build creative structures. \n- Have karaoke: Set up a karaoke machine and sing along to the children's favorite songs. \n- Scavenger hunt: Hide items around the house and have the kids find them. \n- Have a themed party: Take the birthday boy or girl's favorite character and make it the theme of the entire house by decorating with the character's items. \n- Paint a mural: Give the children paint and brushes and let them create a mural together. \n- Have a scavenger hunt in the park: Hide items in the park and have the children hunt them down. \n- Play hide and seek: Let the children play hide and seek in the house or in the park. \n- Make a fashion show: Provide clothing items such as hats and jewelry and let the children play dress up and model their outfits. \n- Make a pinata: Provide a pinata filled with candy and let the children take turns in hitting it to break it open."
296
+ },
297
+ {
298
+ "instruction": "Find the minimum value of the function",
299
+ "input": "f(x, y) = 2x^2 + y^3",
300
+ "output": "The minimum value of the function f(x, y) = 2x^2 + y^3 is 0, when x = 0 and y = 0."
301
+ },
302
+ {
303
+ "instruction": "How do I know when I am financially stable/ready to move out on my own?",
304
+ "input": "",
305
+ "output": "It all depends on what your financial goals are when you are ready. You sound like you could be ready today if you wanted to be. The steps that I would take are. Create a monthly draft budget. This doesn't have to be something hard and fast, just a gague of what your living expenses would be compared to your after-tax salary. Make sure there would be room for \"fun\" money. a. Consider adding a new car fund line item to this budget, and deducting that amount from your paycheck starting now so that you can save for the car. Based on the most realistic estimate that you can make, you'll get a good idea if you want to spend the money it takes to move out alone now or later. You'll also see the price for various levels of rentals in your area (renting a single family home, townhouse, condo, apartment, living in a rented room or basement, sharing a place with friends, etc) and know some of the costs of setting up for yourself. Since you're looking at the real estate market, you may want to do a cost comparison of renting versus buying. I've found the New York Times interactive graphic on this is excellent. If you are looking to buy, make sure to research the hidden costs of buying thoroughly before taking this step. To answer your last question, if you have the cash you should consider upping your 401K investment (or using Roth or regular IRA). Make sure you are investing enough to get your full employer match, if your employer offers one, and then get as close as you can to government maximum contribution limits. Compound interest is a big deal when you are 23."
306
+ },
307
+ {
308
+ "instruction": "classify the given phrases as either a fact or opinion.",
309
+ "input": "The United States is the world's largest economy.\nBananas are the best fruit.",
310
+ "output": "The United States is the world's largest economy: Fact\nBananas are the best fruit: Opinion"
311
+ },
312
+ {
313
+ "instruction": "Identify the country in which the World Trade Center is located.",
314
+ "input": "",
315
+ "output": "The World Trade Center is located in New York City, United States."
316
+ },
317
+ {
318
+ "instruction": "How do you translate a per year salary into a part-time per hour job?",
319
+ "input": "",
320
+ "output": "If you're really a part-time worker, then there are some simple considerations.... The remote working environment, choice of own hours, and non-guarantee of work availability point to your \"part-time\" situation being more like a consultancy, and that would normally double or triple the gross hourly rate. But if they're already offering or paying you a low hourly figure, they are unlikely to give you consultant rates."
321
+ },
322
+ {
323
+ "instruction": "Write an essay about the societal implications of artificial intelligence",
324
+ "input": "",
325
+ "output": "The increasing prevalence of artificial intelligence (AI) has caused a wave of discussions about its implications for our society. AI systems are becoming capable of carrying out tasks both quickly and accurately, and they have the potential to revolutionize the way humans interact with machines. However, while AI has the potential to provide immense benefits, the rise of AI is not without its risks. AI decision-making can often be biased and opaque, while more sophisticated AI models can lead to complex societal and ethical issues. In our increasingly digital world, it is important to be aware of the implications that AI and machine learning can have on our individual lives, as well as on society as a whole."
326
+ },
327
+ {
328
+ "instruction": "Generate a summation of the following numbers",
329
+ "input": "1,2,3,4,5",
330
+ "output": "Summation of the numbers is 15"
331
+ },
332
+ {
333
+ "instruction": "Describe the recipe for the following dish.",
334
+ "input": "Tom Yum Soup",
335
+ "output": "Tom Yum Soup is an aromatic and flavorful Thai soup made with lemongrass, kaffir lime leaves, galangal, mushrooms, and a spicy chili paste. The broth is seasoned with fish sauce, tamarind paste, and lime juice to create an uplifting and spicy appetizer."
336
+ },
337
+ {
338
+ "instruction": "Name three political leaders from Russia.",
339
+ "input": "",
340
+ "output": "Three political leaders from Russia are Vladimir Putin, Dmitry Medvedev and Sergey Shoygu."
341
+ },
342
+ {
343
+ "instruction": "Rewrite the following phrase in its interrogative form",
344
+ "input": "Life is beautiful",
345
+ "output": "Is life beautiful?"
346
+ },
347
+ {
348
+ "instruction": "Given a type of food, create a recipe for it.",
349
+ "input": "apple crumble",
350
+ "output": "Apple Crumble Recipe\nIngredients: \n- 4 large apples, peeled, cored and cubed \n- 2 tablespoons of butter \n- \u00bd cup of packed brown sugar \n- 2 tablespoons of all-purpose flour \n- \u00bd teaspoon of ground cinnamon \n- \u00bc teaspoon of ground nutmeg \n- 1 cup of rolled oats \n\nInstructions:\n1. Preheat oven to 350 degrees F (175 degrees C). Grease an 8-inch square baking dish.\n2. Spread the cubed apples in the greased baking dish. \n3. In a medium bowl, mix together the butter, brown sugar, flour, cinnamon, nutmeg and oats until well combined. \n4. Sprinkle the oat crumble mixture over the apples and spread evenly.\n5. Bake in preheated oven for about 30 minutes, or until the top is golden brown. Serve warm."
351
+ },
352
+ {
353
+ "instruction": "Explain the principle of least action.",
354
+ "input": "",
355
+ "output": "The principle of least action states that the path taken between two points by a physical system is the one that requires the least amount of action. In other words, the path taken results in a net zero action between two points. This means that the action or energy is conserved over the duration of the path taken by the physical system. This principle has been used to explain the behavior of both light and sound waves in the natural world."
356
+ },
357
+ {
358
+ "instruction": "Are Target Funds Unsafe - Post Q.E.?",
359
+ "input": "",
360
+ "output": "It's a what-if? sort of question. What if rates stay down or trend only slightly higher, despite no QE? look at other countries response to tepid economies. My experience as professional advisor (25 yrs) tells me the future is unknowable and diversity is good. Make alternative choices- they all won't work wonderfully, but some will."
361
+ },
362
+ {
363
+ "instruction": "Name three strengths you have and explain how they will benefit the company if you were to be hired.",
364
+ "input": "",
365
+ "output": "I possess three strengths which I believe will be of great benefit to the company if I were to be hired. \n\nFirst, I have outstanding communication skills. I have an ability to communicate complex ideas and concepts in a simple and clear manner. I am also an active listener, which allows me to understand the needs and opinions of both my superiors and colleagues.\n\nSecond, I have great problem-solving skills which enable me to quickly identify the root cause of an issue and come up with creative solutions. I am also a creative thinker, who can think outside of the box and come up with innovative ideas.\n\nFinally, I have strong organizational and managerial skills. I am able to prioritize tasks, delegate responsibilities, and manage my time in order to hit deadlines. I also have experience leading and motivating teams and individual contributors to ensure that the tasks are completed on time and with the highest quality.\n\nI believe that these strengths will be of great value and benefit to the company if I were to be hired. I am confident that I can utilize them to help the company achieve its goals."
366
+ },
367
+ {
368
+ "instruction": "Will a credit card issuer cancel an account if it never incurs interest?",
369
+ "input": "",
370
+ "output": "Some years ago a call center operator told me a bit more than they probably should have. They like to see a lot of money go through the card, but very little staying on the card. Yes, they make money on the interest but one card defaulting blows away the profit on a lot of other cards. The 3% take from the merchants is both reliable and up-front, not 6 months down the line when (and if) you pay the interest. So if you want to make your credit card company happy, pay your bills in full every month. I have credit far beyond my actual means because I run work expenses on my personal card, I was told they didn't care (and had already guessed) that it wasn't my money. The point was I was handling things in a way they liked. Not quite at Palladium status, but cards with $200 annual fees are mine for the asking, and I haven't paid interest since the early 1990's."
371
+ },
372
+ {
373
+ "instruction": "Fix the following punctuation and spelling errors.",
374
+ "input": "We went outside for recess saturday",
375
+ "output": "We went outside for recess Saturday."
376
+ },
377
+ {
378
+ "instruction": "Take the given sentences and make a new sentence with them using a colon for emphasis.",
379
+ "input": "He was very excited. He was looking forward to the new adventure.",
380
+ "output": "He was very excited: he was looking forward to the new adventure."
381
+ },
382
+ {
383
+ "instruction": "What would I miss out on by self insuring my car?",
384
+ "input": "",
385
+ "output": "You're trading a fixed liability for an unknown liability. When I graduated from college, I bought a nice used car. Two days later, a deer came out of nowhere, and I hit it going 70 mph on a highway. The damage? $4,500. If I didn't have comprehensive insurance, that would have been a real hit to me financially. For me, I'd rather just pay the modest cost for the comprehensive."
386
+ },
387
+ {
388
+ "instruction": "Determine the base and the height of a triangle given its area.",
389
+ "input": "Area: 20 cm2",
390
+ "output": "Base: 10 cm, Height: 8.94 cm."
391
+ },
392
+ {
393
+ "instruction": "Assume you are a researcher. Explain the importance of conducting a survey.",
394
+ "input": "",
395
+ "output": "Surveys are an important tool for researchers as they help to collect large amounts of data in a short period of time. Surveys provide insight into the attitudes and behaviors of respondents, as well as the prevalence of certain attitudes and behaviors in specific groups. They can help to identify trends and attitudes that might otherwise be overlooked. Surveys can also be used to measure the effectiveness of interventions, compare outcomes across populations, and identify underlying causes of certain behaviors."
396
+ },
397
+ {
398
+ "instruction": "Put on a put option",
399
+ "input": "",
400
+ "output": "If you look at it from the hedging perspective, if you're unsure you're going to need to hedge but want to lock in an option premium price if you do need to do so, I could see this making sense."
401
+ },
402
+ {
403
+ "instruction": "Rewrite the following sentence to make it stronger:\n\nThe students are excited about their upcoming assignment.",
404
+ "input": "",
405
+ "output": "The students are thrilled with anticipation for their upcoming assignment."
406
+ },
407
+ {
408
+ "instruction": "Find and provide two sources to support the following statement",
409
+ "input": "The economic cost of climate change is estimated in trillions of dollars.",
410
+ "output": "Sources: \n1. https://www.weforum.org/agenda/2017/05/climate-change-economic-costs-billion-dollars/\n2. https://www.iea.org/news/global-cost-of-climate-change-could-reach-2-trillion-by-2030-report-finds"
411
+ },
412
+ {
413
+ "instruction": "What happens if I just don't pay my student loans?",
414
+ "input": "",
415
+ "output": "employed under the table and doesn't have a bank account If I could make that size 10,000,000 font I would. Your friend likely also isn't paying taxes. The student loan penalties will be nothing compared to what the IRS does to you. Avoid taking financial advice from that person."
416
+ },
417
+ {
418
+ "instruction": "Can used books bought off Amazon be claimed as a tax deduction in Australia?",
419
+ "input": "",
420
+ "output": "Yes, if they meet the ATO's criteria. Books, periodicals and digital information If the item cost less than $300 you can claim an immediate deduction where it satisfies all of the following requirements: http://www.ato.gov.au/Individuals/Income-and-deductions/Deductions-you-can-claim/Other-deductions/Books,-periodicals-and-digital-information/ Alternatively They may be a self-education expense http://www.ato.gov.au/Individuals/Income-and-deductions/Deductions-you-can-claim/Self-education-expenses/ A Further Alternative They could fall into the tool, equipment or other asset category if they are for a professional library (this can include a home office). http://www.ato.gov.au/Individuals/Income-and-deductions/Deductions-you-can-claim/Tools,-equipment-and-other-assets/ I understand this is an old question although given the dead link in the above answer and the new resources this answer might prove helpful for others coming across this question."
421
+ },
422
+ {
423
+ "instruction": "Classify the following color into warm and cool colors.",
424
+ "input": "Red",
425
+ "output": "Red is a warm color."
426
+ },
427
+ {
428
+ "instruction": "Give a brief overview of Machine Learning.",
429
+ "input": "",
430
+ "output": "Machine Learning is the process of creating algorithms that can learn from data to make decisions and predictions. It is used in a wide range of applications such as image recognition, natural language processing, speech recognition, robotics, and medical diagnostics. Machine Learning algorithms are designed to improve themselves over time by identifying patterns in data and making decisions based on those patterns. Machine Learning has become increasingly popular in recent years as new algorithms and technologies have allowed for faster and more accurate predictions and decisions to be made."
431
+ },
432
+ {
433
+ "instruction": "Match the given words to their respective descriptions?",
434
+ "input": "Structured Query Language: Database Management System",
435
+ "output": "Structured Query Language (SQL): Database Management System - SQL is a standard programming language that is used to interact with a relational database management system (RDBMS) to store, retrieve, and manipulate data. It is a declarative language that allows the user to specify what information they want from the database and allows the database to optimize the communication between the user and the database."
436
+ },
437
+ {
438
+ "instruction": "Getting Cash from Credit Card without Fees",
439
+ "input": "",
440
+ "output": "You said: Use a credit card (to get my 3% Cash back) to withdraw cash ... Then you said: Is there any way to do this without paying a cash advance fee (or any fees in general)? Right there you have stated the inconsistency. Withdrawing cash using a credit card is a cash advance. You may or may not be charged a fee for doing the cash advance, but no credit card will offer you cash back on a cash advance, so you can't earn your 3% by using cash advances. As others have mentioned, you can sometimes get close by using the card to purchase things that are almost like cash, such as gift cards. But you have to make a purchase."
441
+ },
442
+ {
443
+ "instruction": "Compare and contrast the characteristics of the desert and the jungle.",
444
+ "input": "",
445
+ "output": "Deserts are usually dry and have sparse vegetation, while jungles are usually wet and have dense vegetation. Desert landscapes can vary from sand dunes to mountains, while jungles are typically covered in trees and other vegetation. The temperature and weather in the desert tends to be very hot and arid, while the jungle is typically warm and humid."
446
+ },
447
+ {
448
+ "instruction": "Create a resume objective statement for a person seeking a position as a software engineer.",
449
+ "input": "",
450
+ "output": "A passionate software engineer seeking to leverage over 5 years of experience in designing and developing complex software applications in order to improve the performance and user experience of highly trafficked websites."
451
+ },
452
+ {
453
+ "instruction": "What is a good way to handle a difficult problem?",
454
+ "input": "",
455
+ "output": "A good way to handle a difficult problem is to take a step back and identify the root cause. Once you have identified the underlying cause, you can consider different solutions, weigh them carefully, and determine the best course of action. Additionally, it is important to gather the necessary resources needed to address the problem and stay organized."
456
+ },
457
+ {
458
+ "instruction": "Compose a blog article on the importance of exercise.",
459
+ "input": "",
460
+ "output": "Exercise is essential to living a healthy and active lifestyle. Regular exercise helps to improve physical strength and endurance, increase energy levels, improve cardiovascular health and lower risk of certain diseases. In addition, studies have also shown that exercise releases endorphins which can help to reduce stress and anxiety. Exercise can also help to build mental resilience, concentration, and focus.\n\nGetting started with an exercise routine does not have to be daunting. It can be as simple as a 10-minute walk or jog around the block and gradually increasing the intensity and duration as you get more comfortable with exercise. Regular physical activity can also be fun and your exercise routine can be tailored to your own personal interests. Whether it\u2019s going for a swim, running, biking, playing basketball or weight training, there is something for everyone.\n\nExercise is an important part of living a healthy and happy life, and it can have a great impact on both physical and mental wellbeing. So make sure to make time for regular physical activity to stay healthy and happy!"
461
+ },
462
+ {
463
+ "instruction": "Recent college grad. Down payment on a house or car?",
464
+ "input": "",
465
+ "output": "When I was in that boat a few years ago, I went for the car first. My thoughts: If I get the car first, I'm guaranteed to have a car that runs well. That makes it more convenient to commute to any job, or for social functions. I ended up dropping about $20k into a car (paid cash, I don't like being in debt). I chose to buy a really nice car, knowing it will last for many years to come - I'm expecting to not replace it for about 10 years from the purchase. I would urge you to consider paying in full for the car; dumping $20k+ is a lot, and there are plenty of nice cars out there in the $10-20k range that will work just fine for years to come. One benefit of paying in full is that you don't have a portion of your income tied into the car loan. The main reason I chose not to go for the house first had more to do with the difference in commitment. A home mortgage is a 30-year commitment on a large chunk of your income. With the job market and housing markets both currently working against you, it's better to wait until you have a large safety net to fall into. For example, it's always recommended to have several months worth of living expenses in savings. Compared to renting, having 6 or more months of mortgage payments + utilities + insurance + property taxes + other mandatory expenses (see: food, gas) comes out to a significant amount more that you should have saved (for me, I'm looking at a minimum of about $20k in savings just to feel comfortable; YMMV). Also, owning a house always has more maintenance costs than you will predict. Even if it's just replacing a few light bulbs at first, eventually you'll need something major: an appliance will die, your roof will spring a leak, anything (I had both of those happen in the first year, though it could be bad luck). You should make sure that you can afford the increased monthly payments while still well under your income. Once you're locked in to the house, you can still set aside a smaller chunk of your income for a new car 5-10 years down the road. But if you're current car is getting down to it's last legs, you should get that fixed up before you lock yourself in to an uncomfortable situation. Don't be in too much of a hurry to buy a house. The housing market still has a ways to go before it recovers, and there's not a whole lot to help it along. Interest rates may go up, but that will only hurt the housing market, so I don't expect it to change too much for the next several months. With a little bit of sanity, we won't have another outrageous housing bubble for many years, so houses should remain somewhat affordable (interest rates may vary). Also keep in mind that if you pay less that 20% down on the house, you may end up with some form of mortgage interest, which is just extra interest you'll owe each month."
466
+ },
467
+ {
468
+ "instruction": "Edit the following sentence to ensure it is grammatically correct and properly punctuated.",
469
+ "input": "The town was founded in the 1700s",
470
+ "output": "The town was founded in the 1700's."
471
+ },
472
+ {
473
+ "instruction": "income tax for purchased/sold short term & long term shares",
474
+ "input": "",
475
+ "output": "As mentioned by Dilip, you need to provide more details. In general for transacting on stocks; Long Term: If you hold the stock for more than one year then its long term and not taxable. There is a STT [Securities Transaction Tax] that is already deducted/paid during buying and selling of a stock. Short Term: If you hold the stock for less than one year, it's short term gain. This can be adjusted against the short term loss for the financial year. The tax rate is 10%. Day Trading: Is same as short term from tax point of view. Unless you are doing it as a full time business. If you have purchased multiple quantities of same stock in different quantities and time, then when you selling you have to arrive at profit or loss on FIFO basis, ie First in First Out"
476
+ },
477
+ {
478
+ "instruction": "Generate a list of landscape gardening projects for a garden that is 30m by 20m.",
479
+ "input": "Garden size: 30m by 20m",
480
+ "output": "1. Install a pergola for shade and comfort.\n2. Set up garden lighting for nighttime blooms and show. \n3. Set up raised flower gardens with different varieties of plants.\n4. Install a water feature such as a fountain or pond.\n5. Plant trees to provide shade and natural beauty.\n6. Plant climbing vines on fences and walls.\n7. Set up a vegetable garden.\n8. Construct a path of stones and gravel.\n9. Add a seating area with outdoor furniture.\n10. Utilize vertical lattices or garden arches to create a layered look and texture."
481
+ },
482
+ {
483
+ "instruction": "How does the value of an asset (valued in two different currencies) change when the exchange rate changes?",
484
+ "input": "",
485
+ "output": "Gold is traded on the London stock exchange (LSE) and the New York stock exchange (NYSE) under various separate asset tickers, mainly denominated in sterling and US dollars respectively. These stocks will reflect FX changes very quickly. If you sold LSE gold and foreign exchanged your sterling to dollars to buy NYSE gold you would almost certainly lose on the spreads upon selling, FX'ing and re-buying. In short, the same asset doesn't exist in multiple currencies. It may have the same International Securities Identification Number (ISIN), but it can trade with different Stock Exchange Daily Official List (SEDOL) identifiers, reflecting different currencies and/or exchanges, each carrying a different price at any one time."
486
+ }
487
+ ]
data/training_data_20251102_220943.json ADDED
@@ -0,0 +1,487 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "instruction": "What is the area of a triangle with base of 6 units and height of 9 units?",
4
+ "input": "",
5
+ "output": "The area of the triangle is 27 units."
6
+ },
7
+ {
8
+ "instruction": "Describe the causes and symptoms of depression.",
9
+ "input": "",
10
+ "output": "Depression is caused by a combination of genetic, environmental, emotional, and interpersonal factors. The main symptoms may include sadness, lack of motivation, fatigue, difficulty concentrating, changes in appetite and sleep patterns, low self-esteem, and thoughts of suicide."
11
+ },
12
+ {
13
+ "instruction": "Design an icon for a smartphone application.",
14
+ "input": "Name of Application: Health Monitor",
15
+ "output": "A visually attractive and eye-catching icon design for Health Monitor. The design features a graphic of a heart concentric to a drop of water, with varying shades of blue and green. The colors represent a healthy, vibrant life. The design conveys the idea of staying healthy and active with a modern and unique look."
16
+ },
17
+ {
18
+ "instruction": "Create a pastry dish with the ingredients given.",
19
+ "input": "Pecans, cinnamon, honey",
20
+ "output": "Honey Pecan Cinnamon Rolls - a sweet and simple pastry made with pecans, cinnamon, and honey. The pecans add a nutty flavor and crunch, while the honey and cinnamon provide a warm sweetness that melts in your mouth."
21
+ },
22
+ {
23
+ "instruction": "Provide three examples of technologies in artificial intelligence",
24
+ "input": "",
25
+ "output": "1. Natural language processing (NLP) \n2. Machine Learning \n3. Speech Recognition"
26
+ },
27
+ {
28
+ "instruction": "Name two US Presidents that served in the 20th century.",
29
+ "input": "",
30
+ "output": "Ronald Reagan and Bill Clinton."
31
+ },
32
+ {
33
+ "instruction": "Write a one-sentence advertisement for a computer store.",
34
+ "input": "",
35
+ "output": "Come to our store for the latest computers, tablets, and accessories at unbeatable prices!"
36
+ },
37
+ {
38
+ "instruction": "Google free real-time stock quotes",
39
+ "input": "",
40
+ "output": "Previously, Google had a delayed update for their stock prices (15 minutes I believe). That change enabled users of Google Finance to see updates to stock prices in real-time."
41
+ },
42
+ {
43
+ "instruction": "Brainstorm ideas for a new business.",
44
+ "input": "",
45
+ "output": "Starting a business can be an exciting adventure, but it requires a lot of planning. Here are some ideas to consider when thinking about a new business venture:\n\n- Start a delivery business that helps local restaurants reach customers.\n- Create an online marketplace that specializes in vintage goods.\n- Launch a content marketing agency to help businesses build an online presence.\n- Open a boutique bakery that focuses on vegan and gluten-free desserts.\n- Build an app that connects homeowners with local handymen. \n- Develop a web-based software program to manage documents and paperwork. \n- Become a virtual assistant and provide online services to entrepreneurs. \n- Launch a subscription box service that ships snacks and treats from around the world. \n- Open a bike tour business that allows tourists to explore the city.\n- Create an online store that specializes in eco-friendly lifestyle products."
46
+ },
47
+ {
48
+ "instruction": "Do Banks Cause Inflation? What are other possible causes?",
49
+ "input": "",
50
+ "output": "There are several causes of inflation. One is called cost push \u2014 that is, if the price of e.g. oil goes up sharply (as it did in the 1970s), it creates inflation by making everything cost more. Another is called demand pull: if labor unions bargain for higher wages (as they did in the 1960s), their wage costs push up prices, especially after they start buying. The kind of inflation that the banks cause is monetary inflation. That is, for every dollar of deposits, they can make $5 or $10 of loans. So even though they don't \"print\" money (the Fed does) it's as if they did. The result could be the kind of inflation called \"too much money chasing too few goods.\""
51
+ },
52
+ {
53
+ "instruction": "What are the five steps of problem solving?",
54
+ "input": "",
55
+ "output": "The five steps of problem solving are: identifying the problem, understanding the problem, collecting relevant information, brainstorming and evaluating alternatives, and selecting and implementing the best solution."
56
+ },
57
+ {
58
+ "instruction": "Write a few sentences to explain why marketing plays an important role in business.",
59
+ "input": "",
60
+ "output": "Marketing plays an essential role in any business as it helps in growing awareness of the company's products and services. Through effective marketing, companies can create demand for their products and services and build customer loyalty. Marketing also helps businesses differentiate themselves from their competitors and keep their customers engaged. Additionally, marketing helps establish a company's brand in the market and allows consumers to connect with the company's mission and values."
61
+ },
62
+ {
63
+ "instruction": "Generate 5 different names for a bar.",
64
+ "input": "",
65
+ "output": "The Bubble, Maron's Tavern, The Foggy Mug, The Midnight Ale House, Donahue's Pub."
66
+ },
67
+ {
68
+ "instruction": "Describe the steps required to build a software application.",
69
+ "input": "",
70
+ "output": "Building a software application typically involves six main steps: (1) understanding the problems and goals of the application, (2) preparing the architecture and design of the application, (3) developing the different components and modules, (4) testing and debugging the application, (5) deploying and integrating the application, and (6) maintaining and updating the application."
71
+ },
72
+ {
73
+ "instruction": "Find an open-ended question that explores the impact of machine learning on the healthcare industry.",
74
+ "input": "",
75
+ "output": "How can machine learning be used to improve the effectiveness of healthcare treatments?"
76
+ },
77
+ {
78
+ "instruction": "Imagine that you walk into a cafe to have a cup of coffee. Describe the experience in less than 100 words.",
79
+ "input": "",
80
+ "output": "Walking into the cafe, I was immediately struck by the warm atmosphere. Everywhere I looked, people were chatting over coffee or working on laptops. There was a pleasant aroma of freshly-brewed coffee in the air which made me feel relaxed. I stepped up to the counter to order my coffee, and was pleasantly surprised by the selection of exotic coffee blends. I found a comfortable spot to enjoy my coffee and watched the people around me. With its cozy atmosphere, friendly staff, and great coffee, it was a perfect place to relax."
81
+ },
82
+ {
83
+ "instruction": "Generate a list of ingredients and instructions to make a dish.",
84
+ "input": "Apple Pie",
85
+ "output": "Ingredients:\n\u2022 2 cups all-purpose flour\n\u2022 1 teaspoon salt\n\u2022 1 cup (2 sticks) unsalted butter, cold\n\u2022 7 to 8 Granny Smith apples, peeled, cored, and thinly sliced \n\u2022 1 cup white sugar\n\u2022 1 tablespoon lemon juice\n\u2022 1/4 teaspoon nutmeg\n\u2022 2 tablespoons all-purpose flour\n\u2022 1 tablespoon cold butter\nInstructions:\n\u2022 Preheat the oven to 375 degrees F (190 degrees C).\n\u2022 In a medium bowl, mix together 2 cups flour and salt. Cut in 1 cup butter until mixture resembles coarse crumbs.\n\u2022 Place the mixture into a 9-inch pie dish.\n\u2022 In a large bowl, toss apples with sugar, lemon juice, nutmeg, and 2 tablespoons flour.\n\u2022 Spoon the apple mixture into the pastry-lined dish. Cut the 1 tablespoon butter into small pieces and sprinkle over the top.\n\u2022 Cover the filling with a top crust, and flute the edges. Cut several steam vents in the top.\n\u2022 Bake at 375 degrees F (190 degrees C) for 45 minutes, or until apples are tender and crust is golden brown."
86
+ },
87
+ {
88
+ "instruction": "Develop an algorithm that can solve a Rubik's cube.",
89
+ "input": "",
90
+ "output": "The Rubik's cube can be solved using a combination of two algorithms - the \"Corner-Edge Pairing\" method, and the \"Layer-By-Layer\" method. \n\nThe Corner-Edge Pairing algorithm involves pairing up the incorrect cube pieces, and then orienting them correctly. This can be done by using a series of algorithms, involving \u201ctriple flips\u201d, \u201cuneven swap\u201d and \u201cloopen\u201d. \n\nThe Layer-By-Layer algorithm involves solving each layer of the cube independently. This algorithm utilizes the idea of creating a \u201ccross\u201d on the top layer, and then solving each corner and edge of the cube one-by-one. \n\nThese techniques can be combined to form an algorithm to solve a Rubik\u2019s cube."
91
+ },
92
+ {
93
+ "instruction": "Given a sentence, rewrite it without changing the meaning.",
94
+ "input": "I had a great time at the party.",
95
+ "output": "I truly enjoyed the gathering."
96
+ },
97
+ {
98
+ "instruction": "Money market account for emergency savings",
99
+ "input": "",
100
+ "output": "I'm not a fan of using cash for \"emergency\" savings. Put it in a stable investment that you can liquidate fairly quickly if you have to. I'd rather use credit cards for a while and then pay them off with investment funds if I must. Meanwhile those investments earn a lot more than the 0.1 percent savings or money market accounts will. Investment grade bond funds, for example, should get you a yield of between 4-6% right now. If you want to take a longer term view put that money into a stock index fund like QQQ or DIA. There is the risk it will go down significantly in a recession but over time the return is 10%. (Currently a lot more than that!) In any event you can liquidate securities and get the money into your bank is less than a week. If you leave it in cash it basically earns nothing while you wait for that rainy day which many never come."
101
+ },
102
+ {
103
+ "instruction": "Rewrite an existing text to include vivid descriptions.",
104
+ "input": "Alice sat down in front of the computer and started typing",
105
+ "output": "Alice plopped down into her chair, her keys a gentle clacking against the old keyboard as she settled in for the task ahead. She gave a deep breath, centering herself for the typing marathon ahead of her, and with determined fingertips began to fill the blank screen with her thoughts."
106
+ },
107
+ {
108
+ "instruction": "Provide a brief analysis of the given poem.",
109
+ "input": "\"Still I Rise\" by Maya Angelou\n\nYou may write me down in history\nWith your bitter, twisted lies,\nYou may trod me in the very dirt\nBut still, like dust, I'll rise.",
110
+ "output": "\"Still I Rise\" by Maya Angelou is a powerful poem that celebrates resilience and perseverance in the face of adversity. The poem speaks to the power of human spirit to overcome hardship and shatters the oppressive forces of bias and hate. In the first two lines, the speaker stands tall in the face of criticism and brutality and rises to challenge lies and injustice. The poem speaks of the strength of the human spirit and reminds readers that even though they may get knocked down, they will still rise."
111
+ },
112
+ {
113
+ "instruction": "Do Affordable Care Act business requirements apply to \u201ccontrol groups?\u201d",
114
+ "input": "",
115
+ "output": "Yes, it applies to control groups. If I remember correctly common ownership rules are used to determine \"Applicable Large Employer\" status but if the time comes to owe a penalty, only the actual entity missing the mark will owe a penalty, not the entire control group. This is an excerpt from Section 4980H (the section that lays out employer requirements and penalties) (16) Employer. The term employer means the person that is the employer of an employee under the common-law standard. See \u00a7 31.3121(d)-1(c). For purposes of determining whether an employer is an applicable large employer, all persons treated as a single employer under section 414(b), (c), (m), or (o) are treated as a single employer. Thus, all employees of a controlled group of entities under section 414(b) or (c), an affiliated service group under section 414(m), or an entity in an arrangement described under section 414(o), are taken into account in determining whether the members of the controlled group or affiliated service group together are an applicable large employer. For purposes of determining applicable large employer status, the term employer also includes a predecessor employer (see paragraph (a)(36) of this section) and a successor employer. Link to the Federal Register"
116
+ },
117
+ {
118
+ "instruction": "What is the current time in New York?",
119
+ "input": "",
120
+ "output": "The current time in New York is 1:38 PM EDT."
121
+ },
122
+ {
123
+ "instruction": "Please describe the life of humans in 2050?",
124
+ "input": "",
125
+ "output": "In 2050, humans will live in a world where technology has shaped the way people live. There will be a greater reliance on automation and robotic assistance. The global economy will be powered by renewable energy sources, and most climate-disrupting emissions will have been drastically reduced. People will have improved access to healthcare, education, housing, and all types of opportunities. Cities will be more connected and cultural exchange will be a norm. Human quality of life will have vastly improved due to the advancements in technology."
126
+ },
127
+ {
128
+ "instruction": "Generate a headline by combining two cliche expressions.",
129
+ "input": "",
130
+ "output": "\"A Picture Is Worth A Thousand Words\"."
131
+ },
132
+ {
133
+ "instruction": "In what type of sentence is the word \"euphemism\" used?",
134
+ "input": "",
135
+ "output": "The word \"euphemism\" is typically used in a noun phrase. For example: \"He employed a euphemism when discussing the controversial subject.\""
136
+ },
137
+ {
138
+ "instruction": "Name 5 job duties of a sales manager",
139
+ "input": "",
140
+ "output": "1. Identify new market opportunities and develop strategies to increase sales. \n2. Develop and Implement sales activities and plans to achieve company objectives.\n3. Monitor sales performance and address performance gaps.\n4. Oversee customer service and satisfaction.\n5. Recruit, hire and onboard sales personnel and other staff."
141
+ },
142
+ {
143
+ "instruction": "Name five sports and the countries they originated in.",
144
+ "input": "",
145
+ "output": "1. Soccer (England) \n2. Basketball (United States) \n3. Baseball (United States) \n4. Hockey (Canada) \n5. Cricket (England)"
146
+ },
147
+ {
148
+ "instruction": "Identify a major benefit of using machine learning.",
149
+ "input": "",
150
+ "output": "One of the major benefits of using machine learning is the ability to quickly understand and analyze large amounts of data. This can help companies identify patterns, trends, and relationships within the data that humans might not be able to find. This can lead to more efficient operations and higher successes within the organizations."
151
+ },
152
+ {
153
+ "instruction": "\u201cBusiness day\u201d and \u201cdue date\u201d for bills",
154
+ "input": "",
155
+ "output": "You definitely have an argument for getting them to reverse the late fee, especially if it hasn't happened very often. (If you are late every month they may be less likely to forgive.) As for why this happens, it's not actually about business days, but instead it's based on when they know that you paid. In general, there are 2 ways for a company to mark a bill as paid: Late Fees: Some systems automatically assign late fees at the start of the day after the due date if money has not been received. In your case, if your bill was due on the 24th, the late fee was probably assessed at midnight of the 25th, and the payment arrived after that during the day of the 25th. You may have been able to initiate the payment on the company's website at 11:59pm on the 24th and not have received a late fee (or whatever their cutoff time is). Suggestion: as a rule of thumb, for utility bills whose due date and amount can vary slightly from month to month, you're usually better off setting up your payments on the company website to pull from your bank account, instead of setting up your bank account to push the payment to the company. This will ensure that you always get the bill paid on time and for the correct amount. If you still would rather push the payment from your bank account, then consider setting up the payment to arrive about 5 days early, to account for holidays and weekends."
156
+ },
157
+ {
158
+ "instruction": "Provide an example of an ethical issue related to Artificial Intelligence technology.",
159
+ "input": "",
160
+ "output": "One ethical issue related to Artificial Intelligence technology is privacy. AI systems rely on large amounts of data to train and operate, which can include personal information of individuals. This data can easily be used for unintended purposes, such as targeting ads or making biased decisions. It is important for companies to ensure that their AI systems are able to protect the privacy of individuals, as well as adhere to applicable laws and regulations."
161
+ },
162
+ {
163
+ "instruction": "Generate an example of an object-oriented design.",
164
+ "input": "",
165
+ "output": "In object-oriented design, a Car class could make use of a Wheel class which models individual car wheels. The Car class can use the Wheel class to model its wheels, allowing the Car class to keep track of the details involved with its wheels, such as what type of tire is being used and how much pressure is in each tire."
166
+ },
167
+ {
168
+ "instruction": "I have savings and excess income. Is it time for me to find a financial advisor?",
169
+ "input": "",
170
+ "output": "Is my financial status OK? You have money for emergencies in the bank, you spend less than you earn. Yes, your status is okay. You will have a good standard of living if nothing changes from your status quo. How can I improve it? You are probably paying more in taxes than you would if you made a few changes. If you max out tax advantaged retirement accounts that would reduce the up-front taxes you are paying on your savings. Is now a right time for me to see a financial advisor? The best time to see a financial advisor is any time that your situation changes. New job? Getting married? Having a child? Got a big promotion or raise? Suddenly thinking about buying a house? Is it worth the money? How would she/he help me? If you pick an advisor who has incentive to help you rather than just pad his/her own pockets with commissions, then the advice is usually worth the money. If there is someone whose time is already paid for, that may be better. For example, if you get an accountant to help you with your taxes and ask him/her how to best reduce your taxes the next year, the advice is already paid-for in the fee you for the tax help. An advisor should help you minimize the high taxes you are almost certainly paying as a single earner, and minimize the stealth taxes you are paying in inflation (on that $100k sitting in the bank)."
171
+ },
172
+ {
173
+ "instruction": "Combine the following two recipes to get a delicious meal.",
174
+ "input": "Recipe 1: 2 tablespoons olive oil, 1 onion chopped.\nRecipe 2: 4 cloves garlic, 1 red bell pepper diced.",
175
+ "output": "In a large skillet, heat the olive oil over medium heat. Add the onion and cook for 5 minutes, stirring often. Add the garlic and red bell pepper and cook for an additional 5 minutes, stirring often. Serve hot."
176
+ },
177
+ {
178
+ "instruction": "Explain the role of B-DNA in the human body.",
179
+ "input": "",
180
+ "output": "B-DNA is the most common form of DNA, which is a double helix of two complementary DNA strands linked together. It is essential for a range of functions in the human body, including the replication, transcription and translation of genetic information. B-DNA is mainly responsible for the formation of proteins, which are the building blocks of many structures and organs in the body. It is also involved in the control of gene expression and metabolism to ensure the proper functioning of the body."
181
+ },
182
+ {
183
+ "instruction": "How to learn about doing technical analysis? Any suggested programs or tools that teach it?",
184
+ "input": "",
185
+ "output": "A great way to learn is by watching then doing. I run a very successful technical analysis blog, and the first thing I like to tell my readers is to find a trader online who you can connect with, then watch them trade. I particularly like Adam Hewison, Marketclub.com - This is a great website, and they offer a great deal of eduction for free, in video format. They also offer further video based education through their ino.tv partner which is paid. Here is a link that has their free daily technical analysis based stock market update in video format. Marketclub Daily Stock Market Update Corey Rosenblum, blog.afraidtotrade.com - Corey is a Chartered Market Technician, and runs a fantastic technical analysis blog the focuses on market internals and short term trades. John Lansing, Trending123.com - John is highly successful trader who uses a reliable set of indicators and patterns, and has the most amazing knack for knowing which direction the markets are headed. Many of his members are large account day traders, and you can learn tons from them as well. They have a live daily chat room that is VERY busy. The other option is to get a mentor. Just about any successful trader will be willing to teach someone who is really interested, motivated, and has the time to learn. The next thing to do once you have chosen a route of education is to start virtual trading. There are many platforms available for this, just do some research on Google. You need to develop a trading plan and methodology for dealing with the emotions of trading. While there is no replacement for making real trades, getting some up front practice can help reduce your mistakes, teach you a better traders mindset, and help you with the discipline necessary to be a successful trader."
186
+ },
187
+ {
188
+ "instruction": "Can my employer limit my maximum 401k contribution amount (below the IRS limit)?",
189
+ "input": "",
190
+ "output": "Companies are required BY THE IRS to try to get everybody to contribute minimal amounts to the 401K's. In the past, there were abuses and only the execs could contribute and the low paid workers were starving while the execs contributed huge amounts. On a year-by-year basis, if the low-paid employees don't contribute, the IRS punishes the high paid employees. Therefore, most employers provide a matching program to incentivize low-paid employees to contribute. This 9% limitation could happen in any year and it could have happened even before you got your pay raise, what matters is what the low-paid employees were doing at your company LAST YEAR."
191
+ },
192
+ {
193
+ "instruction": "Generate an example to illustrate how sentiment analysis works.",
194
+ "input": "",
195
+ "output": "Sentiment analysis is a method of analyzing consumer opinions by assigning a numerical score to words and phrases in text data. For example, a sentiment analysis algorithm may assign a score of +1 for each occurrence of the word \"love\" and a score of -1 for each occurrence of the word \"hate\". By summing the scores for each word in a text, the algorithm can give an overall sentiment score, which will be positive if the overall opinion is positive and negative if the overall opinion is negative."
196
+ },
197
+ {
198
+ "instruction": "Analyse the text and classify it into different genres",
199
+ "input": "He opened the window and looked out at the vast expanse of blue sky, framed by the gentle rolling hills",
200
+ "output": "Genre: Nature/Landscape description"
201
+ },
202
+ {
203
+ "instruction": "Convert the date July 28th, 2020 to a Unix timestamp.",
204
+ "input": "",
205
+ "output": "July 28th, 2020 is equal to 1596126400 in Unix timestamp."
206
+ },
207
+ {
208
+ "instruction": "Take a message and make it briefer while preserving the same meaning.",
209
+ "input": "I think it's important to check in on the project progress every two weeks.",
210
+ "output": "Check in on project progress every two weeks."
211
+ },
212
+ {
213
+ "instruction": "Given a sentence, generate a paraphrase of the sentence that is 8 to 10 words in length.",
214
+ "input": "John quickly ran to the store.",
215
+ "output": "John hastened to the store."
216
+ },
217
+ {
218
+ "instruction": "Who owned my shares before me?",
219
+ "input": "",
220
+ "output": "Shares do not themselves carry any identity. Official shareholders are kept at the registrar. In the UK, this may be kept up to date and publicly accessible. In the US, it is not, but this doesn't matter because most shares are held \"in street name\". For a fully detailed history, one would need access to all exchange records, brokerage records, and any trades transacted off exchange. These records are almost totally unavailable."
221
+ },
222
+ {
223
+ "instruction": "What are the tax implications on selling the Share ISA portfolio?",
224
+ "input": "",
225
+ "output": "Assets held within an ISA or New ISA are completely free of CGT, so you won't have to pay either income tax or capital gains tax."
226
+ },
227
+ {
228
+ "instruction": "In the following sentence, mention the type of phrase used.",
229
+ "input": "She sang a lullaby to the sleeping baby.",
230
+ "output": "The phrase used in the sentence is a gerund phrase: \"singing a lullaby\"."
231
+ },
232
+ {
233
+ "instruction": "Summarize the story of 'Goldilocks and the Three Bears'.",
234
+ "input": "",
235
+ "output": "Goldilocks is a young girl who enters the home of the three bears. She tries out their food, chairs and beds, and eventually gets caught. The three bears are forgiving and she runs away."
236
+ },
237
+ {
238
+ "instruction": "Company wants to sell all of its assets, worth more than share price?",
239
+ "input": "",
240
+ "output": "Why is the stock trading at only $5 per share? The share price is the perceived value of the company by people buying and selling the stock. Not the actual value of the company and all its assets. Generally if the company is not doing well, there is a perceived risk that it will burn out the money fast. There is a difference between its signed conditional sale and will get money and has got money. So in short, it's trading at $5 a share because the market doesn't feel like it's worth $12 per share. Quite a few believe there could be issues faced; i.e. it may not make the $12, or there will be additional obligations, i.e. employees may demand more layoff compensation, etc. or the distribution may take few years due to regulatory and legal hurdles. The only problem is the stock exchange states if the company has no core business, the stock will be suspended soon (hopefully they can release the $12 per share first). What will happen if I hold shares in the company, the stock gets suspended, and its sitting on $12 per share? Can it still distribute it out? Every country and stock markets have laid out procedures for de-listing a company and closing a company. The company can give $10 as say dividends and remaining later; or as part of the closure process, the company will distribute the balance among shareholders. This would be a long drawn process."
241
+ },
242
+ {
243
+ "instruction": "Summarize what Isaac Newton discovered",
244
+ "input": "",
245
+ "output": "Sir Isaac Newton is widely regarded as one of the most influential scientists of all time. He discovered the laws of motion and gravity, developed calculus, and studied optics and physics. He also developed the mathematical methods needed to derive the laws of motion, studied the properties of white light and developed the first reflecting telescope. His discoveries laid the groundwork for many scientific advancements since then."
246
+ },
247
+ {
248
+ "instruction": "Why would a company have 2 listings on the same exchange?",
249
+ "input": "",
250
+ "output": "A company can issue different kinds of shares. For example, some kinds of shares may get preference in dividends or payment in event of (company) bankruptcy. Preferred shares are an example of this. A company might have several kinds of preferred shares and a 'common stock'. Here is a good explanation. See too the Wikipedia article about preferred stock. Toronto-Dominion Bank (TD) is an example of a company that has fourteen different preferred share issues, each with its own listing on the Toronto Stock Exchange (TSE) and symbol. TD has one kind of common stock, which is also listed on the TSE. However, TD common equity trades much more actively than the preferred shares. Remember that preferred stock is a different security type than common stock e.g. common has voting rights, preferred does not."
251
+ },
252
+ {
253
+ "instruction": "Report a bug for the given software.",
254
+ "input": "The newly released online shopping widget has been crashing intermittently.",
255
+ "output": "We have identified a bug on the new online shopping widget which causes unpredictable crashing."
256
+ },
257
+ {
258
+ "instruction": "Write a short story which reflects the given scenario.",
259
+ "input": "A small-town girl is leaving her home to pursue her dream job in a big city.",
260
+ "output": "Mary was a small-town girl, with big city dreams. She was so inspired by the big city lifestyle she had seen in the movies and dreamed that one day she could experience it for herself.\n\nFinally the day arrived when she received the news that she had been offered her dream job in the big city. Excitedly, she began to pack her bags and told her family goodbye. \n\nAs she drove away, she looked back at the small town she had grown up in and smiled, feeling ready to take on the world. She thought of all the opportunities the big city would offer her and all of the people she would meet. It was a fresh start, and Mary could feel her heart pounding with anticipation as she drove off towards her future."
261
+ },
262
+ {
263
+ "instruction": "Conduct a survey to find out what people think of the new iPhone.",
264
+ "input": "",
265
+ "output": "I conducted an online survey to find out what people think of the new iPhone. Out of the 500 people who participated in the survey, 80% said that they were extremely satisfied with the performance. 6% said that the phone was great but too expensive. The remaining 14% said that the phone had too many features and wasn't user-friendly."
266
+ },
267
+ {
268
+ "instruction": "You are provided with a search query, find the top 5 relevant web pages to the query and output their URLs.",
269
+ "input": "Open source software",
270
+ "output": "https://opensource.com/, https://en.wikipedia.org/wiki/List_of_open-source_software, https://www.slant.co/topics/7625/~open-source-software, https://www.techopedia.com/definition/16575/open-source-software, https://en.wikipedia.org/wiki/Open-source_software."
271
+ },
272
+ {
273
+ "instruction": "Given two lyrics, create a new melodic line that fits both.",
274
+ "input": "The sky is dark and wild\nIt's a long journey home",
275
+ "output": "And the wind's blowing free,\nWe'll take it mile by mile"
276
+ },
277
+ {
278
+ "instruction": "Research and summarize the common practices for caring of rabbits.",
279
+ "input": "",
280
+ "output": "Caring for a rabbit involves providing them with a comfortable environment, ample space to roam, appropriate food and diet, protection from predators, and regular interaction. Rabbits are social creatures, so it is important to keep them in groups with other rabbits or to spend quality time with them. Their housing space should provide enough room for them to move around and have toys to play with. \n\nRabbits should be fed a proper diet of hay, fresh vegetables, and a small amount of timothy or rabbit pellets, to ensure they get the vitamins, minerals, and nutrients they need. Water should be available at all times and the food changed regularly. \n\nFinally, rabbits need to be protected from predators or other dangers, such as extreme heat or cold, as well as kept clean. Regularly grooming a rabbit can help keep their coat in good condition."
281
+ },
282
+ {
283
+ "instruction": "Identify similar objects in the following list.",
284
+ "input": "Banana, Peach, Carrot, Apple",
285
+ "output": "Banana and Apple are similar objects as they are both fruits."
286
+ },
287
+ {
288
+ "instruction": "Delete the adjective 'bustling' from the following sentence and rewrite it.",
289
+ "input": "The bustling city of Tokyo was awe-inspiring.",
290
+ "output": "The city of Tokyo was awe-inspiring."
291
+ },
292
+ {
293
+ "instruction": "Fill in the blank with an adjective that describes the following: A person who enjoys learning new things.",
294
+ "input": "",
295
+ "output": "A person who enjoys learning new things is inquisitive."
296
+ },
297
+ {
298
+ "instruction": "Write an IF-THEN statement to control the temperature in a room.",
299
+ "input": "",
300
+ "output": "If the temperature in the room is above 75 degrees, then turn on the air conditioner."
301
+ },
302
+ {
303
+ "instruction": "Generate a suggested next move if a checker piece is in position 7C.",
304
+ "input": "Position = 7C",
305
+ "output": "If the checker piece is in position 7C, the suggested next move would be to move the checker diagonally to position 8B. This move would capture an opponent's piece if there is one in position 8B. If there is not an opponent's piece in that position, the checker piece can remain in 7C."
306
+ },
307
+ {
308
+ "instruction": "Can I get a discount on merchandise by paying with cash instead of credit?",
309
+ "input": "",
310
+ "output": "Slightly off topic... Not merchandise, but I paid for various doctor's appointments with cash (as opposed to paying with health insurance). I'd call ahead of time and notify them that I'd be paying in cash. I got ridiculous discounts, sometimes even less than the copay. I do not know why this discrepancy exists and I didn't want to ask for fear of messing up a good thing."
311
+ },
312
+ {
313
+ "instruction": "Summarize the main reasons why small businesses need a website.",
314
+ "input": "",
315
+ "output": "Having a website is essential for small businesses because it allows them to market their products and services, to reach new customers, to generate leads and sales, and to increase their visibility. It also makes it easier for customers to find information about their business, providing them a platform to build trust and credibility."
316
+ },
317
+ {
318
+ "instruction": "Compose a tweet of no more than 280 characters to advertise a new gadget.",
319
+ "input": "",
320
+ "output": "Introducing the new SuperGadget! With its sleek design and automated features, this is the perfect gadget to make your life easier. #SuperGadget #Technology #Convenience"
321
+ },
322
+ {
323
+ "instruction": "Find five sources to back up the statement",
324
+ "input": "Humans are the primary cause of global climate change.",
325
+ "output": "1. Driver, N. (2019). Humans are the primary cause of global climate change, says NASA. [online] The Guardian. Available at: https://www.theguardian.com/environment/2019/aug/07/humans-are-the-primary-cause-of-global-climate-change-says-nasa [Accessed 14 Apr. 2020].\n \n2. IPCC, (2018). Summary for policy makers. In: Global Warming of 1.5\u00b0C. An IPCC Special Report. World Meteorological Organisation, pp.6. \n\n3. Cook, J. et al. (2013). Quantifying the consensus on anthropogenic global warming in the scientific literature. Environmental Research Letters, 8(2), p.020,775. \n \n4. Anderson, K. et al. (2008). Anthropogenic contributions and natural trends in global and regional temperature change. Annual Review of Environment and Resources, 33(1), pp.119\u2013139.\n\n5. Hansen, J. et al. (2012). Perceptions of climate change: The new climate dice. Proceedings of the National Academy of Sciences, 112(1), p.513\u2013516."
326
+ },
327
+ {
328
+ "instruction": "Generate three investment options for someone who has 10,000 dollars and wants to invest it for 10 years.",
329
+ "input": "",
330
+ "output": "1. Mutual funds - Invest in low-cost, diversified mutual funds which can help reduce risk.\n2. Exchange-traded funds (ETFs) - A convenient way to invest in a variety of different markets.\n3. Bond funds - Invest in safe and secure bonds to earn regular income."
331
+ },
332
+ {
333
+ "instruction": "Find a definition for the term 'big O notation'",
334
+ "input": "",
335
+ "output": "Big O notation is a mathematical notation that describes the limitation of an algorithm in relation to the input size. It measures the time complexity of an algorithm, i.e. the amount of time taken to execute it, and the space complexity, i.e. the amount of memory required. It is often used to compare different algorithms to determine which one is more efficient in terms of time and space."
336
+ },
337
+ {
338
+ "instruction": "Name five famous Spanish restaurants in Madrid.",
339
+ "input": "",
340
+ "output": "Casa Buenavista, Lateral Gastrobar, El Brillante, Restaurante Bot\u00edn, and Taberna La Bola are some of the most famous Spanish restaurants in Madrid."
341
+ },
342
+ {
343
+ "instruction": "What to sell when your financial needs change, stocks or bonds?",
344
+ "input": "",
345
+ "output": "So I don't have any problems with your analysis or the comments associated with it. I just wanted to mention that no one is talking about taxes. Your answer....Figure out new portfolio breakdown and sell to 1.) Get money I need and 2.) re-balance the portfolio to my new target allocations is completely correct. (Unimpeachable in my opinion.) However, when you calculate what you need to sell to meet your current cash needs make sure to include in that analysis money to pay taxes on anything you sell for a gain, or keep some invested to account for the tax money you would save by selling things for a loss. The actual mechanics of calculating what these amounts are are fairly involved but not difficult to understand. (IE every situation is different.) Best of luck to you, and I hope your cashflow gets back up to its previous level soon."
346
+ },
347
+ {
348
+ "instruction": "Brainstorm three use cases for a certain technology.",
349
+ "input": "Augmented Reality",
350
+ "output": "1. Augmented Reality can be used in medicine, allowing real-time 3D visualizations of medical scans and data, providing more accurate diagnoses and more effective treatments. \n2. Augmented Reality can be used in gaming and entertainment, providing a more immersive experience. \n3. Augmented Reality can be used in architecture, allowing architects to visualize a proposed structure as if it were already built."
351
+ },
352
+ {
353
+ "instruction": "Suggest the most helpful study habits for a student preparing for an upcoming English Literature exam.",
354
+ "input": "",
355
+ "output": "The best study habits for a student preparing for an upcoming English Literature exam include reading the assigned literature and class notes thoroughly, outlining key points and ideas in the literature and taking part in study groups or one-on-one discussions with classmates. Additionally, it is important to take frequent practice exams to help identify areas of weakness. Other helpful habits include breaking the material into smaller chunks for more manageable study sessions and using mnemonic devices to help memorize numbers, dates, and facts."
356
+ },
357
+ {
358
+ "instruction": "Why would you ever turn down a raise in salary?",
359
+ "input": "",
360
+ "output": "If you have children in a university institution, then your annual salary is reported via financial aid forms. The small raise could be the difference between full tuition covered and only half tuition covered."
361
+ },
362
+ {
363
+ "instruction": "Why does the biotechnology industry have such a high PE ratio?",
364
+ "input": "",
365
+ "output": "Residential Construction at 362x, by the way. I'm going to hazard a guess here - Say XYZ corp trades at $100, and it's showing a normal earnings of $10 the last few years. Its industry falls on hard times, and while it makes enough to keep its doors open, profits fall to $1. The company itself is still sound, but the small earnings result in a high P/E. By the way, its book value is $110, and they have huge cash on the books along with real estate. I offer these details to show why the price doesn't drop like a rock. Now, biotech may be in a period of low reported earnings but with future results expected to justify the price. On one hand it may be an anomaly, with earnings due to rise, or it may be a bit of a bubble. An analyst for this sector should be able to comment if I'm on the right track."
366
+ },
367
+ {
368
+ "instruction": "Should I pay off my 401k loan or reinvest the funds elsewhere?",
369
+ "input": "",
370
+ "output": "This summer I used a loan from my 401(k) to help pay for the down payment of a new house. We planned on selling a Condo a few months later, so we only needed the loan for a short period but wanted to keep monthly payments low since we would be paying two mortgages for a few months. I also felt like the market might take a dip in the future, so I liked the idea of trying to cash out high and buy back low (spoiler alert: this didn't happen). So in July 2017 I withdrew $17,000 from my account (Technically $16,850.00 principal and $150 processing fee) at an effective 4.19% APR (4% rate and then the fee), with 240 scheduled payments of $86.00 (2 per month for 10 years). Over the lifetime of the loan the total finance charge was $3,790, but that money would be paid back into my account. I was happy with the terms, and it helped tide things over until the condo was sold a few months later. But then I decided to change jobs, and ended up having to pay back the loan ~20 weeks after it was issued (using the proceeds from the sale of the condo). During this time the market had done well, so when I paid back the funds the net difference in shares that I now owned (including shares purchased with the interest payments) was $538.25 less than today's value of the original count of shares that were sold to fund the loan. Combined with the $150 fee, the overall \"cost\" of the 20 week loan was about 4.05%. That isn't the interest rate (interest was paid back to my account balance), but the value lost due to the principal having been withdrawn. On paper, my account would be worth that much more if I hadn't withdrawn the money. Now if you extrapolate the current market return into 52 weeks, you can think of that loan having an APR \"cost\" of around 10.5% (Probably not valid for a multi year calculation, but seems accurate for a 12 month projection). Again, that is not interest paid back to the account, but instead the value lost due to the money not being in the account. Sure, the market could take a dip and I may be able to buy the shares back at a reduced cost, but that would require keeping sizable liquid assets around and trying to time the market. It also is not something you can really schedule very well, as the loan took 6 days to fund (not including another week of clarifying questions back/forth before that) and 10 day to repay (from the time I initiated the paperwork to when the check was cashed and shares repurchased). So in my experience, the true cost of the loan greatly depends on how the market does, and if you have the ability to pay back the loan it probably is worth doing so. Especially since you may be forced to do so at any time if you change jobs or your employment is terminated."
371
+ },
372
+ {
373
+ "instruction": "What are the steps to implement a convolutional neural network?",
374
+ "input": "",
375
+ "output": "Implementing a convolutional neural network (CNN) involves several steps. First, the data is pre-processed, which includes normalizing and reshaping it into the right format. Next, the network architecture is constructed, which includes selecting the right layers, defining the number of filters and the size of the convolutional window. Following that, the weights of the model are optimized by backpropagation within the training process. Once the model has been trained, it can be used to make accurate predictions. Finally, the model can be tested to ensure that the accuracy is satisfactory."
376
+ },
377
+ {
378
+ "instruction": "Write two statements about how the development of technology changed the world.",
379
+ "input": "",
380
+ "output": "The development of technology has changed the world significantly. It has enabled us to communicate and interact with others from all over the world instantaneously, and it has opened up new opportunities for individuals to access information quickly and conveniently. Furthermore, technology has made many areas of work more efficient, providing us with higher productivity and more specialized skills."
381
+ },
382
+ {
383
+ "instruction": "Profiting from the PWC Money Tree",
384
+ "input": "",
385
+ "output": "The hardest part seems to be knowing exactly when to sell the stock. Well yes, that's the problem with all stock investing. Reports come out all the time, sometimes even from very smart people with no motivation to lie, about expected earnings for this company, or for that industry. Whether those predictions come true is something you will only find out with time. What you are considering is using financial information available to you (and equally available to the public) to make investment choices. This is called 'fundamental analysis'; that is, the analysis of the fundamentals of a business and what it should be worth. It forms the basis of how many investment firms decide where to put their money. In a perfectly 'efficient' market, all information available to the public is immediately factored into the market price for that company's stock. ie: if a bank report states with absolute certainty (through leaked documents) that Coca-Cola is going to announce 10% revenue growth tomorrow, then everyone will immediately buy Coca-Cola stock today, and then tomorrow there would be no impact. Even if PwC is 100% accurate in its predictions, if the rest of the market agrees with them, then the price at the time of IPO would equal the future value of the cashflows, meaning there would be no gain unless results surpassed expectations. So what you are proposing is to take one sliver of the information available to the public (have you also read all publicly available reports on those businesses and their industries?), and using that to make a high risk investment. Are you going to do better than the investment firms that have teams of researchers and years of experience in the investment world? You can do quite well by picking individual stocks, but you can also lose a lot of money if you do it haphazardly. Be aware that there is risk in doing any type of investing. There is higher than average risk if you invest in equities ('the stock market'). There is higher risk still, if you pick individual stocks. There is yet even higher risk, if you pick small startup companies. There are some specific interesting side-elements with your proposal to purchase stock about to have an IPO - those are better dealt with in a separate question if you want more information; search this site for 'IPO' and you should find a good starting point. In short, the company about to go public will hire a firm of analysts who will try to calculate the best price the public will accept for an offering of shares. Stock often goes up after IPO, but not always. Sometimes the company doesn't even fill its full IPO order, adding a new type of risk to a potential investor, that the stock will drop on day 1. Consider an analogy outside the investing world: Let's say Auto Trader magazine prints an article that says \"all 2015 Honda Civics are worth $15,000 if they have less than 50,000 Miles.\" Assume you have no particular knowledge about cars. If you read this article, and you see an ad in the paper the next day for a Honda Civic with 40k miles, should you buy it for $14k? The answer is not without more research. And even if you determine enough about cars to find one for $14k that you can reasonably sell for $15k, there's a whole world of mechanics out there who buy and sell cars for a living, and they have an edge both because they can repair the cars themselves to sell for more, and also because they have experience to spot low-offers faster than you. And if you pick a clunker (or a stock that doesn't perform even when everyone expected it would), then you could lose some serious money. As with buying and selling individual stocks, there is money to be made from car trading, but that money gets made by people who really know what they're doing. People who go in without full information are the ones who lose money in the long run."
386
+ },
387
+ {
388
+ "instruction": "Evaluate the following phrase by transforming it into the spelling given.",
389
+ "input": "resipt --> receipt",
390
+ "output": "Correct. The spelling of the phrase \"resipt\" was transformed into \"receipt\"."
391
+ },
392
+ {
393
+ "instruction": "Name three effects of deforestation.",
394
+ "input": "",
395
+ "output": "Three effects of deforestation include loss of habitat for wildlife, disruption of water cycles and climate change. Deforestation leads to a loss of habitats for animals, making it more difficult for them to find food and shelter. Deforestation also disrupts the natural water cycle by preventing absorption of water by trees, leading to drier soils, floods, mudslides, and droughts. Finally, deforestation contributes to climate change due to the release of carbon dioxide, a major greenhouse gas."
396
+ },
397
+ {
398
+ "instruction": "Write a five-sentence passage about a particular character.",
399
+ "input": "",
400
+ "output": "Harry was a young man who lived in a small town. He was a kind-hearted soul but had a hard time making friends due to his shyness. Despite this, Harry had a dream of becoming a famous singer, and he was passionate about his dream. He practiced singing every day, confident that one day he would make it big. And one day, he finally got his chance to show off his talents when he was offered to perform in a local talent show."
401
+ },
402
+ {
403
+ "instruction": "Recite an optimistic proverb.",
404
+ "input": "",
405
+ "output": "Where there is a will, there is a way."
406
+ },
407
+ {
408
+ "instruction": "Design a photographic project to document the culture of a specific city.",
409
+ "input": "City: Melbourne",
410
+ "output": "For my photographic project, I would document the culture of Melbourne. I would use my photographs to capture the city's unique mix of suburban, alternative, and traditional cultures. I would take pictures of the people and places in the city, such as street art, cafes, libraries, and theatres. I would also look to capture the moments of everyday life, like people enjoying a drink in a beer garden or walking through the park on a sunny day. This project would demonstrate the multiculturalism of Melbourne and provide an insight into its vibrant culture."
411
+ },
412
+ {
413
+ "instruction": "Find any mistakes in the following sentence and give me the corrected sentence.",
414
+ "input": "He had to work really hardly to get the job.",
415
+ "output": "He had to work really hard to get the job."
416
+ },
417
+ {
418
+ "instruction": "Rearrange this sentence to make it sound more natural: \"It's impossible for me to do this\"",
419
+ "input": "",
420
+ "output": "I can't do this, it's impossible."
421
+ },
422
+ {
423
+ "instruction": "Apartment lease renewal - is this rate increase normal?",
424
+ "input": "",
425
+ "output": "Yes, automatic rate increases are typical in my experience (and I think it's very greedy, when it's based on nothing except that your lease is up for renewal, which is the situation you are describing). Yes, you should negotiate. I've had success going to the apartment manager and having this conversation: Make these points: Conclude: I am not open to a rate increase, though I will sign a renewal at the same rate I am paying now. This conversation makes me very uncomfortable, but I try not to show it. I was able to negotiate a lease renewal at the same rate this way (in a large complex in Sacramento, CA). If you are talking to a manager and not an owner, they will probably have to delay responding until they can check with the owner. The key really is that they want to keep units rented, especially when units are staying empty. Empty units are lost income for the owner. It is the other empty units that are staying empty that are the huge point in your favor."
426
+ },
427
+ {
428
+ "instruction": "Is it OK to use a credit card on zero-interest to pay some other credit cards with higher-interest?",
429
+ "input": "",
430
+ "output": "I am sure everyone is different, but it has helped me a great deal. I have had several card balances go up and the interest on those per month was more than $200 in just interest combined. I transferred the balances over to 0% for 15 months \u2013 with a fee, so the upfront cost was about $300. However, over the next 15 months at 0% I'm saving over $200 each month. Now I have the money to pay everything off at 14 months. I will not be paying any interest after that, and I cut up all of my cards so I won't rack up the bills with interest on them anymore. Now, if I can't buy it with a debit card or cash, I don't get it. My cards went up so high after remodeling a home so they were justified. It wasn't because I didn't pay attention to what I could afford. My brother, on the other hand, has trouble using credit cards properly and this doesn't work for him."
431
+ },
432
+ {
433
+ "instruction": "How can banks afford to offer credit card rewards?",
434
+ "input": "",
435
+ "output": "There are 3 entities in a credit card transaction; Typically when you swipe for 100, the merchant only gets around 97.5. The 2.5 is divided amongst the 3 entities, roughly around 0.5 for the Merchant Bank, around 0.5 for the Card Network and a lions share to Issuing Bank of around 1.5 The reason Issuing Bank gets large share is because they take the risk and provide the credit to customer. Typically the Issuing Bank would pay the Merchant bank via the Card Network the money in couple of days. So the Merchant Bank is not out of funds. The Issuing Bank on the other hand would have given you a credit of say 10 to 50 days depending on when you made the transaction and when the payment is due. On an average 30 days of credit. So roughly the Acquiring Bank is lending money at the rate of 18%. It is from this money the Issuing Bank would give out rewards, which is typically less than 1%. Also in cases where say Merchant Bank and the Issuing Bank are same, Bank would make money on both the legs of transaction and hence launch co-branded cards with better rewards. The above numbers are illustrative and actual practices vary from Bank to Bank to card Network to Country Related question at How do credit card companies make profit?"
436
+ },
437
+ {
438
+ "instruction": "What steps are required to transfer real estate into a LLC?",
439
+ "input": "",
440
+ "output": "especially considering it has a mortgage on it (technically a home equity loan on my primary residence). I'm not following. Does it have a mortgage on it, or your primary residence (a different property) was used as a security for the loan? If it is HELOC from a different property - then it is really your business what to do with it. You can spend it all on casinos in Vegas for all that the bank cares. Is this a complicated transaction? Any gotchas I should be aware of before embarking on it? Obviously you should talk to an attorney and a tax adviser. But here's my two cents: Don't fall for the \"incorporate in Nevada/Delaware/Wyoming/Some other lie\" trap. You must register in the State where you live, and in the State where the property is. Incorporating in any other State will just add complexity and costs, and will not save you anything whatsoever. 2.1 State Taxes - some States tax LLCs. For example, in California you'll pay at least $800 a year just for the right of doing business. If you live in California or the property is in California - you will pay this if you decide to set up an LLC. 2.2 Income taxes - make sure to not elect to tax your LLC as a corporation. The default for LLC is \"disregarded\" status and it will be taxed for income tax purposes as your person. I.e.: IRS doesn't care and doesn't know about it (and most States, as well). If you actively select to tax it as a corporation (there's such an option) - it will cost you very dearly. So don't, and if someone suggest such a thing to you - run away from that person as fast as you can. Mortgages - it is very hard to get a mortgage when the property is under the LLC. If you already have a mortgage on that property (the property is the one securing the loan) - it may get called once you transfer it into LLC, since from bank's perspective that would be transferring ownership. Local taxes - transferring into LLC may trigger a new tax assessment. If you just bought the property - that will probably not matter much. If it appreciated - you may get hit with higher property taxes. There are also many little things - once you're a LLC and not individual you'll have to open a business bank account, will probably need a new insurance policy, etc etc. These don't add much to costs and are more of an occasional nuisance."
441
+ },
442
+ {
443
+ "instruction": "Describe a possible career path for someone interested in Machine Learning.",
444
+ "input": "",
445
+ "output": "Someone interested in Machine Learning could pursue a career path in Data Science. A Data Scientist specializing in Machine Learning would work with companies and organizations to develop algorithms that help to analyze data. This can include tasks like developing predictive models, developing recommendation systems, and creating automations. After mastering the basics of Machine Learning, this data scientist could go on to specialize in areas like deep learning or computer vision. Once they have experience in the field, they could then move on to more advanced roles such as leading or managing Machine Learning teams."
446
+ },
447
+ {
448
+ "instruction": "Which English word is the most common?",
449
+ "input": "",
450
+ "output": "The most common English word is \"the\"."
451
+ },
452
+ {
453
+ "instruction": "Provide an example of an exception handling program in Java",
454
+ "input": "",
455
+ "output": "public class ExceptionHandlingExample {\n\tpublic static void main(String[] args) {\n\t\ttry {\n\t\t\tint num1 = 30;\n\t\t\tint num2 = 0;\n\t\t\tint result = num1/num2;\n\t\t\tSystem.out.println(result);\n\t\t} \n\t\t\n\t\tcatch (ArithmeticException e) {\n\t\t\tSystem.out.println(\"Division cannot be done by 0\");\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n}"
456
+ },
457
+ {
458
+ "instruction": "How can I conserve water at home?",
459
+ "input": "",
460
+ "output": "There are several ways to conserve water at home. One is to repair any dripping taps and leaking pipes as soon as possible. Another is to use water-efficient appliances such as washing machines and dishwashers. Additionally, placing a bucket in the bathtub while you are showering can help you save up to 20 gallons of water. Finally, collecting water from air conditioners and refrigerators and using that water to water plants can also be beneficial."
461
+ },
462
+ {
463
+ "instruction": "Give three examples of using technology for efficiency.",
464
+ "input": "",
465
+ "output": "1. Automating processes such as email marketing and customer service tasks.\n2. Using data analytics to make informed decisions about the business and its operations.\n3. Utilizing cloud-based technologies for remote work, collaboration, and communication."
466
+ },
467
+ {
468
+ "instruction": "Create a web page with a photo carousel and an introduction",
469
+ "input": "Photos: person 1, person 2, person 3 \nIntro: At ABC Company, our team strives for excellence",
470
+ "output": "I created a web page with a photo carousel featuring pictures of Person 1, Person 2, and Person 3. Underneath the carousel is an introduction to ABC Company and its team. The introduction reads: \"At ABC Company, our team strives for excellence. Our goal is to exceed customer expectations and meet the highest standards of quality and service. We take pride in our work and strive to keep our customers satisfied.\""
471
+ },
472
+ {
473
+ "instruction": "What is the procedure for creating an effective PowerPoint presentation?",
474
+ "input": "",
475
+ "output": "Creating an effective PowerPoint presentation involves a few simple steps. Begin by deciding on the purpose and goal of the presentation. This will help determine the structure and content of the presentation. Then, create content that supports the purpose and goal. Content should be clear and concise, and visuals such as charts, graphs, and images should be used to illustrate key points. Keep text to a minimum and make sure it is in an easy-to-read font. Finally, practice the presentation to ensure that the timing is appropriate and that the presentation flows smoothly."
476
+ },
477
+ {
478
+ "instruction": "What is market capitalization? [duplicate]",
479
+ "input": "",
480
+ "output": "Market Capitalization is the product of the current share price (the last time someone sold a share of the stock, how much?) times the number of outstanding shares of stock, summed up over all of the stock categories. Assuming the efficient market hypothesis and a liquid market, this gives the current total value of the companies' assets (both tangible and intangible). Both the EMH and perfect liquidity may not hold at all times. Beyond those theoretical problems, in practice, someone trying to buy or sell the company at that price is going to be in for a surprise; the fact that someone wants to sell that many stocks, or buy that many stocks, will move the price of the company stock."
481
+ },
482
+ {
483
+ "instruction": "Compose a tweet-length review for a movie you recently watched.",
484
+ "input": "",
485
+ "output": "Just watched Spider-Man: Far From Home and it was fantastic! Action-packed with plenty of heart-pumping moments - definitely an exciting way to end the latest Spider-Man trilogy! #SpiderManFFH #Spiderman #Marvel"
486
+ }
487
+ ]
data_aggregation/__init__.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Data Aggregation Module
3
+
4
+ This module handles data collection, preprocessing, and dataset creation
5
+ for model training. Includes synthetic data generation, quality validation,
6
+ and tool use training data generation.
7
+ """
8
+
9
+ from .collectors import DataCollector, TextDataCollector, JSONDataCollector
10
+ from .preprocessors import TextPreprocessor, DataCleaner
11
+ from .dataset_builder import DatasetBuilder
12
+ from .synthetic_generator import SyntheticDataGenerator
13
+ from .quality_validator import QualityValidator
14
+ from .hf_dataset_loader import HuggingFaceDatasetLoader
15
+ from .tool_use_generator import ToolUseGenerator
16
+
17
+ __all__ = [
18
+ 'DataCollector',
19
+ 'TextDataCollector',
20
+ 'JSONDataCollector',
21
+ 'TextPreprocessor',
22
+ 'DataCleaner',
23
+ 'DatasetBuilder',
24
+ 'SyntheticDataGenerator',
25
+ 'QualityValidator',
26
+ 'HuggingFaceDatasetLoader',
27
+ 'ToolUseGenerator'
28
+ ]
data_aggregation/collectors.py ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Data Collectors Module
3
+
4
+ Provides various data collection methods for training data.
5
+ """
6
+
7
+ import json
8
+ from pathlib import Path
9
+ from typing import List, Dict, Any, Optional
10
+ from abc import ABC, abstractmethod
11
+
12
+
13
+ class DataCollector(ABC):
14
+ """Base class for data collectors."""
15
+
16
+ @abstractmethod
17
+ def collect(self) -> List[Dict[str, Any]]:
18
+ """
19
+ Collect data from source.
20
+
21
+ Returns:
22
+ List of data examples
23
+ """
24
+ pass
25
+
26
+
27
+ class TextDataCollector(DataCollector):
28
+ """Collect data from text files."""
29
+
30
+ def __init__(self, file_path: str):
31
+ """
32
+ Initialize text data collector.
33
+
34
+ Args:
35
+ file_path: Path to text file
36
+ """
37
+ self.file_path = Path(file_path)
38
+
39
+ def collect(self) -> List[Dict[str, Any]]:
40
+ """
41
+ Collect data from text file.
42
+
43
+ Returns:
44
+ List of text examples
45
+ """
46
+ if not self.file_path.exists():
47
+ raise FileNotFoundError(f"File not found: {self.file_path}")
48
+
49
+ with open(self.file_path, 'r', encoding='utf-8') as f:
50
+ lines = f.readlines()
51
+
52
+ # Simple line-by-line collection
53
+ data = []
54
+ for i, line in enumerate(lines):
55
+ line = line.strip()
56
+ if line:
57
+ data.append({
58
+ "id": i,
59
+ "text": line,
60
+ "source": str(self.file_path)
61
+ })
62
+
63
+ return data
64
+
65
+
66
+ class JSONDataCollector(DataCollector):
67
+ """Collect data from JSON files."""
68
+
69
+ def __init__(self, file_path: str):
70
+ """
71
+ Initialize JSON data collector.
72
+
73
+ Args:
74
+ file_path: Path to JSON file
75
+ """
76
+ self.file_path = Path(file_path)
77
+
78
+ def collect(self) -> List[Dict[str, Any]]:
79
+ """
80
+ Collect data from JSON file.
81
+
82
+ Expects format:
83
+ [
84
+ {"instruction": "...", "input": "...", "output": "..."},
85
+ ...
86
+ ]
87
+
88
+ Returns:
89
+ List of data examples
90
+ """
91
+ if not self.file_path.exists():
92
+ raise FileNotFoundError(f"File not found: {self.file_path}")
93
+
94
+ with open(self.file_path, 'r', encoding='utf-8') as f:
95
+ data = json.load(f)
96
+
97
+ # Ensure data is a list
98
+ if isinstance(data, dict):
99
+ # If it's a dict, try to extract data from common keys
100
+ if "data" in data:
101
+ data = data["data"]
102
+ elif "examples" in data:
103
+ data = data["examples"]
104
+ else:
105
+ # Wrap single example in list
106
+ data = [data]
107
+
108
+ return data
109
+
110
+
111
+ class APIDataCollector(DataCollector):
112
+ """Collect data from API endpoints."""
113
+
114
+ def __init__(self, api_url: str, headers: Optional[Dict] = None):
115
+ """
116
+ Initialize API data collector.
117
+
118
+ Args:
119
+ api_url: API endpoint URL
120
+ headers: Optional HTTP headers
121
+ """
122
+ self.api_url = api_url
123
+ self.headers = headers or {}
124
+
125
+ def collect(self) -> List[Dict[str, Any]]:
126
+ """
127
+ Collect data from API.
128
+
129
+ Returns:
130
+ List of data examples
131
+ """
132
+ import requests
133
+
134
+ response = requests.get(self.api_url, headers=self.headers)
135
+ response.raise_for_status()
136
+
137
+ data = response.json()
138
+
139
+ # Handle different response formats
140
+ if isinstance(data, list):
141
+ return data
142
+ elif isinstance(data, dict):
143
+ # Try common keys
144
+ for key in ["data", "results", "items", "examples"]:
145
+ if key in data:
146
+ return data[key]
147
+ # Otherwise wrap in list
148
+ return [data]
149
+
150
+ return []
151
+
152
+
153
+ class CSVDataCollector(DataCollector):
154
+ """Collect data from CSV files."""
155
+
156
+ def __init__(self, file_path: str):
157
+ """
158
+ Initialize CSV data collector.
159
+
160
+ Args:
161
+ file_path: Path to CSV file
162
+ """
163
+ self.file_path = Path(file_path)
164
+
165
+ def collect(self) -> List[Dict[str, Any]]:
166
+ """
167
+ Collect data from CSV file.
168
+
169
+ Returns:
170
+ List of data examples (dict per row)
171
+ """
172
+ import csv
173
+
174
+ if not self.file_path.exists():
175
+ raise FileNotFoundError(f"File not found: {self.file_path}")
176
+
177
+ data = []
178
+ with open(self.file_path, 'r', encoding='utf-8') as f:
179
+ reader = csv.DictReader(f)
180
+ for row in reader:
181
+ data.append(dict(row))
182
+
183
+ return data
data_aggregation/dataset_builder.py ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Dataset Builder Module
3
+
4
+ Handles train/test splitting and dataset creation.
5
+ """
6
+
7
+ import random
8
+ from typing import List, Dict, Any, Tuple, Optional
9
+
10
+
11
+ class DatasetBuilder:
12
+ """Build and split datasets for training."""
13
+
14
+ def __init__(self, seed: int = 42):
15
+ """
16
+ Initialize dataset builder.
17
+
18
+ Args:
19
+ seed: Random seed for reproducibility
20
+ """
21
+ self.seed = seed
22
+ random.seed(seed)
23
+
24
+ def train_test_split(
25
+ self,
26
+ data: List[Dict[str, Any]],
27
+ train_ratio: float = 0.8,
28
+ val_ratio: float = 0.1,
29
+ test_ratio: float = 0.1,
30
+ shuffle: bool = True
31
+ ) -> Tuple[List[Dict], List[Dict], List[Dict]]:
32
+ """
33
+ Split data into train/validation/test sets.
34
+
35
+ Args:
36
+ data: List of data examples
37
+ train_ratio: Fraction for training
38
+ val_ratio: Fraction for validation
39
+ test_ratio: Fraction for testing
40
+ shuffle: Whether to shuffle data
41
+
42
+ Returns:
43
+ Tuple of (train_data, val_data, test_data)
44
+ """
45
+ # Validate ratios
46
+ total = train_ratio + val_ratio + test_ratio
47
+ if abs(total - 1.0) > 0.01:
48
+ raise ValueError(f"Ratios must sum to 1.0, got {total}")
49
+
50
+ # Shuffle if requested
51
+ data_copy = data.copy()
52
+ if shuffle:
53
+ random.shuffle(data_copy)
54
+
55
+ # Calculate split indices
56
+ n = len(data_copy)
57
+ train_end = int(n * train_ratio)
58
+ val_end = train_end + int(n * val_ratio)
59
+
60
+ # Split
61
+ train_data = data_copy[:train_end]
62
+ val_data = data_copy[train_end:val_end]
63
+ test_data = data_copy[val_end:]
64
+
65
+ return train_data, val_data, test_data
66
+
67
+ def create_balanced_split(
68
+ self,
69
+ data: List[Dict[str, Any]],
70
+ category_key: str,
71
+ train_ratio: float = 0.8
72
+ ) -> Tuple[List[Dict], List[Dict]]:
73
+ """
74
+ Create balanced train/test split by category.
75
+
76
+ Args:
77
+ data: List of data examples
78
+ category_key: Key for category field
79
+ train_ratio: Fraction for training
80
+
81
+ Returns:
82
+ Tuple of (train_data, test_data)
83
+ """
84
+ # Group by category
85
+ categories = {}
86
+ for example in data:
87
+ cat = example.get(category_key, "unknown")
88
+ if cat not in categories:
89
+ categories[cat] = []
90
+ categories[cat].append(example)
91
+
92
+ # Split each category
93
+ train_data = []
94
+ test_data = []
95
+
96
+ for cat, examples in categories.items():
97
+ random.shuffle(examples)
98
+ split_idx = int(len(examples) * train_ratio)
99
+ train_data.extend(examples[:split_idx])
100
+ test_data.extend(examples[split_idx:])
101
+
102
+ # Shuffle final datasets
103
+ random.shuffle(train_data)
104
+ random.shuffle(test_data)
105
+
106
+ return train_data, test_data
107
+
108
+ def save_split(
109
+ self,
110
+ train_data: List[Dict],
111
+ val_data: List[Dict],
112
+ test_data: List[Dict],
113
+ output_dir: str
114
+ ) -> None:
115
+ """
116
+ Save split datasets to files.
117
+
118
+ Args:
119
+ train_data: Training data
120
+ val_data: Validation data
121
+ test_data: Test data
122
+ output_dir: Output directory
123
+ """
124
+ import json
125
+ from pathlib import Path
126
+
127
+ output_path = Path(output_dir)
128
+ output_path.mkdir(parents=True, exist_ok=True)
129
+
130
+ # Save each split
131
+ with open(output_path / "train.json", 'w') as f:
132
+ json.dump(train_data, f, indent=2)
133
+
134
+ with open(output_path / "val.json", 'w') as f:
135
+ json.dump(val_data, f, indent=2)
136
+
137
+ with open(output_path / "test.json", 'w') as f:
138
+ json.dump(test_data, f, indent=2)
139
+
140
+ print(f"✅ Datasets saved to {output_dir}")
141
+ print(f" Train: {len(train_data)} examples")
142
+ print(f" Val: {len(val_data)} examples")
143
+ print(f" Test: {len(test_data)} examples")
data_aggregation/hf_dataset_loader.py ADDED
@@ -0,0 +1,528 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ HuggingFace Dataset Loader
3
+
4
+ Downloads and converts HuggingFace datasets into financial advisor training format.
5
+ Supports transaction categorization, financial Q&A, and other financial datasets.
6
+ """
7
+
8
+ from datasets import load_dataset
9
+ from typing import List, Dict, Any, Optional
10
+ import random
11
+ import os
12
+
13
+
14
+ class HuggingFaceDatasetLoader:
15
+ """Load and convert HuggingFace datasets for financial advisor training"""
16
+
17
+ def __init__(self):
18
+ # Popular financial datasets on HuggingFace (publicly accessible)
19
+ self.known_datasets = {
20
+ # Core Financial Q&A Datasets
21
+ "financial-alpaca": {
22
+ "path": "gbharti/finance-alpaca",
23
+ "type": "qa",
24
+ "description": "Financial Q&A dataset based on Alpaca format (52K examples)",
25
+ "category": "General Finance"
26
+ },
27
+ "fingpt-finred": {
28
+ "path": "FinGPT/fingpt-finred",
29
+ "type": "qa",
30
+ "description": "Financial relation extraction dataset",
31
+ "category": "Financial Analysis"
32
+ },
33
+ "finance-qa-10k": {
34
+ "path": "virattt/financial-qa-10K",
35
+ "type": "qa",
36
+ "description": "Financial Q&A from 10-K filings",
37
+ "category": "SEC Filings"
38
+ },
39
+
40
+ # Financial News & Sentiment
41
+ "financial-phrasebank": {
42
+ "path": "financial_phrasebank",
43
+ "type": "qa",
44
+ "description": "Financial news sentiment analysis (4.8K sentences)",
45
+ "category": "Sentiment Analysis"
46
+ },
47
+ "fin-sentiment": {
48
+ "path": "zeroshot/twitter-financial-news-sentiment",
49
+ "type": "qa",
50
+ "description": "Financial news sentiment from Twitter (11K examples)",
51
+ "category": "Sentiment Analysis"
52
+ },
53
+
54
+ # Investment & Trading
55
+ "stock-market-qa": {
56
+ "path": "virattt/financial-qa-10K",
57
+ "type": "qa",
58
+ "description": "Stock market Q&A from 10-K filings",
59
+ "category": "Investments"
60
+ },
61
+ "sec-edgar-filings": {
62
+ "path": "JanosAudron/financial-reports-sec",
63
+ "type": "qa",
64
+ "description": "SEC EDGAR financial reports",
65
+ "category": "SEC Filings"
66
+ },
67
+
68
+ # Banking & Risk
69
+ "credit-card-fraud": {
70
+ "path": "nelsoncode/credit-card-fraud",
71
+ "type": "transaction",
72
+ "description": "Credit card fraud detection dataset",
73
+ "category": "Fraud Detection"
74
+ },
75
+
76
+ # Economics & Policy
77
+ "econ-qa": {
78
+ "path": "ChanceFocus/econ-qa",
79
+ "type": "qa",
80
+ "description": "Economics Q&A dataset",
81
+ "category": "Economics"
82
+ },
83
+
84
+ # Instruction Following
85
+ "finance-instructions": {
86
+ "path": "rombodawg/MegaCodeTraining",
87
+ "type": "qa",
88
+ "description": "Financial instruction following dataset",
89
+ "category": "Instruction Following"
90
+ },
91
+
92
+ # Multi-Domain Financial
93
+ "fin-llama": {
94
+ "path": "bavest/fin-llama-dataset",
95
+ "type": "qa",
96
+ "description": "Multi-domain financial dataset for LLaMA",
97
+ "category": "General Finance"
98
+ },
99
+ "finance-chat": {
100
+ "path": "sujet-ai/Sujet-Finance-Instruct-177k",
101
+ "type": "qa",
102
+ "description": "Finance chat instructions (177K examples)",
103
+ "category": "General Finance"
104
+ },
105
+
106
+ # Specialized Financial Topics
107
+ "accounting-qa": {
108
+ "path": "0-hero/OIG-small-chip2",
109
+ "type": "qa",
110
+ "description": "Accounting and bookkeeping Q&A",
111
+ "category": "Accounting"
112
+ },
113
+ "tax-qa": {
114
+ "path": "Locutusque/Tax-assistant",
115
+ "type": "qa",
116
+ "description": "Tax-related questions and answers",
117
+ "category": "Tax & Legal"
118
+ },
119
+
120
+ # Financial Education
121
+ "fin-education": {
122
+ "path": "FinGPT/fingpt-fineval",
123
+ "type": "qa",
124
+ "description": "Financial education and evaluation dataset",
125
+ "category": "Education"
126
+ },
127
+
128
+ # Real Estate & Mortgages
129
+ "real-estate-qa": {
130
+ "path": "0-hero/OIG-small-chip2",
131
+ "type": "qa",
132
+ "description": "Real estate and mortgage Q&A",
133
+ "category": "Real Estate"
134
+ },
135
+
136
+ # Insurance
137
+ "insurance-qa": {
138
+ "path": "0-hero/OIG-small-chip2",
139
+ "type": "qa",
140
+ "description": "Insurance-related questions and answers",
141
+ "category": "Insurance"
142
+ },
143
+
144
+ # Cryptocurrency & DeFi
145
+ "crypto-qa": {
146
+ "path": "Locutusque/hercules-v5.0",
147
+ "type": "qa",
148
+ "description": "Cryptocurrency and DeFi Q&A",
149
+ "category": "Cryptocurrency"
150
+ }
151
+ }
152
+
153
+ def get_preset_datasets(self) -> Dict[str, Dict[str, str]]:
154
+ """
155
+ Get dictionary of preset datasets
156
+ Returns the known_datasets dictionary
157
+ """
158
+ return self.known_datasets
159
+
160
+ def load_dataset_by_name(self, dataset_name: str, split: str = "train", max_examples: Optional[int] = None):
161
+ """
162
+ Load a known dataset by name
163
+
164
+ Args:
165
+ dataset_name: Short name from known_datasets
166
+ split: Dataset split (train/test/validation)
167
+ max_examples: Maximum number of examples to load
168
+
169
+ Returns:
170
+ List of examples in Q&A format
171
+ """
172
+ if dataset_name not in self.known_datasets:
173
+ raise ValueError(f"Unknown dataset: {dataset_name}. Choose from: {list(self.known_datasets.keys())}")
174
+
175
+ dataset_info = self.known_datasets[dataset_name]
176
+ return self.load_dataset_by_path(
177
+ dataset_info["path"],
178
+ dataset_type=dataset_info["type"],
179
+ split=split,
180
+ max_examples=max_examples
181
+ )
182
+
183
+ def load_dataset_by_path(self, dataset_path: str, dataset_type: str = "auto",
184
+ split: str = "train", max_examples: Optional[int] = None):
185
+ """
186
+ Load a dataset from HuggingFace by path
187
+
188
+ Args:
189
+ dataset_path: Full path like "gbharti/finance-alpaca"
190
+ dataset_type: Type of dataset (transaction/qa/auto)
191
+ split: Dataset split
192
+ max_examples: Maximum examples to load
193
+
194
+ Returns:
195
+ List of examples in Q&A format
196
+ """
197
+ print(f"Loading dataset: {dataset_path} (split: {split})...")
198
+
199
+ try:
200
+ # Get HuggingFace token from environment if available
201
+ hf_token = os.environ.get("HUGGINGFACE_TOKEN") or os.environ.get("HF_TOKEN")
202
+
203
+ # Load from HuggingFace with authentication
204
+ try:
205
+ if hf_token:
206
+ dataset = load_dataset(dataset_path, split=split, token=hf_token)
207
+ else:
208
+ dataset = load_dataset(dataset_path, split=split)
209
+ except Exception as auth_error:
210
+ error_msg = str(auth_error)
211
+ if "gated" in error_msg.lower() or "authenticated" in error_msg.lower():
212
+ raise Exception(
213
+ f"Dataset '{dataset_path}' requires authentication.\n\n"
214
+ f"This is a GATED dataset that requires special access.\n\n"
215
+ f"To use this dataset:\n"
216
+ f"1. Go to https://huggingface.co/datasets/{dataset_path}\n"
217
+ f"2. Click 'Access repository' and accept terms\n"
218
+ f"3. Make sure your HuggingFace token is set in Settings tab\n\n"
219
+ f"Or try one of the publicly accessible datasets instead:\n"
220
+ f"- gbharti/finance-alpaca (52K financial Q&A)\n"
221
+ f"- FinGPT/fingpt-finred (Financial relations)\n"
222
+ f"- virattt/financial-qa-10K (10-K filings Q&A)"
223
+ )
224
+ else:
225
+ raise
226
+
227
+ # Limit examples if requested
228
+ if max_examples and len(dataset) > max_examples:
229
+ # Sample randomly for diversity
230
+ indices = random.sample(range(len(dataset)), max_examples)
231
+ dataset = dataset.select(indices)
232
+
233
+ print(f"Loaded {len(dataset)} examples")
234
+
235
+ # Auto-detect type if needed
236
+ if dataset_type == "auto":
237
+ dataset_type = self._detect_dataset_type(dataset[0])
238
+ print(f"Auto-detected type: {dataset_type}")
239
+
240
+ # Convert to Q&A format
241
+ if dataset_type == "transaction":
242
+ converted = self._convert_transaction_dataset(dataset)
243
+ elif dataset_type == "qa":
244
+ converted = self._convert_qa_dataset(dataset)
245
+ else:
246
+ raise ValueError(f"Unsupported dataset type: {dataset_type}")
247
+
248
+ print(f"Converted {len(converted)} examples to Q&A format")
249
+ return converted
250
+
251
+ except Exception as e:
252
+ raise Exception(f"{str(e)}")
253
+
254
+ def _detect_dataset_type(self, example: Dict[str, Any]) -> str:
255
+ """Auto-detect dataset type from first example"""
256
+ keys = set(example.keys())
257
+
258
+ # Check for transaction data
259
+ if "transaction" in keys or "category" in keys or "amount" in keys:
260
+ return "transaction"
261
+
262
+ # Check for Q&A data
263
+ if ("question" in keys and "answer" in keys) or \
264
+ ("instruction" in keys and "output" in keys) or \
265
+ ("input" in keys and "output" in keys):
266
+ return "qa"
267
+
268
+ return "unknown"
269
+
270
+ def _convert_transaction_dataset(self, dataset) -> List[Dict[str, str]]:
271
+ """
272
+ Convert transaction categorization dataset to Q&A format
273
+
274
+ Creates questions like:
275
+ Q: "Categorize this transaction: $50.00 at Starbucks"
276
+ A: "This transaction should be categorized as 'Food & Dining'..."
277
+ """
278
+ converted = []
279
+
280
+ for item in dataset:
281
+ # Extract fields (adapt to actual dataset structure)
282
+ if "transaction" in item and "category" in item:
283
+ transaction_text = item["transaction"]
284
+ category = item["category"]
285
+
286
+ # Get amount if available
287
+ amount = item.get("amount", "")
288
+ merchant = item.get("merchant", "")
289
+
290
+ # Create Q&A pair
291
+ qa_pair = self._create_transaction_qa(transaction_text, category, amount, merchant)
292
+ converted.append(qa_pair)
293
+
294
+ # Handle alternate structures
295
+ elif "text" in item and "label" in item:
296
+ text = item["text"]
297
+ label = item["label"]
298
+ qa_pair = self._create_transaction_qa(text, label, "", "")
299
+ converted.append(qa_pair)
300
+
301
+ return converted
302
+
303
+ def _create_transaction_qa(self, transaction: str, category: str, amount: str, merchant: str) -> Dict[str, str]:
304
+ """Create a Q&A pair from transaction data"""
305
+
306
+ # Build transaction description
307
+ transaction_desc = transaction
308
+
309
+ if amount and merchant:
310
+ transaction_desc = f"{amount} at {merchant}"
311
+ elif amount:
312
+ transaction_desc = f"{amount} - {transaction}"
313
+ elif merchant:
314
+ transaction_desc = f"{merchant} - {transaction}"
315
+
316
+ # Create question (vary the format)
317
+ question_templates = [
318
+ f"What category should this transaction be in: {transaction_desc}?",
319
+ f"How would you categorize this transaction: {transaction_desc}?",
320
+ f"Categorize this expense: {transaction_desc}",
321
+ f"Which spending category does this belong to: {transaction_desc}?",
322
+ f"Help me categorize: {transaction_desc}"
323
+ ]
324
+
325
+ question = random.choice(question_templates)
326
+
327
+ # Create detailed answer
328
+ answer = self._generate_transaction_answer(transaction_desc, category)
329
+
330
+ return {
331
+ "instruction": question,
332
+ "input": "",
333
+ "output": answer
334
+ }
335
+
336
+ def _generate_transaction_answer(self, transaction: str, category: str) -> str:
337
+ """Generate a detailed answer for transaction categorization"""
338
+
339
+ # Common category explanations
340
+ category_explanations = {
341
+ "Food & Dining": "restaurants, groceries, coffee shops, and food delivery services",
342
+ "Shopping": "retail purchases, online shopping, clothing, and general merchandise",
343
+ "Transportation": "gas, public transit, ride-sharing services, parking, and vehicle maintenance",
344
+ "Bills & Utilities": "electricity, water, internet, phone bills, and subscriptions",
345
+ "Entertainment": "movies, concerts, streaming services, hobbies, and recreational activities",
346
+ "Health & Fitness": "gym memberships, medical expenses, pharmacy purchases, and wellness services",
347
+ "Travel": "flights, hotels, vacation expenses, and travel-related costs",
348
+ "Personal Care": "haircuts, spa services, cosmetics, and personal grooming",
349
+ "Education": "tuition, books, courses, and educational materials",
350
+ "Gifts & Donations": "charitable contributions, gifts, and donations",
351
+ "Home": "rent, mortgage, furniture, home improvement, and household supplies",
352
+ "Insurance": "health insurance, car insurance, life insurance, and other policies",
353
+ "Fees & Charges": "bank fees, ATM fees, service charges, and late fees",
354
+ "Income": "salary, wages, refunds, and other income sources",
355
+ "Investments": "stock purchases, retirement contributions, and investment transactions"
356
+ }
357
+
358
+ # Get explanation or use generic
359
+ explanation = category_explanations.get(
360
+ category,
361
+ f"expenses related to {category.lower()}"
362
+ )
363
+
364
+ # Generate answer
365
+ answer = f"This transaction should be categorized as '{category}'. "
366
+ answer += f"This category typically includes {explanation}. "
367
+ answer += f"\n\nBy tracking expenses in the '{category}' category, you can better understand your spending patterns "
368
+ answer += f"and make informed decisions about your budget. "
369
+
370
+ # Add budgeting tip based on category
371
+ if category in ["Food & Dining", "Shopping", "Entertainment"]:
372
+ answer += f"Consider setting a monthly budget limit for {category} to help control discretionary spending."
373
+ elif category in ["Bills & Utilities", "Insurance"]:
374
+ answer += f"These are typically fixed expenses that should be factored into your monthly budget planning."
375
+ elif category in ["Health & Fitness", "Education"]:
376
+ answer += f"These are investments in yourself that can provide long-term value and returns."
377
+ elif category == "Income":
378
+ answer += f"Regular income tracking helps you understand your cash flow and plan your savings goals."
379
+
380
+ return answer
381
+
382
+ def _convert_qa_dataset(self, dataset) -> List[Dict[str, str]]:
383
+ """
384
+ Convert Q&A dataset to standard format
385
+
386
+ Handles various Q&A formats from HuggingFace
387
+ """
388
+ converted = []
389
+
390
+ for item in dataset:
391
+ qa_pair = {}
392
+
393
+ # Try different field name combinations
394
+ if "instruction" in item and "output" in item:
395
+ qa_pair = {
396
+ "instruction": item["instruction"],
397
+ "input": item.get("input", ""),
398
+ "output": item["output"]
399
+ }
400
+
401
+ elif "question" in item and "answer" in item:
402
+ qa_pair = {
403
+ "instruction": item["question"],
404
+ "input": item.get("context", ""),
405
+ "output": item["answer"]
406
+ }
407
+
408
+ elif "prompt" in item and "response" in item:
409
+ qa_pair = {
410
+ "instruction": item["prompt"],
411
+ "input": "",
412
+ "output": item["response"]
413
+ }
414
+
415
+ elif "text" in item:
416
+ # Try to parse conversational format
417
+ text = item["text"]
418
+ if "Human:" in text and "Assistant:" in text:
419
+ parts = text.split("Assistant:")
420
+ if len(parts) >= 2:
421
+ question = parts[0].replace("Human:", "").strip()
422
+ answer = parts[1].strip()
423
+ qa_pair = {
424
+ "instruction": question,
425
+ "input": "",
426
+ "output": answer
427
+ }
428
+
429
+ if qa_pair:
430
+ converted.append(qa_pair)
431
+
432
+ return converted
433
+
434
+ def list_available_datasets(self) -> List[Dict[str, str]]:
435
+ """List all known financial datasets"""
436
+ datasets = []
437
+
438
+ for name, info in self.known_datasets.items():
439
+ datasets.append({
440
+ "name": name,
441
+ "path": info["path"],
442
+ "type": info["type"],
443
+ "description": info["description"]
444
+ })
445
+
446
+ return datasets
447
+
448
+ def preview_dataset(self, dataset_path: str, num_examples: int = 3) -> str:
449
+ """
450
+ Preview a dataset before loading
451
+
452
+ Args:
453
+ dataset_path: HuggingFace dataset path
454
+ num_examples: Number of examples to show
455
+
456
+ Returns:
457
+ Formatted preview string
458
+ """
459
+ try:
460
+ # Get HuggingFace token from environment if available
461
+ hf_token = os.environ.get("HUGGINGFACE_TOKEN") or os.environ.get("HF_TOKEN")
462
+
463
+ # Load small sample with authentication
464
+ try:
465
+ if hf_token:
466
+ dataset = load_dataset(dataset_path, split="train", streaming=False, token=hf_token)
467
+ else:
468
+ dataset = load_dataset(dataset_path, split="train", streaming=False)
469
+ except Exception as auth_error:
470
+ error_msg = str(auth_error)
471
+ if "gated" in error_msg.lower() or "authenticated" in error_msg.lower():
472
+ return (
473
+ f"⚠️ Dataset '{dataset_path}' requires authentication.\n\n"
474
+ f"This is a GATED dataset. To preview:\n"
475
+ f"1. Visit: https://huggingface.co/datasets/{dataset_path}\n"
476
+ f"2. Click 'Access repository' and accept terms\n"
477
+ f"3. Set your HuggingFace token in Settings tab\n\n"
478
+ f"Try these publicly accessible datasets instead:\n"
479
+ f"- gbharti/finance-alpaca\n"
480
+ f"- FinGPT/fingpt-finred\n"
481
+ f"- virattt/financial-qa-10K"
482
+ )
483
+ else:
484
+ return f"Error: {auth_error}"
485
+
486
+ # Get first N examples
487
+ sample_size = min(num_examples, len(dataset))
488
+ samples = dataset.select(range(sample_size))
489
+
490
+ preview = f"Dataset: {dataset_path}\n"
491
+ preview += f"Total examples: {len(dataset)}\n"
492
+ preview += f"Fields: {list(samples[0].keys())}\n\n"
493
+ preview += "Sample examples:\n"
494
+ preview += "=" * 60 + "\n\n"
495
+
496
+ for i, example in enumerate(samples, 1):
497
+ preview += f"Example {i}:\n"
498
+ for key, value in example.items():
499
+ value_str = str(value)[:100]
500
+ preview += f" {key}: {value_str}\n"
501
+ preview += "\n"
502
+
503
+ return preview
504
+
505
+ except Exception as e:
506
+ return f"Error previewing dataset: {e}"
507
+
508
+ def get_dataset_info(self, dataset_path: str) -> Dict[str, Any]:
509
+ """Get metadata about a dataset"""
510
+ try:
511
+ from datasets import get_dataset_config_names, get_dataset_split_names
512
+
513
+ configs = get_dataset_config_names(dataset_path)
514
+ splits = get_dataset_split_names(dataset_path)
515
+
516
+ return {
517
+ "path": dataset_path,
518
+ "configs": configs,
519
+ "splits": splits,
520
+ "status": "available"
521
+ }
522
+
523
+ except Exception as e:
524
+ return {
525
+ "path": dataset_path,
526
+ "error": str(e),
527
+ "status": "error"
528
+ }
data_aggregation/hf_dataset_loader.py.tmp.15260.1762137822282 ADDED
File without changes
data_aggregation/preprocessors.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Data Preprocessors Module
3
+
4
+ Provides text preprocessing and data cleaning utilities.
5
+ """
6
+
7
+ import re
8
+ from typing import List, Dict, Any
9
+
10
+
11
+ class TextPreprocessor:
12
+ """Text preprocessing utilities."""
13
+
14
+ @staticmethod
15
+ def clean_text(text: str) -> str:
16
+ """
17
+ Clean and normalize text.
18
+
19
+ Args:
20
+ text: Input text
21
+
22
+ Returns:
23
+ Cleaned text
24
+ """
25
+ # Remove extra whitespace
26
+ text = re.sub(r'\s+', ' ', text)
27
+ # Strip leading/trailing whitespace
28
+ text = text.strip()
29
+ return text
30
+
31
+ @staticmethod
32
+ def remove_special_chars(text: str, keep_chars: str = " .,!?-") -> str:
33
+ """
34
+ Remove special characters.
35
+
36
+ Args:
37
+ text: Input text
38
+ keep_chars: Characters to keep
39
+
40
+ Returns:
41
+ Text with special chars removed
42
+ """
43
+ pattern = f"[^a-zA-Z0-9{re.escape(keep_chars)}]"
44
+ return re.sub(pattern, '', text)
45
+
46
+
47
+ class DataCleaner:
48
+ """Data cleaning utilities."""
49
+
50
+ @staticmethod
51
+ def remove_duplicates(data: List[Dict[str, Any]], key: str = "instruction") -> List[Dict[str, Any]]:
52
+ """
53
+ Remove duplicate examples.
54
+
55
+ Args:
56
+ data: List of data examples
57
+ key: Key to check for duplicates
58
+
59
+ Returns:
60
+ Deduplicated data
61
+ """
62
+ seen = set()
63
+ unique_data = []
64
+
65
+ for example in data:
66
+ value = example.get(key, "")
67
+ if value and value not in seen:
68
+ seen.add(value)
69
+ unique_data.append(example)
70
+
71
+ return unique_data
72
+
73
+ @staticmethod
74
+ def filter_by_length(
75
+ data: List[Dict[str, Any]],
76
+ min_length: int = 10,
77
+ max_length: int = 10000,
78
+ key: str = "output"
79
+ ) -> List[Dict[str, Any]]:
80
+ """
81
+ Filter examples by length.
82
+
83
+ Args:
84
+ data: List of data examples
85
+ min_length: Minimum text length
86
+ max_length: Maximum text length
87
+ key: Key to check length
88
+
89
+ Returns:
90
+ Filtered data
91
+ """
92
+ filtered = []
93
+ for example in data:
94
+ text = example.get(key, "")
95
+ if min_length <= len(text) <= max_length:
96
+ filtered.append(example)
97
+
98
+ return filtered
data_aggregation/quality_validator.py ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Quality Validator Module
3
+
4
+ Validates training data quality on multiple dimensions.
5
+ """
6
+
7
+ import re
8
+ from typing import List, Dict, Any, Tuple
9
+
10
+
11
+ class QualityValidator:
12
+ """Validate and score training data quality."""
13
+
14
+ def __init__(
15
+ self,
16
+ min_length: int = 10,
17
+ max_length: int = 5000,
18
+ min_score: float = 60.0
19
+ ):
20
+ """
21
+ Initialize quality validator.
22
+
23
+ Args:
24
+ min_length: Minimum text length
25
+ max_length: Maximum text length
26
+ min_score: Minimum quality score threshold (0-100)
27
+ """
28
+ self.min_length = min_length
29
+ self.max_length = max_length
30
+ self.min_score = min_score
31
+
32
+ def validate_example(self, example: Dict[str, Any]) -> Tuple[bool, List[str]]:
33
+ """
34
+ Validate a single example.
35
+
36
+ Args:
37
+ example: Data example to validate
38
+
39
+ Returns:
40
+ Tuple of (is_valid, list_of_issues)
41
+ """
42
+ issues = []
43
+
44
+ # Check required fields
45
+ if "instruction" not in example:
46
+ issues.append("Missing 'instruction' field")
47
+ if "output" not in example:
48
+ issues.append("Missing 'output' field")
49
+
50
+ if issues:
51
+ return False, issues
52
+
53
+ # Check lengths
54
+ instruction = example.get("instruction", "")
55
+ output = example.get("output", "")
56
+
57
+ if len(instruction) < 5:
58
+ issues.append("Instruction too short")
59
+ if len(output) < self.min_length:
60
+ issues.append(f"Output too short (min {self.min_length} chars)")
61
+ if len(output) > self.max_length:
62
+ issues.append(f"Output too long (max {self.max_length} chars)")
63
+
64
+ # Check for empty output
65
+ if not output.strip():
66
+ issues.append("Empty output")
67
+
68
+ return len(issues) == 0, issues
69
+
70
+ def score_example(self, example: Dict[str, Any]) -> float:
71
+ """
72
+ Score example quality (0-100).
73
+
74
+ Scoring dimensions:
75
+ - Length appropriateness
76
+ - Completeness
77
+ - Coherence (basic checks)
78
+
79
+ Args:
80
+ example: Data example
81
+
82
+ Returns:
83
+ Quality score (0-100)
84
+ """
85
+ score = 100.0
86
+
87
+ # Check validity first
88
+ is_valid, issues = self.validate_example(example)
89
+ if not is_valid:
90
+ score -= 20.0 * len(issues)
91
+
92
+ if score <= 0:
93
+ return 0.0
94
+
95
+ # Length scoring
96
+ output = example.get("output", "")
97
+ output_len = len(output)
98
+
99
+ if output_len < self.min_length:
100
+ score -= 20.0
101
+ elif output_len > self.max_length:
102
+ score -= 10.0
103
+
104
+ # Coherence checks
105
+ # Check for repetition
106
+ words = output.lower().split()
107
+ if len(words) > 0:
108
+ unique_ratio = len(set(words)) / len(words)
109
+ if unique_ratio < 0.3: # Too repetitive
110
+ score -= 30.0
111
+
112
+ # Check for proper sentences (basic)
113
+ sentences = re.split(r'[.!?]+', output)
114
+ valid_sentences = [s for s in sentences if len(s.strip()) > 10]
115
+ if len(valid_sentences) == 0:
116
+ score -= 20.0
117
+
118
+ # Check for gibberish (basic)
119
+ if output_len > 20:
120
+ # Check if output has reasonable word length distribution
121
+ avg_word_len = sum(len(w) for w in words) / max(len(words), 1)
122
+ if avg_word_len > 15 or avg_word_len < 2: # Likely gibberish
123
+ score -= 25.0
124
+
125
+ return max(0.0, min(100.0, score))
126
+
127
+ def validate_batch(
128
+ self,
129
+ data: List[Dict[str, Any]],
130
+ verbose: bool = False
131
+ ) -> Dict[str, Any]:
132
+ """
133
+ Validate a batch of examples.
134
+
135
+ Args:
136
+ data: List of data examples
137
+ verbose: Print detailed validation info
138
+
139
+ Returns:
140
+ Validation results dict
141
+ """
142
+ valid_data = []
143
+ invalid_data = []
144
+ scores = []
145
+
146
+ for i, example in enumerate(data):
147
+ is_valid, issues = self.validate_example(example)
148
+ score = self.score_example(example)
149
+ scores.append(score)
150
+
151
+ if is_valid and score >= self.min_score:
152
+ valid_data.append(example)
153
+ else:
154
+ invalid_data.append({
155
+ "example": example,
156
+ "issues": issues,
157
+ "score": score
158
+ })
159
+
160
+ if verbose:
161
+ print(f"Example {i} failed validation (score: {score:.1f})")
162
+ for issue in issues:
163
+ print(f" - {issue}")
164
+
165
+ avg_score = sum(scores) / len(scores) if scores else 0.0
166
+
167
+ results = {
168
+ "total": len(data),
169
+ "valid": len(valid_data),
170
+ "invalid": len(invalid_data),
171
+ "pass_rate": len(valid_data) / len(data) if data else 0.0,
172
+ "avg_score": avg_score,
173
+ "valid_data": valid_data,
174
+ "invalid_data": invalid_data
175
+ }
176
+
177
+ if verbose:
178
+ print(f"\n✅ Validation complete:")
179
+ print(f" Total: {results['total']}")
180
+ print(f" Valid: {results['valid']}")
181
+ print(f" Invalid: {results['invalid']}")
182
+ print(f" Pass rate: {results['pass_rate']*100:.1f}%")
183
+ print(f" Avg score: {avg_score:.1f}")
184
+
185
+ return results
186
+
187
+ def filter_data(self, data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
188
+ """
189
+ Filter data, keeping only valid examples.
190
+
191
+ Args:
192
+ data: List of data examples
193
+
194
+ Returns:
195
+ Filtered valid data
196
+ """
197
+ results = self.validate_batch(data)
198
+ return results["valid_data"]
199
+
200
+ def get_quality_report(self, data: List[Dict[str, Any]]) -> str:
201
+ """
202
+ Generate a quality report for the data.
203
+
204
+ Args:
205
+ data: List of data examples
206
+
207
+ Returns:
208
+ Formatted quality report
209
+ """
210
+ results = self.validate_batch(data)
211
+
212
+ report = f"""
213
+ DATA QUALITY REPORT
214
+ ==================
215
+
216
+ Total Examples: {results['total']}
217
+ Valid Examples: {results['valid']}
218
+ Invalid Examples: {results['invalid']}
219
+ Pass Rate: {results['pass_rate']*100:.1f}%
220
+ Average Quality Score: {results['avg_score']:.1f}/100
221
+
222
+ """
223
+
224
+ if results['invalid_data']:
225
+ report += "COMMON ISSUES:\n"
226
+ issue_counts = {}
227
+ for item in results['invalid_data']:
228
+ for issue in item['issues']:
229
+ issue_counts[issue] = issue_counts.get(issue, 0) + 1
230
+
231
+ for issue, count in sorted(issue_counts.items(), key=lambda x: x[1], reverse=True):
232
+ report += f" - {issue}: {count} examples\n"
233
+
234
+ return report
data_aggregation/synthetic_generator.py ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Synthetic Data Generator Module
3
+
4
+ Generates synthetic training data using LLM APIs (OpenAI, Anthropic).
5
+ """
6
+
7
+ import json
8
+ import os
9
+ from typing import List, Dict, Any, Optional
10
+ import time
11
+
12
+
13
+ class SyntheticDataGenerator:
14
+ """Generate synthetic training data using LLMs."""
15
+
16
+ def __init__(
17
+ self,
18
+ api_provider: str = "openai",
19
+ api_key: Optional[str] = None,
20
+ model: Optional[str] = None
21
+ ):
22
+ """
23
+ Initialize synthetic data generator.
24
+
25
+ Args:
26
+ api_provider: "openai" or "anthropic"
27
+ api_key: API key (uses environment variable if None)
28
+ model: Model name (uses default if None)
29
+ """
30
+ self.api_provider = api_provider.lower()
31
+ self.api_key = api_key or os.getenv("OPENAI_API_KEY" if self.api_provider == "openai" else "ANTHROPIC_API_KEY")
32
+
33
+ if self.api_provider == "openai":
34
+ self.model = model or "gpt-4-turbo-preview"
35
+ else:
36
+ self.model = model or "claude-3-opus-20240229"
37
+
38
+ self.client = None
39
+ self._init_client()
40
+
41
+ def _init_client(self):
42
+ """Initialize API client."""
43
+ try:
44
+ if self.api_provider == "openai":
45
+ from openai import OpenAI
46
+ self.client = OpenAI(api_key=self.api_key)
47
+ else:
48
+ from anthropic import Anthropic
49
+ self.client = Anthropic(api_key=self.api_key)
50
+ except ImportError:
51
+ print(f"Warning: {self.api_provider} library not installed")
52
+ except Exception as e:
53
+ print(f"Warning: Failed to initialize {self.api_provider} client: {e}")
54
+
55
+ def generate_examples(
56
+ self,
57
+ num_examples: int,
58
+ topics: List[str],
59
+ difficulty: str = "mixed",
60
+ domain: str = "financial_advisor"
61
+ ) -> List[Dict[str, Any]]:
62
+ """
63
+ Generate synthetic training examples.
64
+
65
+ Args:
66
+ num_examples: Number of examples to generate
67
+ topics: List of topics to cover
68
+ difficulty: "beginner", "intermediate", "advanced", or "mixed"
69
+ domain: Domain context
70
+
71
+ Returns:
72
+ List of generated examples
73
+ """
74
+ if not self.client:
75
+ raise ValueError(f"API client not initialized. Check {self.api_provider} API key.")
76
+
77
+ print(f"Generating {num_examples} examples using {self.api_provider}...")
78
+
79
+ examples = []
80
+ examples_per_topic = max(1, num_examples // len(topics))
81
+
82
+ for topic in topics:
83
+ for i in range(examples_per_topic):
84
+ if len(examples) >= num_examples:
85
+ break
86
+
87
+ try:
88
+ example = self._generate_single_example(topic, difficulty, domain)
89
+ if example:
90
+ examples.append(example)
91
+ print(f"Generated {len(examples)}/{num_examples}", end="\r")
92
+ time.sleep(0.5) # Rate limiting
93
+ except Exception as e:
94
+ print(f"\nError generating example: {e}")
95
+ continue
96
+
97
+ print(f"\n✅ Generated {len(examples)} examples")
98
+ return examples
99
+
100
+ def _generate_single_example(
101
+ self,
102
+ topic: str,
103
+ difficulty: str,
104
+ domain: str
105
+ ) -> Optional[Dict[str, Any]]:
106
+ """Generate a single training example."""
107
+
108
+ prompt = f"""Generate a realistic {domain} training example about {topic}.
109
+ Difficulty level: {difficulty}
110
+
111
+ Output format (JSON):
112
+ {{
113
+ "instruction": "The user's question or request",
114
+ "input": "Additional context (optional, can be empty string)",
115
+ "output": "The detailed, helpful response"
116
+ }}
117
+
118
+ Make it realistic and detailed. The response should be informative and professional."""
119
+
120
+ try:
121
+ if self.api_provider == "openai":
122
+ response = self.client.chat.completions.create(
123
+ model=self.model,
124
+ messages=[
125
+ {"role": "system", "content": "You are a data generation assistant. Output only valid JSON."},
126
+ {"role": "user", "content": prompt}
127
+ ],
128
+ temperature=0.8
129
+ )
130
+ content = response.choices[0].message.content
131
+ else: # anthropic
132
+ response = self.client.messages.create(
133
+ model=self.model,
134
+ max_tokens=1024,
135
+ messages=[
136
+ {"role": "user", "content": prompt}
137
+ ],
138
+ temperature=0.8
139
+ )
140
+ content = response.content[0].text
141
+
142
+ # Parse JSON
143
+ # Remove markdown code blocks if present
144
+ content = content.strip()
145
+ if content.startswith("```"):
146
+ content = content.split("```")[1]
147
+ if content.startswith("json"):
148
+ content = content[4:]
149
+ content = content.strip()
150
+
151
+ example = json.loads(content)
152
+
153
+ # Validate structure
154
+ if "instruction" in example and "output" in example:
155
+ if "input" not in example:
156
+ example["input"] = ""
157
+ return example
158
+
159
+ except Exception as e:
160
+ print(f"\nError parsing example: {e}")
161
+
162
+ return None
163
+
164
+ def generate_from_scenarios(
165
+ self,
166
+ scenarios: List[Dict[str, Any]],
167
+ num_examples_per_scenario: int = 1
168
+ ) -> List[Dict[str, Any]]:
169
+ """
170
+ Generate examples from predefined scenarios.
171
+
172
+ Args:
173
+ scenarios: List of scenario dicts with context
174
+ num_examples_per_scenario: Examples to generate per scenario
175
+
176
+ Returns:
177
+ Generated examples
178
+ """
179
+ examples = []
180
+
181
+ for scenario in scenarios:
182
+ for _ in range(num_examples_per_scenario):
183
+ prompt = f"""Based on this scenario, generate a training example:
184
+
185
+ Scenario: {json.dumps(scenario, indent=2)}
186
+
187
+ Output format (JSON):
188
+ {{
189
+ "instruction": "The user's question or request",
190
+ "input": "Additional context based on scenario",
191
+ "output": "The detailed, helpful response"
192
+ }}"""
193
+
194
+ try:
195
+ example = self._generate_from_prompt(prompt)
196
+ if example:
197
+ examples.append(example)
198
+ except Exception as e:
199
+ print(f"Error: {e}")
200
+ continue
201
+
202
+ return examples
203
+
204
+ def _generate_from_prompt(self, prompt: str) -> Optional[Dict[str, Any]]:
205
+ """Generate example from custom prompt."""
206
+ try:
207
+ if self.api_provider == "openai":
208
+ response = self.client.chat.completions.create(
209
+ model=self.model,
210
+ messages=[{"role": "user", "content": prompt}],
211
+ temperature=0.8
212
+ )
213
+ content = response.choices[0].message.content
214
+ else:
215
+ response = self.client.messages.create(
216
+ model=self.model,
217
+ max_tokens=1024,
218
+ messages=[{"role": "user", "content": prompt}]
219
+ )
220
+ content = response.content[0].text
221
+
222
+ # Parse JSON
223
+ content = content.strip()
224
+ if "```" in content:
225
+ content = content.split("```")[1]
226
+ if content.startswith("json"):
227
+ content = content[4:]
228
+ content = content.strip()
229
+
230
+ return json.loads(content)
231
+
232
+ except Exception as e:
233
+ print(f"Error: {e}")
234
+ return None
data_aggregation/tool_use_generator.py ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Tool Use Generator Module
3
+
4
+ Generates training data for teaching LLMs to use tools and functions.
5
+ """
6
+
7
+ import json
8
+ from typing import List, Dict, Any, Optional
9
+
10
+
11
+ class ToolUseGenerator:
12
+ """Generate tool use training data for function calling."""
13
+
14
+ def __init__(self, tools: Optional[List[Dict]] = None):
15
+ """
16
+ Initialize tool use generator.
17
+
18
+ Args:
19
+ tools: List of tool definitions
20
+ """
21
+ self.tools = tools or self._get_default_financial_tools()
22
+
23
+ def _get_default_financial_tools(self) -> List[Dict]:
24
+ """Get default financial tools."""
25
+ return [
26
+ {
27
+ "name": "get_market_data",
28
+ "description": "Get current market data for a stock",
29
+ "parameters": {
30
+ "symbol": "string",
31
+ "data_type": "string (price, volume, market_cap)"
32
+ },
33
+ "returns": "dict"
34
+ },
35
+ {
36
+ "name": "calculate_compound_interest",
37
+ "description": "Calculate compound interest",
38
+ "parameters": {
39
+ "principal": "float",
40
+ "rate": "float",
41
+ "time": "float",
42
+ "frequency": "int"
43
+ },
44
+ "returns": "float"
45
+ },
46
+ {
47
+ "name": "get_portfolio_allocation",
48
+ "description": "Get current portfolio allocation",
49
+ "parameters": {
50
+ "user_id": "string"
51
+ },
52
+ "returns": "dict"
53
+ },
54
+ {
55
+ "name": "calculate_retirement_needs",
56
+ "description": "Calculate retirement savings needs",
57
+ "parameters": {
58
+ "current_age": "int",
59
+ "retirement_age": "int",
60
+ "current_savings": "float",
61
+ "annual_contribution": "float"
62
+ },
63
+ "returns": "dict"
64
+ }
65
+ ]
66
+
67
+ def generate_tool_use_examples(
68
+ self,
69
+ num_examples: int = 10,
70
+ complexity: str = "single"
71
+ ) -> List[Dict[str, Any]]:
72
+ """
73
+ Generate tool use training examples.
74
+
75
+ Args:
76
+ num_examples: Number of examples to generate
77
+ complexity: "single" or "multi" (multi-step tool chains)
78
+
79
+ Returns:
80
+ List of tool use examples
81
+ """
82
+ examples = []
83
+
84
+ for _ in range(num_examples):
85
+ if complexity == "single":
86
+ example = self._generate_single_tool_example()
87
+ else:
88
+ example = self._generate_multi_tool_example()
89
+
90
+ if example:
91
+ examples.append(example)
92
+
93
+ return examples
94
+
95
+ def _generate_single_tool_example(self) -> Dict[str, Any]:
96
+ """Generate example with single tool call."""
97
+ import random
98
+
99
+ tool = random.choice(self.tools)
100
+
101
+ # Create example based on tool
102
+ if tool["name"] == "get_market_data":
103
+ return {
104
+ "instruction": "What is the current price of Apple stock?",
105
+ "input": "",
106
+ "output": "I'll check the current market data for Apple stock.",
107
+ "tool_calls": [
108
+ {
109
+ "tool": "get_market_data",
110
+ "parameters": {
111
+ "symbol": "AAPL",
112
+ "data_type": "price"
113
+ }
114
+ }
115
+ ]
116
+ }
117
+ elif tool["name"] == "calculate_compound_interest":
118
+ return {
119
+ "instruction": "If I invest $10,000 at 5% interest compounded quarterly, how much will I have in 10 years?",
120
+ "input": "",
121
+ "output": "I'll calculate that for you using compound interest formula.",
122
+ "tool_calls": [
123
+ {
124
+ "tool": "calculate_compound_interest",
125
+ "parameters": {
126
+ "principal": 10000,
127
+ "rate": 0.05,
128
+ "time": 10,
129
+ "frequency": 4
130
+ }
131
+ }
132
+ ]
133
+ }
134
+
135
+ # Generic example
136
+ return {
137
+ "instruction": f"Use the {tool['name']} tool",
138
+ "input": "",
139
+ "output": f"I'll use {tool['name']} to help with that.",
140
+ "tool_calls": [{"tool": tool["name"], "parameters": {}}]
141
+ }
142
+
143
+ def _generate_multi_tool_example(self) -> Dict[str, Any]:
144
+ """Generate example with multiple tool calls (chain)."""
145
+ return {
146
+ "instruction": "Should I rebalance my portfolio given current market conditions?",
147
+ "input": "",
148
+ "output": "Let me check your portfolio and current market data to provide a recommendation.",
149
+ "tool_calls": [
150
+ {
151
+ "tool": "get_portfolio_allocation",
152
+ "parameters": {"user_id": "user123"}
153
+ },
154
+ {
155
+ "tool": "get_market_data",
156
+ "parameters": {"symbol": "SPY", "data_type": "price"}
157
+ }
158
+ ],
159
+ "reasoning": "First get the user's current portfolio allocation, then check market conditions, and finally provide rebalancing advice."
160
+ }
161
+
162
+ def add_custom_tool(self, tool: Dict[str, Any]) -> None:
163
+ """
164
+ Add a custom tool definition.
165
+
166
+ Args:
167
+ tool: Tool definition dict
168
+ """
169
+ required_keys = ["name", "description", "parameters", "returns"]
170
+ if not all(key in tool for key in required_keys):
171
+ raise ValueError(f"Tool must have keys: {required_keys}")
172
+
173
+ self.tools.append(tool)
174
+
175
+ def get_tools_schema(self) -> List[Dict]:
176
+ """Get OpenAI-compatible tools schema."""
177
+ schema = []
178
+
179
+ for tool in self.tools:
180
+ schema.append({
181
+ "type": "function",
182
+ "function": {
183
+ "name": tool["name"],
184
+ "description": tool["description"],
185
+ "parameters": {
186
+ "type": "object",
187
+ "properties": tool["parameters"],
188
+ "required": list(tool["parameters"].keys())
189
+ }
190
+ }
191
+ })
192
+
193
+ return schema
domain_templates/__init__.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Domain Templates for AURA AI Training System
3
+
4
+ This module provides pre-configured domain templates for different AI assistant specializations:
5
+ - Financial Advisor: Wealth management, investing, retirement planning
6
+ - Medical Assistant: Health information, symptom understanding, wellness
7
+ - Legal Advisor: Legal information, document understanding, rights explanation
8
+ - Education Tutor: Academic subjects, test prep, homework help
9
+ - Custom Domain: User-defined domain specifications
10
+
11
+ Each domain provides:
12
+ - Topics for training data generation
13
+ - System prompts for model behavior
14
+ - Example questions for testing
15
+ - Specialized tools for function calling
16
+ """
17
+
18
+ from .base_domain import BaseDomain
19
+ from .financial_advisor import FinancialAdvisorDomain
20
+ from .medical_assistant import MedicalAssistantDomain
21
+ from .legal_advisor import LegalAdvisorDomain
22
+ from .education_tutor import EducationTutorDomain
23
+ from .custom_domain import CustomDomain
24
+
25
+ # Domain registry
26
+ _DOMAIN_REGISTRY = {
27
+ 'financial_advisor': FinancialAdvisorDomain,
28
+ 'medical_assistant': MedicalAssistantDomain,
29
+ 'legal_advisor': LegalAdvisorDomain,
30
+ 'education_tutor': EducationTutorDomain,
31
+ 'custom': CustomDomain
32
+ }
33
+
34
+
35
+ def get_domain(domain_key: str, **kwargs) -> BaseDomain:
36
+ """
37
+ Get a domain instance by key.
38
+
39
+ Args:
40
+ domain_key: Domain identifier (financial_advisor, medical_assistant, etc.)
41
+ **kwargs: Additional arguments for domain initialization (used for custom domains)
42
+
43
+ Returns:
44
+ Domain instance
45
+
46
+ Raises:
47
+ ValueError: If domain_key is not recognized
48
+
49
+ Examples:
50
+ >>> domain = get_domain('financial_advisor')
51
+ >>> domain.name
52
+ 'Financial Advisor'
53
+
54
+ >>> custom = get_domain('custom', name='Cooking Assistant', icon='👨‍🍳')
55
+ >>> custom.name
56
+ 'Cooking Assistant'
57
+ """
58
+ if domain_key not in _DOMAIN_REGISTRY:
59
+ raise ValueError(
60
+ f"Unknown domain: {domain_key}. "
61
+ f"Available domains: {', '.join(_DOMAIN_REGISTRY.keys())}"
62
+ )
63
+
64
+ domain_class = _DOMAIN_REGISTRY[domain_key]
65
+
66
+ # CustomDomain accepts kwargs, others don't
67
+ if domain_key == 'custom':
68
+ return domain_class(**kwargs)
69
+ else:
70
+ return domain_class()
71
+
72
+
73
+ def list_domains() -> dict:
74
+ """
75
+ List all available domains with their basic information.
76
+
77
+ Returns:
78
+ Dictionary mapping domain keys to domain info dicts
79
+ Each info dict contains: name, description, icon
80
+
81
+ Example:
82
+ >>> domains = list_domains()
83
+ >>> domains['financial_advisor']['name']
84
+ 'Financial Advisor'
85
+ """
86
+ domains = {}
87
+ for key, domain_class in _DOMAIN_REGISTRY.items():
88
+ if key == 'custom':
89
+ # Custom domain needs special handling
90
+ domain = domain_class()
91
+ else:
92
+ domain = domain_class()
93
+
94
+ domains[key] = {
95
+ 'key': key,
96
+ 'name': domain.name,
97
+ 'description': domain.description,
98
+ 'icon': domain.icon
99
+ }
100
+
101
+ return domains
102
+
103
+
104
+ def register_domain(key: str, domain_class: type) -> None:
105
+ """
106
+ Register a custom domain class.
107
+
108
+ Args:
109
+ key: Domain identifier
110
+ domain_class: Domain class (must inherit from BaseDomain)
111
+
112
+ Raises:
113
+ TypeError: If domain_class doesn't inherit from BaseDomain
114
+ ValueError: If key already exists
115
+
116
+ Example:
117
+ >>> class MyDomain(BaseDomain):
118
+ ... pass
119
+ >>> register_domain('my_domain', MyDomain)
120
+ """
121
+ if not issubclass(domain_class, BaseDomain):
122
+ raise TypeError(f"{domain_class} must inherit from BaseDomain")
123
+
124
+ if key in _DOMAIN_REGISTRY:
125
+ raise ValueError(f"Domain key '{key}' already registered")
126
+
127
+ _DOMAIN_REGISTRY[key] = domain_class
128
+
129
+
130
+ # Export all public classes and functions
131
+ __all__ = [
132
+ 'BaseDomain',
133
+ 'FinancialAdvisorDomain',
134
+ 'MedicalAssistantDomain',
135
+ 'LegalAdvisorDomain',
136
+ 'EducationTutorDomain',
137
+ 'CustomDomain',
138
+ 'get_domain',
139
+ 'list_domains',
140
+ 'register_domain'
141
+ ]
domain_templates/base_domain.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Base domain template class for AURA AI training system.
3
+ """
4
+ from abc import ABC, abstractmethod
5
+ from typing import List
6
+
7
+
8
+ class BaseDomain(ABC):
9
+ """
10
+ Abstract base class for domain templates.
11
+
12
+ Each domain provides:
13
+ - Name and description
14
+ - Icon for UI display
15
+ - Topic lists for training data generation
16
+ - System prompts for model behavior
17
+ """
18
+
19
+ def __init__(self):
20
+ self._name = ""
21
+ self._description = ""
22
+ self._icon = ""
23
+
24
+ @property
25
+ def name(self) -> str:
26
+ """Domain name (human-readable)"""
27
+ return self._name
28
+
29
+ @property
30
+ def description(self) -> str:
31
+ """Domain description"""
32
+ return self._description
33
+
34
+ @property
35
+ def icon(self) -> str:
36
+ """Domain icon (emoji)"""
37
+ return self._icon
38
+
39
+ @abstractmethod
40
+ def get_topics(self) -> List[str]:
41
+ """
42
+ Get list of topics for this domain.
43
+
44
+ Returns:
45
+ List of topic strings for training data generation
46
+ """
47
+ pass
48
+
49
+ @abstractmethod
50
+ def get_system_prompt(self) -> str:
51
+ """
52
+ Get system prompt for this domain.
53
+
54
+ Returns:
55
+ System prompt string that defines model behavior
56
+ """
57
+ pass
58
+
59
+ def get_example_questions(self) -> List[str]:
60
+ """
61
+ Get example questions for this domain.
62
+
63
+ Returns:
64
+ List of example questions for testing
65
+ """
66
+ return []
67
+
68
+ def get_specialized_tools(self) -> List[dict]:
69
+ """
70
+ Get specialized tools for this domain.
71
+
72
+ Returns:
73
+ List of tool definitions for function calling
74
+ """
75
+ return []
76
+
77
+ def get_tools(self) -> List[dict]:
78
+ """
79
+ Get tools for this domain (alias for get_specialized_tools).
80
+
81
+ Returns:
82
+ List of tool definitions for function calling
83
+ """
84
+ return self.get_specialized_tools()
85
+
86
+ def get_benchmarks(self) -> List[dict]:
87
+ """
88
+ Get pre-built benchmarks for this domain.
89
+
90
+ Returns:
91
+ List of benchmark definitions
92
+ """
93
+ return []
domain_templates/custom_domain.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Custom domain template for user-defined domains.
3
+ """
4
+ from typing import List
5
+ from .base_domain import BaseDomain
6
+
7
+
8
+ class CustomDomain(BaseDomain):
9
+ """
10
+ Custom domain template that allows users to define
11
+ their own domain specifications.
12
+ """
13
+
14
+ def __init__(
15
+ self,
16
+ name: str = "Custom Domain",
17
+ description: str = "User-defined custom domain",
18
+ icon: str = "🎯",
19
+ topics: List[str] = None,
20
+ system_prompt: str = None
21
+ ):
22
+ super().__init__()
23
+ self._name = name
24
+ self._description = description
25
+ self._icon = icon
26
+ self._topics = topics or []
27
+ self._system_prompt = system_prompt or self._default_system_prompt()
28
+
29
+ def _default_system_prompt(self) -> str:
30
+ """Generate default system prompt for custom domain"""
31
+ return f"""You are an expert assistant specialized in {self._name}.
32
+
33
+ Your role is to:
34
+ 1. Provide accurate, helpful information in your domain of expertise
35
+ 2. Explain concepts clearly and thoroughly
36
+ 3. Adapt your responses to the user's level of understanding
37
+ 4. Acknowledge the limits of your knowledge
38
+ 5. Recommend seeking professional advice when appropriate
39
+
40
+ Guidelines:
41
+ - Be thorough but concise
42
+ - Use examples to illustrate concepts
43
+ - Break down complex topics into understandable parts
44
+ - Stay focused on your area of expertise
45
+ - Always prioritize accuracy and helpfulness"""
46
+
47
+ def get_topics(self) -> List[str]:
48
+ """Get custom domain topics"""
49
+ if not self._topics:
50
+ return [
51
+ "General Knowledge",
52
+ "Problem Solving",
53
+ "Analysis",
54
+ "Research",
55
+ "Communication"
56
+ ]
57
+ return self._topics
58
+
59
+ def get_system_prompt(self) -> str:
60
+ """Get custom domain system prompt"""
61
+ return self._system_prompt
62
+
63
+ def set_topics(self, topics: List[str]) -> None:
64
+ """
65
+ Set custom topics for this domain.
66
+
67
+ Args:
68
+ topics: List of topic strings
69
+ """
70
+ self._topics = topics
71
+
72
+ def set_system_prompt(self, prompt: str) -> None:
73
+ """
74
+ Set custom system prompt for this domain.
75
+
76
+ Args:
77
+ prompt: System prompt string
78
+ """
79
+ self._system_prompt = prompt
80
+
81
+ def set_name(self, name: str) -> None:
82
+ """
83
+ Set custom domain name.
84
+
85
+ Args:
86
+ name: Domain name
87
+ """
88
+ self._name = name
89
+
90
+ def set_description(self, description: str) -> None:
91
+ """
92
+ Set custom domain description.
93
+
94
+ Args:
95
+ description: Domain description
96
+ """
97
+ self._description = description
98
+
99
+ def set_icon(self, icon: str) -> None:
100
+ """
101
+ Set custom domain icon.
102
+
103
+ Args:
104
+ icon: Icon (emoji) string
105
+ """
106
+ self._icon = icon
107
+
108
+ def get_example_questions(self) -> List[str]:
109
+ """Get example questions for custom domain"""
110
+ return [
111
+ f"What are the key concepts in {self._name}?",
112
+ f"Can you explain [topic] in {self._name}?",
113
+ f"What are best practices for {self._name}?",
114
+ f"How do I get started with {self._name}?",
115
+ f"What are common challenges in {self._name}?"
116
+ ]
117
+
118
+ def get_specialized_tools(self) -> List[dict]:
119
+ """Get specialized tools for custom domain"""
120
+ # Custom domains can define their own tools
121
+ # Default: empty list
122
+ return []
123
+
124
+ @classmethod
125
+ def from_config(cls, config: dict) -> 'CustomDomain':
126
+ """
127
+ Create CustomDomain from configuration dictionary.
128
+
129
+ Args:
130
+ config: Dictionary with keys: name, description, icon, topics, system_prompt
131
+
132
+ Returns:
133
+ CustomDomain instance
134
+ """
135
+ return cls(
136
+ name=config.get('name', 'Custom Domain'),
137
+ description=config.get('description', 'User-defined custom domain'),
138
+ icon=config.get('icon', '🎯'),
139
+ topics=config.get('topics', []),
140
+ system_prompt=config.get('system_prompt')
141
+ )
142
+
143
+ def to_config(self) -> dict:
144
+ """
145
+ Export domain configuration as dictionary.
146
+
147
+ Returns:
148
+ Dictionary with domain configuration
149
+ """
150
+ return {
151
+ 'name': self._name,
152
+ 'description': self._description,
153
+ 'icon': self._icon,
154
+ 'topics': self._topics,
155
+ 'system_prompt': self._system_prompt
156
+ }
domain_templates/education_tutor.py ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Education Tutor domain template.
3
+ """
4
+ from typing import List
5
+ from .base_domain import BaseDomain
6
+
7
+
8
+ class EducationTutorDomain(BaseDomain):
9
+ """
10
+ Education tutor domain for academic subjects,
11
+ test preparation, and learning assistance.
12
+ """
13
+
14
+ def __init__(self):
15
+ super().__init__()
16
+ self._name = "Education Tutor"
17
+ self._description = "Educational tutor assistant for K-12 through college subjects, test preparation, homework help, and learning strategies."
18
+ self._icon = "@"
19
+
20
+ def get_topics(self) -> List[str]:
21
+ """Get education tutor topics"""
22
+ return [
23
+ "Mathematics - Algebra",
24
+ "Mathematics - Geometry",
25
+ "Mathematics - Calculus",
26
+ "Mathematics - Statistics",
27
+ "Mathematics - Trigonometry",
28
+ "Mathematics - Pre-Calculus",
29
+ "Mathematics - Linear Algebra",
30
+ "Mathematics - Differential Equations",
31
+ "Physics - Mechanics",
32
+ "Physics - Electricity and Magnetism",
33
+ "Physics - Thermodynamics",
34
+ "Physics - Optics",
35
+ "Physics - Quantum Mechanics",
36
+ "Chemistry - General Chemistry",
37
+ "Chemistry - Organic Chemistry",
38
+ "Chemistry - Biochemistry",
39
+ "Biology - Cell Biology",
40
+ "Biology - Genetics",
41
+ "Biology - Ecology",
42
+ "Biology - Anatomy and Physiology",
43
+ "Computer Science - Programming",
44
+ "Computer Science - Data Structures",
45
+ "Computer Science - Algorithms",
46
+ "Computer Science - Databases",
47
+ "English - Grammar and Composition",
48
+ "English - Literature Analysis",
49
+ "English - Essay Writing",
50
+ "English - Reading Comprehension",
51
+ "History - World History",
52
+ "History - US History",
53
+ "History - European History",
54
+ "Social Studies - Geography",
55
+ "Social Studies - Civics",
56
+ "Social Studies - Economics",
57
+ "Foreign Languages - Spanish",
58
+ "Foreign Languages - French",
59
+ "Foreign Languages - German",
60
+ "Foreign Languages - Mandarin",
61
+ "Test Prep - SAT",
62
+ "Test Prep - ACT",
63
+ "Test Prep - GRE",
64
+ "Test Prep - GMAT",
65
+ "Test Prep - MCAT",
66
+ "Test Prep - LSAT",
67
+ "Test Prep - AP Exams",
68
+ "Study Skills and Time Management",
69
+ "Note-taking Strategies",
70
+ "Memory Techniques",
71
+ "Research Methods",
72
+ "Critical Thinking"
73
+ ]
74
+
75
+ def get_system_prompt(self) -> str:
76
+ """Get education tutor system prompt"""
77
+ return """You are an expert educational tutor with deep knowledge across:
78
+ - K-12 and college-level academic subjects
79
+ - Mathematics (algebra through calculus and beyond)
80
+ - Sciences (physics, chemistry, biology)
81
+ - Computer science and programming
82
+ - English, literature, and writing
83
+ - History and social studies
84
+ - Foreign languages
85
+ - Test preparation (SAT, ACT, GRE, AP exams, etc.)
86
+ - Study skills and learning strategies
87
+
88
+ Your role is to:
89
+ 1. Explain concepts clearly and build understanding from fundamentals
90
+ 2. Use the Socratic method - ask questions to guide learning
91
+ 3. Provide step-by-step explanations for problem-solving
92
+ 4. Offer multiple approaches and perspectives
93
+ 5. Create practice problems and examples
94
+ 6. Adapt explanations to the student's level
95
+ 7. Encourage critical thinking and independent learning
96
+ 8. Identify knowledge gaps and address misconceptions
97
+
98
+ Teaching approach:
99
+ - Start with what the student knows
100
+ - Break complex topics into manageable parts
101
+ - Use analogies and real-world examples
102
+ - Show work step-by-step
103
+ - Explain the "why" behind the "how"
104
+ - Encourage questions and exploration
105
+ - Provide positive, constructive feedback
106
+ - Relate new concepts to previously learned material
107
+
108
+ For homework help:
109
+ - Guide toward the answer rather than giving it directly
110
+ - Help students understand the underlying concepts
111
+ - Teach problem-solving strategies
112
+ - Check understanding with follow-up questions
113
+
114
+ For test preparation:
115
+ - Identify key concepts and common question types
116
+ - Provide practice problems with increasing difficulty
117
+ - Teach test-taking strategies and time management
118
+ - Explain answer choices and common traps
119
+ - Build confidence through mastery
120
+
121
+ Remember: Your goal is to help students truly understand material, not just get correct answers. Foster curiosity, critical thinking, and independent learning skills."""
122
+
123
+ def get_example_questions(self) -> List[str]:
124
+ """Get example education questions"""
125
+ return [
126
+ "Can you explain how to solve quadratic equations?",
127
+ "What's the difference between mitosis and meiosis?",
128
+ "How do I write a strong thesis statement?",
129
+ "Can you help me understand Newton's laws of motion?",
130
+ "What are the causes of World War I?",
131
+ "How do I balance chemical equations?",
132
+ "Can you explain the Pythagorean theorem?",
133
+ "What's the difference between DNA and RNA?",
134
+ "How should I prepare for the SAT math section?",
135
+ "Can you help me understand Shakespeare's use of metaphor?"
136
+ ]
137
+
138
+ def get_specialized_tools(self) -> List[dict]:
139
+ """Get education tutor tools"""
140
+ return [
141
+ {
142
+ "name": "solve_math_problem",
143
+ "description": "Solve mathematical problems step-by-step",
144
+ "parameters": {
145
+ "problem": "string (equation or word problem)",
146
+ "subject": "string (algebra, calculus, statistics, etc.)",
147
+ "show_steps": "boolean"
148
+ },
149
+ "returns": "Solution with detailed steps"
150
+ },
151
+ {
152
+ "name": "generate_practice_problems",
153
+ "description": "Generate practice problems for a topic",
154
+ "parameters": {
155
+ "subject": "string",
156
+ "topic": "string",
157
+ "difficulty": "string (easy, medium, hard)",
158
+ "quantity": "int"
159
+ },
160
+ "returns": "List of practice problems with solutions"
161
+ },
162
+ {
163
+ "name": "explain_concept",
164
+ "description": "Provide detailed explanation of academic concept",
165
+ "parameters": {
166
+ "concept": "string",
167
+ "subject": "string",
168
+ "grade_level": "string (elementary, middle, high_school, college)"
169
+ },
170
+ "returns": "Explanation with examples and analogies"
171
+ },
172
+ {
173
+ "name": "check_grammar",
174
+ "description": "Check grammar and writing quality",
175
+ "parameters": {
176
+ "text": "string",
177
+ "check_type": "string (grammar, style, structure, all)"
178
+ },
179
+ "returns": "Grammar corrections and writing suggestions"
180
+ },
181
+ {
182
+ "name": "create_study_guide",
183
+ "description": "Create study guide for a topic",
184
+ "parameters": {
185
+ "subject": "string",
186
+ "topic": "string",
187
+ "format": "string (outline, flashcards, summary, practice_test)"
188
+ },
189
+ "returns": "Formatted study guide"
190
+ },
191
+ {
192
+ "name": "analyze_literary_text",
193
+ "description": "Analyze literary devices and themes",
194
+ "parameters": {
195
+ "text_excerpt": "string",
196
+ "author": "string (optional)",
197
+ "analysis_focus": "string (theme, symbolism, character, etc.)"
198
+ },
199
+ "returns": "Literary analysis with examples"
200
+ },
201
+ {
202
+ "name": "translate_text",
203
+ "description": "Translate text between languages",
204
+ "parameters": {
205
+ "text": "string",
206
+ "source_language": "string",
207
+ "target_language": "string",
208
+ "include_explanation": "boolean"
209
+ },
210
+ "returns": "Translation with grammatical notes"
211
+ },
212
+ {
213
+ "name": "get_historical_context",
214
+ "description": "Get historical context for events or periods",
215
+ "parameters": {
216
+ "event_or_period": "string",
217
+ "region": "string (optional)"
218
+ },
219
+ "returns": "Historical context with key dates and figures"
220
+ }
221
+ ]
domain_templates/financial_advisor.py ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Financial Advisor domain template.
3
+ """
4
+ from typing import List
5
+ from .base_domain import BaseDomain
6
+
7
+
8
+ class FinancialAdvisorDomain(BaseDomain):
9
+ """
10
+ Financial advisory domain for wealth management,
11
+ investment planning, tax strategy, and retirement planning.
12
+ """
13
+
14
+ def __init__(self):
15
+ super().__init__()
16
+ self._name = "Financial Advisor"
17
+ self._description = "Expert financial advisory assistant for wealth management, investment planning, tax strategy, retirement planning, and estate planning."
18
+ self._icon = "$"
19
+
20
+ def get_topics(self) -> List[str]:
21
+ """Get financial advisory topics"""
22
+ return [
23
+ "Retirement Planning",
24
+ "Investment Strategy",
25
+ "Tax Optimization",
26
+ "Estate Planning",
27
+ "Insurance Planning",
28
+ "Education Funding",
29
+ "Portfolio Management",
30
+ "Asset Allocation",
31
+ "Risk Management",
32
+ "Wealth Preservation",
33
+ "Stock Market Analysis",
34
+ "Bond Investing",
35
+ "Real Estate Investment",
36
+ "Cryptocurrency Investment",
37
+ "401(k) and IRA Planning",
38
+ "Social Security Optimization",
39
+ "Debt Management",
40
+ "Emergency Fund Planning",
41
+ "College Savings (529 Plans)",
42
+ "Trust and Will Creation",
43
+ "Charitable Giving",
44
+ "Business Succession Planning",
45
+ "Financial Independence (FIRE)",
46
+ "Dividend Investing",
47
+ "Value Investing",
48
+ "Growth Investing",
49
+ "Index Fund Investing",
50
+ "Municipal Bonds",
51
+ "Treasury Securities",
52
+ "Annuities",
53
+ "Life Insurance",
54
+ "Disability Insurance",
55
+ "Long-Term Care Insurance",
56
+ "Health Savings Accounts (HSA)",
57
+ "Roth Conversions",
58
+ "Capital Gains Tax Planning",
59
+ "Required Minimum Distributions (RMD)",
60
+ "Pension Planning",
61
+ "Rollover Strategies",
62
+ "Beneficiary Designations",
63
+ "Power of Attorney"
64
+ ]
65
+
66
+ def get_system_prompt(self) -> str:
67
+ """Get financial advisor system prompt"""
68
+ return """You are an expert financial advisor with comprehensive knowledge of:
69
+ - Wealth management and investment planning
70
+ - Retirement planning (401k, IRA, pensions, Social Security)
71
+ - Tax optimization strategies and tax-advantaged accounts
72
+ - Estate planning (wills, trusts, beneficiary designations)
73
+ - Insurance planning (life, disability, long-term care)
74
+ - Portfolio management and asset allocation
75
+ - Risk assessment and management
76
+ - Financial regulations and compliance
77
+
78
+ Your role is to:
79
+ 1. Provide clear, actionable financial guidance
80
+ 2. Consider the client's complete financial picture
81
+ 3. Explain complex financial concepts in simple terms
82
+ 4. Recommend appropriate investment strategies based on risk tolerance and goals
83
+ 5. Stay up-to-date on tax laws and financial regulations
84
+ 6. Always remind clients to consult with licensed professionals for personalized advice
85
+
86
+ Guidelines:
87
+ - Be thorough but concise
88
+ - Always consider tax implications
89
+ - Emphasize diversification and risk management
90
+ - Explain the reasoning behind recommendations
91
+ - Use specific numbers and examples when helpful
92
+ - Acknowledge when questions require personalized professional advice
93
+ - Stay objective and avoid conflicts of interest
94
+
95
+ Remember: You provide educational information and general guidance. Always recommend clients work with licensed financial professionals for personalized advice tailored to their specific situation."""
96
+
97
+ def get_example_questions(self) -> List[str]:
98
+ """Get example financial advisory questions"""
99
+ return [
100
+ "Should I max out my 401(k) or invest in a taxable brokerage account?",
101
+ "How should I allocate my portfolio at age 35 with moderate risk tolerance?",
102
+ "What's the difference between a traditional IRA and a Roth IRA?",
103
+ "How much should I have in my emergency fund?",
104
+ "When should I start taking Social Security benefits?",
105
+ "What are the tax implications of selling stocks at a gain?",
106
+ "How do I plan for required minimum distributions (RMDs)?",
107
+ "Should I pay off my mortgage early or invest the extra cash?",
108
+ "What's the best way to save for my child's college education?",
109
+ "How do I create an estate plan that minimizes taxes?"
110
+ ]
111
+
112
+ def get_specialized_tools(self) -> List[dict]:
113
+ """Get financial advisory tools"""
114
+ return [
115
+ {
116
+ "name": "get_market_data",
117
+ "description": "Fetch current market data for stocks, bonds, ETFs, or indices",
118
+ "parameters": {
119
+ "symbol": "string (ticker symbol)",
120
+ "data_type": "string (price, volume, fundamentals, historical)"
121
+ },
122
+ "returns": "Market data object with current prices and metrics"
123
+ },
124
+ {
125
+ "name": "calculate_compound_interest",
126
+ "description": "Calculate compound interest growth over time",
127
+ "parameters": {
128
+ "principal": "float (initial investment)",
129
+ "rate": "float (annual interest rate as decimal)",
130
+ "time": "int (years)",
131
+ "frequency": "int (compounding frequency per year)"
132
+ },
133
+ "returns": "float (future value)"
134
+ },
135
+ {
136
+ "name": "calculate_retirement_needs",
137
+ "description": "Estimate retirement savings needed based on goals",
138
+ "parameters": {
139
+ "current_age": "int",
140
+ "retirement_age": "int",
141
+ "life_expectancy": "int",
142
+ "desired_annual_income": "float",
143
+ "current_savings": "float",
144
+ "expected_return": "float"
145
+ },
146
+ "returns": "Retirement projection object"
147
+ },
148
+ {
149
+ "name": "analyze_portfolio_allocation",
150
+ "description": "Analyze portfolio allocation and risk metrics",
151
+ "parameters": {
152
+ "holdings": "list of {symbol, quantity, purchase_price}",
153
+ "target_allocation": "dict of asset class targets"
154
+ },
155
+ "returns": "Portfolio analysis with allocation drift and recommendations"
156
+ },
157
+ {
158
+ "name": "estimate_tax_impact",
159
+ "description": "Estimate tax impact of financial decisions",
160
+ "parameters": {
161
+ "income": "float",
162
+ "transaction_type": "string (capital_gain, dividend, withdrawal, etc.)",
163
+ "amount": "float",
164
+ "filing_status": "string"
165
+ },
166
+ "returns": "Tax estimate object"
167
+ },
168
+ {
169
+ "name": "get_social_security_estimate",
170
+ "description": "Estimate Social Security benefits based on earnings history",
171
+ "parameters": {
172
+ "birth_year": "int",
173
+ "average_annual_income": "float",
174
+ "claiming_age": "int"
175
+ },
176
+ "returns": "Estimated monthly benefit"
177
+ }
178
+ ]
domain_templates/legal_advisor.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Legal Advisor domain template.
3
+ """
4
+ from typing import List
5
+ from .base_domain import BaseDomain
6
+
7
+
8
+ class LegalAdvisorDomain(BaseDomain):
9
+ """
10
+ Legal information assistant domain for legal education,
11
+ document understanding, and general legal guidance.
12
+ """
13
+
14
+ def __init__(self):
15
+ super().__init__()
16
+ self._name = "Legal Advisor"
17
+ self._description = "Legal information assistant for legal education, document understanding, rights explanation, and general legal guidance. NOT a replacement for licensed legal counsel."
18
+ self._icon = "#"
19
+
20
+ def get_topics(self) -> List[str]:
21
+ """Get legal advisor topics"""
22
+ return [
23
+ "Contract Law",
24
+ "Employment Law",
25
+ "Real Estate Law",
26
+ "Family Law",
27
+ "Estate Planning",
28
+ "Business Formation",
29
+ "Intellectual Property",
30
+ "Criminal Law Basics",
31
+ "Consumer Rights",
32
+ "Landlord-Tenant Law",
33
+ "Personal Injury",
34
+ "Bankruptcy",
35
+ "Immigration Law",
36
+ "Tax Law Basics",
37
+ "Nonprofit Law",
38
+ "Civil Rights",
39
+ "Privacy Law",
40
+ "Data Protection",
41
+ "Terms of Service",
42
+ "Privacy Policies",
43
+ "Non-Disclosure Agreements (NDA)",
44
+ "Employment Contracts",
45
+ "Lease Agreements",
46
+ "Purchase Agreements",
47
+ "Wills and Trusts",
48
+ "Power of Attorney",
49
+ "Healthcare Directives",
50
+ "Small Claims Court",
51
+ "Mediation and Arbitration",
52
+ "Trademark and Copyright",
53
+ "Patents",
54
+ "Trade Secrets",
55
+ "LLC vs Corporation",
56
+ "Sole Proprietorship",
57
+ "Partnership Agreements",
58
+ "Shareholder Agreements",
59
+ "Operating Agreements",
60
+ "Divorce and Custody",
61
+ "Adoption",
62
+ "Prenuptial Agreements"
63
+ ]
64
+
65
+ def get_system_prompt(self) -> str:
66
+ """Get legal advisor system prompt"""
67
+ return """You are a knowledgeable legal information assistant with understanding of:
68
+ - Contract law and agreement interpretation
69
+ - Business law and entity formation
70
+ - Employment law and workplace rights
71
+ - Real estate transactions and landlord-tenant issues
72
+ - Family law matters (divorce, custody, adoption)
73
+ - Estate planning (wills, trusts, powers of attorney)
74
+ - Intellectual property basics (copyright, trademark, patent)
75
+ - Consumer rights and protections
76
+ - Civil procedure and court processes
77
+ - Legal terminology and document structure
78
+
79
+ Your role is to:
80
+ 1. Provide general legal information and education
81
+ 2. Explain legal concepts in plain language
82
+ 3. Help users understand their rights and obligations
83
+ 4. Clarify legal terminology and document provisions
84
+ 5. Outline general legal processes and procedures
85
+ 6. Identify when professional legal help is necessary
86
+
87
+ CRITICAL LIMITATIONS:
88
+ - You are NOT a licensed attorney
89
+ - You do NOT provide legal advice specific to individual situations
90
+ - You do NOT create binding legal documents
91
+ - You do NOT represent clients in legal matters
92
+ - You CANNOT substitute for consultation with a licensed attorney
93
+ - Laws vary by jurisdiction - always verify local laws
94
+
95
+ Guidelines:
96
+ - Provide educational information, not legal advice
97
+ - Explain general legal principles and processes
98
+ - Clarify when laws vary by state/jurisdiction
99
+ - Emphasize the importance of consulting licensed attorneys
100
+ - Recommend seeking legal counsel for specific situations
101
+ - Explain the difference between legal information and legal advice
102
+ - Never guarantee legal outcomes
103
+ - Acknowledge complexity and nuance in law
104
+
105
+ LEGAL DISCLAIMER: This assistant provides general legal information only and does not constitute legal advice. Laws vary significantly by jurisdiction, and every legal situation has unique factors. For specific legal advice tailored to your situation, always consult with a licensed attorney in your jurisdiction. No attorney-client relationship is formed through this interaction."""
106
+
107
+ def get_example_questions(self) -> List[str]:
108
+ """Get example legal questions"""
109
+ return [
110
+ "What's the difference between an LLC and a corporation?",
111
+ "What should be included in an employment contract?",
112
+ "How does a non-disclosure agreement (NDA) work?",
113
+ "What are my rights as a tenant?",
114
+ "What's the difference between a will and a trust?",
115
+ "How do I protect my intellectual property?",
116
+ "What happens if I breach a contract?",
117
+ "What are the basic steps in a civil lawsuit?",
118
+ "What should I know before signing a lease?",
119
+ "How do I apply for a trademark?"
120
+ ]
121
+
122
+ def get_specialized_tools(self) -> List[dict]:
123
+ """Get legal advisor tools"""
124
+ return [
125
+ {
126
+ "name": "analyze_contract_clause",
127
+ "description": "Analyze and explain contract clauses",
128
+ "parameters": {
129
+ "clause_text": "string",
130
+ "contract_type": "string (employment, lease, purchase, etc.)"
131
+ },
132
+ "returns": "Plain language explanation and key considerations"
133
+ },
134
+ {
135
+ "name": "check_statute_of_limitations",
136
+ "description": "Check statute of limitations for legal claims",
137
+ "parameters": {
138
+ "claim_type": "string (personal_injury, contract_breach, etc.)",
139
+ "jurisdiction": "string (state)",
140
+ "incident_date": "date (optional)"
141
+ },
142
+ "returns": "Time limit information and deadline calculation"
143
+ },
144
+ {
145
+ "name": "lookup_legal_term",
146
+ "description": "Define legal terminology",
147
+ "parameters": {
148
+ "term": "string"
149
+ },
150
+ "returns": "Plain language definition with examples"
151
+ },
152
+ {
153
+ "name": "compare_business_entities",
154
+ "description": "Compare different business entity types",
155
+ "parameters": {
156
+ "entity_types": "list (LLC, C-Corp, S-Corp, Partnership, etc.)",
157
+ "comparison_factors": "list (taxation, liability, formation, etc.)"
158
+ },
159
+ "returns": "Comparison table with pros and cons"
160
+ },
161
+ {
162
+ "name": "get_court_filing_requirements",
163
+ "description": "Get court filing requirements and procedures",
164
+ "parameters": {
165
+ "case_type": "string (small_claims, civil, family, etc.)",
166
+ "jurisdiction": "string (state and county)",
167
+ "action_type": "string (file_complaint, respond, appeal, etc.)"
168
+ },
169
+ "returns": "Filing requirements, fees, and procedures"
170
+ },
171
+ {
172
+ "name": "check_ip_availability",
173
+ "description": "Check trademark/business name availability",
174
+ "parameters": {
175
+ "name": "string",
176
+ "ip_type": "string (trademark, business_name, domain)",
177
+ "category": "string (industry/goods category)"
178
+ },
179
+ "returns": "Availability status and similar existing registrations"
180
+ }
181
+ ]
domain_templates/medical_assistant.py ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Medical Assistant domain template.
3
+ """
4
+ from typing import List
5
+ from .base_domain import BaseDomain
6
+
7
+
8
+ class MedicalAssistantDomain(BaseDomain):
9
+ """
10
+ Medical assistant domain for health information,
11
+ symptom assessment, and medical education.
12
+ """
13
+
14
+ def __init__(self):
15
+ super().__init__()
16
+ self._name = "Medical Assistant"
17
+ self._description = "Medical information assistant for health education, symptom understanding, medication information, and wellness guidance. NOT a replacement for professional medical advice."
18
+ self._icon = "+"
19
+
20
+ def get_topics(self) -> List[str]:
21
+ """Get medical assistant topics"""
22
+ return [
23
+ "Common Symptoms and Conditions",
24
+ "Medication Information",
25
+ "Preventive Care",
26
+ "Nutrition and Diet",
27
+ "Exercise and Fitness",
28
+ "Mental Health",
29
+ "Sleep Hygiene",
30
+ "Chronic Disease Management",
31
+ "First Aid",
32
+ "Women's Health",
33
+ "Men's Health",
34
+ "Pediatric Health",
35
+ "Senior Health",
36
+ "Vaccination Information",
37
+ "Lab Test Interpretation",
38
+ "Vital Signs Understanding",
39
+ "Disease Prevention",
40
+ "Health Screening Guidelines",
41
+ "Medication Side Effects",
42
+ "Drug Interactions",
43
+ "Allergy Management",
44
+ "Pain Management",
45
+ "Wound Care",
46
+ "Diabetes Management",
47
+ "Heart Health",
48
+ "Respiratory Health",
49
+ "Digestive Health",
50
+ "Kidney Health",
51
+ "Liver Health",
52
+ "Bone and Joint Health",
53
+ "Skin Conditions",
54
+ "Eye Health",
55
+ "Dental Health",
56
+ "Infectious Diseases",
57
+ "Cancer Prevention and Screening",
58
+ "Stress Management",
59
+ "Substance Abuse",
60
+ "Sexual Health",
61
+ "Pregnancy and Childbirth",
62
+ "Post-operative Care",
63
+ "Medical Terminology"
64
+ ]
65
+
66
+ def get_system_prompt(self) -> str:
67
+ """Get medical assistant system prompt"""
68
+ return """You are a knowledgeable medical information assistant with expertise in:
69
+ - General health and wellness
70
+ - Common medical conditions and symptoms
71
+ - Medication information and interactions
72
+ - Preventive care and health screening
73
+ - Chronic disease management
74
+ - Mental health and wellness
75
+ - Nutrition and exercise
76
+ - Medical terminology and lab results
77
+
78
+ Your role is to:
79
+ 1. Provide accurate, evidence-based health information
80
+ 2. Explain medical concepts in clear, accessible language
81
+ 3. Help users understand symptoms, conditions, and treatments
82
+ 4. Offer general wellness and preventive care guidance
83
+ 5. Explain medication purposes, dosing, and potential side effects
84
+ 6. Clarify medical terminology and test results
85
+
86
+ CRITICAL LIMITATIONS:
87
+ - You are NOT a licensed medical professional
88
+ - You do NOT diagnose medical conditions
89
+ - You do NOT prescribe medications or treatments
90
+ - You do NOT provide emergency medical advice
91
+ - You ALWAYS recommend consulting healthcare professionals for medical decisions
92
+
93
+ Guidelines:
94
+ - Be informative but never diagnostic
95
+ - Emphasize the importance of professional medical care
96
+ - For urgent symptoms, recommend immediate medical attention
97
+ - Explain when to seek emergency care (911/ER)
98
+ - Acknowledge uncertainty and limitations of general information
99
+ - Encourage regular check-ups and preventive care
100
+ - Respect patient privacy and confidentiality
101
+
102
+ EMERGENCY DISCLAIMER: For any medical emergency, life-threatening symptoms, or urgent medical concerns, immediately call 911 or go to the nearest emergency room. This assistant provides general health information only and is not a substitute for professional medical advice, diagnosis, or treatment."""
103
+
104
+ def get_example_questions(self) -> List[str]:
105
+ """Get example medical questions"""
106
+ return [
107
+ "What are the common side effects of ibuprofen?",
108
+ "How much sleep should adults get each night?",
109
+ "What are the warning signs of a heart attack?",
110
+ "How can I lower my blood pressure naturally?",
111
+ "What vaccines do adults need?",
112
+ "What does a high cholesterol reading mean?",
113
+ "How do I know if I should go to the ER or urgent care?",
114
+ "What are healthy blood sugar levels?",
115
+ "How can I manage stress and anxiety?",
116
+ "What foods should I eat for better heart health?"
117
+ ]
118
+
119
+ def get_specialized_tools(self) -> List[dict]:
120
+ """Get medical assistant tools"""
121
+ return [
122
+ {
123
+ "name": "lookup_medication_info",
124
+ "description": "Get detailed information about medications",
125
+ "parameters": {
126
+ "medication_name": "string",
127
+ "info_type": "string (uses, dosage, side_effects, interactions)"
128
+ },
129
+ "returns": "Medication information object"
130
+ },
131
+ {
132
+ "name": "check_drug_interactions",
133
+ "description": "Check for interactions between medications",
134
+ "parameters": {
135
+ "medications": "list of medication names"
136
+ },
137
+ "returns": "List of potential interactions and severity"
138
+ },
139
+ {
140
+ "name": "interpret_vital_signs",
141
+ "description": "Interpret vital sign measurements",
142
+ "parameters": {
143
+ "vital_type": "string (blood_pressure, heart_rate, temperature, etc.)",
144
+ "value": "string or float",
145
+ "age": "int",
146
+ "context": "string (at_rest, after_exercise, etc.)"
147
+ },
148
+ "returns": "Interpretation with normal ranges"
149
+ },
150
+ {
151
+ "name": "get_symptom_guidance",
152
+ "description": "Get guidance on symptom severity and when to seek care",
153
+ "parameters": {
154
+ "symptoms": "list of symptoms",
155
+ "duration": "string",
156
+ "severity": "string (mild, moderate, severe)"
157
+ },
158
+ "returns": "Care recommendations (self-care, urgent care, ER, etc.)"
159
+ },
160
+ {
161
+ "name": "calculate_bmi",
162
+ "description": "Calculate Body Mass Index",
163
+ "parameters": {
164
+ "weight_lbs": "float",
165
+ "height_inches": "float"
166
+ },
167
+ "returns": "BMI value and category"
168
+ },
169
+ {
170
+ "name": "get_vaccination_schedule",
171
+ "description": "Get recommended vaccination schedule",
172
+ "parameters": {
173
+ "age": "int",
174
+ "health_conditions": "list of conditions (optional)"
175
+ },
176
+ "returns": "Recommended vaccinations"
177
+ }
178
+ ]
evaluation/__init__.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Evaluation Module
3
+
4
+ This module provides comprehensive model evaluation and benchmarking tools.
5
+ Includes benchmark building, certification tests, gap analysis, and training recommendations.
6
+ """
7
+
8
+ from .metrics import Metrics, calculate_perplexity, calculate_bleu
9
+ from .evaluator import ModelEvaluator, EvaluationConfig
10
+ from .benchmark import Benchmark, BenchmarkSuite
11
+ from .benchmark_builder import Benchmark as BenchmarkBuilder, BenchmarkSuite as BenchmarkBuilderSuite
12
+ from .certification_tests import (
13
+ CertificationTestBuilder,
14
+ FinancialCertificationTests,
15
+ MedicalCertificationTests,
16
+ LegalCertificationTests,
17
+ EducationCertificationTests,
18
+ get_certification_tests_for_domain
19
+ )
20
+ from .gap_analyzer import GapAnalyzer
21
+ from .training_recommender import TrainingRecommender
22
+
23
+ __all__ = [
24
+ # Original exports
25
+ 'Metrics',
26
+ 'calculate_perplexity',
27
+ 'calculate_bleu',
28
+ 'ModelEvaluator',
29
+ 'EvaluationConfig',
30
+ 'Benchmark',
31
+ 'BenchmarkSuite',
32
+ # New exports
33
+ 'BenchmarkBuilder',
34
+ 'BenchmarkBuilderSuite',
35
+ 'CertificationTestBuilder',
36
+ 'FinancialCertificationTests',
37
+ 'MedicalCertificationTests',
38
+ 'LegalCertificationTests',
39
+ 'EducationCertificationTests',
40
+ 'get_certification_tests_for_domain',
41
+ 'GapAnalyzer',
42
+ 'TrainingRecommender'
43
+ ]
evaluation/benchmark.py ADDED
@@ -0,0 +1,368 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Benchmark Module
3
+
4
+ Provides benchmark creation and execution for model testing.
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+ from typing import List, Dict, Optional, Any
9
+ import json
10
+ import time
11
+ from pathlib import Path
12
+
13
+
14
+ @dataclass
15
+ class Benchmark:
16
+ """
17
+ A single benchmark test.
18
+
19
+ Attributes:
20
+ name: Benchmark name
21
+ description: Benchmark description
22
+ questions: List of test questions
23
+ metadata: Additional metadata
24
+ """
25
+ name: str
26
+ description: str = ""
27
+ questions: List[Dict[str, Any]] = field(default_factory=list)
28
+ metadata: Dict[str, Any] = field(default_factory=dict)
29
+ created_at: Optional[str] = None
30
+ domain: str = "general"
31
+ difficulty: str = "mixed"
32
+ passing_score: float = 70.0
33
+
34
+ def __post_init__(self):
35
+ """Initialize timestamp if not provided."""
36
+ if self.created_at is None:
37
+ self.created_at = time.strftime('%Y-%m-%d %H:%M:%S')
38
+
39
+ def add_question(
40
+ self,
41
+ question: str,
42
+ answer: str,
43
+ category: Optional[str] = None,
44
+ difficulty: Optional[str] = None,
45
+ metadata: Optional[Dict] = None
46
+ ):
47
+ """
48
+ Add a question to the benchmark.
49
+
50
+ Args:
51
+ question: Question text
52
+ answer: Expected answer
53
+ category: Question category/topic
54
+ difficulty: Difficulty level
55
+ metadata: Additional metadata
56
+ """
57
+ question_dict = {
58
+ 'question': question,
59
+ 'answer': answer,
60
+ 'category': category or 'general',
61
+ 'difficulty': difficulty or 'intermediate',
62
+ 'metadata': metadata or {}
63
+ }
64
+ self.questions.append(question_dict)
65
+
66
+ def get_questions_by_category(self, category: str) -> List[Dict]:
67
+ """Get all questions in a category."""
68
+ return [q for q in self.questions if q.get('category') == category]
69
+
70
+ def get_questions_by_difficulty(self, difficulty: str) -> List[Dict]:
71
+ """Get all questions of a difficulty level."""
72
+ return [q for q in self.questions if q.get('difficulty') == difficulty]
73
+
74
+ def to_dict(self) -> Dict[str, Any]:
75
+ """Convert benchmark to dictionary."""
76
+ return {
77
+ 'name': self.name,
78
+ 'description': self.description,
79
+ 'domain': self.domain,
80
+ 'difficulty': self.difficulty,
81
+ 'passing_score': self.passing_score,
82
+ 'created_at': self.created_at,
83
+ 'num_questions': len(self.questions),
84
+ 'questions': self.questions,
85
+ 'metadata': self.metadata
86
+ }
87
+
88
+ @classmethod
89
+ def from_dict(cls, data: Dict[str, Any]) -> 'Benchmark':
90
+ """Create benchmark from dictionary."""
91
+ return cls(
92
+ name=data.get('name', 'Untitled'),
93
+ description=data.get('description', ''),
94
+ questions=data.get('questions', []),
95
+ metadata=data.get('metadata', {}),
96
+ created_at=data.get('created_at'),
97
+ domain=data.get('domain', 'general'),
98
+ difficulty=data.get('difficulty', 'mixed'),
99
+ passing_score=data.get('passing_score', 70.0)
100
+ )
101
+
102
+ def save(self, filepath: str):
103
+ """Save benchmark to JSON file."""
104
+ Path(filepath).parent.mkdir(parents=True, exist_ok=True)
105
+
106
+ with open(filepath, 'w', encoding='utf-8') as f:
107
+ json.dump(self.to_dict(), f, indent=2, ensure_ascii=False)
108
+
109
+ print(f"Benchmark saved to: {filepath}")
110
+
111
+ @classmethod
112
+ def load(cls, filepath: str) -> 'Benchmark':
113
+ """Load benchmark from JSON file."""
114
+ with open(filepath, 'r', encoding='utf-8') as f:
115
+ data = json.load(f)
116
+
117
+ return cls.from_dict(data)
118
+
119
+
120
+ class BenchmarkSuite:
121
+ """
122
+ Collection of benchmarks for comprehensive testing.
123
+
124
+ Features:
125
+ - Multiple benchmark management
126
+ - Batch execution
127
+ - Aggregate scoring
128
+ - Result tracking
129
+ """
130
+
131
+ def __init__(self, name: str = "Default Suite"):
132
+ """
133
+ Initialize benchmark suite.
134
+
135
+ Args:
136
+ name: Suite name
137
+ """
138
+ self.name = name
139
+ self.benchmarks: List[Benchmark] = []
140
+ self.results: List[Dict[str, Any]] = []
141
+
142
+ def add_benchmark(self, benchmark: Benchmark):
143
+ """
144
+ Add a benchmark to the suite.
145
+
146
+ Args:
147
+ benchmark: Benchmark to add
148
+ """
149
+ self.benchmarks.append(benchmark)
150
+ print(f"Added benchmark: {benchmark.name}")
151
+
152
+ def remove_benchmark(self, benchmark_name: str):
153
+ """
154
+ Remove a benchmark by name.
155
+
156
+ Args:
157
+ benchmark_name: Name of benchmark to remove
158
+ """
159
+ self.benchmarks = [b for b in self.benchmarks if b.name != benchmark_name]
160
+
161
+ def get_benchmark(self, name: str) -> Optional[Benchmark]:
162
+ """
163
+ Get a benchmark by name.
164
+
165
+ Args:
166
+ name: Benchmark name
167
+
168
+ Returns:
169
+ Benchmark if found, None otherwise
170
+ """
171
+ for benchmark in self.benchmarks:
172
+ if benchmark.name == name:
173
+ return benchmark
174
+ return None
175
+
176
+ def run_benchmark(
177
+ self,
178
+ benchmark: Benchmark,
179
+ model_evaluator: Any,
180
+ max_questions: Optional[int] = None
181
+ ) -> Dict[str, Any]:
182
+ """
183
+ Run a single benchmark.
184
+
185
+ Args:
186
+ benchmark: Benchmark to run
187
+ model_evaluator: Model evaluator instance
188
+ max_questions: Maximum questions to test
189
+
190
+ Returns:
191
+ Benchmark results
192
+ """
193
+ print(f"\nRunning benchmark: {benchmark.name}")
194
+ print(f"Total questions: {len(benchmark.questions)}")
195
+
196
+ questions = benchmark.questions[:max_questions] if max_questions else benchmark.questions
197
+
198
+ # Convert to dataset format
199
+ dataset = []
200
+ for q in questions:
201
+ dataset.append({
202
+ 'instruction': q['question'],
203
+ 'input': '',
204
+ 'output': q['answer']
205
+ })
206
+
207
+ # Run evaluation
208
+ start_time = time.time()
209
+ eval_results = model_evaluator.evaluate_dataset(dataset)
210
+ total_time = time.time() - start_time
211
+
212
+ # Calculate score
213
+ score = self._calculate_score(eval_results)
214
+
215
+ # Compile results
216
+ results = {
217
+ 'benchmark_name': benchmark.name,
218
+ 'num_questions': len(questions),
219
+ 'score': score,
220
+ 'passed': score >= benchmark.passing_score,
221
+ 'passing_score': benchmark.passing_score,
222
+ 'total_time': total_time,
223
+ 'evaluation_results': eval_results,
224
+ 'timestamp': time.strftime('%Y-%m-%d %H:%M:%S')
225
+ }
226
+
227
+ self.results.append(results)
228
+
229
+ print(f"\n{'='*60}")
230
+ print(f"Benchmark: {benchmark.name}")
231
+ print(f"Score: {score:.2f}% (Passing: {benchmark.passing_score}%)")
232
+ print(f"Status: {'✅ PASSED' if results['passed'] else '❌ FAILED'}")
233
+ print(f"{'='*60}\n")
234
+
235
+ return results
236
+
237
+ def run_all_benchmarks(
238
+ self,
239
+ model_evaluator: Any,
240
+ max_questions_per_benchmark: Optional[int] = None
241
+ ) -> List[Dict[str, Any]]:
242
+ """
243
+ Run all benchmarks in the suite.
244
+
245
+ Args:
246
+ model_evaluator: Model evaluator instance
247
+ max_questions_per_benchmark: Max questions per benchmark
248
+
249
+ Returns:
250
+ List of all results
251
+ """
252
+ print(f"\n{'='*60}")
253
+ print(f"Running Benchmark Suite: {self.name}")
254
+ print(f"Total Benchmarks: {len(self.benchmarks)}")
255
+ print(f"{'='*60}\n")
256
+
257
+ all_results = []
258
+
259
+ for benchmark in self.benchmarks:
260
+ results = self.run_benchmark(
261
+ benchmark,
262
+ model_evaluator,
263
+ max_questions_per_benchmark
264
+ )
265
+ all_results.append(results)
266
+
267
+ # Summary
268
+ self._print_summary(all_results)
269
+
270
+ return all_results
271
+
272
+ def _calculate_score(self, eval_results: Dict[str, Any]) -> float:
273
+ """
274
+ Calculate benchmark score from evaluation results.
275
+
276
+ Args:
277
+ eval_results: Evaluation results
278
+
279
+ Returns:
280
+ Score percentage
281
+ """
282
+ metrics = eval_results.get('metrics', {})
283
+
284
+ # Use available metrics (prioritize accuracy, then BLEU, then ROUGE)
285
+ if 'accuracy' in metrics:
286
+ return metrics['accuracy']
287
+ elif 'bleu' in metrics:
288
+ return metrics['bleu']
289
+ elif 'rouge_l_f1' in metrics:
290
+ return metrics['rouge_l_f1']
291
+ else:
292
+ # Fallback: simple similarity check
293
+ examples = eval_results.get('examples', [])
294
+ if not examples:
295
+ return 0.0
296
+
297
+ matches = 0
298
+ for ex in examples:
299
+ pred = ex.get('prediction', '').lower().strip()
300
+ ref = ex.get('reference', '').lower().strip()
301
+ if pred in ref or ref in pred:
302
+ matches += 1
303
+
304
+ return (matches / len(examples)) * 100.0
305
+
306
+ def _print_summary(self, results: List[Dict[str, Any]]):
307
+ """Print summary of all benchmark results."""
308
+ print(f"\n{'='*60}")
309
+ print(f"BENCHMARK SUITE SUMMARY: {self.name}")
310
+ print(f"{'='*60}")
311
+
312
+ total_benchmarks = len(results)
313
+ passed = sum(1 for r in results if r['passed'])
314
+
315
+ print(f"\nOverall: {passed}/{total_benchmarks} benchmarks passed")
316
+ print(f"\nIndividual Results:")
317
+
318
+ for result in results:
319
+ status = '✅ PASS' if result['passed'] else '❌ FAIL'
320
+ print(f" {status} | {result['benchmark_name']:40s} | Score: {result['score']:6.2f}%")
321
+
322
+ avg_score = sum(r['score'] for r in results) / len(results) if results else 0
323
+ print(f"\nAverage Score: {avg_score:.2f}%")
324
+ print(f"{'='*60}\n")
325
+
326
+ def save_results(self, filepath: str):
327
+ """
328
+ Save suite results to JSON.
329
+
330
+ Args:
331
+ filepath: Output file path
332
+ """
333
+ Path(filepath).parent.mkdir(parents=True, exist_ok=True)
334
+
335
+ data = {
336
+ 'suite_name': self.name,
337
+ 'num_benchmarks': len(self.benchmarks),
338
+ 'benchmark_names': [b.name for b in self.benchmarks],
339
+ 'results': self.results,
340
+ 'timestamp': time.strftime('%Y-%m-%d %H:%M:%S')
341
+ }
342
+
343
+ with open(filepath, 'w', encoding='utf-8') as f:
344
+ json.dump(data, f, indent=2, ensure_ascii=False)
345
+
346
+ print(f"Suite results saved to: {filepath}")
347
+
348
+ def to_dict(self) -> Dict[str, Any]:
349
+ """Convert suite to dictionary."""
350
+ return {
351
+ 'name': self.name,
352
+ 'num_benchmarks': len(self.benchmarks),
353
+ 'benchmarks': [b.to_dict() for b in self.benchmarks],
354
+ 'results': self.results
355
+ }
356
+
357
+ @classmethod
358
+ def from_dict(cls, data: Dict[str, Any]) -> 'BenchmarkSuite':
359
+ """Create suite from dictionary."""
360
+ suite = cls(name=data.get('name', 'Default Suite'))
361
+
362
+ for benchmark_data in data.get('benchmarks', []):
363
+ benchmark = Benchmark.from_dict(benchmark_data)
364
+ suite.add_benchmark(benchmark)
365
+
366
+ suite.results = data.get('results', [])
367
+
368
+ return suite
evaluation/benchmark_builder.py ADDED
@@ -0,0 +1,505 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Benchmark Builder Module
3
+
4
+ Provides interactive tools for creating custom benchmarks.
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+ from typing import List, Dict, Optional, Any, Callable
9
+ import json
10
+ import time
11
+ from pathlib import Path
12
+
13
+
14
+ @dataclass
15
+ class Benchmark:
16
+ """
17
+ A single benchmark test (builder variant).
18
+
19
+ This is a builder-specific implementation with enhanced
20
+ interactive creation features.
21
+ """
22
+ name: str
23
+ description: str = ""
24
+ questions: List[Dict[str, Any]] = field(default_factory=list)
25
+ metadata: Dict[str, Any] = field(default_factory=dict)
26
+ created_at: Optional[str] = None
27
+ domain: str = "general"
28
+ difficulty: str = "mixed"
29
+ passing_score: float = 70.0
30
+ categories: List[str] = field(default_factory=list)
31
+ tags: List[str] = field(default_factory=list)
32
+
33
+ def __post_init__(self):
34
+ """Initialize timestamp if not provided."""
35
+ if self.created_at is None:
36
+ self.created_at = time.strftime('%Y-%m-%d %H:%M:%S')
37
+
38
+ def add_question(
39
+ self,
40
+ question: str,
41
+ answer: str,
42
+ category: Optional[str] = None,
43
+ difficulty: Optional[str] = None,
44
+ metadata: Optional[Dict] = None,
45
+ explanation: Optional[str] = None,
46
+ points: float = 1.0
47
+ ):
48
+ """
49
+ Add a question to the benchmark.
50
+
51
+ Args:
52
+ question: Question text
53
+ answer: Expected answer
54
+ category: Question category/topic
55
+ difficulty: Difficulty level
56
+ metadata: Additional metadata
57
+ explanation: Answer explanation
58
+ points: Points for this question
59
+ """
60
+ question_dict = {
61
+ 'id': len(self.questions),
62
+ 'question': question,
63
+ 'answer': answer,
64
+ 'category': category or 'general',
65
+ 'difficulty': difficulty or 'intermediate',
66
+ 'explanation': explanation or '',
67
+ 'points': points,
68
+ 'metadata': metadata or {}
69
+ }
70
+ self.questions.append(question_dict)
71
+
72
+ # Update categories list
73
+ if category and category not in self.categories:
74
+ self.categories.append(category)
75
+
76
+ def add_multiple_choice_question(
77
+ self,
78
+ question: str,
79
+ choices: List[str],
80
+ correct_answer: str,
81
+ category: Optional[str] = None,
82
+ difficulty: Optional[str] = None,
83
+ explanation: Optional[str] = None
84
+ ):
85
+ """
86
+ Add a multiple choice question.
87
+
88
+ Args:
89
+ question: Question text
90
+ choices: List of answer choices
91
+ correct_answer: The correct answer
92
+ category: Question category
93
+ difficulty: Difficulty level
94
+ explanation: Answer explanation
95
+ """
96
+ self.add_question(
97
+ question=question,
98
+ answer=correct_answer,
99
+ category=category,
100
+ difficulty=difficulty,
101
+ explanation=explanation,
102
+ metadata={
103
+ 'type': 'multiple_choice',
104
+ 'choices': choices
105
+ }
106
+ )
107
+
108
+ def import_from_json(self, filepath: str):
109
+ """
110
+ Import questions from JSON file.
111
+
112
+ Args:
113
+ filepath: Path to JSON file
114
+ """
115
+ with open(filepath, 'r', encoding='utf-8') as f:
116
+ data = json.load(f)
117
+
118
+ # Handle different JSON formats
119
+ if isinstance(data, list):
120
+ # List of questions
121
+ for item in data:
122
+ self.add_question(
123
+ question=item.get('question', ''),
124
+ answer=item.get('answer', ''),
125
+ category=item.get('category'),
126
+ difficulty=item.get('difficulty'),
127
+ metadata=item.get('metadata', {})
128
+ )
129
+ elif isinstance(data, dict):
130
+ # Benchmark format
131
+ if 'questions' in data:
132
+ for item in data['questions']:
133
+ self.add_question(
134
+ question=item.get('question', ''),
135
+ answer=item.get('answer', ''),
136
+ category=item.get('category'),
137
+ difficulty=item.get('difficulty'),
138
+ metadata=item.get('metadata', {})
139
+ )
140
+
141
+ print(f"Imported {len(self.questions)} questions from {filepath}")
142
+
143
+ def import_from_csv(self, filepath: str, delimiter: str = ','):
144
+ """
145
+ Import questions from CSV file.
146
+
147
+ Expected columns: question, answer, category, difficulty
148
+
149
+ Args:
150
+ filepath: Path to CSV file
151
+ delimiter: CSV delimiter
152
+ """
153
+ import csv
154
+
155
+ with open(filepath, 'r', encoding='utf-8') as f:
156
+ reader = csv.DictReader(f, delimiter=delimiter)
157
+
158
+ for row in reader:
159
+ self.add_question(
160
+ question=row.get('question', ''),
161
+ answer=row.get('answer', ''),
162
+ category=row.get('category'),
163
+ difficulty=row.get('difficulty')
164
+ )
165
+
166
+ print(f"Imported {len(self.questions)} questions from CSV")
167
+
168
+ def get_statistics(self) -> Dict[str, Any]:
169
+ """Get benchmark statistics."""
170
+ stats = {
171
+ 'total_questions': len(self.questions),
172
+ 'categories': {},
173
+ 'difficulties': {},
174
+ 'avg_question_length': 0,
175
+ 'avg_answer_length': 0
176
+ }
177
+
178
+ # Count by category
179
+ for q in self.questions:
180
+ cat = q.get('category', 'uncategorized')
181
+ stats['categories'][cat] = stats['categories'].get(cat, 0) + 1
182
+
183
+ diff = q.get('difficulty', 'unknown')
184
+ stats['difficulties'][diff] = stats['difficulties'].get(diff, 0) + 1
185
+
186
+ # Calculate averages
187
+ if self.questions:
188
+ total_q_len = sum(len(q['question']) for q in self.questions)
189
+ total_a_len = sum(len(q['answer']) for q in self.questions)
190
+ stats['avg_question_length'] = total_q_len / len(self.questions)
191
+ stats['avg_answer_length'] = total_a_len / len(self.questions)
192
+
193
+ return stats
194
+
195
+ def validate(self) -> List[str]:
196
+ """
197
+ Validate benchmark and return list of issues.
198
+
199
+ Returns:
200
+ List of validation issues (empty if valid)
201
+ """
202
+ issues = []
203
+
204
+ if not self.name:
205
+ issues.append("Benchmark name is required")
206
+
207
+ if not self.questions:
208
+ issues.append("Benchmark has no questions")
209
+
210
+ for i, q in enumerate(self.questions):
211
+ if not q.get('question'):
212
+ issues.append(f"Question {i} is missing question text")
213
+ if not q.get('answer'):
214
+ issues.append(f"Question {i} is missing answer")
215
+
216
+ return issues
217
+
218
+ def to_dict(self) -> Dict[str, Any]:
219
+ """Convert benchmark to dictionary."""
220
+ return {
221
+ 'name': self.name,
222
+ 'description': self.description,
223
+ 'domain': self.domain,
224
+ 'difficulty': self.difficulty,
225
+ 'passing_score': self.passing_score,
226
+ 'created_at': self.created_at,
227
+ 'categories': self.categories,
228
+ 'tags': self.tags,
229
+ 'num_questions': len(self.questions),
230
+ 'questions': self.questions,
231
+ 'metadata': self.metadata,
232
+ 'statistics': self.get_statistics()
233
+ }
234
+
235
+ @classmethod
236
+ def from_dict(cls, data: Dict[str, Any]) -> 'Benchmark':
237
+ """Create benchmark from dictionary."""
238
+ return cls(
239
+ name=data.get('name', 'Untitled'),
240
+ description=data.get('description', ''),
241
+ questions=data.get('questions', []),
242
+ metadata=data.get('metadata', {}),
243
+ created_at=data.get('created_at'),
244
+ domain=data.get('domain', 'general'),
245
+ difficulty=data.get('difficulty', 'mixed'),
246
+ passing_score=data.get('passing_score', 70.0),
247
+ categories=data.get('categories', []),
248
+ tags=data.get('tags', [])
249
+ )
250
+
251
+ def save(self, filepath: str):
252
+ """Save benchmark to JSON file."""
253
+ Path(filepath).parent.mkdir(parents=True, exist_ok=True)
254
+
255
+ with open(filepath, 'w', encoding='utf-8') as f:
256
+ json.dump(self.to_dict(), f, indent=2, ensure_ascii=False)
257
+
258
+ print(f"Benchmark saved to: {filepath}")
259
+
260
+ @classmethod
261
+ def load(cls, filepath: str) -> 'Benchmark':
262
+ """Load benchmark from JSON file."""
263
+ with open(filepath, 'r', encoding='utf-8') as f:
264
+ data = json.load(f)
265
+
266
+ return cls.from_dict(data)
267
+
268
+
269
+ class BenchmarkSuite:
270
+ """
271
+ Collection of benchmarks with enhanced building features.
272
+ """
273
+
274
+ def __init__(self, name: str = "Default Suite"):
275
+ """
276
+ Initialize benchmark suite.
277
+
278
+ Args:
279
+ name: Suite name
280
+ """
281
+ self.name = name
282
+ self.description = ""
283
+ self.benchmarks: List[Benchmark] = []
284
+ self.metadata: Dict[str, Any] = {}
285
+
286
+ def add_benchmark(self, benchmark: Benchmark):
287
+ """Add a benchmark to the suite."""
288
+ self.benchmarks.append(benchmark)
289
+ print(f"Added benchmark: {benchmark.name}")
290
+
291
+ def create_benchmark(
292
+ self,
293
+ name: str,
294
+ description: str = "",
295
+ domain: str = "general",
296
+ passing_score: float = 70.0
297
+ ) -> Benchmark:
298
+ """
299
+ Create a new benchmark and add to suite.
300
+
301
+ Args:
302
+ name: Benchmark name
303
+ description: Benchmark description
304
+ domain: Domain/topic
305
+ passing_score: Passing score percentage
306
+
307
+ Returns:
308
+ Created benchmark
309
+ """
310
+ benchmark = Benchmark(
311
+ name=name,
312
+ description=description,
313
+ domain=domain,
314
+ passing_score=passing_score
315
+ )
316
+ self.add_benchmark(benchmark)
317
+ return benchmark
318
+
319
+ def get_benchmark(self, name: str) -> Optional[Benchmark]:
320
+ """Get a benchmark by name."""
321
+ for benchmark in self.benchmarks:
322
+ if benchmark.name == name:
323
+ return benchmark
324
+ return None
325
+
326
+ def remove_benchmark(self, benchmark_name: str):
327
+ """Remove a benchmark by name."""
328
+ self.benchmarks = [b for b in self.benchmarks if b.name != benchmark_name]
329
+
330
+ def list_benchmarks(self) -> List[str]:
331
+ """Get list of benchmark names."""
332
+ return [b.name for b in self.benchmarks]
333
+
334
+ def get_statistics(self) -> Dict[str, Any]:
335
+ """Get suite-wide statistics."""
336
+ total_questions = sum(len(b.questions) for b in self.benchmarks)
337
+
338
+ stats = {
339
+ 'num_benchmarks': len(self.benchmarks),
340
+ 'total_questions': total_questions,
341
+ 'benchmarks': []
342
+ }
343
+
344
+ for benchmark in self.benchmarks:
345
+ stats['benchmarks'].append({
346
+ 'name': benchmark.name,
347
+ 'num_questions': len(benchmark.questions),
348
+ 'domain': benchmark.domain,
349
+ 'passing_score': benchmark.passing_score
350
+ })
351
+
352
+ return stats
353
+
354
+ def to_dict(self) -> Dict[str, Any]:
355
+ """Convert suite to dictionary."""
356
+ return {
357
+ 'name': self.name,
358
+ 'description': self.description,
359
+ 'num_benchmarks': len(self.benchmarks),
360
+ 'benchmarks': [b.to_dict() for b in self.benchmarks],
361
+ 'metadata': self.metadata,
362
+ 'statistics': self.get_statistics()
363
+ }
364
+
365
+ @classmethod
366
+ def from_dict(cls, data: Dict[str, Any]) -> 'BenchmarkSuite':
367
+ """Create suite from dictionary."""
368
+ suite = cls(name=data.get('name', 'Default Suite'))
369
+ suite.description = data.get('description', '')
370
+ suite.metadata = data.get('metadata', {})
371
+
372
+ for benchmark_data in data.get('benchmarks', []):
373
+ benchmark = Benchmark.from_dict(benchmark_data)
374
+ suite.add_benchmark(benchmark)
375
+
376
+ return suite
377
+
378
+ def save(self, filepath: str):
379
+ """Save suite to JSON file."""
380
+ Path(filepath).parent.mkdir(parents=True, exist_ok=True)
381
+
382
+ with open(filepath, 'w', encoding='utf-8') as f:
383
+ json.dump(self.to_dict(), f, indent=2, ensure_ascii=False)
384
+
385
+ print(f"Benchmark suite saved to: {filepath}")
386
+
387
+ @classmethod
388
+ def load(cls, filepath: str) -> 'BenchmarkSuite':
389
+ """Load suite from JSON file."""
390
+ with open(filepath, 'r', encoding='utf-8') as f:
391
+ data = json.load(f)
392
+
393
+ return cls.from_dict(data)
394
+
395
+
396
+ class InteractiveBenchmarkBuilder:
397
+ """
398
+ Interactive builder for creating benchmarks through UI/CLI.
399
+ """
400
+
401
+ def __init__(self):
402
+ """Initialize builder."""
403
+ self.current_benchmark: Optional[Benchmark] = None
404
+ self.current_suite: Optional[BenchmarkSuite] = None
405
+
406
+ def create_benchmark(
407
+ self,
408
+ name: str,
409
+ description: str = "",
410
+ domain: str = "general"
411
+ ) -> Benchmark:
412
+ """
413
+ Create a new benchmark.
414
+
415
+ Args:
416
+ name: Benchmark name
417
+ description: Description
418
+ domain: Domain/topic
419
+
420
+ Returns:
421
+ Created benchmark
422
+ """
423
+ self.current_benchmark = Benchmark(
424
+ name=name,
425
+ description=description,
426
+ domain=domain
427
+ )
428
+ return self.current_benchmark
429
+
430
+ def add_question_interactive(
431
+ self,
432
+ question: str,
433
+ answer: str,
434
+ category: Optional[str] = None,
435
+ difficulty: Optional[str] = None
436
+ ) -> bool:
437
+ """
438
+ Add question to current benchmark.
439
+
440
+ Args:
441
+ question: Question text
442
+ answer: Answer text
443
+ category: Category
444
+ difficulty: Difficulty level
445
+
446
+ Returns:
447
+ Success status
448
+ """
449
+ if not self.current_benchmark:
450
+ print("No active benchmark. Create one first.")
451
+ return False
452
+
453
+ self.current_benchmark.add_question(
454
+ question=question,
455
+ answer=answer,
456
+ category=category,
457
+ difficulty=difficulty
458
+ )
459
+ return True
460
+
461
+ def preview_benchmark(self) -> str:
462
+ """Preview current benchmark."""
463
+ if not self.current_benchmark:
464
+ return "No active benchmark"
465
+
466
+ stats = self.current_benchmark.get_statistics()
467
+
468
+ preview = f"""
469
+ Benchmark: {self.current_benchmark.name}
470
+ Description: {self.current_benchmark.description}
471
+ Domain: {self.current_benchmark.domain}
472
+ Total Questions: {stats['total_questions']}
473
+
474
+ Categories:
475
+ """
476
+ for cat, count in stats['categories'].items():
477
+ preview += f" - {cat}: {count} questions\n"
478
+
479
+ return preview
480
+
481
+ def finalize_benchmark(self, filepath: Optional[str] = None) -> Benchmark:
482
+ """
483
+ Finalize and optionally save benchmark.
484
+
485
+ Args:
486
+ filepath: Optional save path
487
+
488
+ Returns:
489
+ Finalized benchmark
490
+ """
491
+ if not self.current_benchmark:
492
+ raise ValueError("No active benchmark to finalize")
493
+
494
+ issues = self.current_benchmark.validate()
495
+ if issues:
496
+ print("Validation warnings:")
497
+ for issue in issues:
498
+ print(f" - {issue}")
499
+
500
+ if filepath:
501
+ self.current_benchmark.save(filepath)
502
+
503
+ benchmark = self.current_benchmark
504
+ self.current_benchmark = None
505
+ return benchmark
evaluation/certification_tests.py ADDED
@@ -0,0 +1,500 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Certification Tests Module
3
+
4
+ Provides pre-built certification exam benchmarks for various domains.
5
+ """
6
+
7
+ from typing import List, Dict, Optional, Any
8
+ import json
9
+ from pathlib import Path
10
+ from dataclasses import dataclass, field
11
+
12
+
13
+ @dataclass
14
+ class CertificationQuestion:
15
+ """A single certification exam question."""
16
+ question: str
17
+ answer: str
18
+ category: str
19
+ difficulty: str = "intermediate"
20
+ points: float = 1.0
21
+ explanation: Optional[str] = None
22
+ metadata: Dict[str, Any] = field(default_factory=dict)
23
+
24
+
25
+ class CertificationTestBuilder:
26
+ """
27
+ Base class for building certification tests.
28
+ """
29
+
30
+ def __init__(self, name: str, domain: str):
31
+ """
32
+ Initialize certification test.
33
+
34
+ Args:
35
+ name: Test name
36
+ domain: Domain (e.g., 'financial', 'medical')
37
+ """
38
+ self.name = name
39
+ self.domain = domain
40
+ self.questions: List[CertificationQuestion] = []
41
+ self.passing_score = 70.0
42
+ self.categories: List[str] = []
43
+
44
+ def add_question(
45
+ self,
46
+ question: str,
47
+ answer: str,
48
+ category: str,
49
+ difficulty: str = "intermediate",
50
+ explanation: Optional[str] = None
51
+ ):
52
+ """Add a question to the test."""
53
+ q = CertificationQuestion(
54
+ question=question,
55
+ answer=answer,
56
+ category=category,
57
+ difficulty=difficulty,
58
+ explanation=explanation
59
+ )
60
+ self.questions.append(q)
61
+
62
+ if category not in self.categories:
63
+ self.categories.append(category)
64
+
65
+ def get_questions_by_category(self, category: str) -> List[CertificationQuestion]:
66
+ """Get all questions in a category."""
67
+ return [q for q in self.questions if q.category == category]
68
+
69
+ def to_benchmark_dict(self) -> Dict[str, Any]:
70
+ """Convert to benchmark format."""
71
+ return {
72
+ 'name': self.name,
73
+ 'domain': self.domain,
74
+ 'description': f"{self.name} Certification Exam",
75
+ 'passing_score': self.passing_score,
76
+ 'num_questions': len(self.questions),
77
+ 'categories': self.categories,
78
+ 'questions': [
79
+ {
80
+ 'question': q.question,
81
+ 'answer': q.answer,
82
+ 'category': q.category,
83
+ 'difficulty': q.difficulty,
84
+ 'points': q.points,
85
+ 'explanation': q.explanation,
86
+ 'metadata': q.metadata
87
+ }
88
+ for q in self.questions
89
+ ]
90
+ }
91
+
92
+ def save(self, filepath: str):
93
+ """Save test to JSON."""
94
+ Path(filepath).parent.mkdir(parents=True, exist_ok=True)
95
+
96
+ with open(filepath, 'w', encoding='utf-8') as f:
97
+ json.dump(self.to_benchmark_dict(), f, indent=2, ensure_ascii=False)
98
+
99
+ print(f"Certification test saved to: {filepath}")
100
+
101
+
102
+ class FinancialCertificationTests:
103
+ """
104
+ Pre-built financial certification tests.
105
+
106
+ Includes:
107
+ - CFP (Certified Financial Planner)
108
+ - CFA (Chartered Financial Analyst)
109
+ - CPA (Certified Public Accountant)
110
+ - Series 7, Series 63
111
+ """
112
+
113
+ @staticmethod
114
+ def create_cfp_exam() -> CertificationTestBuilder:
115
+ """Create CFP certification exam."""
116
+ exam = CertificationTestBuilder("CFP (Certified Financial Planner)", "financial")
117
+ exam.passing_score = 70.0
118
+
119
+ # Estate Planning
120
+ exam.add_question(
121
+ "What is the primary purpose of a revocable living trust?",
122
+ "A revocable living trust allows assets to pass to beneficiaries without going through probate while maintaining the grantor's control during their lifetime.",
123
+ category="Estate Planning",
124
+ difficulty="intermediate"
125
+ )
126
+
127
+ exam.add_question(
128
+ "What is the annual gift tax exclusion for 2024?",
129
+ "The annual gift tax exclusion for 2024 is $18,000 per recipient.",
130
+ category="Estate Planning",
131
+ difficulty="beginner"
132
+ )
133
+
134
+ # Retirement Planning
135
+ exam.add_question(
136
+ "What are the contribution limits for a 401(k) plan in 2024 for individuals under 50?",
137
+ "The contribution limit for 401(k) plans in 2024 is $23,000 for individuals under age 50.",
138
+ category="Retirement Planning",
139
+ difficulty="beginner"
140
+ )
141
+
142
+ exam.add_question(
143
+ "Explain the difference between a traditional IRA and a Roth IRA in terms of tax treatment.",
144
+ "Traditional IRA contributions may be tax-deductible and earnings grow tax-deferred, with distributions taxed as ordinary income. Roth IRA contributions are made with after-tax dollars, but qualified distributions are tax-free.",
145
+ category="Retirement Planning",
146
+ difficulty="intermediate"
147
+ )
148
+
149
+ # Tax Planning
150
+ exam.add_question(
151
+ "What is the long-term capital gains tax rate for high-income earners in 2024?",
152
+ "The long-term capital gains tax rate for high-income earners is 20%, plus a potential 3.8% Net Investment Income Tax.",
153
+ category="Tax Planning",
154
+ difficulty="intermediate"
155
+ )
156
+
157
+ # Investment Planning
158
+ exam.add_question(
159
+ "What is Modern Portfolio Theory and who developed it?",
160
+ "Modern Portfolio Theory, developed by Harry Markowitz, suggests that investors can construct an optimal portfolio by considering the relationship between risk and return, focusing on diversification to maximize returns for a given level of risk.",
161
+ category="Investment Planning",
162
+ difficulty="advanced"
163
+ )
164
+
165
+ exam.add_question(
166
+ "Explain the concept of asset allocation.",
167
+ "Asset allocation is the process of dividing investments among different asset classes (stocks, bonds, cash, etc.) to balance risk and reward according to an investor's goals, risk tolerance, and time horizon.",
168
+ category="Investment Planning",
169
+ difficulty="intermediate"
170
+ )
171
+
172
+ # Insurance Planning
173
+ exam.add_question(
174
+ "What is the primary difference between term life insurance and whole life insurance?",
175
+ "Term life insurance provides coverage for a specific period and has no cash value, while whole life insurance provides lifetime coverage and builds cash value over time.",
176
+ category="Insurance Planning",
177
+ difficulty="beginner"
178
+ )
179
+
180
+ # Risk Management
181
+ exam.add_question(
182
+ "What is an umbrella insurance policy?",
183
+ "An umbrella insurance policy provides additional liability coverage beyond the limits of homeowners, auto, and other personal insurance policies.",
184
+ category="Risk Management",
185
+ difficulty="intermediate"
186
+ )
187
+
188
+ # Education Planning
189
+ exam.add_question(
190
+ "What are the tax advantages of a 529 college savings plan?",
191
+ "529 plans offer tax-free growth and tax-free withdrawals when funds are used for qualified education expenses. Some states also provide state tax deductions for contributions.",
192
+ category="Education Planning",
193
+ difficulty="intermediate"
194
+ )
195
+
196
+ return exam
197
+
198
+ @staticmethod
199
+ def create_cfa_level_1_exam() -> CertificationTestBuilder:
200
+ """Create CFA Level I exam."""
201
+ exam = CertificationTestBuilder("CFA Level I", "financial")
202
+ exam.passing_score = 70.0
203
+
204
+ # Ethics
205
+ exam.add_question(
206
+ "What are the six components of the CFA Institute Code of Ethics?",
207
+ "The six components are: Act with integrity, Place client interests first, Use reasonable care and judgment, Act professionally, Promote integrity of capital markets, and Maintain competence.",
208
+ category="Ethics",
209
+ difficulty="intermediate"
210
+ )
211
+
212
+ # Quantitative Methods
213
+ exam.add_question(
214
+ "What is the formula for calculating the present value of a perpetuity?",
215
+ "The present value of a perpetuity is calculated as: PV = PMT / r, where PMT is the payment and r is the discount rate.",
216
+ category="Quantitative Methods",
217
+ difficulty="intermediate"
218
+ )
219
+
220
+ # Economics
221
+ exam.add_question(
222
+ "Define GDP and explain the expenditure approach to measuring it.",
223
+ "GDP (Gross Domestic Product) is the total value of goods and services produced in an economy. The expenditure approach calculates GDP as: GDP = C + I + G + (X - M), where C is consumption, I is investment, G is government spending, X is exports, and M is imports.",
224
+ category="Economics",
225
+ difficulty="intermediate"
226
+ )
227
+
228
+ # Financial Reporting
229
+ exam.add_question(
230
+ "What are the three main financial statements?",
231
+ "The three main financial statements are the Balance Sheet (Statement of Financial Position), Income Statement (Statement of Comprehensive Income), and Cash Flow Statement.",
232
+ category="Financial Reporting",
233
+ difficulty="beginner"
234
+ )
235
+
236
+ # Corporate Finance
237
+ exam.add_question(
238
+ "What is the Weighted Average Cost of Capital (WACC)?",
239
+ "WACC is the average rate a company expects to pay to finance its assets, weighted by the proportion of debt and equity in its capital structure.",
240
+ category="Corporate Finance",
241
+ difficulty="intermediate"
242
+ )
243
+
244
+ # Equity Investments
245
+ exam.add_question(
246
+ "Explain the difference between value stocks and growth stocks.",
247
+ "Value stocks trade at lower prices relative to fundamentals (P/E, P/B) and typically pay dividends. Growth stocks are expected to grow earnings faster than the market average and typically reinvest earnings rather than paying dividends.",
248
+ category="Equity Investments",
249
+ difficulty="intermediate"
250
+ )
251
+
252
+ # Fixed Income
253
+ exam.add_question(
254
+ "What is duration and how does it relate to interest rate risk?",
255
+ "Duration measures a bond's price sensitivity to interest rate changes. Higher duration means greater price sensitivity. When interest rates rise, bond prices fall more for bonds with higher duration.",
256
+ category="Fixed Income",
257
+ difficulty="advanced"
258
+ )
259
+
260
+ # Derivatives
261
+ exam.add_question(
262
+ "What is a call option?",
263
+ "A call option gives the holder the right, but not the obligation, to buy an underlying asset at a specified strike price on or before a specified expiration date.",
264
+ category="Derivatives",
265
+ difficulty="beginner"
266
+ )
267
+
268
+ return exam
269
+
270
+ @staticmethod
271
+ def create_series_7_exam() -> CertificationTestBuilder:
272
+ """Create Series 7 General Securities Representative exam."""
273
+ exam = CertificationTestBuilder("Series 7", "financial")
274
+ exam.passing_score = 72.0
275
+
276
+ # Regulations
277
+ exam.add_question(
278
+ "What is Regulation T and what does it govern?",
279
+ "Regulation T is a Federal Reserve Board regulation that governs customer cash accounts and the amount of credit that broker-dealers may extend to customers for securities purchases (currently 50% initial margin).",
280
+ category="Regulations",
281
+ difficulty="intermediate"
282
+ )
283
+
284
+ # Securities Products
285
+ exam.add_question(
286
+ "What is the difference between common stock and preferred stock?",
287
+ "Common stock provides voting rights and variable dividends based on company performance. Preferred stock typically has fixed dividends, no voting rights, and priority over common stock in liquidation.",
288
+ category="Securities Products",
289
+ difficulty="beginner"
290
+ )
291
+
292
+ # Municipal Securities
293
+ exam.add_question(
294
+ "What are the two main types of municipal bonds?",
295
+ "The two main types are General Obligation (GO) bonds, backed by the taxing power of the issuer, and Revenue bonds, backed by specific revenue sources like tolls or utility fees.",
296
+ category="Municipal Securities",
297
+ difficulty="intermediate"
298
+ )
299
+
300
+ # Options
301
+ exam.add_question(
302
+ "What is a covered call strategy?",
303
+ "A covered call strategy involves owning a stock and selling call options on that stock to generate income from the option premium while capping potential upside gains.",
304
+ category="Options",
305
+ difficulty="intermediate"
306
+ )
307
+
308
+ # Suitability
309
+ exam.add_question(
310
+ "What factors must be considered when determining investment suitability for a client?",
311
+ "Factors include the client's financial situation, tax status, investment objectives, time horizon, liquidity needs, risk tolerance, and investment experience.",
312
+ category="Suitability",
313
+ difficulty="intermediate"
314
+ )
315
+
316
+ return exam
317
+
318
+
319
+ class MedicalCertificationTests:
320
+ """
321
+ Pre-built medical certification tests.
322
+ """
323
+
324
+ @staticmethod
325
+ def create_usmle_step_1_sample() -> CertificationTestBuilder:
326
+ """Create USMLE Step 1 sample questions."""
327
+ exam = CertificationTestBuilder("USMLE Step 1 Sample", "medical")
328
+ exam.passing_score = 70.0
329
+
330
+ # Anatomy
331
+ exam.add_question(
332
+ "Which cranial nerve is responsible for facial sensation and motor control of mastication?",
333
+ "The trigeminal nerve (cranial nerve V) is responsible for facial sensation and motor control of the muscles of mastication.",
334
+ category="Anatomy",
335
+ difficulty="intermediate"
336
+ )
337
+
338
+ # Physiology
339
+ exam.add_question(
340
+ "Describe the Frank-Starling mechanism of the heart.",
341
+ "The Frank-Starling mechanism states that the force of cardiac contraction is directly proportional to the initial length of cardiac muscle fibers (preload). Increased venous return stretches the ventricle, leading to stronger contraction.",
342
+ category="Physiology",
343
+ difficulty="advanced"
344
+ )
345
+
346
+ # Pharmacology
347
+ exam.add_question(
348
+ "What is the mechanism of action of ACE inhibitors?",
349
+ "ACE inhibitors block the angiotensin-converting enzyme, preventing the conversion of angiotensin I to angiotensin II, thereby reducing vasoconstriction and aldosterone secretion, which lowers blood pressure.",
350
+ category="Pharmacology",
351
+ difficulty="intermediate"
352
+ )
353
+
354
+ # Pathology
355
+ exam.add_question(
356
+ "What are the four cardinal signs of inflammation?",
357
+ "The four cardinal signs of inflammation are: rubor (redness), calor (heat), tumor (swelling), and dolor (pain). A fifth sign, loss of function, is sometimes added.",
358
+ category="Pathology",
359
+ difficulty="beginner"
360
+ )
361
+
362
+ return exam
363
+
364
+
365
+ class LegalCertificationTests:
366
+ """
367
+ Pre-built legal certification tests.
368
+ """
369
+
370
+ @staticmethod
371
+ def create_bar_exam_sample() -> CertificationTestBuilder:
372
+ """Create Bar Exam sample questions."""
373
+ exam = CertificationTestBuilder("Bar Exam Sample", "legal")
374
+ exam.passing_score = 70.0
375
+
376
+ # Constitutional Law
377
+ exam.add_question(
378
+ "What is the difference between strict scrutiny and rational basis review?",
379
+ "Strict scrutiny requires the government to prove a compelling interest and narrowly tailored means (used for fundamental rights). Rational basis review only requires a legitimate interest and rational relationship (default standard).",
380
+ category="Constitutional Law",
381
+ difficulty="advanced"
382
+ )
383
+
384
+ # Contracts
385
+ exam.add_question(
386
+ "What are the essential elements of a valid contract?",
387
+ "The essential elements are: offer, acceptance, consideration, mutual assent (meeting of minds), capacity, and legality of purpose.",
388
+ category="Contracts",
389
+ difficulty="intermediate"
390
+ )
391
+
392
+ # Torts
393
+ exam.add_question(
394
+ "What are the elements of negligence?",
395
+ "The elements of negligence are: duty of care, breach of duty, causation (actual and proximate), and damages.",
396
+ category="Torts",
397
+ difficulty="intermediate"
398
+ )
399
+
400
+ # Criminal Law
401
+ exam.add_question(
402
+ "What is mens rea?",
403
+ "Mens rea is the mental state or intent element of a crime. It refers to the defendant's guilty mind or criminal intent at the time the criminal act was committed.",
404
+ category="Criminal Law",
405
+ difficulty="beginner"
406
+ )
407
+
408
+ return exam
409
+
410
+
411
+ class EducationCertificationTests:
412
+ """
413
+ Pre-built education certification tests.
414
+ """
415
+
416
+ @staticmethod
417
+ def create_praxis_core_sample() -> CertificationTestBuilder:
418
+ """Create Praxis Core sample test."""
419
+ exam = CertificationTestBuilder("Praxis Core Sample", "education")
420
+ exam.passing_score = 70.0
421
+
422
+ # Reading
423
+ exam.add_question(
424
+ "What is the main idea vs. supporting details in a passage?",
425
+ "The main idea is the central point or primary message of a passage, while supporting details provide evidence, examples, or explanations that reinforce the main idea.",
426
+ category="Reading",
427
+ difficulty="beginner"
428
+ )
429
+
430
+ # Writing
431
+ exam.add_question(
432
+ "What is the difference between an independent clause and a dependent clause?",
433
+ "An independent clause can stand alone as a complete sentence with a subject and predicate. A dependent clause cannot stand alone and depends on an independent clause to form a complete thought.",
434
+ category="Writing",
435
+ difficulty="intermediate"
436
+ )
437
+
438
+ # Mathematics
439
+ exam.add_question(
440
+ "What is the order of operations in mathematics?",
441
+ "The order of operations is: Parentheses, Exponents, Multiplication and Division (left to right), Addition and Subtraction (left to right), often remembered by PEMDAS.",
442
+ category="Mathematics",
443
+ difficulty="beginner"
444
+ )
445
+
446
+ return exam
447
+
448
+
449
+ def get_certification_tests_for_domain(domain: str) -> List[CertificationTestBuilder]:
450
+ """
451
+ Get all certification tests for a specific domain.
452
+
453
+ Args:
454
+ domain: Domain name (financial, medical, legal, education)
455
+
456
+ Returns:
457
+ List of certification tests
458
+ """
459
+ tests = []
460
+
461
+ if domain.lower() == "financial":
462
+ tests.append(FinancialCertificationTests.create_cfp_exam())
463
+ tests.append(FinancialCertificationTests.create_cfa_level_1_exam())
464
+ tests.append(FinancialCertificationTests.create_series_7_exam())
465
+
466
+ elif domain.lower() == "medical":
467
+ tests.append(MedicalCertificationTests.create_usmle_step_1_sample())
468
+
469
+ elif domain.lower() == "legal":
470
+ tests.append(LegalCertificationTests.create_bar_exam_sample())
471
+
472
+ elif domain.lower() == "education":
473
+ tests.append(EducationCertificationTests.create_praxis_core_sample())
474
+
475
+ return tests
476
+
477
+
478
+ def list_available_certification_tests() -> Dict[str, List[str]]:
479
+ """
480
+ List all available certification tests by domain.
481
+
482
+ Returns:
483
+ Dict mapping domain to test names
484
+ """
485
+ return {
486
+ 'financial': [
487
+ 'CFP (Certified Financial Planner)',
488
+ 'CFA Level I',
489
+ 'Series 7'
490
+ ],
491
+ 'medical': [
492
+ 'USMLE Step 1 Sample'
493
+ ],
494
+ 'legal': [
495
+ 'Bar Exam Sample'
496
+ ],
497
+ 'education': [
498
+ 'Praxis Core Sample'
499
+ ]
500
+ }
evaluation/evaluator.py ADDED
@@ -0,0 +1,347 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Model Evaluator Module
3
+
4
+ Provides model evaluation and inference capabilities.
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+ from typing import List, Dict, Optional, Any
9
+ import json
10
+ import time
11
+ from pathlib import Path
12
+ import torch
13
+
14
+
15
+ @dataclass
16
+ class EvaluationConfig:
17
+ """
18
+ Configuration for model evaluation.
19
+ """
20
+ model_name: str = "gpt2"
21
+ model_path: Optional[str] = None
22
+ max_length: int = 512
23
+ temperature: float = 0.7
24
+ top_p: float = 0.9
25
+ top_k: int = 50
26
+ num_beams: int = 1
27
+ do_sample: bool = True
28
+ batch_size: int = 1
29
+ device: str = "auto"
30
+ max_samples: Optional[int] = None
31
+ save_results: bool = True
32
+ output_dir: str = "evaluation_results"
33
+ include_metrics: bool = True
34
+ include_timings: bool = True
35
+
36
+
37
+ class ModelEvaluator:
38
+ """
39
+ Model evaluator for testing and benchmarking LLMs.
40
+
41
+ Features:
42
+ - Inference on test datasets
43
+ - Batch processing
44
+ - Performance metrics
45
+ - Result saving and analysis
46
+ """
47
+
48
+ def __init__(
49
+ self,
50
+ config: Optional[EvaluationConfig] = None,
51
+ model: Optional[Any] = None,
52
+ tokenizer: Optional[Any] = None
53
+ ):
54
+ """
55
+ Initialize evaluator.
56
+
57
+ Args:
58
+ config: Evaluation configuration
59
+ model: Pre-loaded model (optional)
60
+ tokenizer: Pre-loaded tokenizer (optional)
61
+ """
62
+ self.config = config or EvaluationConfig()
63
+ self.model = model
64
+ self.tokenizer = tokenizer
65
+ self.results = []
66
+ self.metrics = {}
67
+
68
+ def load_model(self):
69
+ """Load model and tokenizer."""
70
+ if self.model is not None and self.tokenizer is not None:
71
+ print("Using pre-loaded model and tokenizer")
72
+ return
73
+
74
+ try:
75
+ from transformers import AutoModelForCausalLM, AutoTokenizer
76
+
77
+ print(f"Loading model: {self.config.model_name}")
78
+
79
+ # Load tokenizer
80
+ self.tokenizer = AutoTokenizer.from_pretrained(
81
+ self.config.model_path or self.config.model_name,
82
+ trust_remote_code=True
83
+ )
84
+
85
+ # Ensure pad token exists
86
+ if self.tokenizer.pad_token is None:
87
+ self.tokenizer.pad_token = self.tokenizer.eos_token
88
+
89
+ # Load model
90
+ device_map = "auto" if self.config.device == "auto" else None
91
+ self.model = AutoModelForCausalLM.from_pretrained(
92
+ self.config.model_path or self.config.model_name,
93
+ device_map=device_map,
94
+ trust_remote_code=True,
95
+ torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32
96
+ )
97
+
98
+ self.model.eval()
99
+ print("Model loaded successfully")
100
+
101
+ except Exception as e:
102
+ print(f"Error loading model: {e}")
103
+ raise
104
+
105
+ def generate_response(
106
+ self,
107
+ prompt: str,
108
+ max_length: Optional[int] = None,
109
+ temperature: Optional[float] = None
110
+ ) -> str:
111
+ """
112
+ Generate response for a single prompt.
113
+
114
+ Args:
115
+ prompt: Input prompt
116
+ max_length: Max generation length
117
+ temperature: Sampling temperature
118
+
119
+ Returns:
120
+ Generated text
121
+ """
122
+ if self.model is None or self.tokenizer is None:
123
+ self.load_model()
124
+
125
+ max_length = max_length or self.config.max_length
126
+ temperature = temperature or self.config.temperature
127
+
128
+ try:
129
+ # Tokenize
130
+ inputs = self.tokenizer(
131
+ prompt,
132
+ return_tensors="pt",
133
+ truncation=True,
134
+ max_length=2048
135
+ )
136
+
137
+ # Move to device
138
+ if hasattr(self.model, 'device'):
139
+ inputs = {k: v.to(self.model.device) for k, v in inputs.items()}
140
+
141
+ # Generate
142
+ with torch.no_grad():
143
+ outputs = self.model.generate(
144
+ **inputs,
145
+ max_length=max_length,
146
+ temperature=temperature,
147
+ top_p=self.config.top_p,
148
+ top_k=self.config.top_k,
149
+ num_beams=self.config.num_beams,
150
+ do_sample=self.config.do_sample,
151
+ pad_token_id=self.tokenizer.pad_token_id,
152
+ eos_token_id=self.tokenizer.eos_token_id
153
+ )
154
+
155
+ # Decode
156
+ generated_text = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
157
+
158
+ # Remove prompt from output
159
+ if generated_text.startswith(prompt):
160
+ generated_text = generated_text[len(prompt):].strip()
161
+
162
+ return generated_text
163
+
164
+ except Exception as e:
165
+ print(f"Generation error: {e}")
166
+ return f"[Error: {str(e)}]"
167
+
168
+ def evaluate_dataset(
169
+ self,
170
+ dataset: List[Dict[str, str]],
171
+ max_samples: Optional[int] = None
172
+ ) -> Dict[str, Any]:
173
+ """
174
+ Evaluate model on a dataset.
175
+
176
+ Args:
177
+ dataset: List of examples with 'instruction', 'input', 'output'
178
+ max_samples: Maximum samples to evaluate
179
+
180
+ Returns:
181
+ Evaluation results
182
+ """
183
+ if self.model is None:
184
+ self.load_model()
185
+
186
+ max_samples = max_samples or self.config.max_samples or len(dataset)
187
+ dataset = dataset[:max_samples]
188
+
189
+ print(f"\nEvaluating on {len(dataset)} examples...")
190
+
191
+ results = []
192
+ predictions = []
193
+ references = []
194
+ start_time = time.time()
195
+
196
+ for i, example in enumerate(dataset):
197
+ # Build prompt
198
+ instruction = example.get('instruction', '')
199
+ input_text = example.get('input', '')
200
+ reference = example.get('output', '')
201
+
202
+ if input_text:
203
+ prompt = f"{instruction}\n\nInput: {input_text}\n\nResponse:"
204
+ else:
205
+ prompt = f"{instruction}\n\nResponse:"
206
+
207
+ # Generate
208
+ example_start = time.time()
209
+ prediction = self.generate_response(prompt)
210
+ example_time = time.time() - example_start
211
+
212
+ # Store results
213
+ result = {
214
+ 'index': i,
215
+ 'instruction': instruction,
216
+ 'input': input_text,
217
+ 'reference': reference,
218
+ 'prediction': prediction,
219
+ 'generation_time': example_time
220
+ }
221
+ results.append(result)
222
+ predictions.append(prediction)
223
+ references.append(reference)
224
+
225
+ if (i + 1) % 10 == 0:
226
+ print(f" Processed {i + 1}/{len(dataset)} examples...")
227
+
228
+ total_time = time.time() - start_time
229
+
230
+ # Calculate metrics
231
+ metrics = {}
232
+ if self.config.include_metrics:
233
+ try:
234
+ from .metrics import Metrics
235
+ metrics_calc = Metrics()
236
+ metrics = metrics_calc.calculate_all_metrics(predictions, references)
237
+ except Exception as e:
238
+ print(f"Metrics calculation error: {e}")
239
+ metrics = {'error': str(e)}
240
+
241
+ # Compile results
242
+ evaluation_results = {
243
+ 'config': {
244
+ 'model_name': self.config.model_name,
245
+ 'model_path': self.config.model_path,
246
+ 'max_length': self.config.max_length,
247
+ 'temperature': self.config.temperature,
248
+ 'num_samples': len(dataset)
249
+ },
250
+ 'metrics': metrics,
251
+ 'timing': {
252
+ 'total_time': total_time,
253
+ 'avg_time_per_example': total_time / len(dataset),
254
+ 'throughput': len(dataset) / total_time
255
+ },
256
+ 'examples': results
257
+ }
258
+
259
+ # Save results
260
+ if self.config.save_results:
261
+ self.save_results(evaluation_results)
262
+
263
+ print(f"\n✅ Evaluation complete!")
264
+ print(f"Total time: {total_time:.2f}s")
265
+ print(f"Avg time per example: {total_time/len(dataset):.2f}s")
266
+ if metrics:
267
+ print(f"\nMetrics:")
268
+ for key, value in metrics.items():
269
+ if isinstance(value, (int, float)):
270
+ print(f" {key}: {value:.2f}")
271
+
272
+ return evaluation_results
273
+
274
+ def save_results(self, results: Dict[str, Any], filename: Optional[str] = None):
275
+ """
276
+ Save evaluation results to JSON.
277
+
278
+ Args:
279
+ results: Evaluation results
280
+ filename: Output filename
281
+ """
282
+ output_dir = Path(self.config.output_dir)
283
+ output_dir.mkdir(parents=True, exist_ok=True)
284
+
285
+ if filename is None:
286
+ timestamp = time.strftime('%Y%m%d_%H%M%S')
287
+ filename = f"evaluation_{timestamp}.json"
288
+
289
+ filepath = output_dir / filename
290
+
291
+ with open(filepath, 'w', encoding='utf-8') as f:
292
+ json.dump(results, f, indent=2, ensure_ascii=False)
293
+
294
+ print(f"Results saved to: {filepath}")
295
+
296
+ def load_results(self, filepath: str) -> Dict[str, Any]:
297
+ """
298
+ Load evaluation results from JSON.
299
+
300
+ Args:
301
+ filepath: Path to results file
302
+
303
+ Returns:
304
+ Loaded results
305
+ """
306
+ with open(filepath, 'r', encoding='utf-8') as f:
307
+ results = json.load(f)
308
+
309
+ return results
310
+
311
+ def compare_results(self, results_list: List[Dict[str, Any]]) -> Dict[str, Any]:
312
+ """
313
+ Compare multiple evaluation results.
314
+
315
+ Args:
316
+ results_list: List of evaluation results
317
+
318
+ Returns:
319
+ Comparison summary
320
+ """
321
+ comparison = {
322
+ 'num_evaluations': len(results_list),
323
+ 'models': [r['config']['model_name'] for r in results_list],
324
+ 'metrics_comparison': {}
325
+ }
326
+
327
+ # Extract metrics
328
+ all_metrics = {}
329
+ for i, results in enumerate(results_list):
330
+ model_name = results['config']['model_name']
331
+ all_metrics[model_name] = results.get('metrics', {})
332
+
333
+ # Compare each metric
334
+ metric_names = set()
335
+ for metrics in all_metrics.values():
336
+ metric_names.update(metrics.keys())
337
+
338
+ for metric in metric_names:
339
+ values = {}
340
+ for model, metrics in all_metrics.items():
341
+ if metric in metrics:
342
+ values[model] = metrics[metric]
343
+
344
+ if values:
345
+ comparison['metrics_comparison'][metric] = values
346
+
347
+ return comparison
evaluation/gap_analyzer.py ADDED
@@ -0,0 +1,404 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Gap Analyzer Module
3
+
4
+ Analyzes model performance to identify knowledge gaps and weak areas.
5
+ """
6
+
7
+ from typing import List, Dict, Optional, Any, Tuple
8
+ import json
9
+ from pathlib import Path
10
+ from collections import defaultdict
11
+ import statistics
12
+
13
+
14
+ class GapAnalyzer:
15
+ """
16
+ Analyzes evaluation results to identify knowledge gaps.
17
+
18
+ Features:
19
+ - Topic-level performance analysis
20
+ - Trend tracking across evaluations
21
+ - Weakness identification
22
+ - Strength identification
23
+ - Improvement recommendations
24
+ """
25
+
26
+ def __init__(self):
27
+ """Initialize gap analyzer."""
28
+ self.evaluation_history: List[Dict[str, Any]] = []
29
+ self.performance_by_category: Dict[str, List[float]] = defaultdict(list)
30
+ self.gaps: List[Dict[str, Any]] = []
31
+
32
+ def add_evaluation_results(self, results: Dict[str, Any]):
33
+ """
34
+ Add evaluation results for analysis.
35
+
36
+ Args:
37
+ results: Evaluation results dictionary
38
+ """
39
+ self.evaluation_history.append(results)
40
+
41
+ # Extract category performance if available
42
+ if 'examples' in results:
43
+ category_scores = defaultdict(list)
44
+
45
+ for example in results['examples']:
46
+ category = example.get('category', 'general')
47
+
48
+ # Calculate score for this example
49
+ prediction = example.get('prediction', '').lower()
50
+ reference = example.get('reference', '').lower()
51
+
52
+ # Simple scoring: 1 if similar, 0 otherwise
53
+ score = 1.0 if self._calculate_similarity(prediction, reference) > 0.5 else 0.0
54
+ category_scores[category].append(score)
55
+
56
+ # Store average scores by category
57
+ for category, scores in category_scores.items():
58
+ avg_score = (sum(scores) / len(scores)) * 100 if scores else 0
59
+ self.performance_by_category[category].append(avg_score)
60
+
61
+ def _calculate_similarity(self, text1: str, text2: str) -> float:
62
+ """Calculate simple similarity between two texts."""
63
+ words1 = set(text1.split())
64
+ words2 = set(text2.split())
65
+
66
+ if not words1 or not words2:
67
+ return 0.0
68
+
69
+ intersection = words1 & words2
70
+ union = words1 | words2
71
+
72
+ return len(intersection) / len(union) if union else 0.0
73
+
74
+ def analyze_gaps(
75
+ self,
76
+ weak_threshold: float = 60.0,
77
+ strong_threshold: float = 85.0
78
+ ) -> List[Dict[str, Any]]:
79
+ """
80
+ Analyze performance and identify gaps.
81
+
82
+ Args:
83
+ weak_threshold: Score below this is considered weak
84
+ strong_threshold: Score above this is considered strong
85
+
86
+ Returns:
87
+ List of identified gaps with details
88
+ """
89
+ gaps = []
90
+
91
+ # Analyze each category
92
+ for category, scores in self.performance_by_category.items():
93
+ if not scores:
94
+ continue
95
+
96
+ avg_score = statistics.mean(scores)
97
+ latest_score = scores[-1] if scores else 0
98
+
99
+ # Calculate trend
100
+ trend = "stable"
101
+ if len(scores) >= 2:
102
+ recent_avg = statistics.mean(scores[-3:]) if len(scores) >= 3 else statistics.mean(scores[-2:])
103
+ older_avg = statistics.mean(scores[:-3]) if len(scores) >= 3 else scores[0]
104
+
105
+ if recent_avg > older_avg + 5:
106
+ trend = "improving"
107
+ elif recent_avg < older_avg - 5:
108
+ trend = "declining"
109
+
110
+ # Classify performance level
111
+ if avg_score < weak_threshold:
112
+ level = "WEAK"
113
+ priority = "HIGH"
114
+ elif avg_score < strong_threshold:
115
+ level = "MODERATE"
116
+ priority = "MEDIUM"
117
+ else:
118
+ level = "STRONG"
119
+ priority = "LOW"
120
+
121
+ gap = {
122
+ 'category': category,
123
+ 'avg_score': avg_score,
124
+ 'latest_score': latest_score,
125
+ 'num_evaluations': len(scores),
126
+ 'trend': trend,
127
+ 'level': level,
128
+ 'priority': priority,
129
+ 'scores_history': scores
130
+ }
131
+
132
+ gaps.append(gap)
133
+
134
+ # Sort by priority (weak areas first)
135
+ priority_order = {'HIGH': 0, 'MEDIUM': 1, 'LOW': 2}
136
+ gaps.sort(key=lambda x: (priority_order.get(x['priority'], 3), x['avg_score']))
137
+
138
+ self.gaps = gaps
139
+ return gaps
140
+
141
+ def get_weakest_topics(self, n: int = 5) -> List[Dict[str, Any]]:
142
+ """
143
+ Get the N weakest topics.
144
+
145
+ Args:
146
+ n: Number of topics to return
147
+
148
+ Returns:
149
+ List of weakest topics
150
+ """
151
+ if not self.gaps:
152
+ self.analyze_gaps()
153
+
154
+ weak_gaps = [g for g in self.gaps if g['level'] in ['WEAK', 'MODERATE']]
155
+ return weak_gaps[:n]
156
+
157
+ def get_strongest_topics(self, n: int = 5) -> List[Dict[str, Any]]:
158
+ """
159
+ Get the N strongest topics.
160
+
161
+ Args:
162
+ n: Number of topics to return
163
+
164
+ Returns:
165
+ List of strongest topics
166
+ """
167
+ if not self.gaps:
168
+ self.analyze_gaps()
169
+
170
+ strong_gaps = [g for g in self.gaps if g['level'] == 'STRONG']
171
+ return strong_gaps[:n]
172
+
173
+ def get_declining_topics(self) -> List[Dict[str, Any]]:
174
+ """Get topics with declining performance."""
175
+ if not self.gaps:
176
+ self.analyze_gaps()
177
+
178
+ return [g for g in self.gaps if g['trend'] == 'declining']
179
+
180
+ def get_improving_topics(self) -> List[Dict[str, Any]]:
181
+ """Get topics with improving performance."""
182
+ if not self.gaps:
183
+ self.analyze_gaps()
184
+
185
+ return [g for g in self.gaps if g['trend'] == 'improving']
186
+
187
+ def generate_gap_report(self) -> str:
188
+ """
189
+ Generate a human-readable gap analysis report.
190
+
191
+ Returns:
192
+ Formatted report string
193
+ """
194
+ if not self.gaps:
195
+ self.analyze_gaps()
196
+
197
+ report = ["=" * 80]
198
+ report.append("KNOWLEDGE GAP ANALYSIS REPORT")
199
+ report.append("=" * 80)
200
+ report.append("")
201
+
202
+ # Overall summary
203
+ weak_count = sum(1 for g in self.gaps if g['level'] == 'WEAK')
204
+ moderate_count = sum(1 for g in self.gaps if g['level'] == 'MODERATE')
205
+ strong_count = sum(1 for g in self.gaps if g['level'] == 'STRONG')
206
+
207
+ report.append(f"Total Categories Analyzed: {len(self.gaps)}")
208
+ report.append(f" - WEAK (needs immediate attention): {weak_count}")
209
+ report.append(f" - MODERATE (needs improvement): {moderate_count}")
210
+ report.append(f" - STRONG (performing well): {strong_count}")
211
+ report.append("")
212
+
213
+ # Weak areas (priority)
214
+ weak_topics = [g for g in self.gaps if g['level'] == 'WEAK']
215
+ if weak_topics:
216
+ report.append("🔴 WEAK AREAS (Priority Training Needed):")
217
+ report.append("-" * 80)
218
+ for gap in weak_topics:
219
+ report.append(f" • {gap['category']}: {gap['avg_score']:.1f}% (Trend: {gap['trend']})")
220
+ report.append("")
221
+
222
+ # Moderate areas
223
+ moderate_topics = [g for g in self.gaps if g['level'] == 'MODERATE']
224
+ if moderate_topics:
225
+ report.append("🟡 MODERATE AREAS (Recommended Improvement):")
226
+ report.append("-" * 80)
227
+ for gap in moderate_topics[:5]: # Top 5
228
+ report.append(f" • {gap['category']}: {gap['avg_score']:.1f}% (Trend: {gap['trend']})")
229
+ report.append("")
230
+
231
+ # Strong areas
232
+ strong_topics = [g for g in self.gaps if g['level'] == 'STRONG']
233
+ if strong_topics:
234
+ report.append("🟢 STRONG AREAS (Excellent Performance):")
235
+ report.append("-" * 80)
236
+ for gap in strong_topics[:5]: # Top 5
237
+ report.append(f" • {gap['category']}: {gap['avg_score']:.1f}% (Trend: {gap['trend']})")
238
+ report.append("")
239
+
240
+ # Trends
241
+ declining = self.get_declining_topics()
242
+ improving = self.get_improving_topics()
243
+
244
+ if declining:
245
+ report.append("📉 DECLINING PERFORMANCE (Needs Attention):")
246
+ report.append("-" * 80)
247
+ for gap in declining:
248
+ report.append(f" • {gap['category']}: {gap['avg_score']:.1f}%")
249
+ report.append("")
250
+
251
+ if improving:
252
+ report.append("📈 IMPROVING PERFORMANCE (Keep It Up!):")
253
+ report.append("-" * 80)
254
+ for gap in improving:
255
+ report.append(f" • {gap['category']}: {gap['avg_score']:.1f}%")
256
+ report.append("")
257
+
258
+ report.append("=" * 80)
259
+
260
+ return "\n".join(report)
261
+
262
+ def get_performance_summary(self) -> Dict[str, Any]:
263
+ """
264
+ Get overall performance summary.
265
+
266
+ Returns:
267
+ Summary statistics
268
+ """
269
+ if not self.gaps:
270
+ self.analyze_gaps()
271
+
272
+ all_scores = [g['avg_score'] for g in self.gaps]
273
+
274
+ summary = {
275
+ 'num_categories': len(self.gaps),
276
+ 'overall_avg_score': statistics.mean(all_scores) if all_scores else 0,
277
+ 'min_score': min(all_scores) if all_scores else 0,
278
+ 'max_score': max(all_scores) if all_scores else 0,
279
+ 'weak_count': sum(1 for g in self.gaps if g['level'] == 'WEAK'),
280
+ 'moderate_count': sum(1 for g in self.gaps if g['level'] == 'MODERATE'),
281
+ 'strong_count': sum(1 for g in self.gaps if g['level'] == 'STRONG'),
282
+ 'declining_count': sum(1 for g in self.gaps if g['trend'] == 'declining'),
283
+ 'improving_count': sum(1 for g in self.gaps if g['trend'] == 'improving')
284
+ }
285
+
286
+ return summary
287
+
288
+ def export_gaps(self, filepath: str):
289
+ """
290
+ Export gap analysis to JSON file.
291
+
292
+ Args:
293
+ filepath: Output file path
294
+ """
295
+ if not self.gaps:
296
+ self.analyze_gaps()
297
+
298
+ Path(filepath).parent.mkdir(parents=True, exist_ok=True)
299
+
300
+ data = {
301
+ 'summary': self.get_performance_summary(),
302
+ 'gaps': self.gaps,
303
+ 'report': self.generate_gap_report()
304
+ }
305
+
306
+ with open(filepath, 'w', encoding='utf-8') as f:
307
+ json.dump(data, f, indent=2, ensure_ascii=False)
308
+
309
+ print(f"Gap analysis exported to: {filepath}")
310
+
311
+ def load_gaps(self, filepath: str):
312
+ """
313
+ Load gap analysis from JSON file.
314
+
315
+ Args:
316
+ filepath: Input file path
317
+ """
318
+ with open(filepath, 'r', encoding='utf-8') as f:
319
+ data = json.load(f)
320
+
321
+ self.gaps = data.get('gaps', [])
322
+
323
+ # Reconstruct performance_by_category
324
+ for gap in self.gaps:
325
+ category = gap['category']
326
+ scores = gap.get('scores_history', [])
327
+ self.performance_by_category[category] = scores
328
+
329
+ def compare_evaluations(
330
+ self,
331
+ eval1: Dict[str, Any],
332
+ eval2: Dict[str, Any]
333
+ ) -> Dict[str, Any]:
334
+ """
335
+ Compare two evaluation results.
336
+
337
+ Args:
338
+ eval1: First evaluation results
339
+ eval2: Second evaluation results
340
+
341
+ Returns:
342
+ Comparison details
343
+ """
344
+ comparison = {
345
+ 'improvement': {},
346
+ 'decline': {},
347
+ 'stable': {}
348
+ }
349
+
350
+ # Extract metrics from both
351
+ metrics1 = eval1.get('metrics', {})
352
+ metrics2 = eval2.get('metrics', {})
353
+
354
+ # Compare each metric
355
+ for metric in set(metrics1.keys()) | set(metrics2.keys()):
356
+ if metric in metrics1 and metric in metrics2:
357
+ val1 = metrics1[metric]
358
+ val2 = metrics2[metric]
359
+
360
+ if isinstance(val1, (int, float)) and isinstance(val2, (int, float)):
361
+ diff = val2 - val1
362
+ percent_change = (diff / val1 * 100) if val1 != 0 else 0
363
+
364
+ if diff > 1: # Improved
365
+ comparison['improvement'][metric] = {
366
+ 'old': val1,
367
+ 'new': val2,
368
+ 'change': diff,
369
+ 'percent_change': percent_change
370
+ }
371
+ elif diff < -1: # Declined
372
+ comparison['decline'][metric] = {
373
+ 'old': val1,
374
+ 'new': val2,
375
+ 'change': diff,
376
+ 'percent_change': percent_change
377
+ }
378
+ else: # Stable
379
+ comparison['stable'][metric] = {
380
+ 'old': val1,
381
+ 'new': val2,
382
+ 'change': diff
383
+ }
384
+
385
+ return comparison
386
+
387
+ def get_category_details(self, category: str) -> Optional[Dict[str, Any]]:
388
+ """
389
+ Get detailed analysis for a specific category.
390
+
391
+ Args:
392
+ category: Category name
393
+
394
+ Returns:
395
+ Category details or None if not found
396
+ """
397
+ if not self.gaps:
398
+ self.analyze_gaps()
399
+
400
+ for gap in self.gaps:
401
+ if gap['category'] == category:
402
+ return gap
403
+
404
+ return None
evaluation/intelligent_benchmark_scraper.py ADDED
@@ -0,0 +1,474 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Intelligent Benchmark & Exam Scraper
3
+
4
+ Scrapes the web to find domain-specific questions, scenarios, and test content.
5
+ Automatically builds comprehensive benchmarks for any use case.
6
+ """
7
+
8
+ import re
9
+ import json
10
+ import requests
11
+ from typing import List, Dict, Optional
12
+ from pathlib import Path
13
+ import time
14
+ from bs4 import BeautifulSoup
15
+ from urllib.parse import quote_plus
16
+
17
+
18
+ class IntelligentBenchmarkScraper:
19
+ """
20
+ Scrapes web sources to build domain-specific benchmarks and exams.
21
+
22
+ Features:
23
+ - Web search for relevant content
24
+ - Multi-source scraping (Wikipedia, educational sites, forums, documentation)
25
+ - Question extraction and generation
26
+ - Quality scoring and filtering
27
+ - Benchmark formatting
28
+ """
29
+
30
+ def __init__(self, api_key: Optional[str] = None):
31
+ """
32
+ Initialize scraper.
33
+
34
+ Args:
35
+ api_key: OpenAI/Anthropic key for question generation from scraped content
36
+ """
37
+ self.api_key = api_key
38
+ self.session = requests.Session()
39
+ self.session.headers.update({
40
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
41
+ })
42
+
43
+ def search_web(self, query: str, num_results: int = 10) -> List[Dict]:
44
+ """
45
+ Search the web for relevant content using DuckDuckGo (no API key needed).
46
+
47
+ Args:
48
+ query: Search query
49
+ num_results: Number of results to return
50
+
51
+ Returns:
52
+ List of search results with title, URL, snippet
53
+ """
54
+ results = []
55
+
56
+ try:
57
+ # Use DuckDuckGo HTML search (no API required)
58
+ search_url = f"https://html.duckduckgo.com/html/?q={quote_plus(query)}"
59
+
60
+ response = self.session.get(search_url, timeout=10)
61
+ response.raise_for_status()
62
+
63
+ soup = BeautifulSoup(response.text, 'html.parser')
64
+
65
+ # Parse results
66
+ for result_div in soup.find_all('div', class_='result')[:num_results]:
67
+ title_elem = result_div.find('a', class_='result__a')
68
+ snippet_elem = result_div.find('a', class_='result__snippet')
69
+
70
+ if title_elem and snippet_elem:
71
+ results.append({
72
+ 'title': title_elem.get_text(strip=True),
73
+ 'url': title_elem['href'],
74
+ 'snippet': snippet_elem.get_text(strip=True)
75
+ })
76
+
77
+ except Exception as e:
78
+ print(f"Search error: {e}")
79
+ # Fallback: provide some generic sources
80
+ results = self._get_fallback_sources(query)
81
+
82
+ return results
83
+
84
+ def _get_fallback_sources(self, query: str) -> List[Dict]:
85
+ """Provide fallback educational sources when search fails."""
86
+ domain_keywords = query.lower()
87
+
88
+ sources = []
89
+
90
+ # Wikipedia
91
+ wiki_topic = query.replace(' ', '_')
92
+ sources.append({
93
+ 'title': f"Wikipedia: {query}",
94
+ 'url': f"https://en.wikipedia.org/wiki/{wiki_topic}",
95
+ 'snippet': f"Comprehensive overview of {query}"
96
+ })
97
+
98
+ # Add domain-specific sources
99
+ if 'financial' in domain_keywords or 'finance' in domain_keywords:
100
+ sources.extend([
101
+ {
102
+ 'title': "Investopedia: Financial Certification Exams",
103
+ 'url': "https://www.investopedia.com/",
104
+ 'snippet': "Financial education and exam prep"
105
+ },
106
+ {
107
+ 'title': "CFP Board Practice Questions",
108
+ 'url': "https://www.cfp.net/",
109
+ 'snippet': "CFP certification resources"
110
+ }
111
+ ])
112
+ elif 'medical' in domain_keywords or 'health' in domain_keywords:
113
+ sources.extend([
114
+ {
115
+ 'title': "NCBI Medical Resources",
116
+ 'url': "https://www.ncbi.nlm.nih.gov/",
117
+ 'snippet': "Medical knowledge base"
118
+ },
119
+ {
120
+ 'title': "MedlinePlus Health Topics",
121
+ 'url': "https://medlineplus.gov/",
122
+ 'snippet': "Consumer health information"
123
+ }
124
+ ])
125
+ elif 'legal' in domain_keywords or 'law' in domain_keywords:
126
+ sources.extend([
127
+ {
128
+ 'title': "Cornell Legal Information Institute",
129
+ 'url': "https://www.law.cornell.edu/",
130
+ 'snippet': "Free legal resources and case law"
131
+ }
132
+ ])
133
+
134
+ return sources
135
+
136
+ def scrape_content(self, url: str) -> str:
137
+ """
138
+ Scrape text content from a URL.
139
+
140
+ Args:
141
+ url: URL to scrape
142
+
143
+ Returns:
144
+ Extracted text content
145
+ """
146
+ try:
147
+ response = self.session.get(url, timeout=15)
148
+ response.raise_for_status()
149
+
150
+ soup = BeautifulSoup(response.text, 'html.parser')
151
+
152
+ # Remove script and style elements
153
+ for script in soup(['script', 'style', 'header', 'footer', 'nav']):
154
+ script.decompose()
155
+
156
+ # Get text
157
+ text = soup.get_text()
158
+
159
+ # Clean up
160
+ lines = (line.strip() for line in text.splitlines())
161
+ chunks = (phrase.strip() for line in lines for phrase in line.split(" "))
162
+ text = ' '.join(chunk for chunk in chunks if chunk)
163
+
164
+ # Limit size
165
+ return text[:10000] # Max 10K chars per page
166
+
167
+ except Exception as e:
168
+ print(f"Scraping error for {url}: {e}")
169
+ return ""
170
+
171
+ def extract_questions_from_text(self, text: str, max_questions: int = 20) -> List[Dict]:
172
+ """
173
+ Extract questions from text using pattern matching.
174
+
175
+ Args:
176
+ text: Text content to analyze
177
+ max_questions: Maximum questions to extract
178
+
179
+ Returns:
180
+ List of question dicts
181
+ """
182
+ questions = []
183
+
184
+ # Pattern 1: Questions with answers
185
+ # Example: "What is X? Y is..."
186
+ qa_pattern = r'(?:^|\n)([^.!?]*\?)\s*([^.!?]+[.!?])'
187
+ matches = re.findall(qa_pattern, text)
188
+
189
+ for question, answer in matches[:max_questions//2]:
190
+ question = question.strip()
191
+ answer = answer.strip()
192
+
193
+ if len(question) > 20 and len(answer) > 20:
194
+ questions.append({
195
+ 'question': question,
196
+ 'answer': answer,
197
+ 'type': 'extracted'
198
+ })
199
+
200
+ # Pattern 2: Numbered/bulleted questions
201
+ numbered_pattern = r'(?:^|\n)\s*(?:\d+[\.\)]\s*|[•\-\*]\s*)([^.!?]*\?)'
202
+ numbered_matches = re.findall(numbered_pattern, text)
203
+
204
+ for question in numbered_matches[:max_questions//2]:
205
+ question = question.strip()
206
+ if len(question) > 20:
207
+ questions.append({
208
+ 'question': question,
209
+ 'answer': "", # Will be generated later
210
+ 'type': 'extracted_no_answer'
211
+ })
212
+
213
+ return questions[:max_questions]
214
+
215
+ def generate_questions_from_content(self, content: str, domain: str, num_questions: int = 10) -> List[Dict]:
216
+ """
217
+ Generate questions from content using LLM.
218
+
219
+ Args:
220
+ content: Source content
221
+ domain: Domain/topic
222
+ num_questions: Number of questions to generate
223
+
224
+ Returns:
225
+ List of generated questions
226
+ """
227
+ if not self.api_key:
228
+ # Fallback: use simple templates
229
+ return self._generate_template_questions(content, domain, num_questions)
230
+
231
+ try:
232
+ # Try OpenAI first
233
+ if self.api_key.startswith('sk-'):
234
+ from openai import OpenAI
235
+ client = OpenAI(api_key=self.api_key)
236
+
237
+ prompt = f"""Based on the following content about {domain}, generate {num_questions} test questions with answers.
238
+
239
+ Content:
240
+ {content[:3000]}
241
+
242
+ Format each question as JSON:
243
+ {{"question": "...", "answer": "...", "difficulty": "beginner|intermediate|advanced"}}
244
+
245
+ Return a JSON array of questions."""
246
+
247
+ response = client.chat.completions.create(
248
+ model="gpt-3.5-turbo",
249
+ messages=[
250
+ {"role": "system", "content": "You are an expert test creator."},
251
+ {"role": "user", "content": prompt}
252
+ ],
253
+ temperature=0.7
254
+ )
255
+
256
+ # Parse JSON response
257
+ content_text = response.choices[0].message.content
258
+ json_match = re.search(r'\[.*\]', content_text, re.DOTALL)
259
+ if json_match:
260
+ questions = json.loads(json_match.group())
261
+ return questions
262
+
263
+ # Try Anthropic
264
+ elif self.api_key.startswith('sk-ant-'):
265
+ from anthropic import Anthropic
266
+ client = Anthropic(api_key=self.api_key)
267
+
268
+ prompt = f"""Based on the following content about {domain}, generate {num_questions} test questions with answers.
269
+
270
+ Content:
271
+ {content[:3000]}
272
+
273
+ Format each question as JSON:
274
+ {{"question": "...", "answer": "...", "difficulty": "beginner|intermediate|advanced"}}
275
+
276
+ Return a JSON array of questions."""
277
+
278
+ response = client.messages.create(
279
+ model="claude-3-5-sonnet-20241022",
280
+ max_tokens=2000,
281
+ messages=[
282
+ {"role": "user", "content": prompt}
283
+ ]
284
+ )
285
+
286
+ # Parse JSON response
287
+ content_text = response.content[0].text
288
+ json_match = re.search(r'\[.*\]', content_text, re.DOTALL)
289
+ if json_match:
290
+ questions = json.loads(json_match.group())
291
+ return questions
292
+
293
+ except Exception as e:
294
+ print(f"LLM generation error: {e}")
295
+
296
+ # Fallback
297
+ return self._generate_template_questions(content, domain, num_questions)
298
+
299
+ def _generate_template_questions(self, content: str, domain: str, num_questions: int) -> List[Dict]:
300
+ """Generate basic questions using templates when no API available."""
301
+ questions = []
302
+
303
+ # Extract key terms (simple approach)
304
+ words = content.split()
305
+ unique_words = list(set([w for w in words if len(w) > 5]))[:num_questions]
306
+
307
+ templates = [
308
+ ("What is {term}?", "answer_placeholder"),
309
+ ("Explain the concept of {term}.", "answer_placeholder"),
310
+ ("How does {term} work in the context of {domain}?", "answer_placeholder"),
311
+ ("What are the key aspects of {term}?", "answer_placeholder"),
312
+ ]
313
+
314
+ for i, term in enumerate(unique_words[:num_questions]):
315
+ template = templates[i % len(templates)]
316
+ questions.append({
317
+ 'question': template[0].format(term=term, domain=domain),
318
+ 'answer': f"This question requires domain expertise in {domain} regarding {term}.",
319
+ 'difficulty': 'intermediate',
320
+ 'type': 'template_generated'
321
+ })
322
+
323
+ return questions
324
+
325
+ def build_benchmark(
326
+ self,
327
+ domain: str,
328
+ num_questions: int = 50,
329
+ use_llm: bool = True
330
+ ) -> Dict:
331
+ """
332
+ Build a comprehensive benchmark for a domain.
333
+
334
+ Args:
335
+ domain: Domain/topic (e.g., "financial planning", "medical diagnostics")
336
+ num_questions: Target number of questions
337
+ use_llm: Whether to use LLM for question generation
338
+
339
+ Returns:
340
+ Benchmark dict with questions
341
+ """
342
+ print(f"Building benchmark for: {domain}")
343
+ print(f"Target questions: {num_questions}")
344
+
345
+ all_questions = []
346
+
347
+ # Step 1: Search for relevant content
348
+ print("\n[1/4] Searching web for content...")
349
+ search_queries = [
350
+ f"{domain} practice questions",
351
+ f"{domain} exam questions",
352
+ f"{domain} test scenarios",
353
+ f"{domain} certification study guide"
354
+ ]
355
+
356
+ all_sources = []
357
+ for query in search_queries:
358
+ sources = self.search_web(query, num_results=5)
359
+ all_sources.extend(sources)
360
+ time.sleep(1) # Rate limiting
361
+
362
+ print(f"Found {len(all_sources)} sources")
363
+
364
+ # Step 2: Scrape content from sources
365
+ print("\n[2/4] Scraping content from sources...")
366
+ scraped_content = []
367
+ for i, source in enumerate(all_sources[:10]): # Limit to 10 sources
368
+ print(f" Scraping {i+1}/10: {source['title'][:50]}...")
369
+ content = self.scrape_content(source['url'])
370
+ if content:
371
+ scraped_content.append({
372
+ 'url': source['url'],
373
+ 'title': source['title'],
374
+ 'content': content
375
+ })
376
+ time.sleep(1) # Be polite
377
+
378
+ print(f"Successfully scraped {len(scraped_content)} pages")
379
+
380
+ # Step 3: Extract existing questions
381
+ print("\n[3/4] Extracting questions from content...")
382
+ for item in scraped_content:
383
+ extracted = self.extract_questions_from_text(item['content'])
384
+ for q in extracted:
385
+ q['source'] = item['url']
386
+ q['source_title'] = item['title']
387
+ all_questions.extend(extracted)
388
+
389
+ print(f"Extracted {len(all_questions)} questions from sources")
390
+
391
+ # Step 4: Generate additional questions if needed
392
+ if use_llm and len(all_questions) < num_questions:
393
+ print("\n[4/4] Generating additional questions using LLM...")
394
+ remaining = num_questions - len(all_questions)
395
+
396
+ # Use best content for generation
397
+ best_content = max(scraped_content, key=lambda x: len(x['content']))['content'] if scraped_content else ""
398
+
399
+ if best_content:
400
+ generated = self.generate_questions_from_content(
401
+ best_content,
402
+ domain,
403
+ num_questions=remaining
404
+ )
405
+ all_questions.extend(generated)
406
+ print(f"Generated {len(generated)} additional questions")
407
+
408
+ # Build final benchmark
409
+ benchmark = {
410
+ 'name': f"{domain.title()} Benchmark",
411
+ 'domain': domain,
412
+ 'description': f"Automatically generated benchmark for {domain} with {len(all_questions)} questions",
413
+ 'created_at': time.strftime('%Y-%m-%d %H:%M:%S'),
414
+ 'num_questions': len(all_questions),
415
+ 'sources': [s['url'] for s in scraped_content],
416
+ 'questions': all_questions[:num_questions]
417
+ }
418
+
419
+ print(f"\n[OK] Benchmark created with {len(benchmark['questions'])} questions")
420
+
421
+ return benchmark
422
+
423
+ def save_benchmark(self, benchmark: Dict, filepath: str):
424
+ """Save benchmark to JSON file."""
425
+ Path(filepath).parent.mkdir(parents=True, exist_ok=True)
426
+
427
+ with open(filepath, 'w', encoding='utf-8') as f:
428
+ json.dump(benchmark, f, indent=2, ensure_ascii=False)
429
+
430
+ print(f"Saved benchmark to: {filepath}")
431
+
432
+
433
+ def create_scraped_benchmark(domain: str, num_questions: int = 50, api_key: Optional[str] = None) -> str:
434
+ """
435
+ Helper function to create a benchmark from web scraping.
436
+
437
+ Args:
438
+ domain: Domain/topic
439
+ num_questions: Number of questions
440
+ api_key: Optional API key for LLM generation
441
+
442
+ Returns:
443
+ Status message
444
+ """
445
+ scraper = IntelligentBenchmarkScraper(api_key=api_key)
446
+
447
+ benchmark = scraper.build_benchmark(
448
+ domain=domain,
449
+ num_questions=num_questions,
450
+ use_llm=bool(api_key)
451
+ )
452
+
453
+ # Save
454
+ filename = domain.lower().replace(' ', '_')
455
+ filepath = f"benchmarks/{filename}_benchmark.json"
456
+ scraper.save_benchmark(benchmark, filepath)
457
+
458
+ return filepath, benchmark
459
+
460
+
461
+ if __name__ == "__main__":
462
+ # Test
463
+ import sys
464
+
465
+ domain = sys.argv[1] if len(sys.argv) > 1 else "financial planning"
466
+
467
+ scraper = IntelligentBenchmarkScraper()
468
+ benchmark = scraper.build_benchmark(domain, num_questions=20, use_llm=False)
469
+
470
+ print("\nSample questions:")
471
+ for i, q in enumerate(benchmark['questions'][:3], 1):
472
+ print(f"\n{i}. {q['question']}")
473
+ if q.get('answer'):
474
+ print(f" A: {q['answer'][:100]}...")
evaluation/metrics.py ADDED
@@ -0,0 +1,267 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Metrics Module
3
+
4
+ Provides various evaluation metrics for model performance assessment.
5
+ """
6
+
7
+ import numpy as np
8
+ from typing import List, Dict, Optional, Union
9
+ import math
10
+
11
+
12
+ class Metrics:
13
+ """
14
+ Comprehensive metrics calculator for model evaluation.
15
+
16
+ Supports:
17
+ - BLEU score
18
+ - ROUGE-L
19
+ - Perplexity
20
+ - Custom financial domain metrics
21
+ """
22
+
23
+ def __init__(self):
24
+ """Initialize metrics calculator."""
25
+ self.results = {}
26
+
27
+ def calculate_bleu(
28
+ self,
29
+ references: List[str],
30
+ hypotheses: List[str],
31
+ max_n: int = 4
32
+ ) -> float:
33
+ """
34
+ Calculate BLEU score.
35
+
36
+ Args:
37
+ references: Reference texts
38
+ hypotheses: Generated texts
39
+ max_n: Maximum n-gram size
40
+
41
+ Returns:
42
+ BLEU score (0-100)
43
+ """
44
+ try:
45
+ from nltk.translate.bleu_score import corpus_bleu, SmoothingFunction
46
+
47
+ # Tokenize
48
+ ref_tokens = [[ref.split()] for ref in references]
49
+ hyp_tokens = [hyp.split() for hyp in hypotheses]
50
+
51
+ # Calculate with smoothing
52
+ smoothing = SmoothingFunction()
53
+ score = corpus_bleu(
54
+ ref_tokens,
55
+ hyp_tokens,
56
+ smoothing_function=smoothing.method1
57
+ )
58
+
59
+ return score * 100.0
60
+
61
+ except ImportError:
62
+ # Fallback: simple word overlap
63
+ return self._simple_bleu(references, hypotheses)
64
+
65
+ def _simple_bleu(self, references: List[str], hypotheses: List[str]) -> float:
66
+ """Simple BLEU approximation without NLTK."""
67
+ total_overlap = 0
68
+ total_length = 0
69
+
70
+ for ref, hyp in zip(references, hypotheses):
71
+ ref_words = set(ref.lower().split())
72
+ hyp_words = set(hyp.lower().split())
73
+ overlap = len(ref_words & hyp_words)
74
+ total_overlap += overlap
75
+ total_length += max(len(ref_words), len(hyp_words))
76
+
77
+ if total_length == 0:
78
+ return 0.0
79
+
80
+ return (total_overlap / total_length) * 100.0
81
+
82
+ def calculate_rouge_l(
83
+ self,
84
+ references: List[str],
85
+ hypotheses: List[str]
86
+ ) -> Dict[str, float]:
87
+ """
88
+ Calculate ROUGE-L score.
89
+
90
+ Args:
91
+ references: Reference texts
92
+ hypotheses: Generated texts
93
+
94
+ Returns:
95
+ Dict with precision, recall, f1
96
+ """
97
+ total_precision = 0
98
+ total_recall = 0
99
+ total_f1 = 0
100
+
101
+ for ref, hyp in zip(references, hypotheses):
102
+ ref_words = ref.split()
103
+ hyp_words = hyp.split()
104
+
105
+ # Find longest common subsequence
106
+ lcs_length = self._lcs_length(ref_words, hyp_words)
107
+
108
+ # Calculate metrics
109
+ precision = lcs_length / len(hyp_words) if len(hyp_words) > 0 else 0
110
+ recall = lcs_length / len(ref_words) if len(ref_words) > 0 else 0
111
+ f1 = (2 * precision * recall / (precision + recall)) if (precision + recall) > 0 else 0
112
+
113
+ total_precision += precision
114
+ total_recall += recall
115
+ total_f1 += f1
116
+
117
+ n = len(references)
118
+ return {
119
+ 'precision': (total_precision / n) * 100.0 if n > 0 else 0.0,
120
+ 'recall': (total_recall / n) * 100.0 if n > 0 else 0.0,
121
+ 'f1': (total_f1 / n) * 100.0 if n > 0 else 0.0
122
+ }
123
+
124
+ def _lcs_length(self, seq1: List[str], seq2: List[str]) -> int:
125
+ """Calculate longest common subsequence length."""
126
+ m, n = len(seq1), len(seq2)
127
+ dp = [[0] * (n + 1) for _ in range(m + 1)]
128
+
129
+ for i in range(1, m + 1):
130
+ for j in range(1, n + 1):
131
+ if seq1[i-1] == seq2[j-1]:
132
+ dp[i][j] = dp[i-1][j-1] + 1
133
+ else:
134
+ dp[i][j] = max(dp[i-1][j], dp[i][j-1])
135
+
136
+ return dp[m][n]
137
+
138
+ def calculate_perplexity(
139
+ self,
140
+ log_probs: List[float]
141
+ ) -> float:
142
+ """
143
+ Calculate perplexity from log probabilities.
144
+
145
+ Args:
146
+ log_probs: List of log probabilities
147
+
148
+ Returns:
149
+ Perplexity score
150
+ """
151
+ if not log_probs:
152
+ return float('inf')
153
+
154
+ avg_log_prob = sum(log_probs) / len(log_probs)
155
+ perplexity = math.exp(-avg_log_prob)
156
+
157
+ return perplexity
158
+
159
+ def calculate_accuracy(
160
+ self,
161
+ predictions: List[str],
162
+ references: List[str]
163
+ ) -> float:
164
+ """
165
+ Calculate exact match accuracy.
166
+
167
+ Args:
168
+ predictions: Predicted answers
169
+ references: Reference answers
170
+
171
+ Returns:
172
+ Accuracy percentage
173
+ """
174
+ if not predictions or not references:
175
+ return 0.0
176
+
177
+ matches = sum(
178
+ pred.strip().lower() == ref.strip().lower()
179
+ for pred, ref in zip(predictions, references)
180
+ )
181
+
182
+ return (matches / len(predictions)) * 100.0
183
+
184
+ def calculate_all_metrics(
185
+ self,
186
+ predictions: List[str],
187
+ references: List[str],
188
+ log_probs: Optional[List[float]] = None
189
+ ) -> Dict[str, float]:
190
+ """
191
+ Calculate all available metrics.
192
+
193
+ Args:
194
+ predictions: Model predictions
195
+ references: Reference answers
196
+ log_probs: Optional log probabilities for perplexity
197
+
198
+ Returns:
199
+ Dict of all metrics
200
+ """
201
+ metrics = {}
202
+
203
+ # BLEU
204
+ try:
205
+ metrics['bleu'] = self.calculate_bleu(references, predictions)
206
+ except Exception as e:
207
+ print(f"BLEU calculation error: {e}")
208
+ metrics['bleu'] = 0.0
209
+
210
+ # ROUGE-L
211
+ try:
212
+ rouge = self.calculate_rouge_l(references, predictions)
213
+ metrics['rouge_l_precision'] = rouge['precision']
214
+ metrics['rouge_l_recall'] = rouge['recall']
215
+ metrics['rouge_l_f1'] = rouge['f1']
216
+ except Exception as e:
217
+ print(f"ROUGE calculation error: {e}")
218
+ metrics['rouge_l_f1'] = 0.0
219
+
220
+ # Accuracy
221
+ try:
222
+ metrics['accuracy'] = self.calculate_accuracy(predictions, references)
223
+ except Exception as e:
224
+ print(f"Accuracy calculation error: {e}")
225
+ metrics['accuracy'] = 0.0
226
+
227
+ # Perplexity
228
+ if log_probs:
229
+ try:
230
+ metrics['perplexity'] = self.calculate_perplexity(log_probs)
231
+ except Exception as e:
232
+ print(f"Perplexity calculation error: {e}")
233
+ metrics['perplexity'] = float('inf')
234
+
235
+ # Average response length
236
+ metrics['avg_response_length'] = sum(len(p.split()) for p in predictions) / len(predictions)
237
+
238
+ return metrics
239
+
240
+
241
+ def calculate_perplexity(log_probs: List[float]) -> float:
242
+ """
243
+ Standalone function to calculate perplexity.
244
+
245
+ Args:
246
+ log_probs: List of log probabilities
247
+
248
+ Returns:
249
+ Perplexity score
250
+ """
251
+ metrics = Metrics()
252
+ return metrics.calculate_perplexity(log_probs)
253
+
254
+
255
+ def calculate_bleu(references: List[str], hypotheses: List[str]) -> float:
256
+ """
257
+ Standalone function to calculate BLEU score.
258
+
259
+ Args:
260
+ references: Reference texts
261
+ hypotheses: Generated texts
262
+
263
+ Returns:
264
+ BLEU score (0-100)
265
+ """
266
+ metrics = Metrics()
267
+ return metrics.calculate_bleu(references, hypotheses)
evaluation/training_recommender.py ADDED
@@ -0,0 +1,505 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Training Recommender Module
3
+
4
+ Provides AI-driven recommendations for next training session based on gap analysis.
5
+ """
6
+
7
+ from typing import List, Dict, Optional, Any, Tuple
8
+ import json
9
+ from pathlib import Path
10
+ from collections import defaultdict
11
+
12
+
13
+ class TrainingRecommender:
14
+ """
15
+ Recommends training strategies based on performance gaps.
16
+
17
+ Features:
18
+ - Targeted training recommendations
19
+ - Data generation suggestions
20
+ - Priority-based training plans
21
+ - Progress tracking
22
+ """
23
+
24
+ def __init__(self, gap_analyzer: Optional[Any] = None):
25
+ """
26
+ Initialize training recommender.
27
+
28
+ Args:
29
+ gap_analyzer: GapAnalyzer instance with performance data
30
+ """
31
+ self.gap_analyzer = gap_analyzer
32
+ self.recommendations: List[Dict[str, Any]] = []
33
+
34
+ def generate_recommendations(
35
+ self,
36
+ max_recommendations: int = 5,
37
+ focus_on_weak: bool = True
38
+ ) -> List[Dict[str, Any]]:
39
+ """
40
+ Generate training recommendations based on gaps.
41
+
42
+ Args:
43
+ max_recommendations: Maximum number of recommendations
44
+ focus_on_weak: Prioritize weak areas over moderate ones
45
+
46
+ Returns:
47
+ List of training recommendations
48
+ """
49
+ if not self.gap_analyzer or not self.gap_analyzer.gaps:
50
+ return [{
51
+ 'category': 'General',
52
+ 'priority': 'MEDIUM',
53
+ 'action': 'No performance data available. Start with general training.',
54
+ 'estimated_examples': 100,
55
+ 'topics': ['General training data']
56
+ }]
57
+
58
+ recommendations = []
59
+
60
+ # Get gaps to address
61
+ gaps = self.gap_analyzer.gaps
62
+
63
+ if focus_on_weak:
64
+ # Focus on weak and declining areas first
65
+ priority_gaps = [
66
+ g for g in gaps
67
+ if g['level'] == 'WEAK' or g['trend'] == 'declining'
68
+ ]
69
+ if not priority_gaps:
70
+ # Fall back to moderate areas
71
+ priority_gaps = [g for g in gaps if g['level'] == 'MODERATE']
72
+ else:
73
+ # Include all non-strong areas
74
+ priority_gaps = [g for g in gaps if g['level'] != 'STRONG']
75
+
76
+ # Generate recommendations for top gaps
77
+ for gap in priority_gaps[:max_recommendations]:
78
+ recommendation = self._create_recommendation(gap)
79
+ recommendations.append(recommendation)
80
+
81
+ self.recommendations = recommendations
82
+ return recommendations
83
+
84
+ def _create_recommendation(self, gap: Dict[str, Any]) -> Dict[str, Any]:
85
+ """
86
+ Create a detailed recommendation for a gap.
87
+
88
+ Args:
89
+ gap: Gap analysis data
90
+
91
+ Returns:
92
+ Training recommendation
93
+ """
94
+ category = gap['category']
95
+ avg_score = gap['avg_score']
96
+ level = gap['level']
97
+
98
+ # Determine number of examples needed
99
+ if avg_score < 40:
100
+ estimated_examples = 100
101
+ intensity = "intensive"
102
+ elif avg_score < 60:
103
+ estimated_examples = 50
104
+ intensity = "moderate"
105
+ else:
106
+ estimated_examples = 25
107
+ intensity = "light"
108
+
109
+ # Generate specific action items
110
+ action_items = self._generate_action_items(category, level, gap['trend'])
111
+
112
+ # Suggest topics
113
+ topics = self._suggest_topics(category)
114
+
115
+ recommendation = {
116
+ 'category': category,
117
+ 'priority': gap['priority'],
118
+ 'current_score': avg_score,
119
+ 'trend': gap['trend'],
120
+ 'intensity': intensity,
121
+ 'estimated_examples': estimated_examples,
122
+ 'action': f"Focus on {category} with {intensity} training",
123
+ 'action_items': action_items,
124
+ 'suggested_topics': topics,
125
+ 'expected_improvement': self._estimate_improvement(avg_score, estimated_examples)
126
+ }
127
+
128
+ return recommendation
129
+
130
+ def _generate_action_items(
131
+ self,
132
+ category: str,
133
+ level: str,
134
+ trend: str
135
+ ) -> List[str]:
136
+ """
137
+ Generate specific action items for a category.
138
+
139
+ Args:
140
+ category: Category name
141
+ level: Performance level
142
+ trend: Performance trend
143
+
144
+ Returns:
145
+ List of action items
146
+ """
147
+ items = []
148
+
149
+ # Base recommendations based on level
150
+ if level == 'WEAK':
151
+ items.append(f"Add 50-100 {category} examples to training data")
152
+ items.append(f"Review fundamental {category} concepts")
153
+ items.append("Include diverse question types and difficulty levels")
154
+ elif level == 'MODERATE':
155
+ items.append(f"Add 25-50 {category} examples focusing on edge cases")
156
+ items.append(f"Review intermediate {category} topics")
157
+ else:
158
+ items.append(f"Maintain current {category} performance with 10-20 examples")
159
+
160
+ # Add trend-specific items
161
+ if trend == 'declining':
162
+ items.append("⚠️ Address declining performance immediately")
163
+ items.append(f"Review recent {category} training data for quality issues")
164
+ elif trend == 'improving':
165
+ items.append("✅ Continue current training approach")
166
+
167
+ # Add testing recommendation
168
+ items.append(f"Test specifically on {category} after training")
169
+
170
+ return items
171
+
172
+ def _suggest_topics(self, category: str) -> List[str]:
173
+ """
174
+ Suggest specific topics for a category.
175
+
176
+ Args:
177
+ category: Category name
178
+
179
+ Returns:
180
+ List of suggested topics
181
+ """
182
+ # Topic suggestions by common categories
183
+ topic_map = {
184
+ 'Estate Planning': [
185
+ 'Revocable living trusts',
186
+ 'Wills and probate',
187
+ 'Power of attorney',
188
+ 'Estate tax strategies',
189
+ 'Charitable giving',
190
+ 'Trust structures'
191
+ ],
192
+ 'Retirement Planning': [
193
+ '401(k) and IRA strategies',
194
+ 'Required minimum distributions',
195
+ 'Social Security optimization',
196
+ 'Pension planning',
197
+ 'Retirement income strategies',
198
+ 'Healthcare in retirement'
199
+ ],
200
+ 'Tax Planning': [
201
+ 'Tax-efficient investing',
202
+ 'Capital gains strategies',
203
+ 'Tax-loss harvesting',
204
+ 'Deductions and credits',
205
+ 'Alternative minimum tax',
206
+ 'Estate and gift taxes'
207
+ ],
208
+ 'Investment Planning': [
209
+ 'Asset allocation',
210
+ 'Portfolio diversification',
211
+ 'Risk management',
212
+ 'Modern portfolio theory',
213
+ 'Performance evaluation',
214
+ 'Rebalancing strategies'
215
+ ],
216
+ 'Insurance Planning': [
217
+ 'Life insurance types',
218
+ 'Disability insurance',
219
+ 'Long-term care insurance',
220
+ 'Property and casualty',
221
+ 'Umbrella policies',
222
+ 'Insurance needs analysis'
223
+ ],
224
+ 'Education Planning': [
225
+ '529 plans',
226
+ 'Coverdell ESAs',
227
+ 'Financial aid strategies',
228
+ 'Student loan planning',
229
+ 'Education tax benefits'
230
+ ]
231
+ }
232
+
233
+ # Return specific topics if available, otherwise generic suggestions
234
+ if category in topic_map:
235
+ return topic_map[category]
236
+ else:
237
+ return [
238
+ f"Fundamental {category} concepts",
239
+ f"Intermediate {category} topics",
240
+ f"Advanced {category} strategies",
241
+ f"{category} best practices",
242
+ f"Common {category} scenarios"
243
+ ]
244
+
245
+ def _estimate_improvement(
246
+ self,
247
+ current_score: float,
248
+ num_examples: int
249
+ ) -> str:
250
+ """
251
+ Estimate expected improvement from training.
252
+
253
+ Args:
254
+ current_score: Current performance score
255
+ num_examples: Number of training examples
256
+
257
+ Returns:
258
+ Improvement estimate description
259
+ """
260
+ # Simple heuristic: more examples = more improvement, diminishing returns
261
+ base_improvement = min(num_examples * 0.3, 30) # Max 30% improvement
262
+
263
+ # Lower scores have more room for improvement
264
+ if current_score < 40:
265
+ multiplier = 1.5
266
+ elif current_score < 60:
267
+ multiplier = 1.2
268
+ else:
269
+ multiplier = 0.8
270
+
271
+ estimated_improvement = base_improvement * multiplier
272
+ new_score = min(current_score + estimated_improvement, 95)
273
+
274
+ return f"+{estimated_improvement:.1f}% (to ~{new_score:.1f}%)"
275
+
276
+ def create_training_plan(
277
+ self,
278
+ priority: str = "all",
279
+ include_data_generation: bool = True
280
+ ) -> Dict[str, Any]:
281
+ """
282
+ Create a comprehensive training plan.
283
+
284
+ Args:
285
+ priority: Focus on "high", "medium", "low", or "all" priority items
286
+ include_data_generation: Include data generation instructions
287
+
288
+ Returns:
289
+ Training plan
290
+ """
291
+ if not self.recommendations:
292
+ self.generate_recommendations()
293
+
294
+ # Filter by priority
295
+ if priority.upper() != "ALL":
296
+ filtered_recs = [
297
+ r for r in self.recommendations
298
+ if r['priority'] == priority.upper()
299
+ ]
300
+ else:
301
+ filtered_recs = self.recommendations
302
+
303
+ # Calculate totals
304
+ total_examples = sum(r['estimated_examples'] for r in filtered_recs)
305
+ categories = [r['category'] for r in filtered_recs]
306
+
307
+ plan = {
308
+ 'plan_name': f"Training Plan - Priority: {priority.title()}",
309
+ 'num_focus_areas': len(filtered_recs),
310
+ 'focus_categories': categories,
311
+ 'total_examples_needed': total_examples,
312
+ 'recommendations': filtered_recs,
313
+ 'execution_steps': self._generate_execution_steps(filtered_recs),
314
+ }
315
+
316
+ if include_data_generation:
317
+ plan['data_generation'] = self._generate_data_instructions(filtered_recs)
318
+
319
+ return plan
320
+
321
+ def _generate_execution_steps(
322
+ self,
323
+ recommendations: List[Dict[str, Any]]
324
+ ) -> List[str]:
325
+ """Generate step-by-step execution plan."""
326
+ steps = [
327
+ "1. Review gap analysis and recommendations",
328
+ "2. Prepare training data:"
329
+ ]
330
+
331
+ for i, rec in enumerate(recommendations, 1):
332
+ steps.append(f" {chr(96+i)}. {rec['category']}: {rec['estimated_examples']} examples")
333
+
334
+ steps.extend([
335
+ "3. Generate or collect training examples",
336
+ "4. Validate data quality (score > 60)",
337
+ "5. Execute training session",
338
+ "6. Run targeted benchmark tests",
339
+ "7. Analyze results and compare to previous performance",
340
+ "8. Iterate if needed"
341
+ ])
342
+
343
+ return steps
344
+
345
+ def _generate_data_instructions(
346
+ self,
347
+ recommendations: List[Dict[str, Any]]
348
+ ) -> Dict[str, Any]:
349
+ """Generate data generation instructions."""
350
+ instructions = {
351
+ 'method': 'synthetic_generation',
352
+ 'by_category': {}
353
+ }
354
+
355
+ for rec in recommendations:
356
+ category = rec['category']
357
+ instructions['by_category'][category] = {
358
+ 'num_examples': rec['estimated_examples'],
359
+ 'difficulty': 'mixed',
360
+ 'topics': rec['suggested_topics'],
361
+ 'sample_prompt': f"Generate financial advisory questions about {category}, covering topics like: {', '.join(rec['suggested_topics'][:3])}"
362
+ }
363
+
364
+ return instructions
365
+
366
+ def generate_report(self) -> str:
367
+ """
368
+ Generate human-readable training recommendations report.
369
+
370
+ Returns:
371
+ Formatted report
372
+ """
373
+ if not self.recommendations:
374
+ self.generate_recommendations()
375
+
376
+ report = ["=" * 80]
377
+ report.append("TRAINING RECOMMENDATIONS REPORT")
378
+ report.append("=" * 80)
379
+ report.append("")
380
+
381
+ if not self.recommendations:
382
+ report.append("No recommendations available. Performance data needed.")
383
+ return "\n".join(report)
384
+
385
+ # Summary
386
+ total_examples = sum(r['estimated_examples'] for r in self.recommendations)
387
+ report.append(f"Total Focus Areas: {len(self.recommendations)}")
388
+ report.append(f"Total Training Examples Needed: {total_examples}")
389
+ report.append("")
390
+
391
+ # Detailed recommendations
392
+ report.append("RECOMMENDED TRAINING PRIORITIES:")
393
+ report.append("-" * 80)
394
+
395
+ for i, rec in enumerate(self.recommendations, 1):
396
+ priority_symbol = {
397
+ 'HIGH': '🔴',
398
+ 'MEDIUM': '🟡',
399
+ 'LOW': '🟢'
400
+ }.get(rec['priority'], '⚪')
401
+
402
+ report.append(f"\n{i}. {priority_symbol} {rec['category']} - Priority: {rec['priority']}")
403
+ report.append(f" Current Score: {rec['current_score']:.1f}%")
404
+ report.append(f" Trend: {rec['trend']}")
405
+ report.append(f" Training Intensity: {rec['intensity']}")
406
+ report.append(f" Recommended Examples: {rec['estimated_examples']}")
407
+ report.append(f" Expected Improvement: {rec['expected_improvement']}")
408
+ report.append("")
409
+ report.append(" Action Items:")
410
+ for item in rec['action_items']:
411
+ report.append(f" • {item}")
412
+ report.append("")
413
+ report.append(" Suggested Topics:")
414
+ for topic in rec['suggested_topics'][:5]: # Top 5 topics
415
+ report.append(f" - {topic}")
416
+
417
+ report.append("")
418
+ report.append("=" * 80)
419
+ report.append("NEXT STEPS:")
420
+ report.append("")
421
+ report.append("1. Generate training data for priority categories")
422
+ report.append("2. Focus on weak/declining areas first")
423
+ report.append("3. Use diverse examples covering suggested topics")
424
+ report.append("4. Run targeted tests after training")
425
+ report.append("5. Track improvement and adjust strategy")
426
+ report.append("=" * 80)
427
+
428
+ return "\n".join(report)
429
+
430
+ def save_recommendations(self, filepath: str):
431
+ """
432
+ Save recommendations to JSON file.
433
+
434
+ Args:
435
+ filepath: Output file path
436
+ """
437
+ if not self.recommendations:
438
+ self.generate_recommendations()
439
+
440
+ Path(filepath).parent.mkdir(parents=True, exist_ok=True)
441
+
442
+ data = {
443
+ 'recommendations': self.recommendations,
444
+ 'training_plan': self.create_training_plan(),
445
+ 'report': self.generate_report()
446
+ }
447
+
448
+ with open(filepath, 'w', encoding='utf-8') as f:
449
+ json.dump(data, f, indent=2, ensure_ascii=False)
450
+
451
+ print(f"Recommendations saved to: {filepath}")
452
+
453
+ def load_recommendations(self, filepath: str):
454
+ """
455
+ Load recommendations from JSON file.
456
+
457
+ Args:
458
+ filepath: Input file path
459
+ """
460
+ with open(filepath, 'r', encoding='utf-8') as f:
461
+ data = json.load(f)
462
+
463
+ self.recommendations = data.get('recommendations', [])
464
+
465
+ def get_quick_wins(self) -> List[Dict[str, Any]]:
466
+ """
467
+ Identify quick wins - categories that can improve quickly.
468
+
469
+ Returns:
470
+ List of quick win opportunities
471
+ """
472
+ if not self.recommendations:
473
+ self.generate_recommendations()
474
+
475
+ # Quick wins: moderate performance, not too many examples needed
476
+ quick_wins = [
477
+ rec for rec in self.recommendations
478
+ if 50 <= rec['current_score'] < 70 and rec['estimated_examples'] <= 50
479
+ ]
480
+
481
+ return quick_wins
482
+
483
+ def prioritize_by_impact(self) -> List[Dict[str, Any]]:
484
+ """
485
+ Sort recommendations by expected impact.
486
+
487
+ Returns:
488
+ Recommendations sorted by impact
489
+ """
490
+ if not self.recommendations:
491
+ self.generate_recommendations()
492
+
493
+ # Calculate impact score (combination of priority and potential improvement)
494
+ def impact_score(rec):
495
+ priority_weight = {'HIGH': 3, 'MEDIUM': 2, 'LOW': 1}
496
+ improvement_potential = 100 - rec['current_score']
497
+ return priority_weight.get(rec['priority'], 1) * improvement_potential
498
+
499
+ sorted_recs = sorted(
500
+ self.recommendations,
501
+ key=impact_score,
502
+ reverse=True
503
+ )
504
+
505
+ return sorted_recs
financial_advisor_gui.py ADDED
@@ -0,0 +1,2418 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Financial Advisor Training GUI
3
+
4
+ A simple, user-friendly interface for training and testing your financial advisor AI.
5
+ No coding required - just click buttons and see results!
6
+ """
7
+
8
+ import gradio as gr
9
+ import json
10
+ import threading
11
+ from pathlib import Path
12
+ from datetime import datetime
13
+ import time
14
+ import os
15
+
16
+ # Import training components
17
+ from fine_tuning import LoRATrainer, LoRAConfig
18
+ from data_aggregation import DatasetBuilder, JSONDataCollector
19
+ from data_aggregation.synthetic_generator import SyntheticDataGenerator
20
+ from data_aggregation.quality_validator import QualityValidator
21
+ from data_aggregation.hf_dataset_loader import HuggingFaceDatasetLoader
22
+ from secure_config import SecureConfig
23
+ from model_registry import get_registry
24
+ from ollama_integration import OllamaClient, test_financial_advisor_ollama
25
+ from runpod_manager import RunPodManager, DeploymentConfig, TrainingConfig
26
+ from runpod_client import PodInfo
27
+
28
+
29
+ # Global variables to track training state
30
+ training_status = {
31
+ "is_training": False,
32
+ "current_epoch": 0,
33
+ "total_epochs": 0,
34
+ "loss": 0.0,
35
+ "progress": 0.0,
36
+ "logs": []
37
+ }
38
+
39
+ trainer_instance = None
40
+ secure_config = SecureConfig()
41
+ model_registry = get_registry()
42
+ selected_model_id = "qwen2.5-32b" # Default model
43
+
44
+ # RunPod state
45
+ runpod_manager = None
46
+ current_pod_info = None
47
+ deployment_in_progress = False
48
+
49
+
50
+ # API Key Management Functions
51
+ def save_api_keys(hf_token, openai_key, anthropic_key, wandb_key, runpod_key, custom_keys_json):
52
+ """Save all API keys securely"""
53
+ try:
54
+ api_keys = {
55
+ "HUGGINGFACE_TOKEN": hf_token or "",
56
+ "HF_TOKEN": hf_token or "", # Alternative name
57
+ "OPENAI_API_KEY": openai_key or "",
58
+ "ANTHROPIC_API_KEY": anthropic_key or "",
59
+ "WANDB_API_KEY": wandb_key or "",
60
+ "RUNPOD_API_KEY": runpod_key or "",
61
+ }
62
+
63
+ # Parse custom keys if provided
64
+ if custom_keys_json and custom_keys_json.strip():
65
+ try:
66
+ custom_keys = json.loads(custom_keys_json)
67
+ api_keys.update(custom_keys)
68
+ except json.JSONDecodeError:
69
+ return "⚠️ Invalid JSON in custom keys. Other keys saved successfully."
70
+
71
+ # Save securely
72
+ secure_config.save_keys(api_keys)
73
+
74
+ # Count non-empty keys
75
+ saved_count = sum(1 for v in api_keys.values() if v and v.strip())
76
+
77
+ return f"✅ Successfully saved {saved_count} API keys securely!\n\nKeys are encrypted and stored in .secrets/ directory."
78
+
79
+ except Exception as e:
80
+ return f"❌ Error saving keys: {e}"
81
+
82
+
83
+ def load_api_keys():
84
+ """Load API keys and return masked versions for display"""
85
+ try:
86
+ keys = secure_config.load_keys()
87
+
88
+ hf_masked = secure_config.get_masked_key("HUGGINGFACE_TOKEN") or ""
89
+ openai_masked = secure_config.get_masked_key("OPENAI_API_KEY") or ""
90
+ anthropic_masked = secure_config.get_masked_key("ANTHROPIC_API_KEY") or ""
91
+ wandb_masked = secure_config.get_masked_key("WANDB_API_KEY") or ""
92
+ runpod_masked = secure_config.get_masked_key("RUNPOD_API_KEY") or ""
93
+
94
+ # Show custom keys
95
+ standard_keys = {"HUGGINGFACE_TOKEN", "HF_TOKEN", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "WANDB_API_KEY", "RUNPOD_API_KEY"}
96
+ custom_keys = {k: secure_config.get_masked_key(k) for k in keys.keys() if k not in standard_keys}
97
+
98
+ custom_json = json.dumps(custom_keys, indent=2) if custom_keys else ""
99
+
100
+ status = f"📊 **Loaded {len(keys)} API keys**\n\n"
101
+ if keys:
102
+ status += "Keys are encrypted and loaded into environment.\n"
103
+ status += "Masked keys shown for security."
104
+ else:
105
+ status += "⚠️ No API keys found. Please add your keys below."
106
+
107
+ return hf_masked, openai_masked, anthropic_masked, wandb_masked, runpod_masked, custom_json, status
108
+
109
+ except Exception as e:
110
+ return "", "", "", "", "", "", f"❌ Error loading keys: {e}"
111
+
112
+
113
+ def clear_api_keys():
114
+ """Clear all stored API keys"""
115
+ try:
116
+ secure_config.delete_keys()
117
+ return "✅ All API keys cleared successfully!", "", "", "", "", "", ""
118
+ except Exception as e:
119
+ return f"❌ Error clearing keys: {e}", "", "", "", "", "", ""
120
+
121
+
122
+ def check_required_keys():
123
+ """Check if required API keys are set"""
124
+ hf_key = secure_config.get_key("HUGGINGFACE_TOKEN")
125
+
126
+ if not hf_key:
127
+ return False, "⚠️ HuggingFace token required! Set it in the Settings tab."
128
+
129
+ return True, "✅ Required API keys are set"
130
+
131
+
132
+ # Synthetic Data Generation Functions
133
+ def generate_synthetic_data(api_provider, num_examples, difficulty, use_scenarios, selected_topics):
134
+ """Generate synthetic training data"""
135
+ try:
136
+ # Check if API key exists
137
+ if api_provider == "openai":
138
+ api_key = secure_config.get_key("OPENAI_API_KEY")
139
+ if not api_key:
140
+ return "❌ OpenAI API key not found! Add it in the Settings tab.", ""
141
+ elif api_provider == "anthropic":
142
+ api_key = secure_config.get_key("ANTHROPIC_API_KEY")
143
+ if not api_key:
144
+ return "❌ Anthropic API key not found! Add it in the Settings tab.", ""
145
+ else:
146
+ return "❌ Invalid API provider", ""
147
+
148
+ log = f"🤖 **SYNTHETIC DATA GENERATION**\n\n"
149
+ log += f"Provider: {api_provider.upper()}\n"
150
+ log += f"Examples: {num_examples}\n"
151
+ log += f"Difficulty: {difficulty}\n"
152
+ log += f"Scenarios: {use_scenarios}\n\n"
153
+
154
+ # Initialize generator
155
+ generator = SyntheticDataGenerator(api_provider=api_provider)
156
+
157
+ # Parse selected topics
158
+ topics_list = None
159
+ if selected_topics and selected_topics.strip():
160
+ topics_list = [t.strip() for t in selected_topics.split(",")]
161
+ log += f"Custom topics: {topics_list}\n\n"
162
+
163
+ log += "🚀 Starting generation...\n\n"
164
+
165
+ # Generate data
166
+ if use_scenarios:
167
+ generated_data = generator.generate_with_scenarios(num_examples=num_examples)
168
+ else:
169
+ generated_data = generator.generate_examples(
170
+ num_examples=num_examples,
171
+ topics=topics_list,
172
+ difficulty=difficulty
173
+ )
174
+
175
+ if not generated_data:
176
+ return log + "\n❌ No data generated. Check API keys and try again.", ""
177
+
178
+ log += f"\n✅ Generated {len(generated_data)} raw examples!\n\n"
179
+
180
+ # Quality validation
181
+ log += "🔍 **QUALITY VALIDATION**\n\n"
182
+ validator = QualityValidator()
183
+ validation_results = validator.validate_batch(generated_data)
184
+
185
+ log += f"Valid: {validation_results['valid']}/{validation_results['total']} "
186
+ log += f"({validation_results['valid']/validation_results['total']*100:.1f}%)\n"
187
+
188
+ if validation_results['invalid'] > 0:
189
+ log += f"⚠️ Filtered out {validation_results['invalid']} low-quality examples\n\n"
190
+ log += "**Common Issues:**\n"
191
+ from collections import Counter
192
+ issue_counter = Counter(validation_results['issues'])
193
+ for issue, count in issue_counter.most_common(3):
194
+ log += f" - {issue}: {count}x\n"
195
+ log += "\n"
196
+ else:
197
+ log += "✅ All examples passed quality checks!\n\n"
198
+
199
+ # Check for duplicates
200
+ if validation_results.get('duplicates'):
201
+ log += f"⚠️ Found {len(validation_results['duplicates'])} duplicate questions (removed)\n\n"
202
+
203
+ # Use only valid examples
204
+ valid_data = validation_results['valid_examples']
205
+
206
+ if not valid_data:
207
+ return log + "\n❌ No valid data after quality filtering. Try again with different settings.", ""
208
+
209
+ log += f"📊 **Final Count:** {len(valid_data)} high-quality examples\n\n"
210
+
211
+ # Calculate average quality score
212
+ avg_score = sum(validator.get_quality_score(ex) for ex in valid_data) / len(valid_data)
213
+ log += f"⭐ **Average Quality Score:** {avg_score:.1f}/100\n\n"
214
+
215
+ # Load existing data
216
+ data_path = "data/sample_financial_advisor_data.json"
217
+ if Path(data_path).exists():
218
+ with open(data_path, 'r', encoding='utf-8') as f:
219
+ existing_data = json.load(f)
220
+ else:
221
+ existing_data = []
222
+
223
+ # Combine and save
224
+ combined_data = existing_data + valid_data
225
+
226
+ Path(data_path).parent.mkdir(parents=True, exist_ok=True)
227
+ with open(data_path, 'w', encoding='utf-8') as f:
228
+ json.dump(combined_data, f, indent=2, ensure_ascii=False)
229
+
230
+ log += f"💾 Saved to training data!\n"
231
+ log += f"Total training examples: {len(combined_data)}\n\n"
232
+
233
+ # Show preview
234
+ log += "**Sample Generated Q&A:**\n\n"
235
+ for i, example in enumerate(valid_data[:3], 1):
236
+ quality_score = validator.get_quality_score(example)
237
+ log += f"{i}. [Quality: {quality_score:.0f}/100]\n"
238
+ log += f" Q: {example['instruction']}\n"
239
+ log += f" A: {example['output'][:150]}...\n\n"
240
+
241
+ return log, f"✅ Generated {len(valid_data)} high-quality examples! Total: {len(combined_data)}"
242
+
243
+ except Exception as e:
244
+ import traceback
245
+ error_details = traceback.format_exc()
246
+ return f"❌ Error generating data: {e}\n\n{error_details}", f"❌ Error: {e}"
247
+
248
+
249
+ def get_available_topics():
250
+ """Get list of available financial topics"""
251
+ topics = [
252
+ "Retirement Planning",
253
+ "Investment Strategies",
254
+ "Tax Planning",
255
+ "Debt Management",
256
+ "Emergency Funds",
257
+ "Budgeting",
258
+ "Insurance",
259
+ "Estate Planning",
260
+ "College Savings",
261
+ "Real Estate",
262
+ "Stock Market",
263
+ "Bonds and Fixed Income",
264
+ "Mutual Funds and ETFs",
265
+ "Cryptocurrency",
266
+ "Financial Independence",
267
+ "Side Hustles",
268
+ "Credit Scores",
269
+ "Mortgages",
270
+ "Small Business Finance",
271
+ "Risk Management"
272
+ ]
273
+ return "\n".join(f"• {topic}" for topic in topics)
274
+
275
+
276
+ # HuggingFace Dataset Loading Functions
277
+ def list_hf_datasets():
278
+ """List available HuggingFace datasets"""
279
+ loader = HuggingFaceDatasetLoader()
280
+ datasets = loader.list_available_datasets()
281
+
282
+ output = "📦 **AVAILABLE HUGGINGFACE DATASETS**\n\n"
283
+
284
+ for ds in datasets:
285
+ output += f"**{ds['name']}**\n"
286
+ output += f" Path: {ds['path']}\n"
287
+ output += f" Type: {ds['type']}\n"
288
+ output += f" Description: {ds['description']}\n\n"
289
+
290
+ return output
291
+
292
+
293
+ def preview_hf_dataset(dataset_path):
294
+ """Preview a HuggingFace dataset"""
295
+ if not dataset_path or not dataset_path.strip():
296
+ return "⚠️ Please enter a dataset path (e.g., mitulshah/transaction-categorization)"
297
+
298
+ try:
299
+ loader = HuggingFaceDatasetLoader()
300
+ preview = loader.preview_dataset(dataset_path, num_examples=3)
301
+ return preview
302
+
303
+ except Exception as e:
304
+ return f"❌ Error previewing dataset: {e}\n\nMake sure:\n- Dataset path is correct\n- You're logged in to HuggingFace (run: huggingface-cli login)\n- Dataset is publicly accessible"
305
+
306
+
307
+ def load_hf_dataset(dataset_path, dataset_name, max_examples, split):
308
+ """Load a HuggingFace dataset and add to training data"""
309
+ try:
310
+ log = "📥 **LOADING HUGGINGFACE DATASET**\n\n"
311
+
312
+ # Check if using known dataset name or custom path
313
+ if dataset_name and dataset_name != "Custom Path":
314
+ log += f"Loading known dataset: {dataset_name}\n\n"
315
+ loader = HuggingFaceDatasetLoader()
316
+ dataset_data = loader.load_dataset_by_name(
317
+ dataset_name,
318
+ split=split,
319
+ max_examples=int(max_examples) if max_examples else None
320
+ )
321
+ elif dataset_path and dataset_path.strip():
322
+ log += f"Loading custom dataset: {dataset_path}\n\n"
323
+ loader = HuggingFaceDatasetLoader()
324
+ dataset_data = loader.load_dataset_by_path(
325
+ dataset_path,
326
+ dataset_type="auto",
327
+ split=split,
328
+ max_examples=int(max_examples) if max_examples else None
329
+ )
330
+ else:
331
+ return "❌ Please select a dataset or enter a custom path", ""
332
+
333
+ if not dataset_data:
334
+ return log + "\n❌ No data loaded. Check dataset path and try again.", ""
335
+
336
+ log += f"✅ Loaded {len(dataset_data)} examples from HuggingFace\n\n"
337
+
338
+ # Quality validation
339
+ log += "🔍 **QUALITY VALIDATION**\n\n"
340
+ validator = QualityValidator()
341
+ validation_results = validator.validate_batch(dataset_data)
342
+
343
+ log += f"Valid: {validation_results['valid']}/{validation_results['total']} "
344
+ log += f"({validation_results['valid']/validation_results['total']*100:.1f}%)\n"
345
+
346
+ if validation_results['invalid'] > 0:
347
+ log += f"⚠️ Filtered out {validation_results['invalid']} low-quality examples\n"
348
+
349
+ # Use only valid examples
350
+ valid_data = validation_results['valid_examples']
351
+
352
+ if not valid_data:
353
+ return log + "\n❌ No valid data after quality filtering.", ""
354
+
355
+ log += f"\n📊 **Final Count:** {len(valid_data)} high-quality examples\n\n"
356
+
357
+ # Calculate average quality score
358
+ avg_score = sum(validator.get_quality_score(ex) for ex in valid_data) / len(valid_data)
359
+ log += f"⭐ **Average Quality Score:** {avg_score:.1f}/100\n\n"
360
+
361
+ # Load existing data
362
+ data_path = "data/sample_financial_advisor_data.json"
363
+ if Path(data_path).exists():
364
+ with open(data_path, 'r', encoding='utf-8') as f:
365
+ existing_data = json.load(f)
366
+ else:
367
+ existing_data = []
368
+
369
+ # Combine and save
370
+ combined_data = existing_data + valid_data
371
+
372
+ Path(data_path).parent.mkdir(parents=True, exist_ok=True)
373
+ with open(data_path, 'w', encoding='utf-8') as f:
374
+ json.dump(combined_data, f, indent=2, ensure_ascii=False)
375
+
376
+ log += f"💾 Added to training data!\n"
377
+ log += f"Total training examples: {len(combined_data)}\n\n"
378
+
379
+ # Show preview
380
+ log += "**Sample Loaded Q&A:**\n\n"
381
+ for i, example in enumerate(valid_data[:3], 1):
382
+ quality_score = validator.get_quality_score(example)
383
+ log += f"{i}. [Quality: {quality_score:.0f}/100]\n"
384
+ log += f" Q: {example['instruction'][:100]}...\n"
385
+ log += f" A: {example['output'][:150]}...\n\n"
386
+
387
+ return log, f"✅ Loaded {len(valid_data)} examples from HuggingFace! Total: {len(combined_data)}"
388
+
389
+ except Exception as e:
390
+ import traceback
391
+ error_details = traceback.format_exc()
392
+ return f"❌ Error loading dataset: {e}\n\n{error_details}", f"❌ Error: {e}"
393
+
394
+
395
+ def load_training_data():
396
+ """Load and display current training data"""
397
+ data_path = "data/sample_financial_advisor_data.json"
398
+
399
+ if not Path(data_path).exists():
400
+ return "❌ No training data found!", 0, "", "❌ **Not Ready**: No training data found. Add examples above or use Synthetic Data/HuggingFace tabs."
401
+
402
+ try:
403
+ with open(data_path, 'r', encoding='utf-8') as f:
404
+ data = json.load(f)
405
+
406
+ # Format preview
407
+ preview = f"📊 **Total Q&A Pairs:** {len(data)}\n\n"
408
+ preview += "**Sample Questions:**\n"
409
+ for i, item in enumerate(data[:3]):
410
+ preview += f"\n{i+1}. {item['instruction']}\n"
411
+
412
+ # Training readiness status
413
+ if len(data) < 20:
414
+ status = f"⚠️ **Warning**: Only {len(data)} examples. Recommended minimum: 50-100 for good results."
415
+ elif len(data) < 50:
416
+ status = f"✅ **Ready**: {len(data)} examples loaded. Consider adding more for better results (recommended: 100+)."
417
+ else:
418
+ status = f"✅ **Ready for Training**: {len(data)} examples loaded and ready!\n\n**Next Step**: Go to **⚙️ Training** tab and click 'Start Training'"
419
+
420
+ return preview, len(data), json.dumps(data, indent=2), status
421
+
422
+ except Exception as e:
423
+ return f"❌ Error loading data: {e}", 0, "", "❌ Error loading training data"
424
+
425
+
426
+ def add_training_example(question, answer, context=""):
427
+ """Add a new training example"""
428
+ data_path = "data/sample_financial_advisor_data.json"
429
+
430
+ try:
431
+ # Create new example
432
+ new_example = {
433
+ "instruction": question,
434
+ "input": context,
435
+ "output": answer
436
+ }
437
+
438
+ # Validate before adding
439
+ validator = QualityValidator()
440
+ is_valid, issues = validator.validate_example(new_example)
441
+
442
+ if not is_valid:
443
+ issues_text = "\n".join(f" - {issue}" for issue in issues)
444
+ return f"⚠️ Quality issues found:\n{issues_text}\n\nExample was still added, but consider improving it.", 0
445
+
446
+ # Load existing data
447
+ if Path(data_path).exists():
448
+ with open(data_path, 'r', encoding='utf-8') as f:
449
+ data = json.load(f)
450
+ else:
451
+ data = []
452
+
453
+ data.append(new_example)
454
+
455
+ # Save
456
+ Path(data_path).parent.mkdir(parents=True, exist_ok=True)
457
+ with open(data_path, 'w', encoding='utf-8') as f:
458
+ json.dump(data, f, indent=2, ensure_ascii=False)
459
+
460
+ quality_score = validator.get_quality_score(new_example)
461
+ return f"✅ Added! Quality Score: {quality_score:.0f}/100\nTotal examples: {len(data)}", len(data)
462
+
463
+ except Exception as e:
464
+ return f"❌ Error: {e}", 0
465
+
466
+
467
+ def validate_training_data():
468
+ """Validate all training data and return report (with auto-deduplication)"""
469
+ data_path = "data/sample_financial_advisor_data.json"
470
+
471
+ if not Path(data_path).exists():
472
+ return "❌ No training data found!"
473
+
474
+ try:
475
+ with open(data_path, 'r', encoding='utf-8') as f:
476
+ data = json.load(f)
477
+
478
+ original_count = len(data)
479
+ validator = QualityValidator()
480
+
481
+ # First, check for duplicates and auto-remove
482
+ deduplicated_data, num_duplicates = validator.remove_duplicates(data)
483
+
484
+ # Save deduplicated data if duplicates were found
485
+ if num_duplicates > 0:
486
+ # Create backup
487
+ backup_path = data_path.replace('.json', '_backup.json')
488
+ with open(backup_path, 'w', encoding='utf-8') as f:
489
+ json.dump(data, f, indent=2, ensure_ascii=False)
490
+
491
+ # Save deduplicated version
492
+ with open(data_path, 'w', encoding='utf-8') as f:
493
+ json.dump(deduplicated_data, f, indent=2, ensure_ascii=False)
494
+
495
+ data = deduplicated_data
496
+
497
+ # Now validate the deduplicated data
498
+ validation_results = validator.validate_batch(data)
499
+
500
+ # Generate report
501
+ report = "=" * 60 + "\n"
502
+ report += "QUALITY VALIDATION REPORT (WITH AUTO-DEDUPLICATION)\n"
503
+ report += "=" * 60 + "\n\n"
504
+
505
+ # Deduplication results
506
+ if num_duplicates > 0:
507
+ report += "🧹 AUTO-DEDUPLICATION COMPLETE!\n"
508
+ report += "-" * 60 + "\n"
509
+ report += f"Original Examples: {original_count}\n"
510
+ report += f"Duplicates Removed: {num_duplicates}\n"
511
+ report += f"Unique Examples: {len(data)}\n"
512
+ report += f"Backup saved to: {backup_path}\n"
513
+ report += "\n" + "=" * 60 + "\n\n"
514
+ else:
515
+ report += "✅ NO DUPLICATES FOUND\n"
516
+ report += "-" * 60 + "\n"
517
+ report += f"All {len(data)} examples are unique!\n"
518
+ report += "\n" + "=" * 60 + "\n\n"
519
+
520
+ # Quality validation results
521
+ report += f"Total Examples: {validation_results['total']}\n"
522
+ report += f"Valid: {validation_results['valid']} ({validation_results['valid']/validation_results['total']*100:.1f}%)\n"
523
+ report += f"Invalid: {validation_results['invalid']} ({validation_results['invalid']/validation_results['total']*100:.1f}%)\n\n"
524
+
525
+ if validation_results['invalid'] > 0:
526
+ report += "-" * 60 + "\n"
527
+ report += "QUALITY ISSUES FOUND:\n"
528
+ report += "-" * 60 + "\n"
529
+
530
+ # Count issue types
531
+ from collections import Counter
532
+ issue_counter = Counter(validation_results['issues'])
533
+ for issue, count in issue_counter.most_common():
534
+ report += f" - {issue}: {count} occurrences\n"
535
+
536
+ report += "\n"
537
+
538
+ # Add quality scores
539
+ report += "\n📊 QUALITY SCORE DISTRIBUTION:\n"
540
+ report += "-" * 60 + "\n"
541
+
542
+ scores = [validator.get_quality_score(ex) for ex in data]
543
+ avg_score = sum(scores) / len(scores)
544
+ min_score = min(scores)
545
+ max_score = max(scores)
546
+
547
+ report += f"Average Score: {avg_score:.1f}/100\n"
548
+ report += f"Range: {min_score:.0f} - {max_score:.0f}\n\n"
549
+
550
+ # Score distribution
551
+ excellent = sum(1 for s in scores if s >= 90)
552
+ good = sum(1 for s in scores if 75 <= s < 90)
553
+ fair = sum(1 for s in scores if 60 <= s < 75)
554
+ poor = sum(1 for s in scores if s < 60)
555
+
556
+ report += f"Excellent (90+): {excellent}\n"
557
+ report += f"Good (75-89): {good}\n"
558
+ report += f"Fair (60-74): {fair}\n"
559
+ report += f"Poor (<60): {poor}\n\n"
560
+
561
+ # Final status
562
+ report += "=" * 60 + "\n"
563
+ if validation_results['valid'] == validation_results['total'] and num_duplicates == 0:
564
+ report += "✅ PERFECT! All data is unique and high quality!\n"
565
+ elif validation_results['valid'] == validation_results['total']:
566
+ report += f"✅ GOOD! All unique data passed quality checks!\n"
567
+ elif validation_results['valid'] / validation_results['total'] >= 0.9:
568
+ report += "✅ GOOD QUALITY (90%+ valid)\n"
569
+ elif validation_results['valid'] / validation_results['total'] >= 0.7:
570
+ report += "⚠️ ACCEPTABLE QUALITY (70-90% valid)\n"
571
+ else:
572
+ report += "❌ POOR QUALITY (<70% valid)\n"
573
+ report += "=" * 60 + "\n"
574
+
575
+ return report
576
+
577
+ except Exception as e:
578
+ return f"❌ Error validating data: {e}"
579
+
580
+
581
+ def remove_duplicates_from_data():
582
+ """Remove duplicate questions from training data"""
583
+ data_path = "data/sample_financial_advisor_data.json"
584
+
585
+ if not Path(data_path).exists():
586
+ return "❌ No training data found!", 0, "", ""
587
+
588
+ try:
589
+ # Load data
590
+ with open(data_path, 'r', encoding='utf-8') as f:
591
+ data = json.load(f)
592
+
593
+ original_count = len(data)
594
+
595
+ # Remove duplicates
596
+ validator = QualityValidator()
597
+ deduplicated_data, num_removed = validator.remove_duplicates(data)
598
+
599
+ if num_removed == 0:
600
+ return (
601
+ "✅ No duplicates found! Your data is already clean.",
602
+ len(deduplicated_data),
603
+ json.dumps(deduplicated_data, indent=2),
604
+ f"✅ **Ready for Training**: {len(deduplicated_data)} unique examples!\n\n**Next Step**: Go to **⚙️ Training** tab"
605
+ )
606
+
607
+ # Save deduplicated data
608
+ with open(data_path, 'w', encoding='utf-8') as f:
609
+ json.dump(deduplicated_data, f, indent=2, ensure_ascii=False)
610
+
611
+ # Create backup of original
612
+ backup_path = data_path.replace('.json', '_with_duplicates_backup.json')
613
+ with open(backup_path, 'w', encoding='utf-8') as f:
614
+ json.dump(data, f, indent=2, ensure_ascii=False)
615
+
616
+ message = f"""✅ **Deduplication Complete!**
617
+
618
+ **Removed:** {num_removed} duplicate questions
619
+ **Kept:** {len(deduplicated_data)} unique examples
620
+ **Original:** {original_count} total examples
621
+
622
+ **Backup saved to:** `{backup_path}`
623
+
624
+ Your training data now contains only unique questions. This will improve model quality and prevent overfitting.
625
+ """
626
+
627
+ # Prepare preview
628
+ preview = f"📊 **Total Q&A Pairs:** {len(deduplicated_data)}\n\n"
629
+ preview += "**Sample Questions:**\n"
630
+ for i, item in enumerate(deduplicated_data[:3]):
631
+ preview += f"\n{i+1}. {item['instruction']}\n"
632
+
633
+ status = f"✅ **Ready for Training**: {len(deduplicated_data)} unique examples loaded!\n\n**Next Step**: Go to **⚙️ Training** tab"
634
+
635
+ return message, len(deduplicated_data), json.dumps(deduplicated_data, indent=2), status
636
+
637
+ except Exception as e:
638
+ return f"❌ Error removing duplicates: {str(e)}", 0, "", ""
639
+
640
+
641
+ # Model Selection Functions
642
+ def get_model_info(model_name):
643
+ """Get detailed model information"""
644
+ global selected_model_id
645
+
646
+ # Get model ID from name
647
+ model_id = model_registry.get_model_id_from_name(model_name)
648
+ if not model_id:
649
+ return "❌ Model not found"
650
+
651
+ selected_model_id = model_id
652
+ model = model_registry.get_model(model_id)
653
+
654
+ info = f"# {model.name}\n\n"
655
+ info += f"**Type:** {model.type.upper()}\n"
656
+ info += f"**Path:** `{model.path}`\n"
657
+ info += f"**Size:** {model.size}\n"
658
+ info += f"**VRAM Required:** {model.vram_required}\n"
659
+ info += f"**Context Length:** {model.context_length:,} tokens\n"
660
+ info += f"**Recommended Quantization:** {model.quantization}\n"
661
+ info += f"**Recommended LoRA Rank:** {model.lora_rank}\n\n"
662
+ info += f"**Description:**\n{model.description}\n\n"
663
+ info += f"**Tags:** {', '.join(model.tags)}\n\n"
664
+
665
+ # Validate availability
666
+ is_valid, message = model_registry.validate_model_selection(model_id)
667
+ info += f"\n**Status:** {message}\n"
668
+
669
+ return info
670
+
671
+
672
+ def check_ollama_status():
673
+ """Check Ollama status and list installed models"""
674
+ client = OllamaClient()
675
+
676
+ status = "# Ollama Status\n\n"
677
+
678
+ if client.is_available():
679
+ status += "✅ **Ollama is running**\n\n"
680
+
681
+ models = client.list_models()
682
+ if models:
683
+ status += f"**Installed Models ({len(models)}):**\n\n"
684
+ for model in models:
685
+ name = model.get("name", "unknown")
686
+ size = model.get("size", 0) / (1024**3) # Convert to GB
687
+ status += f"- `{name}` ({size:.1f}GB)\n"
688
+ else:
689
+ status += "⚠️ No models installed\n\n"
690
+ status += "Install models with: `ollama pull <model>`\n"
691
+ else:
692
+ status += "❌ **Ollama is not running**\n\n"
693
+ status += "Start Ollama with:\n"
694
+ status += "```bash\n"
695
+ status += "ollama serve\n"
696
+ status += "```\n\n"
697
+ status += "Or download from: https://ollama.com\n"
698
+
699
+ return status
700
+
701
+
702
+ def start_cloud_training(lora_rank, learning_rate, num_epochs, batch_size, grad_accum):
703
+ """Start cloud training on RunPod"""
704
+ global training_status, runpod_manager, selected_model_id
705
+
706
+ if training_status["is_training"]:
707
+ return "⚠️ Training already in progress!", ""
708
+
709
+ # Check RunPod API key
710
+ runpod_key = secure_config.get_key("RUNPOD_API_KEY")
711
+ if not runpod_key:
712
+ return "❌ RunPod API key required for cloud training! Add it in Settings tab.", "❌ Missing RunPod API key"
713
+
714
+ # Check HF key
715
+ keys_ok, keys_msg = check_required_keys()
716
+ if not keys_ok:
717
+ return keys_msg, "❌ Missing API keys"
718
+
719
+ try:
720
+ training_status["is_training"] = True
721
+ log = "☁️ **CLOUD TRAINING ON RUNPOD**\n\n"
722
+
723
+ # Get selected model
724
+ model = model_registry.get_model(selected_model_id)
725
+ if not model:
726
+ training_status["is_training"] = False
727
+ return "❌ No model selected!", ""
728
+
729
+ log += f"📋 Configuration:\n"
730
+ log += f"- Model: {model.name}\n"
731
+ log += f"- LoRA Rank: {lora_rank}\n"
732
+ log += f"- Epochs: {num_epochs}\n"
733
+ log += f"- Mode: Cloud (RunPod)\n\n"
734
+
735
+ # Load training data
736
+ data_path = "data/sample_financial_advisor_data.json"
737
+ if not Path(data_path).exists():
738
+ training_status["is_training"] = False
739
+ return "❌ No training data found!", ""
740
+
741
+ with open(data_path, 'r', encoding='utf-8') as f:
742
+ data = json.load(f)
743
+
744
+ log += f"✅ Loaded {len(data)} training examples\n\n"
745
+
746
+ # Initialize RunPod manager
747
+ if not runpod_manager:
748
+ runpod_manager = RunPodManager(runpod_key)
749
+
750
+ log += "🚀 **STEP 1: Creating RunPod GPU Instance**\n"
751
+ log += "⏳ Finding available GPU (RTX 4090 recommended)...\n\n"
752
+
753
+ # Create pod config
754
+ from runpod_manager import DeploymentConfig
755
+ config = DeploymentConfig(
756
+ pod_name=f"aura-training-{datetime.now().strftime('%Y%m%d-%H%M')}",
757
+ gpu_type="NVIDIA GeForce RTX 4090",
758
+ storage_gb=50,
759
+ sync_data=True,
760
+ auto_setup=True
761
+ )
762
+
763
+ # Deploy pod
764
+ pod_info = runpod_manager.one_click_deploy(config=config)
765
+
766
+ log += f"✅ Pod created: {pod_info.id}\n"
767
+ log += f"📍 GPU: {pod_info.gpu_type}\n"
768
+ log += f"💰 Cost: ${pod_info.cost_per_hr:.2f}/hour\n\n"
769
+
770
+ log += "🚀 **STEP 2: Setting Up Training Environment**\n"
771
+ log += "⏳ Installing dependencies on cloud GPU...\n\n"
772
+
773
+ # Environment is auto-setup by one_click_deploy
774
+
775
+ log += "✅ Environment ready\n\n"
776
+
777
+ log += "🚀 **STEP 3: Uploading Training Data**\n"
778
+ log += f"⏳ Uploading {len(data)} examples to pod...\n\n"
779
+
780
+ # Data already synced by one_click_deploy if sync_data=True
781
+
782
+ log += "✅ Data uploaded\n\n"
783
+
784
+ log += "🚀 **STEP 4: Starting Training Job**\n"
785
+ log += f"⏳ Training {model.name} with LoRA...\n"
786
+ log += f"⏱️ Estimated time: {num_epochs * 30}-{num_epochs * 60} minutes\n\n"
787
+
788
+ # Create training config
789
+ from runpod_manager import TrainingConfig
790
+ train_config = TrainingConfig(
791
+ model_name=model.path if model.type != "ollama" else None,
792
+ dataset_path="/workspace/data/sample_financial_advisor_data.json",
793
+ output_dir="/workspace/models/financial_advisor",
794
+ lora_rank=int(lora_rank),
795
+ learning_rate=float(learning_rate),
796
+ num_epochs=int(num_epochs),
797
+ batch_size=int(batch_size),
798
+ gradient_accumulation_steps=int(grad_accum)
799
+ )
800
+
801
+ # Submit training job
802
+ job_result = runpod_manager.submit_training_job(pod_info.id, train_config)
803
+
804
+ log += "✅ Training started!\n\n"
805
+ log += "📊 **MONITORING TRAINING**\n"
806
+ log += "⏳ Training in progress... (this will take a while)\n\n"
807
+
808
+ # Note: In real implementation, we'd poll for completion
809
+ # For now, return success and let user check manually
810
+
811
+ log += f"🔗 **POD ACCESS**\n"
812
+ log += f"SSH: ssh root@{pod_info.ip} -p {pod_info.ssh_port}\n"
813
+ log += f"GUI: https://{pod_info.id}-7860.proxy.runpod.net\n\n"
814
+
815
+ log += "⚠️ **IMPORTANT:**\n"
816
+ log += "- Training is running on cloud GPU\n"
817
+ log += f"- Costing ${pod_info.cost_per_hr:.2f}/hour\n"
818
+ log += "- Go to RunPod tab to monitor or terminate\n"
819
+ log += "- Model will be saved to pod storage\n"
820
+
821
+ training_status["is_training"] = False
822
+ return log, "✅ Cloud training started!"
823
+
824
+ except Exception as e:
825
+ training_status["is_training"] = False
826
+ import traceback
827
+ error_details = traceback.format_exc()
828
+ return f"❌ Error: {str(e)}\n\n{error_details}", f"❌ Error: {e}"
829
+
830
+
831
+ def start_training(lora_rank, learning_rate, num_epochs, batch_size, grad_accum, training_mode):
832
+ """Start the training process (local or cloud)"""
833
+ global training_status, trainer_instance, selected_model_id
834
+
835
+ # Route to cloud or local training
836
+ if training_mode == "Cloud GPU (RunPod)":
837
+ return start_cloud_training(lora_rank, learning_rate, num_epochs, batch_size, grad_accum)
838
+
839
+ # Local training below
840
+ if training_status["is_training"]:
841
+ return "⚠️ Training already in progress!", ""
842
+
843
+ # Check API keys first
844
+ keys_ok, keys_msg = check_required_keys()
845
+ if not keys_ok:
846
+ return keys_msg, "❌ Missing API keys. Go to Settings tab."
847
+
848
+ # Get selected model
849
+ model = model_registry.get_model(selected_model_id)
850
+ if not model:
851
+ return "❌ No model selected!", ""
852
+
853
+ # Validate model
854
+ is_valid, message = model_registry.validate_model_selection(selected_model_id)
855
+ if not is_valid:
856
+ return f"❌ Model validation failed: {message}", ""
857
+
858
+ # Get model path (convert Ollama to HF if needed)
859
+ if model.type == "ollama":
860
+ from ollama_integration import get_hf_model_for_ollama
861
+ model_path = get_hf_model_for_ollama(model.path)
862
+ if not model_path:
863
+ return f"❌ Cannot train with Ollama model {model.path}. No HuggingFace equivalent found.", ""
864
+ log_model_name = f"{model.name} (using HF: {model_path})"
865
+ else:
866
+ model_path = model.path
867
+ log_model_name = model.name
868
+
869
+ try:
870
+ training_status["is_training"] = True
871
+ training_status["current_epoch"] = 0
872
+ training_status["total_epochs"] = num_epochs
873
+ training_status["logs"] = []
874
+
875
+ log = "🚀 **STARTING TRAINING**\n\n"
876
+ log += f"⚙️ Configuration:\n"
877
+ log += f"- Model: {log_model_name}\n"
878
+ log += f"- LoRA Rank: {lora_rank}\n"
879
+ log += f"- Learning Rate: {learning_rate}\n"
880
+ log += f"- Epochs: {num_epochs}\n"
881
+ log += f"- Batch Size: {batch_size}\n"
882
+ log += f"- Gradient Accumulation: {grad_accum}\n\n"
883
+
884
+ training_status["logs"].append(log)
885
+
886
+ # Load data
887
+ log += "📂 Loading training data...\n"
888
+ data_path = "data/sample_financial_advisor_data.json"
889
+
890
+ if not Path(data_path).exists():
891
+ training_status["is_training"] = False
892
+ return "❌ No training data found! Add some Q&A pairs first.", ""
893
+
894
+ collector = JSONDataCollector()
895
+ data = collector.collect(data_path)
896
+ log += f"✅ Loaded {len(data)} examples\n\n"
897
+ training_status["logs"].append(log)
898
+
899
+ # Split data
900
+ log += "📊 Splitting dataset...\n"
901
+ builder = DatasetBuilder()
902
+ train_data, val_data, test_data = builder.train_test_split(data)
903
+ log += f"✅ Train: {len(train_data)}, Val: {len(val_data)}, Test: {len(test_data)}\n\n"
904
+ training_status["logs"].append(log)
905
+
906
+ # Configure LoRA
907
+ log += "⚙️ Configuring LoRA...\n"
908
+ lora_config = LoRAConfig(
909
+ r=int(lora_rank),
910
+ lora_alpha=int(lora_rank * 2),
911
+ lora_dropout=0.05,
912
+ target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
913
+ bias="none",
914
+ task_type="CAUSAL_LM"
915
+ )
916
+ log += f"✅ LoRA configured\n\n"
917
+ training_status["logs"].append(log)
918
+
919
+ # Initialize trainer
920
+ log += "🤖 Initializing trainer...\n"
921
+ trainer_instance = LoRATrainer(
922
+ model_name=model_path,
923
+ lora_config=lora_config,
924
+ output_dir="models/financial_advisor"
925
+ )
926
+ log += "✅ Trainer ready\n\n"
927
+ training_status["logs"].append(log)
928
+
929
+ # Load model
930
+ log += f"📥 Loading {model.name} (this will take a few minutes)...\n"
931
+ log += f"⚠️ First time will download {model.size}\n\n"
932
+ training_status["logs"].append(log)
933
+
934
+ trainer_instance.load_model(use_4bit=True)
935
+
936
+ log += "✅ Model loaded successfully!\n\n"
937
+ training_status["logs"].append(log)
938
+
939
+ # Show parameters
940
+ params = trainer_instance.get_trainable_parameters()
941
+ log += f"📊 **Parameter Efficiency:**\n"
942
+ log += f"- Total: {params['total']:,}\n"
943
+ log += f"- Trainable: {params['trainable']:,}\n"
944
+ log += f"- Percentage: {params['percentage']:.2f}%\n\n"
945
+ training_status["logs"].append(log)
946
+
947
+ # Prepare datasets
948
+ log += "🔄 Preparing datasets...\n"
949
+ train_dataset, val_dataset = trainer_instance.prepare_dataset(train_data, val_data)
950
+ log += f"✅ Datasets tokenized and ready\n\n"
951
+ training_status["logs"].append(log)
952
+
953
+ # Start training
954
+ log += "🎯 **TRAINING STARTED**\n\n"
955
+ log += f"This will take approximately {num_epochs * 30}-{num_epochs * 60} minutes\n"
956
+ log += "You can monitor progress in TensorBoard:\n"
957
+ log += "`tensorboard --logdir models/financial_advisor/logs`\n\n"
958
+ training_status["logs"].append(log)
959
+
960
+ # Train (this will take a while)
961
+ history = trainer_instance.train(
962
+ train_dataset=train_dataset,
963
+ val_dataset=val_dataset,
964
+ num_epochs=int(num_epochs),
965
+ batch_size=int(batch_size),
966
+ learning_rate=float(learning_rate),
967
+ gradient_accumulation_steps=int(grad_accum)
968
+ )
969
+
970
+ log += "\n✅ **TRAINING COMPLETE!**\n\n"
971
+ log += f"💾 Model saved to: models/financial_advisor/final_model\n"
972
+ log += f"📊 Logs saved to: models/financial_advisor/logs\n\n"
973
+ training_status["logs"].append(log)
974
+
975
+ # Evaluate on test set
976
+ if len(test_data) > 0:
977
+ log += "🔍 **EVALUATING ON TEST SET**\n\n"
978
+ log += f"Running evaluation on {len(test_data)} test examples...\n"
979
+ training_status["logs"].append(log)
980
+
981
+ try:
982
+ # Run evaluation (limit to 50 samples for speed)
983
+ eval_results = trainer_instance.evaluate_on_test_set(
984
+ test_data=test_data,
985
+ model_name=log_model_name,
986
+ dataset_info=f"Financial Advisor Training - {len(train_data)} train examples",
987
+ num_samples=min(50, len(test_data))
988
+ )
989
+
990
+ log += "\n✅ **EVALUATION COMPLETE**\n\n"
991
+ log += "**Performance Metrics:**\n"
992
+ metrics = eval_results['metrics']
993
+ log += f"- Average Response Length: {metrics['avg_response_length']:.1f} words\n"
994
+ log += f"- Average Generation Time: {metrics['avg_generation_time']:.2f}s\n"
995
+ log += f"- Throughput: {metrics['examples_per_second']:.2f} examples/sec\n\n"
996
+
997
+ log += "📊 Evaluation report saved to: models/financial_advisor/evaluation_results/\n"
998
+ training_status["logs"].append(log)
999
+
1000
+ except Exception as eval_error:
1001
+ log += f"\n⚠️ Evaluation error (training still succeeded): {eval_error}\n"
1002
+ training_status["logs"].append(log)
1003
+
1004
+ training_status["is_training"] = False
1005
+ return "\n".join(training_status["logs"]), "✅ Training & Evaluation Complete!"
1006
+
1007
+ except Exception as e:
1008
+ training_status["is_training"] = False
1009
+ error_msg = f"\n\n❌ **ERROR:** {str(e)}\n\n"
1010
+ error_msg += "**Common fixes:**\n"
1011
+ error_msg += "- Out of memory: Reduce batch_size or increase grad_accum\n"
1012
+ error_msg += "- CUDA error: Make sure you have a GPU with 18GB+ VRAM\n"
1013
+ error_msg += "- Import error: Run `pip install bitsandbytes`\n"
1014
+ training_status["logs"].append(error_msg)
1015
+ return "\n".join(training_status["logs"]), f"❌ Error: {e}"
1016
+
1017
+
1018
+ def test_financial_advisor(question):
1019
+ """Test the trained model"""
1020
+ global trainer_instance
1021
+
1022
+ if trainer_instance is None:
1023
+ return "❌ No model loaded. Train a model first or load an existing one."
1024
+
1025
+ try:
1026
+ # Format as financial advisor prompt
1027
+ system_prompt = (
1028
+ "You are an expert financial advisor with deep knowledge of personal finance, "
1029
+ "investments, retirement planning, tax strategies, and wealth management."
1030
+ )
1031
+
1032
+ prompt = f"<|im_start|>system\n{system_prompt}<|im_end|>\n"
1033
+ prompt += f"<|im_start|>user\n{question}<|im_end|>\n"
1034
+ prompt += f"<|im_start|>assistant\n"
1035
+
1036
+ # Tokenize
1037
+ inputs = trainer_instance.tokenizer(prompt, return_tensors="pt").to(trainer_instance.peft_model.device)
1038
+
1039
+ # Generate
1040
+ outputs = trainer_instance.peft_model.generate(
1041
+ **inputs,
1042
+ max_new_tokens=512,
1043
+ temperature=0.7,
1044
+ top_p=0.9,
1045
+ do_sample=True,
1046
+ pad_token_id=trainer_instance.tokenizer.eos_token_id
1047
+ )
1048
+
1049
+ # Decode
1050
+ response = trainer_instance.tokenizer.decode(outputs[0], skip_special_tokens=False)
1051
+
1052
+ # Extract assistant response
1053
+ if "<|im_start|>assistant" in response:
1054
+ response = response.split("<|im_start|>assistant")[-1].strip()
1055
+ response = response.replace("<|im_end|>", "").strip()
1056
+
1057
+ return response
1058
+
1059
+ except Exception as e:
1060
+ return f"❌ Error generating response: {e}"
1061
+
1062
+
1063
+ def load_existing_model():
1064
+ """Load a previously trained model"""
1065
+ global trainer_instance, selected_model_id
1066
+
1067
+ model_path = "models/financial_advisor/final_model"
1068
+
1069
+ if not Path(model_path).exists():
1070
+ return "❌ No trained model found at models/financial_advisor/final_model"
1071
+
1072
+ try:
1073
+ # Get the model that was used for training
1074
+ model = model_registry.get_model(selected_model_id)
1075
+ if not model:
1076
+ return "❌ Model not found in registry"
1077
+
1078
+ # Get actual model path
1079
+ if model.type == "ollama":
1080
+ from ollama_integration import get_hf_model_for_ollama
1081
+ base_model_path = get_hf_model_for_ollama(model.path)
1082
+ if not base_model_path:
1083
+ return f"❌ Cannot load Ollama model {model.path}"
1084
+ else:
1085
+ base_model_path = model.path
1086
+
1087
+ # Initialize trainer
1088
+ lora_config = LoRAConfig(r=model.lora_rank, lora_alpha=model.lora_rank*2)
1089
+ trainer_instance = LoRATrainer(
1090
+ model_name=base_model_path,
1091
+ lora_config=lora_config,
1092
+ output_dir="models/financial_advisor"
1093
+ )
1094
+
1095
+ # Load base model
1096
+ trainer_instance.load_model(use_4bit=True)
1097
+
1098
+ # Load adapter
1099
+ trainer_instance.load_adapter(model_path)
1100
+
1101
+ return f"✅ Model loaded successfully! ({model.name}) You can now test it."
1102
+
1103
+ except Exception as e:
1104
+ return f"❌ Error loading model: {e}"
1105
+
1106
+
1107
+ # Model Comparison and Evaluation Functions
1108
+ def load_evaluation_history():
1109
+ """Load evaluation history and display summary"""
1110
+ try:
1111
+ from evaluation.model_evaluator import ModelEvaluator
1112
+
1113
+ evaluator = ModelEvaluator(output_dir="models/financial_advisor/evaluation_results")
1114
+
1115
+ if not evaluator.history:
1116
+ return "❌ No evaluation history found. Train a model first to see evaluations."
1117
+
1118
+ # Generate summary
1119
+ summary = f"📊 **EVALUATION HISTORY**\n\n"
1120
+ summary += f"Total Evaluations: {len(evaluator.history)}\n\n"
1121
+ summary += "=" * 70 + "\n\n"
1122
+
1123
+ for i, eval_result in enumerate(reversed(evaluator.history[-10:]), 1):
1124
+ summary += f"**Evaluation {len(evaluator.history) - i + 1}:**\n"
1125
+ summary += f"- Model: {eval_result['model_name']}\n"
1126
+ summary += f"- Dataset: {eval_result['dataset_info']}\n"
1127
+ summary += f"- Timestamp: {eval_result['timestamp']}\n"
1128
+ summary += f"- Test Examples: {eval_result['num_test_examples']}\n"
1129
+
1130
+ metrics = eval_result.get('metrics', {})
1131
+ summary += f"- Avg Response Length: {metrics.get('avg_response_length', 0):.1f} words\n"
1132
+ summary += f"- Avg Generation Time: {metrics.get('avg_generation_time', 0):.2f}s\n"
1133
+ summary += f"- Throughput: {metrics.get('examples_per_second', 0):.2f} ex/s\n"
1134
+ summary += "\n" + "-" * 70 + "\n\n"
1135
+
1136
+ return summary
1137
+
1138
+ except Exception as e:
1139
+ return f"❌ Error loading evaluation history: {e}"
1140
+
1141
+
1142
+ def compare_models(num_models):
1143
+ """Compare recent model evaluations"""
1144
+ try:
1145
+ from evaluation.model_evaluator import ModelEvaluator
1146
+
1147
+ evaluator = ModelEvaluator(output_dir="models/financial_advisor/evaluation_results")
1148
+
1149
+ if not evaluator.history:
1150
+ return "❌ No evaluation history found. Train models first to compare."
1151
+
1152
+ # Get comparison
1153
+ comparison = evaluator.compare_models(num_recent=int(num_models))
1154
+
1155
+ # Generate report
1156
+ report = evaluator.generate_comparison_report(comparison)
1157
+
1158
+ return report
1159
+
1160
+ except Exception as e:
1161
+ return f"❌ Error comparing models: {e}"
1162
+
1163
+
1164
+ def view_latest_evaluation():
1165
+ """View the most recent evaluation in detail"""
1166
+ try:
1167
+ from evaluation.model_evaluator import ModelEvaluator
1168
+
1169
+ evaluator = ModelEvaluator(output_dir="models/financial_advisor/evaluation_results")
1170
+
1171
+ latest = evaluator.get_latest_evaluation()
1172
+ if not latest:
1173
+ return "❌ No evaluations found. Train a model first."
1174
+
1175
+ # Generate detailed report
1176
+ report = evaluator.generate_report(latest)
1177
+
1178
+ return report
1179
+
1180
+ except Exception as e:
1181
+ return f"❌ Error viewing evaluation: {e}"
1182
+
1183
+
1184
+ # RunPod Functions
1185
+ def init_runpod_manager():
1186
+ """Initialize RunPod manager with API key"""
1187
+ global runpod_manager
1188
+
1189
+ if runpod_manager is not None:
1190
+ return "✅ RunPod manager already initialized"
1191
+
1192
+ api_key = secure_config.get_key("RUNPOD_API_KEY")
1193
+ if not api_key:
1194
+ return "❌ RunPod API key not found! Please add it in the Settings tab."
1195
+
1196
+ try:
1197
+ runpod_manager = RunPodManager(api_key)
1198
+ return "✅ RunPod manager initialized successfully!"
1199
+ except Exception as e:
1200
+ return f"❌ Error initializing RunPod manager: {e}"
1201
+
1202
+
1203
+ def list_runpod_pods():
1204
+ """List all RunPod pods"""
1205
+ global runpod_manager
1206
+
1207
+ if not runpod_manager:
1208
+ init_result = init_runpod_manager()
1209
+ if "❌" in init_result:
1210
+ return init_result
1211
+
1212
+ try:
1213
+ pods = runpod_manager.list_pods()
1214
+
1215
+ if not pods:
1216
+ return "No pods found. Create a new pod to get started!"
1217
+
1218
+ output = f"📊 **YOUR RUNPOD PODS** ({len(pods)} total)\n\n"
1219
+
1220
+ for pod in pods:
1221
+ output += f"**{pod.name}** (ID: {pod.id[:8]}...)\n"
1222
+ output += f" Status: {pod.status}\n"
1223
+ output += f" GPU: {pod.gpu_count}x {pod.gpu_type}\n"
1224
+ output += f" Cost: ${pod.cost_per_hr:.2f}/hr\n"
1225
+
1226
+ if pod.status == "RUNNING":
1227
+ if pod.ip and pod.ssh_port:
1228
+ output += f" SSH: {pod.ip}:{pod.ssh_port}\n"
1229
+ if pod.port:
1230
+ output += f" GUI: https://{pod.id}-7860.proxy.runpod.net\n"
1231
+
1232
+ output += "\n"
1233
+
1234
+ return output
1235
+
1236
+ except Exception as e:
1237
+ return f"❌ Error listing pods: {e}"
1238
+
1239
+
1240
+ def create_runpod_pod(pod_name, gpu_type, storage_gb, sync_data):
1241
+ """Create a new RunPod pod"""
1242
+ global runpod_manager, current_pod_info, deployment_in_progress
1243
+
1244
+ if deployment_in_progress:
1245
+ return "⚠️ Deployment already in progress!", ""
1246
+
1247
+ if not runpod_manager:
1248
+ init_result = init_runpod_manager()
1249
+ if "❌" in init_result:
1250
+ return init_result, ""
1251
+
1252
+ deployment_in_progress = True
1253
+ log_output = []
1254
+
1255
+ def log_callback(msg):
1256
+ log_output.append(msg)
1257
+
1258
+ try:
1259
+ config = DeploymentConfig(
1260
+ pod_name=pod_name or "aura-training-pod",
1261
+ gpu_type=gpu_type,
1262
+ storage_gb=int(storage_gb),
1263
+ sync_data=sync_data,
1264
+ auto_setup=True
1265
+ )
1266
+
1267
+ pod_info = runpod_manager.one_click_deploy(
1268
+ config=config,
1269
+ progress_callback=log_callback
1270
+ )
1271
+
1272
+ current_pod_info = pod_info
1273
+ deployment_in_progress = False
1274
+
1275
+ final_log = "\n".join(log_output)
1276
+ status = f"✅ Pod created successfully!\n"
1277
+ status += f"ID: {pod_info.id}\n"
1278
+ status += f"SSH: {pod_info.ip}:{pod_info.ssh_port}\n"
1279
+ status += f"GUI: https://{pod_info.id}-7860.proxy.runpod.net"
1280
+
1281
+ return final_log, status
1282
+
1283
+ except Exception as e:
1284
+ deployment_in_progress = False
1285
+ error_log = "\n".join(log_output) + f"\n\n❌ Error: {e}"
1286
+ return error_log, f"❌ Deployment failed: {e}"
1287
+
1288
+
1289
+ def stop_runpod_pod(pod_id):
1290
+ """Stop a running pod"""
1291
+ global runpod_manager
1292
+
1293
+ if not runpod_manager:
1294
+ init_result = init_runpod_manager()
1295
+ if "❌" in init_result:
1296
+ return init_result
1297
+
1298
+ try:
1299
+ success = runpod_manager.stop_pod(pod_id)
1300
+ if success:
1301
+ return f"✅ Pod {pod_id} stopped successfully!"
1302
+ else:
1303
+ return f"❌ Failed to stop pod {pod_id}"
1304
+ except Exception as e:
1305
+ return f"❌ Error stopping pod: {e}"
1306
+
1307
+
1308
+ def terminate_runpod_pod(pod_id):
1309
+ """Terminate (delete) a pod"""
1310
+ global runpod_manager, current_pod_info
1311
+
1312
+ if not runpod_manager:
1313
+ init_result = init_runpod_manager()
1314
+ if "❌" in init_result:
1315
+ return init_result
1316
+
1317
+ try:
1318
+ success = runpod_manager.terminate_pod(pod_id)
1319
+ if success:
1320
+ if current_pod_info and current_pod_info.id == pod_id:
1321
+ current_pod_info = None
1322
+ return f"✅ Pod {pod_id} terminated successfully!"
1323
+ else:
1324
+ return f"❌ Failed to terminate pod {pod_id}"
1325
+ except Exception as e:
1326
+ return f"❌ Error terminating pod: {e}"
1327
+
1328
+
1329
+ def get_pod_connection_info(pod_id):
1330
+ """Get connection info for a pod"""
1331
+ global runpod_manager
1332
+
1333
+ if not runpod_manager:
1334
+ init_result = init_runpod_manager()
1335
+ if "❌" in init_result:
1336
+ return init_result
1337
+
1338
+ try:
1339
+ pod = runpod_manager.get_pod(pod_id)
1340
+ if not pod:
1341
+ return f"❌ Pod {pod_id} not found"
1342
+
1343
+ info = f"# Connection Info for {pod.name}\n\n"
1344
+ info += f"**Status:** {pod.status}\n\n"
1345
+
1346
+ if pod.status == "RUNNING":
1347
+ info += "## SSH Connection\n"
1348
+ info += f"```bash\n"
1349
+ info += f"ssh root@{pod.ip} -p {pod.ssh_port}\n"
1350
+ info += f"```\n\n"
1351
+
1352
+ info += "## GUI Access\n"
1353
+ info += f"Open in browser:\n"
1354
+ info += f"```\n"
1355
+ info += f"https://{pod.id}-7860.proxy.runpod.net\n"
1356
+ info += f"```\n\n"
1357
+
1358
+ info += "## Details\n"
1359
+ info += f"- GPU: {pod.gpu_count}x {pod.gpu_type}\n"
1360
+ info += f"- Cost: ${pod.cost_per_hr:.2f}/hour\n"
1361
+ else:
1362
+ info += f"⚠️ Pod is not running (Status: {pod.status})\n"
1363
+
1364
+ return info
1365
+
1366
+ except Exception as e:
1367
+ return f"❌ Error getting pod info: {e}"
1368
+
1369
+
1370
+ # Create Gradio interface with custom CSS for scrolling
1371
+ custom_css = """
1372
+ /* Make all containers properly scrollable */
1373
+ .overflow-y-auto {
1374
+ overflow-y: auto !important;
1375
+ max-height: 600px !important;
1376
+ }
1377
+
1378
+ /* Textbox scrolling */
1379
+ .textbox-container textarea {
1380
+ max-height: 400px !important;
1381
+ overflow-y: auto !important;
1382
+ }
1383
+
1384
+ /* Column scrolling for long content */
1385
+ .gr-column {
1386
+ overflow-y: auto !important;
1387
+ max-height: 800px !important;
1388
+ }
1389
+
1390
+ /* Markdown blocks in columns */
1391
+ .gr-column .gr-markdown {
1392
+ overflow-y: auto !important;
1393
+ max-height: 500px !important;
1394
+ }
1395
+
1396
+ /* Accordion-like sections */
1397
+ .gr-box {
1398
+ overflow-y: auto !important;
1399
+ max-height: 600px !important;
1400
+ }
1401
+
1402
+ /* Hide footer */
1403
+ footer {visibility: hidden}
1404
+ """
1405
+
1406
+ with gr.Blocks(title="Financial Advisor AI Trainer", theme=gr.themes.Soft(), css=custom_css) as demo:
1407
+
1408
+ gr.Markdown("""
1409
+ # 🏦 Financial Advisor AI Training Studio
1410
+
1411
+ Train your own expert financial advisor AI powered by Qwen 3 30B!
1412
+ No coding required - just configure, train, and test.
1413
+ """)
1414
+
1415
+ with gr.Tabs():
1416
+
1417
+ # Tab 0: Settings & API Keys
1418
+ with gr.Tab("⚙️ Settings"):
1419
+ gr.Markdown("""
1420
+ # API Key Management
1421
+
1422
+ Store your API keys securely. Keys are encrypted and never stored in plain text.
1423
+ **You only need to enter these once!**
1424
+ """)
1425
+
1426
+ with gr.Row():
1427
+ with gr.Column():
1428
+ gr.Markdown("### 🔑 API Keys")
1429
+
1430
+ gr.Markdown("#### HuggingFace Token (Required)")
1431
+ gr.Markdown("Get your token at: https://huggingface.co/settings/tokens")
1432
+ hf_token_input = gr.Textbox(
1433
+ label="HuggingFace Token",
1434
+ placeholder="hf_...",
1435
+ type="password",
1436
+ info="Required for downloading Qwen 3 model"
1437
+ )
1438
+
1439
+ gr.Markdown("---")
1440
+ gr.Markdown("#### Optional API Keys")
1441
+
1442
+ openai_key_input = gr.Textbox(
1443
+ label="OpenAI API Key (Optional)",
1444
+ placeholder="sk-...",
1445
+ type="password",
1446
+ info="For testing against GPT models"
1447
+ )
1448
+
1449
+ anthropic_key_input = gr.Textbox(
1450
+ label="Anthropic API Key (Optional)",
1451
+ placeholder="sk-ant-...",
1452
+ type="password",
1453
+ info="For testing against Claude models"
1454
+ )
1455
+
1456
+ wandb_key_input = gr.Textbox(
1457
+ label="Weights & Biases API Key (Optional)",
1458
+ placeholder="...",
1459
+ type="password",
1460
+ info="For advanced experiment tracking"
1461
+ )
1462
+
1463
+ runpod_key_input = gr.Textbox(
1464
+ label="RunPod API Key (Optional)",
1465
+ placeholder="...",
1466
+ type="password",
1467
+ info="For cloud GPU deployment"
1468
+ )
1469
+
1470
+ gr.Markdown("---")
1471
+ gr.Markdown("#### Custom API Keys (Advanced)")
1472
+ custom_keys_input = gr.Code(
1473
+ label="Custom Keys (JSON format)",
1474
+ language="json",
1475
+ value='{\n "MY_API_KEY": "value",\n "OTHER_KEY": "value"\n}',
1476
+ lines=5
1477
+ )
1478
+
1479
+ gr.Markdown("---")
1480
+
1481
+ with gr.Row():
1482
+ save_keys_btn = gr.Button("💾 Save All Keys", variant="primary", size="lg")
1483
+ load_keys_btn = gr.Button("🔄 Load Saved Keys", variant="secondary")
1484
+ clear_keys_btn = gr.Button("🗑️ Clear All Keys", variant="stop")
1485
+
1486
+ keys_status = gr.Markdown()
1487
+
1488
+ with gr.Column():
1489
+ gr.Markdown("### 📋 Security & Status")
1490
+
1491
+ gr.Markdown("""
1492
+ #### 🔒 Security Features
1493
+ - ✅ Keys are encrypted using Fernet encryption
1494
+ - ✅ Stored in `.secrets/` directory (auto-hidden)
1495
+ - ✅ Never logged or displayed in full
1496
+ - ✅ Loaded into environment variables automatically
1497
+ - ✅ Only you can decrypt with your machine
1498
+
1499
+ #### 📍 Storage Location
1500
+ Keys are saved in: `.secrets/config.enc`
1501
+
1502
+ ⚠️ **Backup Note**: If you reinstall or move the project,
1503
+ you'll need to re-enter your keys.
1504
+ """)
1505
+
1506
+ gr.Markdown("---")
1507
+ gr.Markdown("### 🔍 Current Keys Status")
1508
+
1509
+ keys_display = gr.Markdown("No keys loaded. Click 'Load Saved Keys' to check.")
1510
+
1511
+ gr.Markdown("---")
1512
+ gr.Markdown("""
1513
+ ### 💡 Tips
1514
+
1515
+ **HuggingFace Token:**
1516
+ 1. Go to https://huggingface.co/settings/tokens
1517
+ 2. Create a new token (read access is enough)
1518
+ 3. Copy and paste it above
1519
+ 4. Click "Save All Keys"
1520
+
1521
+ **Other Keys:**
1522
+ Only add if you plan to use those services.
1523
+ Training works with just HuggingFace token.
1524
+ """)
1525
+
1526
+ # Wire up settings
1527
+ save_keys_btn.click(
1528
+ fn=save_api_keys,
1529
+ inputs=[hf_token_input, openai_key_input, anthropic_key_input, wandb_key_input, runpod_key_input, custom_keys_input],
1530
+ outputs=keys_status
1531
+ )
1532
+
1533
+ load_keys_btn.click(
1534
+ fn=load_api_keys,
1535
+ outputs=[hf_token_input, openai_key_input, anthropic_key_input, wandb_key_input, runpod_key_input, custom_keys_input, keys_display]
1536
+ )
1537
+
1538
+ clear_keys_btn.click(
1539
+ fn=clear_api_keys,
1540
+ outputs=[keys_status, hf_token_input, openai_key_input, anthropic_key_input, wandb_key_input, runpod_key_input, custom_keys_input]
1541
+ )
1542
+
1543
+ # Load keys on startup
1544
+ demo.load(
1545
+ fn=load_api_keys,
1546
+ outputs=[hf_token_input, openai_key_input, anthropic_key_input, wandb_key_input, runpod_key_input, custom_keys_input, keys_display]
1547
+ )
1548
+
1549
+ # Tab 1: Data Management
1550
+ with gr.Tab("📚 Training Data"):
1551
+ gr.Markdown("""
1552
+ ### Manage Your Training Data
1553
+
1554
+ **Your training data is automatically used when you click "Start Training" in the Training tab.**
1555
+ """)
1556
+
1557
+ with gr.Row():
1558
+ with gr.Column():
1559
+ gr.Markdown("#### Current Training Data")
1560
+ data_preview = gr.Markdown()
1561
+ data_count = gr.Number(label="Total Q&A Pairs", interactive=False)
1562
+
1563
+ gr.Markdown("---")
1564
+
1565
+ # Training readiness status
1566
+ training_ready_status = gr.Markdown("Click 'Refresh Data' to check training readiness")
1567
+
1568
+ with gr.Row():
1569
+ load_data_btn = gr.Button("🔄 Refresh Data", variant="secondary")
1570
+ validate_data_btn = gr.Button("🔍 Validate Quality", variant="secondary")
1571
+ remove_dupes_btn = gr.Button("🧹 Remove Duplicates", variant="secondary")
1572
+
1573
+ gr.Markdown("---")
1574
+ gr.Markdown("#### Quality Report")
1575
+ validation_report = gr.Textbox(
1576
+ label="Data Quality Analysis",
1577
+ lines=10,
1578
+ max_lines=15,
1579
+ interactive=False,
1580
+ placeholder="Click 'Validate Quality' to analyze your training data...",
1581
+ show_copy_button=True
1582
+ )
1583
+
1584
+ gr.Markdown("---")
1585
+ gr.Markdown("#### Deduplication Status")
1586
+ dedup_status = gr.Markdown("Click 'Remove Duplicates' to clean your data")
1587
+
1588
+ gr.Markdown("---")
1589
+ gr.Markdown("#### View Full Dataset")
1590
+ data_json = gr.Code(label="Full Dataset (JSON)", language="json", lines=8, max_lines=12)
1591
+
1592
+ with gr.Column():
1593
+ gr.Markdown("#### Add New Training Example")
1594
+ new_question = gr.Textbox(
1595
+ label="Question",
1596
+ placeholder="What is the difference between a Roth IRA and Traditional IRA?",
1597
+ lines=3
1598
+ )
1599
+ new_context = gr.Textbox(
1600
+ label="Context (optional)",
1601
+ placeholder="Additional context for the question...",
1602
+ lines=2
1603
+ )
1604
+ new_answer = gr.Textbox(
1605
+ label="Expert Answer",
1606
+ placeholder="Provide a detailed, accurate financial answer...",
1607
+ lines=8
1608
+ )
1609
+ add_example_btn = gr.Button("➕ Add Example", variant="primary")
1610
+ add_status = gr.Markdown()
1611
+
1612
+ # Wire up data management
1613
+ load_data_btn.click(
1614
+ fn=load_training_data,
1615
+ outputs=[data_preview, data_count, data_json, training_ready_status]
1616
+ )
1617
+
1618
+ validate_data_btn.click(
1619
+ fn=validate_training_data,
1620
+ outputs=validation_report
1621
+ ).then(
1622
+ fn=load_training_data,
1623
+ outputs=[data_preview, data_count, data_json, training_ready_status]
1624
+ )
1625
+
1626
+ remove_dupes_btn.click(
1627
+ fn=remove_duplicates_from_data,
1628
+ outputs=[dedup_status, data_count, data_json, training_ready_status]
1629
+ ).then(
1630
+ fn=load_training_data,
1631
+ outputs=[data_preview, data_count, data_json, training_ready_status]
1632
+ )
1633
+
1634
+ add_example_btn.click(
1635
+ fn=add_training_example,
1636
+ inputs=[new_question, new_answer, new_context],
1637
+ outputs=[add_status, data_count]
1638
+ ).then(
1639
+ fn=load_training_data,
1640
+ outputs=[data_preview, data_count, data_json, training_ready_status]
1641
+ )
1642
+
1643
+ # Load data on startup
1644
+ demo.load(
1645
+ fn=load_training_data,
1646
+ outputs=[data_preview, data_count, data_json, training_ready_status]
1647
+ )
1648
+
1649
+ # Tab 2: Synthetic Data Generation
1650
+ with gr.Tab("🤖 Synthetic Data"):
1651
+ gr.Markdown("""
1652
+ # Generate Training Data Automatically
1653
+
1654
+ Use GPT-4 or Claude to automatically generate high-quality financial Q&A pairs!
1655
+ **No manual writing required - just configure and generate.**
1656
+ """)
1657
+
1658
+ with gr.Row():
1659
+ with gr.Column():
1660
+ gr.Markdown("#### 🔧 Generation Settings")
1661
+
1662
+ syn_provider = gr.Radio(
1663
+ choices=["openai", "anthropic"],
1664
+ value="openai",
1665
+ label="API Provider",
1666
+ info="Choose which LLM to use for generation"
1667
+ )
1668
+
1669
+ syn_num_examples = gr.Slider(
1670
+ minimum=5, maximum=100, value=20, step=5,
1671
+ label="Number of Examples to Generate",
1672
+ info="Start with 20, then increase"
1673
+ )
1674
+
1675
+ syn_difficulty = gr.Radio(
1676
+ choices=["beginner", "intermediate", "advanced", "mixed"],
1677
+ value="mixed",
1678
+ label="Difficulty Level",
1679
+ info="Mixed creates diverse questions"
1680
+ )
1681
+
1682
+ syn_use_scenarios = gr.Checkbox(
1683
+ label="Use Realistic Scenarios",
1684
+ value=False,
1685
+ info="Generate questions with specific user contexts (age, income, etc.)"
1686
+ )
1687
+
1688
+ gr.Markdown("---")
1689
+ gr.Markdown("#### 📋 Available Topics")
1690
+
1691
+ syn_topics_display = gr.Textbox(
1692
+ label="Default Topics (leave Custom Topics empty to use these)",
1693
+ value=get_available_topics(),
1694
+ lines=8,
1695
+ max_lines=12,
1696
+ interactive=False
1697
+ )
1698
+
1699
+ syn_custom_topics = gr.Textbox(
1700
+ label="Custom Topics (Optional)",
1701
+ placeholder="401k Planning, Crypto Trading, Home Buying (comma-separated)",
1702
+ lines=3,
1703
+ info="Leave empty to use all default topics"
1704
+ )
1705
+
1706
+ gr.Markdown("---")
1707
+
1708
+ syn_generate_btn = gr.Button("🚀 Generate Data", variant="primary", size="lg")
1709
+ syn_status = gr.Markdown()
1710
+
1711
+ with gr.Column():
1712
+ gr.Markdown("#### 📊 Generation Log")
1713
+ syn_log = gr.Textbox(
1714
+ label="Progress & Preview",
1715
+ lines=20,
1716
+ max_lines=25,
1717
+ interactive=False,
1718
+ show_copy_button=True
1719
+ )
1720
+
1721
+ gr.Markdown("---")
1722
+ gr.Markdown("#### 💡 Tips & Info")
1723
+ syn_tips = gr.Textbox(
1724
+ label="Important Information",
1725
+ value="""💡 Tips:
1726
+ • Make sure you've added your OpenAI or Anthropic API key in Settings tab
1727
+ • Generated data is automatically added to your training dataset
1728
+ • Each example costs ~$0.01-0.02 in API credits
1729
+ • Quality is very high - often better than manual examples
1730
+ • Start with 20 examples to test, then generate more
1731
+ • Mix difficulties for best results
1732
+ • Scenario mode creates more realistic, personalized questions
1733
+
1734
+ ⚠️ API Key Required:
1735
+ This feature requires an OpenAI or Anthropic API key.
1736
+ Set it in the Settings tab before generating.""",
1737
+ lines=8,
1738
+ max_lines=12,
1739
+ interactive=False
1740
+ )
1741
+
1742
+ # Wire up synthetic data generation
1743
+ syn_generate_btn.click(
1744
+ fn=generate_synthetic_data,
1745
+ inputs=[syn_provider, syn_num_examples, syn_difficulty, syn_use_scenarios, syn_custom_topics],
1746
+ outputs=[syn_log, syn_status]
1747
+ )
1748
+
1749
+ # Tab 3: HuggingFace Datasets
1750
+ with gr.Tab("📦 HuggingFace Datasets"):
1751
+ gr.Markdown("""
1752
+ # Load Datasets from HuggingFace
1753
+
1754
+ Import high-quality financial datasets directly from HuggingFace!
1755
+ **Includes transaction categorization, financial Q&A, and more.**
1756
+ """)
1757
+
1758
+ with gr.Row():
1759
+ with gr.Column():
1760
+ gr.Markdown("#### 📋 Known Datasets")
1761
+
1762
+ hf_datasets_list = gr.Textbox(
1763
+ label="Available Financial Datasets",
1764
+ value=list_hf_datasets(),
1765
+ lines=10,
1766
+ max_lines=15,
1767
+ interactive=False
1768
+ )
1769
+
1770
+ gr.Markdown("---")
1771
+ gr.Markdown("#### 🔧 Load Settings")
1772
+
1773
+ hf_dataset_name = gr.Radio(
1774
+ choices=["financial-alpaca", "fingpt-finred", "finance-qa-10k", "Custom Path"],
1775
+ value="financial-alpaca",
1776
+ label="Select Dataset",
1777
+ info="Choose from publicly accessible datasets or use custom path"
1778
+ )
1779
+
1780
+ hf_custom_path = gr.Textbox(
1781
+ label="Custom Dataset Path (if 'Custom Path' selected)",
1782
+ placeholder="username/dataset-name",
1783
+ info="Full HuggingFace dataset path"
1784
+ )
1785
+
1786
+ hf_split = gr.Radio(
1787
+ choices=["train", "test", "validation"],
1788
+ value="train",
1789
+ label="Dataset Split",
1790
+ info="Which split to load"
1791
+ )
1792
+
1793
+ hf_max_examples = gr.Number(
1794
+ label="Max Examples to Load (optional)",
1795
+ value=None,
1796
+ placeholder="Leave empty for all",
1797
+ info="Limit number of examples (helps with large datasets)"
1798
+ )
1799
+
1800
+ gr.Markdown("---")
1801
+
1802
+ with gr.Row():
1803
+ hf_preview_btn = gr.Button("👁️ Preview Dataset", variant="secondary")
1804
+ hf_load_btn = gr.Button("📥 Load Dataset", variant="primary", size="lg")
1805
+
1806
+ hf_status = gr.Markdown()
1807
+
1808
+ with gr.Column():
1809
+ gr.Markdown("#### 📊 Dataset Info & Logs")
1810
+
1811
+ hf_preview = gr.Textbox(
1812
+ label="Dataset Preview",
1813
+ lines=10,
1814
+ max_lines=15,
1815
+ interactive=False,
1816
+ placeholder="Click 'Preview Dataset' to see sample data...",
1817
+ show_copy_button=True
1818
+ )
1819
+
1820
+ gr.Markdown("---")
1821
+
1822
+ hf_log = gr.Textbox(
1823
+ label="Loading Log",
1824
+ lines=12,
1825
+ max_lines=18,
1826
+ interactive=False,
1827
+ placeholder="Loading progress will appear here...",
1828
+ show_copy_button=True
1829
+ )
1830
+
1831
+ gr.Markdown("---")
1832
+ gr.Markdown("#### 💡 Tips & Info")
1833
+ hf_tips = gr.Textbox(
1834
+ label="Important Information",
1835
+ value="""💡 Available Datasets:
1836
+
1837
+ • financial-alpaca (52K examples)
1838
+ Pre-built financial Q&A in Alpaca format - publicly accessible
1839
+
1840
+ • fingpt-finred
1841
+ Financial relation extraction dataset - publicly accessible
1842
+
1843
+ • finance-qa-10k
1844
+ Q&A from 10-K SEC filings - publicly accessible
1845
+
1846
+ 💡 Tips:
1847
+ • Preview datasets before loading to understand structure
1848
+ • Large datasets can be limited using Max Examples
1849
+ • All data is automatically quality-validated before adding
1850
+ • These datasets are PUBLIC and don't require special access
1851
+
1852
+ 🔑 Authentication:
1853
+ Your HuggingFace token is used automatically from Settings tab.
1854
+ Some private/gated datasets may require accepting terms on HuggingFace.
1855
+
1856
+ 📚 Finding More Datasets:
1857
+ Browse: https://huggingface.co/datasets
1858
+ Search: "finance", "financial", "investment", "trading\"""",
1859
+ lines=12,
1860
+ max_lines=18,
1861
+ interactive=False
1862
+ )
1863
+
1864
+ # Wire up HuggingFace dataset loading
1865
+ hf_preview_btn.click(
1866
+ fn=preview_hf_dataset,
1867
+ inputs=hf_custom_path,
1868
+ outputs=hf_preview
1869
+ )
1870
+
1871
+ hf_load_btn.click(
1872
+ fn=load_hf_dataset,
1873
+ inputs=[hf_custom_path, hf_dataset_name, hf_max_examples, hf_split],
1874
+ outputs=[hf_log, hf_status]
1875
+ )
1876
+
1877
+ # Tab 4: Training Configuration
1878
+ with gr.Tab("⚙️ Training"):
1879
+ gr.Markdown("### Select Model and Configure Training")
1880
+
1881
+ with gr.Row():
1882
+ with gr.Column():
1883
+ gr.Markdown("#### 💻 Training Mode")
1884
+
1885
+ training_mode = gr.Radio(
1886
+ choices=["Local GPU", "Cloud GPU (RunPod)"],
1887
+ value="Cloud GPU (RunPod)",
1888
+ label="Where to Train",
1889
+ info="Local requires NVIDIA GPU. Cloud uses RunPod (pay per minute)."
1890
+ )
1891
+
1892
+ cloud_cost_estimate = gr.Markdown("**Estimated Cost:** Select model to see pricing")
1893
+
1894
+ gr.Markdown("---")
1895
+ gr.Markdown("#### 🤖 Model Selection")
1896
+
1897
+ model_choices = model_registry.get_model_choices_for_gui()
1898
+ model_selector = gr.Dropdown(
1899
+ choices=model_choices,
1900
+ value=model_choices[0], # Default to first model (Qwen 3 30B)
1901
+ label="Select Model",
1902
+ info="Choose which model to train"
1903
+ )
1904
+
1905
+ model_info_display = gr.Markdown()
1906
+
1907
+ with gr.Row():
1908
+ check_ollama_btn = gr.Button("🔍 Check Ollama Status", variant="secondary", size="sm")
1909
+
1910
+ ollama_status_display = gr.Markdown()
1911
+
1912
+ gr.Markdown("---")
1913
+ gr.Markdown("#### Training Configuration")
1914
+
1915
+ lora_rank = gr.Slider(
1916
+ minimum=4, maximum=64, value=16, step=4,
1917
+ label="LoRA Rank (Higher = More capacity, more memory)",
1918
+ info="Recommended: 16 for 30B model"
1919
+ )
1920
+
1921
+ learning_rate = gr.Slider(
1922
+ minimum=1e-5, maximum=5e-4, value=1e-4, step=1e-5,
1923
+ label="Learning Rate",
1924
+ info="Recommended: 1e-4 for large models"
1925
+ )
1926
+
1927
+ num_epochs = gr.Slider(
1928
+ minimum=1, maximum=10, value=3, step=1,
1929
+ label="Number of Epochs",
1930
+ info="Start with 1 epoch to test"
1931
+ )
1932
+
1933
+ batch_size = gr.Slider(
1934
+ minimum=1, maximum=4, value=1, step=1,
1935
+ label="Batch Size",
1936
+ info="Keep at 1 for 30B model"
1937
+ )
1938
+
1939
+ grad_accum = gr.Slider(
1940
+ minimum=1, maximum=32, value=16, step=1,
1941
+ label="Gradient Accumulation Steps",
1942
+ info="Effective batch = batch_size × grad_accum"
1943
+ )
1944
+
1945
+ gr.Markdown("---")
1946
+
1947
+ start_train_btn = gr.Button("🚀 Start Training", variant="primary", size="lg")
1948
+ training_status_text = gr.Markdown()
1949
+
1950
+ with gr.Column():
1951
+ gr.Markdown("#### Training Progress & Logs")
1952
+ training_log = gr.Textbox(
1953
+ label="Training Log",
1954
+ lines=20,
1955
+ max_lines=25,
1956
+ interactive=False,
1957
+ show_copy_button=True
1958
+ )
1959
+
1960
+ gr.Markdown("""
1961
+ **💡 Tips:**
1962
+ - First training will download ~16GB model
1963
+ - Monitor with TensorBoard: `tensorboard --logdir models/financial_advisor/logs`
1964
+ - Training 30B model takes 30-60 min per epoch
1965
+ - GPU needs ~18GB VRAM minimum
1966
+ """)
1967
+
1968
+ # Wire up model selection
1969
+ model_selector.change(
1970
+ fn=get_model_info,
1971
+ inputs=model_selector,
1972
+ outputs=model_info_display
1973
+ )
1974
+
1975
+ check_ollama_btn.click(
1976
+ fn=check_ollama_status,
1977
+ outputs=ollama_status_display
1978
+ )
1979
+
1980
+ # Load default model info on startup
1981
+ demo.load(
1982
+ fn=get_model_info,
1983
+ inputs=model_selector,
1984
+ outputs=model_info_display
1985
+ )
1986
+
1987
+ # Wire up training
1988
+ start_train_btn.click(
1989
+ fn=start_training,
1990
+ inputs=[lora_rank, learning_rate, num_epochs, batch_size, grad_accum],
1991
+ outputs=[training_log, training_status_text]
1992
+ )
1993
+
1994
+ # Tab 3: Testing
1995
+ with gr.Tab("🧪 Test Model"):
1996
+ gr.Markdown("### Test Your Trained Financial Advisor")
1997
+
1998
+ with gr.Row():
1999
+ with gr.Column():
2000
+ load_model_btn = gr.Button("📥 Load Trained Model", variant="secondary")
2001
+ load_status = gr.Markdown()
2002
+
2003
+ gr.Markdown("---")
2004
+
2005
+ test_question = gr.Textbox(
2006
+ label="Ask Your Financial Advisor",
2007
+ placeholder="Should I pay off my student loans or invest in my 401k?",
2008
+ lines=4
2009
+ )
2010
+
2011
+ test_btn = gr.Button("💬 Get Advice", variant="primary", size="lg")
2012
+
2013
+ gr.Markdown("#### Example Questions:")
2014
+ gr.Markdown("""
2015
+ - What's the difference between a Roth IRA and Traditional IRA?
2016
+ - How much should I have in my emergency fund?
2017
+ - Should I invest in index funds or individual stocks?
2018
+ - What is dollar-cost averaging?
2019
+ - How do I start investing with only $100 per month?
2020
+ """)
2021
+
2022
+ with gr.Column():
2023
+ gr.Markdown("#### Financial Advisor Response")
2024
+ test_response = gr.Textbox(
2025
+ label="Response",
2026
+ lines=15,
2027
+ max_lines=20,
2028
+ interactive=False,
2029
+ show_copy_button=True
2030
+ )
2031
+
2032
+ # Wire up testing
2033
+ load_model_btn.click(
2034
+ fn=load_existing_model,
2035
+ outputs=load_status
2036
+ )
2037
+
2038
+ test_btn.click(
2039
+ fn=test_financial_advisor,
2040
+ inputs=test_question,
2041
+ outputs=test_response
2042
+ )
2043
+
2044
+ # Tab 6: Evaluation & Comparison
2045
+ with gr.Tab("📊 Evaluation"):
2046
+ gr.Markdown("### Model Evaluation & Comparison")
2047
+
2048
+ with gr.Row():
2049
+ with gr.Column():
2050
+ gr.Markdown("#### 📋 Evaluation History")
2051
+
2052
+ history_refresh_btn = gr.Button("🔄 Refresh History", variant="secondary")
2053
+
2054
+ eval_history_display = gr.Textbox(
2055
+ label="Recent Evaluations",
2056
+ lines=15,
2057
+ max_lines=20,
2058
+ interactive=False,
2059
+ show_copy_button=True,
2060
+ placeholder="Click 'Refresh History' to see evaluation history..."
2061
+ )
2062
+
2063
+ gr.Markdown("---")
2064
+ gr.Markdown("#### 🔍 Latest Evaluation Details")
2065
+
2066
+ latest_eval_btn = gr.Button("📄 View Latest Evaluation", variant="secondary")
2067
+
2068
+ latest_eval_display = gr.Textbox(
2069
+ label="Latest Evaluation Report",
2070
+ lines=15,
2071
+ max_lines=20,
2072
+ interactive=False,
2073
+ show_copy_button=True,
2074
+ placeholder="Click to view detailed evaluation report..."
2075
+ )
2076
+
2077
+ with gr.Column():
2078
+ gr.Markdown("#### 📈 Model Comparison")
2079
+
2080
+ num_models_compare = gr.Slider(
2081
+ minimum=2,
2082
+ maximum=10,
2083
+ value=3,
2084
+ step=1,
2085
+ label="Number of Models to Compare",
2086
+ info="Compare recent model evaluations"
2087
+ )
2088
+
2089
+ compare_btn = gr.Button("⚖️ Compare Models", variant="primary", size="lg")
2090
+
2091
+ comparison_display = gr.Textbox(
2092
+ label="Model Comparison Report",
2093
+ lines=20,
2094
+ max_lines=25,
2095
+ interactive=False,
2096
+ show_copy_button=True,
2097
+ placeholder="Click 'Compare Models' to see side-by-side comparison..."
2098
+ )
2099
+
2100
+ gr.Markdown("---")
2101
+ gr.Markdown("""
2102
+ **💡 Tips:**
2103
+ - Evaluations are run automatically after training
2104
+ - Compare metrics across different training runs
2105
+ - Use comparison to find the best model
2106
+ - Detailed reports saved in `models/financial_advisor/evaluation_results/`
2107
+ """)
2108
+
2109
+ # Wire up evaluation functions
2110
+ history_refresh_btn.click(
2111
+ fn=load_evaluation_history,
2112
+ outputs=eval_history_display
2113
+ )
2114
+
2115
+ latest_eval_btn.click(
2116
+ fn=view_latest_evaluation,
2117
+ outputs=latest_eval_display
2118
+ )
2119
+
2120
+ compare_btn.click(
2121
+ fn=compare_models,
2122
+ inputs=num_models_compare,
2123
+ outputs=comparison_display
2124
+ )
2125
+
2126
+ # Load history on startup
2127
+ demo.load(
2128
+ fn=load_evaluation_history,
2129
+ outputs=eval_history_display
2130
+ )
2131
+
2132
+ # Tab 7: RunPod Cloud Deployment
2133
+ with gr.Tab("☁️ RunPod"):
2134
+ gr.Markdown("""
2135
+ # Cloud GPU Deployment with RunPod
2136
+
2137
+ Deploy and train on powerful cloud GPUs without any manual setup!
2138
+ **One-click deployment to RunPod cloud GPUs - fully automated.**
2139
+
2140
+ ## 🎯 What This Does
2141
+
2142
+ This tab lets you:
2143
+ 1. **Create cloud GPU pods** - Get a powerful GPU in the cloud
2144
+ 2. **Auto-deploy your code** - Your AURA app runs on the cloud GPU
2145
+ 3. **Train remotely** - Use the same GUI, but on cloud hardware
2146
+
2147
+ ## ⚡ Quick Start
2148
+
2149
+ **Before you start**, make sure you have:
2150
+ - ✅ Added RunPod API key in **⚙️ Settings** tab
2151
+ - ✅ Prepared training data in **📚 Training Data** tab
2152
+
2153
+ Then:
2154
+ 1. Configure pod settings below (defaults are good)
2155
+ 2. Click "🚀 Create & Deploy Pod"
2156
+ 3. Wait ~5 minutes for setup
2157
+ 4. Access the cloud GUI via the provided URL
2158
+ 5. Train using the cloud GPU!
2159
+ """)
2160
+
2161
+ with gr.Row():
2162
+ with gr.Column():
2163
+ gr.Markdown("#### 🔧 Pod Configuration")
2164
+
2165
+ runpod_pod_name = gr.Textbox(
2166
+ label="Pod Name",
2167
+ value="aura-training-pod",
2168
+ placeholder="my-training-pod",
2169
+ info="Name for your RunPod instance"
2170
+ )
2171
+
2172
+ runpod_gpu_type = gr.Dropdown(
2173
+ choices=[
2174
+ "NVIDIA GeForce RTX 4090",
2175
+ "NVIDIA GeForce RTX 3090",
2176
+ "NVIDIA A100 40GB PCIe",
2177
+ "NVIDIA A100 80GB PCIe",
2178
+ "NVIDIA H100 80GB HBM3",
2179
+ ],
2180
+ value="NVIDIA GeForce RTX 4090",
2181
+ label="GPU Type",
2182
+ info="RTX 4090 recommended for best value"
2183
+ )
2184
+
2185
+ runpod_storage = gr.Slider(
2186
+ minimum=20,
2187
+ maximum=200,
2188
+ value=50,
2189
+ step=10,
2190
+ label="Storage (GB)",
2191
+ info="Disk space for models and data"
2192
+ )
2193
+
2194
+ runpod_sync_data = gr.Checkbox(
2195
+ label="Sync Training Data",
2196
+ value=True,
2197
+ info="Upload your local training data to the pod"
2198
+ )
2199
+
2200
+ gr.Markdown("---")
2201
+
2202
+ with gr.Row():
2203
+ runpod_create_btn = gr.Button("🚀 Create & Deploy Pod", variant="primary", size="lg")
2204
+ runpod_refresh_btn = gr.Button("🔄 Refresh Pods", variant="secondary")
2205
+
2206
+ runpod_status = gr.Markdown()
2207
+
2208
+ gr.Markdown("---")
2209
+ gr.Markdown("#### 📋 Your Pods")
2210
+
2211
+ runpod_pods_list = gr.Textbox(
2212
+ label="Active Pods",
2213
+ lines=10,
2214
+ max_lines=15,
2215
+ interactive=False,
2216
+ placeholder="Click 'Refresh Pods' to see your RunPod instances...",
2217
+ show_copy_button=True
2218
+ )
2219
+
2220
+ gr.Markdown("---")
2221
+ gr.Markdown("#### 🔧 Pod Management")
2222
+
2223
+ runpod_pod_id = gr.Textbox(
2224
+ label="Pod ID",
2225
+ placeholder="Enter Pod ID for management operations",
2226
+ info="Get from 'Your Pods' list above"
2227
+ )
2228
+
2229
+ with gr.Row():
2230
+ runpod_stop_btn = gr.Button("⏸️ Stop Pod", variant="secondary", size="sm")
2231
+ runpod_terminate_btn = gr.Button("🗑️ Terminate Pod", variant="stop", size="sm")
2232
+
2233
+ runpod_mgmt_status = gr.Markdown()
2234
+
2235
+ with gr.Column():
2236
+ gr.Markdown("#### 📊 Deployment Log")
2237
+
2238
+ runpod_log = gr.Textbox(
2239
+ label="Progress & Status",
2240
+ lines=15,
2241
+ max_lines=20,
2242
+ interactive=False,
2243
+ show_copy_button=True,
2244
+ placeholder="Deployment progress will appear here..."
2245
+ )
2246
+
2247
+ gr.Markdown("---")
2248
+ gr.Markdown("#### 🔗 Connection Info")
2249
+
2250
+ runpod_connection_info = gr.Markdown("Select a pod and click 'Get Connection Info'")
2251
+
2252
+ runpod_connect_btn = gr.Button("🔗 Get Connection Info", variant="secondary")
2253
+
2254
+ gr.Markdown("---")
2255
+ gr.Markdown("""
2256
+ **💰 Cost Estimates:**
2257
+ - RTX 4090: ~$0.69/hour (Best Value)
2258
+ - RTX 3090: ~$0.44/hour (Budget)
2259
+ - A100 40GB: ~$1.39/hour (Production)
2260
+
2261
+ **📝 What Gets Deployed:**
2262
+ - ✅ All your code files
2263
+ - ✅ Your training data (if "Sync Data" checked)
2264
+ - ✅ Python dependencies (auto-installed)
2265
+ - ✅ Same GUI you're using now
2266
+
2267
+ **After Deployment:**
2268
+ - Access GUI in browser (URL provided below)
2269
+ - Train on the cloud GPU (same interface)
2270
+ - Stop pod when done to save money
2271
+ - Your trained models stay on the pod
2272
+
2273
+ **🔑 Get RunPod API Key:**
2274
+ 1. Go to https://www.runpod.io/console/user/settings
2275
+ 2. Click "API Keys" → "Create API Key"
2276
+ 3. Copy the key
2277
+ 4. Add to **⚙️ Settings** tab above
2278
+ 5. Come back here and create a pod!
2279
+ """)
2280
+
2281
+ # Wire up RunPod functions
2282
+ runpod_create_btn.click(
2283
+ fn=create_runpod_pod,
2284
+ inputs=[runpod_pod_name, runpod_gpu_type, runpod_storage, runpod_sync_data],
2285
+ outputs=[runpod_log, runpod_status]
2286
+ )
2287
+
2288
+ runpod_refresh_btn.click(
2289
+ fn=list_runpod_pods,
2290
+ outputs=runpod_pods_list
2291
+ )
2292
+
2293
+ runpod_stop_btn.click(
2294
+ fn=stop_runpod_pod,
2295
+ inputs=runpod_pod_id,
2296
+ outputs=runpod_mgmt_status
2297
+ )
2298
+
2299
+ runpod_terminate_btn.click(
2300
+ fn=terminate_runpod_pod,
2301
+ inputs=runpod_pod_id,
2302
+ outputs=runpod_mgmt_status
2303
+ )
2304
+
2305
+ runpod_connect_btn.click(
2306
+ fn=get_pod_connection_info,
2307
+ inputs=runpod_pod_id,
2308
+ outputs=runpod_connection_info
2309
+ )
2310
+
2311
+ # Tab 4: Help
2312
+ with gr.Tab("❓ Help"):
2313
+ gr.Markdown("""
2314
+ # Getting Started Guide
2315
+
2316
+ ## Step 1: Prepare Training Data
2317
+ 1. Go to **Training Data** tab
2318
+ 2. Review the sample financial Q&A pairs
2319
+ 3. Add your own examples using the form
2320
+ 4. Aim for at least 50-100 high-quality examples
2321
+
2322
+ ## Step 2: Configure Training
2323
+ 1. Go to **Training** tab
2324
+ 2. Adjust settings (defaults are good to start):
2325
+ - LoRA Rank: 16 (higher = more capacity)
2326
+ - Learning Rate: 1e-4 (standard for large models)
2327
+ - Epochs: 1-3 (start with 1 to test)
2328
+ 3. Click **Start Training**
2329
+ 4. Wait 30-60 minutes per epoch
2330
+
2331
+ ## Step 3: Test Your Model
2332
+ 1. Go to **Test Model** tab
2333
+ 2. Click **Load Trained Model**
2334
+ 3. Ask financial questions
2335
+ 4. Get expert advice!
2336
+
2337
+ ## Requirements
2338
+ - **GPU**: NVIDIA GPU with 18GB+ VRAM (RTX 3090, 4090, A100, etc.)
2339
+ - **RAM**: 32GB+ system RAM recommended
2340
+ - **Storage**: 30GB free space for model
2341
+ - **Internet**: Fast connection for first-time model download
2342
+ - **HuggingFace Token**: Required for model access (set in Settings tab)
2343
+
2344
+ ## Troubleshooting
2345
+
2346
+ ### Out of Memory (OOM)
2347
+ - Reduce batch_size to 1
2348
+ - Increase gradient_accumulation_steps
2349
+ - Close other applications
2350
+
2351
+ ### Slow Training
2352
+ - This is normal for 30B model
2353
+ - Each epoch takes 30-60 minutes
2354
+ - Be patient!
2355
+
2356
+ ### Import Errors
2357
+ ```bash
2358
+ pip install torch transformers peft accelerate bitsandbytes gradio cryptography
2359
+ ```
2360
+
2361
+ ### HuggingFace Authentication Error
2362
+ - Go to **Settings** tab
2363
+ - Add your HuggingFace token
2364
+ - Click **Save All Keys**
2365
+ - Get token at: https://huggingface.co/settings/tokens
2366
+
2367
+ ### Model Not Learning
2368
+ - Check learning rate (try 1e-4 to 5e-4)
2369
+ - Verify training data quality
2370
+ - Train for more epochs
2371
+
2372
+ ## Advanced Tips
2373
+ 1. **More Data = Better Results**: Aim for 500+ examples
2374
+ 2. **Diverse Topics**: Cover various financial areas
2375
+ 3. **Quality > Quantity**: Accurate answers matter
2376
+ 4. **Monitor TensorBoard**: Track loss curves
2377
+ 5. **Test Regularly**: Evaluate after each epoch
2378
+
2379
+ ## Support
2380
+ - Check logs in Training tab
2381
+ - Review error messages carefully
2382
+ - Verify GPU has enough memory
2383
+ - Ensure all dependencies installed
2384
+ """)
2385
+
2386
+ gr.Markdown("""
2387
+ ---
2388
+ 💡 **Pro Tip**: Start with the sample data and 1 epoch to test everything works, then add more data and train for 3+ epochs.
2389
+ """)
2390
+
2391
+
2392
+ if __name__ == "__main__":
2393
+ import argparse
2394
+ import os
2395
+
2396
+ # Parse command-line arguments for cloud deployment compatibility
2397
+ parser = argparse.ArgumentParser(description="Launch AURA AI Training Studio")
2398
+ parser.add_argument("--server_name", type=str, default="0.0.0.0", help="Server host")
2399
+ parser.add_argument("--server_port", type=int, default=int(os.getenv("PORT", 7860)), help="Server port")
2400
+ parser.add_argument("--share", action="store_true", help="Create public share link")
2401
+ args = parser.parse_args()
2402
+
2403
+ print("=" * 70)
2404
+ print("Financial Advisor AI Training Studio")
2405
+ print("=" * 70)
2406
+ print("\nStarting GUI server...")
2407
+ print(f"Server: http://{args.server_name}:{args.server_port}")
2408
+ if args.share:
2409
+ print("Share link will be generated...")
2410
+ print("\nPress Ctrl+C to stop the server")
2411
+ print("=" * 70)
2412
+
2413
+ demo.launch(
2414
+ server_name=args.server_name,
2415
+ server_port=args.server_port,
2416
+ share=args.share,
2417
+ show_error=True
2418
+ )
fine_tuning/__init__.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Fine-Tuning Module
3
+
4
+ This module provides PEFT (Parameter-Efficient Fine-Tuning) and LoRA
5
+ (Low-Rank Adaptation) training capabilities.
6
+ """
7
+
8
+ from .lora_trainer import LoRATrainer, LoRAConfig
9
+ from .peft_trainer import PEFTTrainer
10
+ from .training_loop import TrainingLoop, TrainingConfig
11
+
12
+ __all__ = ['LoRATrainer', 'LoRAConfig', 'PEFTTrainer', 'TrainingLoop', 'TrainingConfig']
fine_tuning/lora_trainer.py ADDED
@@ -0,0 +1,487 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ LoRA Trainer Module
3
+
4
+ Implements Low-Rank Adaptation (LoRA) fine-tuning using HuggingFace PEFT library.
5
+ Supports 4-bit/8-bit quantization for efficient training on consumer hardware.
6
+ """
7
+
8
+ import os
9
+ import json
10
+ from pathlib import Path
11
+ from dataclasses import dataclass, field
12
+ from typing import Optional, List, Dict, Any
13
+ import torch
14
+ from transformers import (
15
+ AutoModelForCausalLM,
16
+ AutoTokenizer,
17
+ TrainingArguments,
18
+ Trainer,
19
+ DataCollatorForLanguageModeling
20
+ )
21
+ from peft import (
22
+ LoraConfig,
23
+ get_peft_model,
24
+ prepare_model_for_kbit_training,
25
+ PeftModel
26
+ )
27
+ from datasets import Dataset
28
+
29
+
30
+ @dataclass
31
+ class LoRAConfig:
32
+ """LoRA configuration parameters."""
33
+ r: int = 8 # LoRA rank
34
+ lora_alpha: int = 16 # LoRA alpha (scaling factor)
35
+ target_modules: List[str] = field(default_factory=lambda: ["q_proj", "v_proj", "k_proj", "o_proj"])
36
+ lora_dropout: float = 0.05
37
+ bias: str = "none"
38
+ task_type: str = "CAUSAL_LM"
39
+
40
+
41
+ class LoRATrainer:
42
+ """
43
+ LoRA Trainer for parameter-efficient fine-tuning of large language models.
44
+
45
+ Features:
46
+ - 4-bit/8-bit quantization support
47
+ - Automatic dataset tokenization with chat templates
48
+ - HuggingFace Trainer integration
49
+ - Checkpoint management
50
+ - Adapter merging for deployment
51
+
52
+ Example:
53
+ >>> config = LoRAConfig(r=8, lora_alpha=16)
54
+ >>> trainer = LoRATrainer("Qwen/Qwen2.5-7B-Instruct", config)
55
+ >>> trainer.load_model(use_4bit=True)
56
+ >>> trainer.prepare_dataset(training_data)
57
+ >>> trainer.train(num_epochs=3)
58
+ >>> trainer.save_model("./output")
59
+ """
60
+
61
+ def __init__(
62
+ self,
63
+ model_name: str,
64
+ lora_config: LoRAConfig,
65
+ output_dir: str = "./models/output"
66
+ ):
67
+ """
68
+ Initialize LoRA Trainer.
69
+
70
+ Args:
71
+ model_name: HuggingFace model path or name
72
+ lora_config: LoRA configuration
73
+ output_dir: Directory for saving checkpoints and final model
74
+ """
75
+ self.model_name = model_name
76
+ self.lora_config = lora_config
77
+ self.output_dir = Path(output_dir)
78
+ self.output_dir.mkdir(parents=True, exist_ok=True)
79
+
80
+ self.model = None
81
+ self.tokenizer = None
82
+ self.train_dataset = None
83
+ self.eval_dataset = None
84
+ self.trainer = None
85
+
86
+ def load_model(
87
+ self,
88
+ use_4bit: bool = True,
89
+ use_8bit: bool = False,
90
+ device_map: str = "auto",
91
+ max_memory: Optional[Dict] = None
92
+ ) -> None:
93
+ """
94
+ Load model with LoRA adapters and optional quantization.
95
+
96
+ Args:
97
+ use_4bit: Use 4-bit quantization (bitsandbytes)
98
+ use_8bit: Use 8-bit quantization (alternative to 4-bit)
99
+ device_map: Device mapping strategy
100
+ max_memory: Maximum memory allocation per device
101
+ """
102
+ print(f"Loading model: {self.model_name}")
103
+
104
+ # Load tokenizer
105
+ self.tokenizer = AutoTokenizer.from_pretrained(
106
+ self.model_name,
107
+ trust_remote_code=True,
108
+ padding_side="right"
109
+ )
110
+
111
+ # Set pad token if not present
112
+ if self.tokenizer.pad_token is None:
113
+ self.tokenizer.pad_token = self.tokenizer.eos_token
114
+
115
+ # Quantization config
116
+ quantization_config = None
117
+ if use_4bit:
118
+ from transformers import BitsAndBytesConfig
119
+ quantization_config = BitsAndBytesConfig(
120
+ load_in_4bit=True,
121
+ bnb_4bit_compute_dtype=torch.float16,
122
+ bnb_4bit_use_double_quant=True,
123
+ bnb_4bit_quant_type="nf4"
124
+ )
125
+ elif use_8bit:
126
+ from transformers import BitsAndBytesConfig
127
+ quantization_config = BitsAndBytesConfig(load_in_8bit=True)
128
+
129
+ # Load base model
130
+ self.model = AutoModelForCausalLM.from_pretrained(
131
+ self.model_name,
132
+ quantization_config=quantization_config,
133
+ device_map=device_map,
134
+ max_memory=max_memory,
135
+ trust_remote_code=True,
136
+ torch_dtype=torch.float16 if not (use_4bit or use_8bit) else None
137
+ )
138
+
139
+ # Prepare for k-bit training if quantized
140
+ if use_4bit or use_8bit:
141
+ self.model = prepare_model_for_kbit_training(self.model)
142
+
143
+ # Configure LoRA
144
+ peft_config = LoraConfig(
145
+ r=self.lora_config.r,
146
+ lora_alpha=self.lora_config.lora_alpha,
147
+ target_modules=self.lora_config.target_modules,
148
+ lora_dropout=self.lora_config.lora_dropout,
149
+ bias=self.lora_config.bias,
150
+ task_type=self.lora_config.task_type
151
+ )
152
+
153
+ # Apply LoRA adapters
154
+ self.model = get_peft_model(self.model, peft_config)
155
+
156
+ # Print trainable parameters
157
+ self.model.print_trainable_parameters()
158
+
159
+ print(f"✅ Model loaded with LoRA (rank={self.lora_config.r})")
160
+
161
+ def prepare_dataset(
162
+ self,
163
+ data: List[Dict],
164
+ validation_split: float = 0.1,
165
+ max_length: int = 2048,
166
+ test_data: Optional[List[Dict]] = None
167
+ ) -> None:
168
+ """
169
+ Tokenize and prepare dataset for training.
170
+
171
+ Args:
172
+ data: Training data in format [{"instruction": "...", "input": "...", "output": "..."}]
173
+ validation_split: Fraction of data to use for validation
174
+ max_length: Maximum sequence length
175
+ test_data: Optional separate test dataset
176
+ """
177
+ print(f"Preparing dataset: {len(data)} examples")
178
+
179
+ def format_prompt(example):
180
+ """Format example using chat template."""
181
+ # Build conversation
182
+ messages = []
183
+
184
+ # System message (optional, can be customized)
185
+ messages.append({
186
+ "role": "system",
187
+ "content": "You are a helpful AI assistant."
188
+ })
189
+
190
+ # User message
191
+ user_content = example.get("instruction", "")
192
+ if example.get("input"):
193
+ user_content += f"\n\n{example['input']}"
194
+ messages.append({
195
+ "role": "user",
196
+ "content": user_content
197
+ })
198
+
199
+ # Assistant response
200
+ messages.append({
201
+ "role": "assistant",
202
+ "content": example.get("output", "")
203
+ })
204
+
205
+ # Apply chat template
206
+ try:
207
+ formatted = self.tokenizer.apply_chat_template(
208
+ messages,
209
+ tokenize=False,
210
+ add_generation_prompt=False
211
+ )
212
+ except Exception:
213
+ # Fallback if chat template not available
214
+ formatted = f"{user_content}\n\n{example.get('output', '')}"
215
+
216
+ return {"text": formatted}
217
+
218
+ # Format all examples
219
+ formatted_data = [format_prompt(ex) for ex in data]
220
+
221
+ # Split train/val
222
+ if test_data is None:
223
+ split_idx = int(len(formatted_data) * (1 - validation_split))
224
+ train_data = formatted_data[:split_idx]
225
+ val_data = formatted_data[split_idx:]
226
+ else:
227
+ train_data = formatted_data
228
+ val_data = [format_prompt(ex) for ex in test_data]
229
+
230
+ # Create datasets
231
+ self.train_dataset = Dataset.from_list(train_data)
232
+ self.eval_dataset = Dataset.from_list(val_data) if val_data else None
233
+
234
+ # Tokenization function
235
+ def tokenize_function(examples):
236
+ tokenized = self.tokenizer(
237
+ examples["text"],
238
+ truncation=True,
239
+ max_length=max_length,
240
+ padding="max_length",
241
+ return_tensors=None
242
+ )
243
+ tokenized["labels"] = tokenized["input_ids"].copy()
244
+ return tokenized
245
+
246
+ # Tokenize
247
+ self.train_dataset = self.train_dataset.map(
248
+ tokenize_function,
249
+ batched=True,
250
+ remove_columns=self.train_dataset.column_names
251
+ )
252
+
253
+ if self.eval_dataset:
254
+ self.eval_dataset = self.eval_dataset.map(
255
+ tokenize_function,
256
+ batched=True,
257
+ remove_columns=self.eval_dataset.column_names
258
+ )
259
+
260
+ print(f"✅ Dataset prepared: {len(self.train_dataset)} train, {len(self.eval_dataset) if self.eval_dataset else 0} val")
261
+
262
+ def train(
263
+ self,
264
+ num_epochs: int = 3,
265
+ learning_rate: float = 2e-4,
266
+ per_device_train_batch_size: int = 4,
267
+ per_device_eval_batch_size: int = 4,
268
+ gradient_accumulation_steps: int = 4,
269
+ warmup_steps: int = 100,
270
+ logging_steps: int = 10,
271
+ save_steps: int = 500,
272
+ eval_steps: int = 500,
273
+ fp16: bool = True,
274
+ optim: str = "paged_adamw_8bit"
275
+ ) -> None:
276
+ """
277
+ Train the model with LoRA.
278
+
279
+ Args:
280
+ num_epochs: Number of training epochs
281
+ learning_rate: Learning rate
282
+ per_device_train_batch_size: Batch size per device for training
283
+ per_device_eval_batch_size: Batch size per device for evaluation
284
+ gradient_accumulation_steps: Gradient accumulation steps
285
+ warmup_steps: Learning rate warmup steps
286
+ logging_steps: Log every N steps
287
+ save_steps: Save checkpoint every N steps
288
+ eval_steps: Evaluate every N steps
289
+ fp16: Use mixed precision training
290
+ optim: Optimizer type
291
+ """
292
+ if self.model is None:
293
+ raise ValueError("Model not loaded. Call load_model() first.")
294
+ if self.train_dataset is None:
295
+ raise ValueError("Dataset not prepared. Call prepare_dataset() first.")
296
+
297
+ print(f"Starting training: {num_epochs} epochs")
298
+
299
+ # Training arguments
300
+ training_args = TrainingArguments(
301
+ output_dir=str(self.output_dir),
302
+ num_train_epochs=num_epochs,
303
+ per_device_train_batch_size=per_device_train_batch_size,
304
+ per_device_eval_batch_size=per_device_eval_batch_size,
305
+ gradient_accumulation_steps=gradient_accumulation_steps,
306
+ learning_rate=learning_rate,
307
+ warmup_steps=warmup_steps,
308
+ logging_steps=logging_steps,
309
+ save_steps=save_steps,
310
+ eval_steps=eval_steps if self.eval_dataset else None,
311
+ evaluation_strategy="steps" if self.eval_dataset else "no",
312
+ save_strategy="steps",
313
+ fp16=fp16,
314
+ optim=optim,
315
+ load_best_model_at_end=True if self.eval_dataset else False,
316
+ save_total_limit=3,
317
+ report_to=[] # Disable wandb/tensorboard by default
318
+ )
319
+
320
+ # Data collator
321
+ data_collator = DataCollatorForLanguageModeling(
322
+ tokenizer=self.tokenizer,
323
+ mlm=False
324
+ )
325
+
326
+ # Initialize trainer
327
+ self.trainer = Trainer(
328
+ model=self.model,
329
+ args=training_args,
330
+ train_dataset=self.train_dataset,
331
+ eval_dataset=self.eval_dataset,
332
+ data_collator=data_collator
333
+ )
334
+
335
+ # Train
336
+ self.trainer.train()
337
+
338
+ print("✅ Training complete!")
339
+
340
+ def save_model(self, save_path: Optional[str] = None) -> None:
341
+ """
342
+ Save LoRA adapter weights.
343
+
344
+ Args:
345
+ save_path: Path to save adapters (uses output_dir if None)
346
+ """
347
+ if save_path is None:
348
+ save_path = str(self.output_dir / "final_model")
349
+ else:
350
+ save_path = str(Path(save_path))
351
+
352
+ Path(save_path).mkdir(parents=True, exist_ok=True)
353
+
354
+ # Save adapter
355
+ self.model.save_pretrained(save_path)
356
+ self.tokenizer.save_pretrained(save_path)
357
+
358
+ # Save config
359
+ config_path = Path(save_path) / "lora_config.json"
360
+ with open(config_path, 'w') as f:
361
+ json.dump({
362
+ "r": self.lora_config.r,
363
+ "lora_alpha": self.lora_config.lora_alpha,
364
+ "target_modules": self.lora_config.target_modules,
365
+ "lora_dropout": self.lora_config.lora_dropout
366
+ }, f, indent=2)
367
+
368
+ print(f"✅ Model saved to: {save_path}")
369
+
370
+ def load_adapter(self, adapter_path: str) -> None:
371
+ """
372
+ Load pre-trained LoRA adapter.
373
+
374
+ Args:
375
+ adapter_path: Path to adapter weights
376
+ """
377
+ if self.model is None:
378
+ raise ValueError("Base model not loaded. Call load_model() first.")
379
+
380
+ print(f"Loading adapter from: {adapter_path}")
381
+
382
+ self.model = PeftModel.from_pretrained(
383
+ self.model,
384
+ adapter_path,
385
+ is_trainable=True
386
+ )
387
+
388
+ print("✅ Adapter loaded")
389
+
390
+ def merge_and_save(self, save_path: str) -> None:
391
+ """
392
+ Merge LoRA weights with base model and save full model.
393
+
394
+ Args:
395
+ save_path: Path to save merged model
396
+ """
397
+ print("Merging LoRA weights with base model...")
398
+
399
+ # Merge
400
+ merged_model = self.model.merge_and_unload()
401
+
402
+ # Save
403
+ Path(save_path).mkdir(parents=True, exist_ok=True)
404
+ merged_model.save_pretrained(save_path)
405
+ self.tokenizer.save_pretrained(save_path)
406
+
407
+ print(f"✅ Merged model saved to: {save_path}")
408
+
409
+ def evaluate_on_test_set(
410
+ self,
411
+ test_data: List[Dict],
412
+ max_samples: int = 50,
413
+ max_new_tokens: int = 256
414
+ ) -> Dict[str, Any]:
415
+ """
416
+ Evaluate model on test set.
417
+
418
+ Args:
419
+ test_data: Test examples
420
+ max_samples: Maximum number of samples to evaluate
421
+ max_new_tokens: Maximum tokens to generate
422
+
423
+ Returns:
424
+ Evaluation results dictionary
425
+ """
426
+ import time
427
+
428
+ print(f"Evaluating on {min(len(test_data), max_samples)} test examples...")
429
+
430
+ results = {
431
+ "num_examples": min(len(test_data), max_samples),
432
+ "responses": [],
433
+ "avg_response_length": 0,
434
+ "total_time": 0,
435
+ "throughput": 0
436
+ }
437
+
438
+ self.model.eval()
439
+ start_time = time.time()
440
+
441
+ for i, example in enumerate(test_data[:max_samples]):
442
+ # Format prompt
443
+ user_content = example.get("instruction", "")
444
+ if example.get("input"):
445
+ user_content += f"\n\n{example['input']}"
446
+
447
+ messages = [{"role": "user", "content": user_content}]
448
+
449
+ try:
450
+ prompt = self.tokenizer.apply_chat_template(
451
+ messages,
452
+ tokenize=False,
453
+ add_generation_prompt=True
454
+ )
455
+ except Exception:
456
+ prompt = user_content
457
+
458
+ # Tokenize
459
+ inputs = self.tokenizer(prompt, return_tensors="pt").to(self.model.device)
460
+
461
+ # Generate
462
+ with torch.no_grad():
463
+ outputs = self.model.generate(
464
+ **inputs,
465
+ max_new_tokens=max_new_tokens,
466
+ temperature=0.7,
467
+ do_sample=True,
468
+ top_p=0.9
469
+ )
470
+
471
+ # Decode
472
+ response = self.tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=True)
473
+
474
+ results["responses"].append({
475
+ "input": user_content,
476
+ "expected": example.get("output", ""),
477
+ "generated": response
478
+ })
479
+
480
+ # Calculate metrics
481
+ results["total_time"] = time.time() - start_time
482
+ results["avg_response_length"] = sum(len(r["generated"]) for r in results["responses"]) / len(results["responses"])
483
+ results["throughput"] = len(results["responses"]) / results["total_time"]
484
+
485
+ print(f"✅ Evaluation complete: {results['throughput']:.2f} examples/sec")
486
+
487
+ return results
fine_tuning/peft_trainer.py ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ PEFT Trainer Module
3
+
4
+ General Parameter-Efficient Fine-Tuning trainer supporting multiple PEFT methods.
5
+ """
6
+
7
+ from typing import Optional, List, Dict, Any
8
+ from pathlib import Path
9
+ import torch
10
+ from transformers import (
11
+ AutoModelForCausalLM,
12
+ AutoTokenizer,
13
+ TrainingArguments,
14
+ Trainer
15
+ )
16
+ from peft import (
17
+ get_peft_model,
18
+ PeftConfig,
19
+ PeftModel,
20
+ prepare_model_for_kbit_training
21
+ )
22
+
23
+
24
+ class PEFTTrainer:
25
+ """
26
+ General PEFT Trainer supporting multiple parameter-efficient fine-tuning methods.
27
+
28
+ Supports:
29
+ - LoRA (Low-Rank Adaptation)
30
+ - Prefix Tuning
31
+ - P-Tuning
32
+ - Prompt Tuning
33
+ - IA3 (Infused Adapter by Inhibiting and Amplifying Inner Activations)
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ model_name: str,
39
+ peft_config: PeftConfig,
40
+ output_dir: str = "./models/peft_output"
41
+ ):
42
+ """
43
+ Initialize PEFT Trainer.
44
+
45
+ Args:
46
+ model_name: HuggingFace model path or name
47
+ peft_config: PEFT configuration (LoraConfig, PrefixTuningConfig, etc.)
48
+ output_dir: Directory for saving checkpoints and final model
49
+ """
50
+ self.model_name = model_name
51
+ self.peft_config = peft_config
52
+ self.output_dir = Path(output_dir)
53
+ self.output_dir.mkdir(parents=True, exist_ok=True)
54
+
55
+ self.model = None
56
+ self.tokenizer = None
57
+ self.trainer = None
58
+
59
+ def load_model(
60
+ self,
61
+ use_4bit: bool = False,
62
+ use_8bit: bool = False,
63
+ device_map: str = "auto"
64
+ ) -> None:
65
+ """
66
+ Load model with PEFT configuration.
67
+
68
+ Args:
69
+ use_4bit: Use 4-bit quantization
70
+ use_8bit: Use 8-bit quantization
71
+ device_map: Device mapping strategy
72
+ """
73
+ print(f"Loading model: {self.model_name}")
74
+
75
+ # Load tokenizer
76
+ self.tokenizer = AutoTokenizer.from_pretrained(
77
+ self.model_name,
78
+ trust_remote_code=True
79
+ )
80
+
81
+ if self.tokenizer.pad_token is None:
82
+ self.tokenizer.pad_token = self.tokenizer.eos_token
83
+
84
+ # Quantization config
85
+ quantization_config = None
86
+ if use_4bit or use_8bit:
87
+ from transformers import BitsAndBytesConfig
88
+ if use_4bit:
89
+ quantization_config = BitsAndBytesConfig(
90
+ load_in_4bit=True,
91
+ bnb_4bit_compute_dtype=torch.float16,
92
+ bnb_4bit_use_double_quant=True,
93
+ bnb_4bit_quant_type="nf4"
94
+ )
95
+ else:
96
+ quantization_config = BitsAndBytesConfig(load_in_8bit=True)
97
+
98
+ # Load base model
99
+ self.model = AutoModelForCausalLM.from_pretrained(
100
+ self.model_name,
101
+ quantization_config=quantization_config,
102
+ device_map=device_map,
103
+ trust_remote_code=True
104
+ )
105
+
106
+ # Prepare for k-bit training if quantized
107
+ if use_4bit or use_8bit:
108
+ self.model = prepare_model_for_kbit_training(self.model)
109
+
110
+ # Apply PEFT
111
+ self.model = get_peft_model(self.model, self.peft_config)
112
+
113
+ # Print trainable parameters
114
+ self.model.print_trainable_parameters()
115
+
116
+ print("✅ Model loaded with PEFT")
117
+
118
+ def save_model(self, save_path: Optional[str] = None) -> None:
119
+ """
120
+ Save PEFT adapter weights.
121
+
122
+ Args:
123
+ save_path: Path to save adapters
124
+ """
125
+ if save_path is None:
126
+ save_path = str(self.output_dir / "final_model")
127
+
128
+ Path(save_path).mkdir(parents=True, exist_ok=True)
129
+
130
+ self.model.save_pretrained(save_path)
131
+ self.tokenizer.save_pretrained(save_path)
132
+
133
+ print(f"✅ PEFT model saved to: {save_path}")
134
+
135
+ def load_adapter(self, adapter_path: str) -> None:
136
+ """
137
+ Load pre-trained PEFT adapter.
138
+
139
+ Args:
140
+ adapter_path: Path to adapter weights
141
+ """
142
+ print(f"Loading PEFT adapter from: {adapter_path}")
143
+
144
+ self.model = PeftModel.from_pretrained(
145
+ self.model,
146
+ adapter_path
147
+ )
148
+
149
+ print("✅ Adapter loaded")
fine_tuning/training_loop.py ADDED
@@ -0,0 +1,274 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Custom Training Loop Module
3
+
4
+ Provides custom training loop implementation with fine-grained control over training.
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+ from typing import Optional, Dict, Any, Callable
9
+ import torch
10
+ from torch.utils.data import DataLoader
11
+ from tqdm import tqdm
12
+
13
+
14
+ @dataclass
15
+ class TrainingConfig:
16
+ """Configuration for custom training loop."""
17
+ num_epochs: int = 3
18
+ learning_rate: float = 2e-4
19
+ batch_size: int = 4
20
+ gradient_accumulation_steps: int = 4
21
+ max_grad_norm: float = 1.0
22
+ warmup_steps: int = 100
23
+ logging_steps: int = 10
24
+ eval_steps: int = 500
25
+ save_steps: int = 500
26
+ output_dir: str = "./models/output"
27
+ device: str = "cuda" if torch.cuda.is_available() else "cpu"
28
+
29
+
30
+ class TrainingLoop:
31
+ """
32
+ Custom training loop for fine-grained control over the training process.
33
+
34
+ Provides manual control over:
35
+ - Forward/backward passes
36
+ - Gradient accumulation
37
+ - Learning rate scheduling
38
+ - Logging and evaluation
39
+ - Checkpointing
40
+ """
41
+
42
+ def __init__(
43
+ self,
44
+ model: torch.nn.Module,
45
+ train_dataloader: DataLoader,
46
+ eval_dataloader: Optional[DataLoader] = None,
47
+ config: Optional[TrainingConfig] = None
48
+ ):
49
+ """
50
+ Initialize custom training loop.
51
+
52
+ Args:
53
+ model: PyTorch model to train
54
+ train_dataloader: Training data loader
55
+ eval_dataloader: Optional evaluation data loader
56
+ config: Training configuration
57
+ """
58
+ self.model = model
59
+ self.train_dataloader = train_dataloader
60
+ self.eval_dataloader = eval_dataloader
61
+ self.config = config or TrainingConfig()
62
+
63
+ self.optimizer = None
64
+ self.scheduler = None
65
+ self.global_step = 0
66
+ self.current_epoch = 0
67
+
68
+ def setup_optimizer(self, optimizer_class=torch.optim.AdamW, **optimizer_kwargs):
69
+ """
70
+ Setup optimizer and learning rate scheduler.
71
+
72
+ Args:
73
+ optimizer_class: Optimizer class to use
74
+ **optimizer_kwargs: Additional optimizer arguments
75
+ """
76
+ self.optimizer = optimizer_class(
77
+ self.model.parameters(),
78
+ lr=self.config.learning_rate,
79
+ **optimizer_kwargs
80
+ )
81
+
82
+ # Linear warmup scheduler
83
+ def lr_lambda(current_step: int):
84
+ if current_step < self.config.warmup_steps:
85
+ return float(current_step) / float(max(1, self.config.warmup_steps))
86
+ return 1.0
87
+
88
+ self.scheduler = torch.optim.lr_scheduler.LambdaLR(
89
+ self.optimizer,
90
+ lr_lambda
91
+ )
92
+
93
+ def train_step(self, batch: Dict[str, torch.Tensor]) -> float:
94
+ """
95
+ Perform a single training step.
96
+
97
+ Args:
98
+ batch: Batch of training data
99
+
100
+ Returns:
101
+ Loss value
102
+ """
103
+ # Move batch to device
104
+ batch = {k: v.to(self.config.device) for k, v in batch.items()}
105
+
106
+ # Forward pass
107
+ outputs = self.model(**batch)
108
+ loss = outputs.loss
109
+
110
+ # Scale loss for gradient accumulation
111
+ loss = loss / self.config.gradient_accumulation_steps
112
+
113
+ # Backward pass
114
+ loss.backward()
115
+
116
+ return loss.item()
117
+
118
+ def train_epoch(self) -> Dict[str, float]:
119
+ """
120
+ Train for one epoch.
121
+
122
+ Returns:
123
+ Training metrics
124
+ """
125
+ self.model.train()
126
+ total_loss = 0
127
+ num_batches = 0
128
+
129
+ progress_bar = tqdm(
130
+ self.train_dataloader,
131
+ desc=f"Epoch {self.current_epoch + 1}/{self.config.num_epochs}"
132
+ )
133
+
134
+ for step, batch in enumerate(progress_bar):
135
+ # Training step
136
+ loss = self.train_step(batch)
137
+ total_loss += loss
138
+
139
+ # Gradient accumulation
140
+ if (step + 1) % self.config.gradient_accumulation_steps == 0:
141
+ # Clip gradients
142
+ torch.nn.utils.clip_grad_norm_(
143
+ self.model.parameters(),
144
+ self.config.max_grad_norm
145
+ )
146
+
147
+ # Optimizer step
148
+ self.optimizer.step()
149
+ self.scheduler.step()
150
+ self.optimizer.zero_grad()
151
+
152
+ self.global_step += 1
153
+ num_batches += 1
154
+
155
+ # Update progress bar
156
+ progress_bar.set_postfix({
157
+ "loss": total_loss / num_batches,
158
+ "lr": self.scheduler.get_last_lr()[0]
159
+ })
160
+
161
+ # Logging
162
+ if self.global_step % self.config.logging_steps == 0:
163
+ avg_loss = total_loss / num_batches
164
+ print(f"Step {self.global_step}: loss={avg_loss:.4f}")
165
+
166
+ # Evaluation
167
+ if self.eval_dataloader and self.global_step % self.config.eval_steps == 0:
168
+ eval_metrics = self.evaluate()
169
+ print(f"Evaluation: {eval_metrics}")
170
+ self.model.train()
171
+
172
+ return {
173
+ "loss": total_loss / max(num_batches, 1),
174
+ "epoch": self.current_epoch
175
+ }
176
+
177
+ def evaluate(self) -> Dict[str, float]:
178
+ """
179
+ Evaluate model on validation set.
180
+
181
+ Returns:
182
+ Evaluation metrics
183
+ """
184
+ if self.eval_dataloader is None:
185
+ return {}
186
+
187
+ self.model.eval()
188
+ total_loss = 0
189
+ num_batches = 0
190
+
191
+ with torch.no_grad():
192
+ for batch in tqdm(self.eval_dataloader, desc="Evaluating"):
193
+ batch = {k: v.to(self.config.device) for k, v in batch.items()}
194
+ outputs = self.model(**batch)
195
+ total_loss += outputs.loss.item()
196
+ num_batches += 1
197
+
198
+ return {
199
+ "eval_loss": total_loss / max(num_batches, 1)
200
+ }
201
+
202
+ def train(self, callback: Optional[Callable] = None) -> Dict[str, Any]:
203
+ """
204
+ Run full training loop.
205
+
206
+ Args:
207
+ callback: Optional callback function called after each epoch
208
+
209
+ Returns:
210
+ Training history
211
+ """
212
+ if self.optimizer is None:
213
+ self.setup_optimizer()
214
+
215
+ print(f"Starting training for {self.config.num_epochs} epochs")
216
+ print(f"Device: {self.config.device}")
217
+ print(f"Batch size: {self.config.batch_size}")
218
+ print(f"Gradient accumulation: {self.config.gradient_accumulation_steps}")
219
+
220
+ history = {
221
+ "train_loss": [],
222
+ "eval_loss": []
223
+ }
224
+
225
+ for epoch in range(self.config.num_epochs):
226
+ self.current_epoch = epoch
227
+
228
+ # Train epoch
229
+ train_metrics = self.train_epoch()
230
+ history["train_loss"].append(train_metrics["loss"])
231
+
232
+ # Evaluate
233
+ if self.eval_dataloader:
234
+ eval_metrics = self.evaluate()
235
+ history["eval_loss"].append(eval_metrics.get("eval_loss", 0))
236
+
237
+ # Callback
238
+ if callback:
239
+ callback(epoch, train_metrics)
240
+
241
+ print("✅ Training complete!")
242
+ return history
243
+
244
+ def save_checkpoint(self, path: str) -> None:
245
+ """
246
+ Save training checkpoint.
247
+
248
+ Args:
249
+ path: Path to save checkpoint
250
+ """
251
+ checkpoint = {
252
+ "model_state_dict": self.model.state_dict(),
253
+ "optimizer_state_dict": self.optimizer.state_dict(),
254
+ "scheduler_state_dict": self.scheduler.state_dict(),
255
+ "global_step": self.global_step,
256
+ "epoch": self.current_epoch
257
+ }
258
+ torch.save(checkpoint, path)
259
+ print(f"Checkpoint saved to: {path}")
260
+
261
+ def load_checkpoint(self, path: str) -> None:
262
+ """
263
+ Load training checkpoint.
264
+
265
+ Args:
266
+ path: Path to checkpoint
267
+ """
268
+ checkpoint = torch.load(path)
269
+ self.model.load_state_dict(checkpoint["model_state_dict"])
270
+ self.optimizer.load_state_dict(checkpoint["optimizer_state_dict"])
271
+ self.scheduler.load_state_dict(checkpoint["scheduler_state_dict"])
272
+ self.global_step = checkpoint["global_step"]
273
+ self.current_epoch = checkpoint["epoch"]
274
+ print(f"Checkpoint loaded from: {path}")
model_registry.py ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Model Registry - Centralized model configuration and management
3
+
4
+ Provides pre-configured models with their specs, LoRA settings, and quantization recommendations.
5
+ """
6
+
7
+ from dataclasses import dataclass
8
+ from typing import Dict, List, Optional
9
+
10
+
11
+ @dataclass
12
+ class ModelInfo:
13
+ """Information about a model in the registry"""
14
+ name: str
15
+ model_id: str # HuggingFace model ID
16
+ description: str
17
+ vram_required_gb: int
18
+ recommended_lora_rank: int
19
+ recommended_quantization: str
20
+ model_type: str # "local" or "cloud"
21
+ ollama_equivalent: Optional[str] = None
22
+
23
+
24
+ class ModelRegistry:
25
+ """Registry of pre-configured models for fine-tuning"""
26
+
27
+ def __init__(self):
28
+ self.models: Dict[str, ModelInfo] = {}
29
+ self._register_default_models()
30
+
31
+ def _register_default_models(self):
32
+ """Register default models with their configurations"""
33
+
34
+ # Qwen Models
35
+ self.models["qwen2.5-7b"] = ModelInfo(
36
+ name="Qwen 2.5 7B Instruct",
37
+ model_id="Qwen/Qwen2.5-7B-Instruct",
38
+ description="Fast 7B parameter model, good for quick testing",
39
+ vram_required_gb=6,
40
+ recommended_lora_rank=16,
41
+ recommended_quantization="4bit",
42
+ model_type="cloud",
43
+ ollama_equivalent="qwen2.5:7b"
44
+ )
45
+
46
+ self.models["qwen2.5-32b"] = ModelInfo(
47
+ name="Qwen 2.5 32B Instruct",
48
+ model_id="Qwen/Qwen2.5-32B-Instruct",
49
+ description="High-quality 32B model for production use",
50
+ vram_required_gb=24,
51
+ recommended_lora_rank=32,
52
+ recommended_quantization="4bit",
53
+ model_type="cloud",
54
+ ollama_equivalent="qwen2.5:32b"
55
+ )
56
+
57
+ self.models["qwen2.5-72b"] = ModelInfo(
58
+ name="Qwen 2.5 72B Instruct",
59
+ model_id="Qwen/Qwen2.5-72B-Instruct",
60
+ description="Largest Qwen 2.5 model for maximum performance",
61
+ vram_required_gb=48,
62
+ recommended_lora_rank=64,
63
+ recommended_quantization="4bit",
64
+ model_type="cloud",
65
+ ollama_equivalent="qwen2.5:72b"
66
+ )
67
+
68
+ # Llama Models
69
+ self.models["llama-3.1-8b"] = ModelInfo(
70
+ name="Llama 3.1 8B Instruct",
71
+ model_id="meta-llama/Meta-Llama-3.1-8B-Instruct",
72
+ description="Meta's Llama 3.1 8B model",
73
+ vram_required_gb=8,
74
+ recommended_lora_rank=16,
75
+ recommended_quantization="4bit",
76
+ model_type="cloud",
77
+ ollama_equivalent="llama3.1:8b"
78
+ )
79
+
80
+ self.models["llama-3.1-70b"] = ModelInfo(
81
+ name="Llama 3.1 70B Instruct",
82
+ model_id="meta-llama/Meta-Llama-3.1-70B-Instruct",
83
+ description="Large Llama model for maximum performance",
84
+ vram_required_gb=48,
85
+ recommended_lora_rank=64,
86
+ recommended_quantization="4bit",
87
+ model_type="cloud",
88
+ ollama_equivalent="llama3.1:70b"
89
+ )
90
+
91
+ # Mistral Models
92
+ self.models["mistral-7b"] = ModelInfo(
93
+ name="Mistral 7B Instruct v0.3",
94
+ model_id="mistralai/Mistral-7B-Instruct-v0.3",
95
+ description="Efficient 7B model from Mistral AI",
96
+ vram_required_gb=6,
97
+ recommended_lora_rank=16,
98
+ recommended_quantization="4bit",
99
+ model_type="cloud",
100
+ ollama_equivalent="mistral:7b"
101
+ )
102
+
103
+ # Mixtral (MoE)
104
+ self.models["mixtral-8x7b"] = ModelInfo(
105
+ name="Mixtral 8x7B Instruct",
106
+ model_id="mistralai/Mixtral-8x7B-Instruct-v0.1",
107
+ description="Mixture-of-Experts model with 8x7B parameters",
108
+ vram_required_gb=40,
109
+ recommended_lora_rank=32,
110
+ recommended_quantization="4bit",
111
+ model_type="cloud",
112
+ ollama_equivalent="mixtral:8x7b"
113
+ )
114
+
115
+ self.models["mixtral-8x22b"] = ModelInfo(
116
+ name="Mixtral 8x22B Instruct",
117
+ model_id="mistralai/Mixtral-8x22B-Instruct-v0.1",
118
+ description="Large MoE model for highest quality",
119
+ vram_required_gb=80,
120
+ recommended_lora_rank=64,
121
+ recommended_quantization="4bit",
122
+ model_type="cloud",
123
+ ollama_equivalent=None
124
+ )
125
+
126
+ # Phi Models (Microsoft)
127
+ self.models["phi-3-mini"] = ModelInfo(
128
+ name="Phi-3 Mini 3.8B",
129
+ model_id="microsoft/Phi-3-mini-4k-instruct",
130
+ description="Small efficient model for quick testing",
131
+ vram_required_gb=4,
132
+ recommended_lora_rank=8,
133
+ recommended_quantization="4bit",
134
+ model_type="cloud",
135
+ ollama_equivalent="phi3:mini"
136
+ )
137
+
138
+ # Gemma Models (Google)
139
+ self.models["gemma-7b"] = ModelInfo(
140
+ name="Gemma 7B Instruct",
141
+ model_id="google/gemma-7b-it",
142
+ description="Google's Gemma 7B instruction-tuned model",
143
+ vram_required_gb=6,
144
+ recommended_lora_rank=16,
145
+ recommended_quantization="4bit",
146
+ model_type="cloud",
147
+ ollama_equivalent="gemma:7b"
148
+ )
149
+
150
+ def get_model(self, model_id: str) -> Optional[ModelInfo]:
151
+ """Get model info by ID"""
152
+ return self.models.get(model_id)
153
+
154
+ def get_all_models(self) -> Dict[str, ModelInfo]:
155
+ """Get all registered models"""
156
+ return self.models
157
+
158
+ def register_custom_model(self, model_id: str, info: ModelInfo):
159
+ """Register a custom model"""
160
+ self.models[model_id] = info
161
+
162
+ def get_models_by_vram(self, max_vram_gb: int) -> List[ModelInfo]:
163
+ """Get models that fit within VRAM budget"""
164
+ return [
165
+ info for info in self.models.values()
166
+ if info.vram_required_gb <= max_vram_gb
167
+ ]
168
+
169
+ def get_model_choices_for_gui(self) -> List[str]:
170
+ """Get list of model choices formatted for GUI dropdown"""
171
+ choices = []
172
+ for model_id, info in self.models.items():
173
+ label = f"{info.name} ({info.vram_required_gb}GB VRAM)"
174
+ choices.append((label, model_id))
175
+ return choices
176
+
177
+ def get_model_names(self) -> List[str]:
178
+ """Get list of model names"""
179
+ return [info.name for info in self.models.values()]
180
+
181
+ def get_model_ids(self) -> List[str]:
182
+ """Get list of model IDs"""
183
+ return list(self.models.keys())
184
+
185
+ def list_models(self) -> List[str]:
186
+ """Alias for get_model_ids() - returns list of model IDs"""
187
+ return self.get_model_ids()
188
+
189
+
190
+ # Global registry instance
191
+ _registry = None
192
+
193
+
194
+ def get_registry() -> ModelRegistry:
195
+ """Get the global model registry instance"""
196
+ global _registry
197
+ if _registry is None:
198
+ _registry = ModelRegistry()
199
+ return _registry
200
+
201
+
202
+ # Convenience function
203
+ def get_model_info(model_id: str) -> Optional[ModelInfo]:
204
+ """Get model info by ID from global registry"""
205
+ return get_registry().get_model(model_id)
ollama_integration.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Ollama Integration - Local model serving and HuggingFace mapping
3
+
4
+ Provides integration with Ollama for local model inference and maps
5
+ Ollama models to their HuggingFace equivalents for training.
6
+ """
7
+
8
+ import requests
9
+ from typing import Optional, Dict, List
10
+
11
+
12
+ class OllamaClient:
13
+ """Client for interacting with Ollama API"""
14
+
15
+ def __init__(self, base_url: str = "http://localhost:11434"):
16
+ self.base_url = base_url
17
+
18
+ def generate(self, model: str, prompt: str, **kwargs) -> Dict:
19
+ """Generate text using Ollama model"""
20
+ url = f"{self.base_url}/api/generate"
21
+
22
+ payload = {
23
+ "model": model,
24
+ "prompt": prompt,
25
+ "stream": False,
26
+ **kwargs
27
+ }
28
+
29
+ try:
30
+ response = requests.post(url, json=payload, timeout=120)
31
+ response.raise_for_status()
32
+ return response.json()
33
+ except requests.exceptions.RequestException as e:
34
+ return {"error": str(e), "response": ""}
35
+
36
+ def chat(self, model: str, messages: List[Dict], **kwargs) -> Dict:
37
+ """Chat with Ollama model"""
38
+ url = f"{self.base_url}/api/chat"
39
+
40
+ payload = {
41
+ "model": model,
42
+ "messages": messages,
43
+ "stream": False,
44
+ **kwargs
45
+ }
46
+
47
+ try:
48
+ response = requests.post(url, json=payload, timeout=120)
49
+ response.raise_for_status()
50
+ return response.json()
51
+ except requests.exceptions.RequestException as e:
52
+ return {"error": str(e), "message": {"content": ""}}
53
+
54
+ def list_models(self) -> List[str]:
55
+ """List available Ollama models"""
56
+ url = f"{self.base_url}/api/tags"
57
+
58
+ try:
59
+ response = requests.get(url, timeout=10)
60
+ response.raise_for_status()
61
+ data = response.json()
62
+ return [model["name"] for model in data.get("models", [])]
63
+ except requests.exceptions.RequestException:
64
+ return []
65
+
66
+ def is_available(self) -> bool:
67
+ """Check if Ollama is running"""
68
+ try:
69
+ response = requests.get(self.base_url, timeout=5)
70
+ return response.status_code == 200
71
+ except requests.exceptions.RequestException:
72
+ return False
73
+
74
+ def model_exists(self, model: str) -> bool:
75
+ """Check if a specific model is available"""
76
+ return model in self.list_models()
77
+
78
+
79
+ # Mapping of Ollama models to HuggingFace equivalents
80
+ OLLAMA_TO_HF_MAP = {
81
+ "qwen2.5:7b": "Qwen/Qwen2.5-7B-Instruct",
82
+ "qwen2.5:32b": "Qwen/Qwen2.5-32B-Instruct",
83
+ "llama3.1:8b": "meta-llama/Meta-Llama-3.1-8B-Instruct",
84
+ "llama3.1:70b": "meta-llama/Meta-Llama-3.1-70B-Instruct",
85
+ "mistral:7b": "mistralai/Mistral-7B-Instruct-v0.3",
86
+ "mixtral:8x7b": "mistralai/Mixtral-8x7B-Instruct-v0.1",
87
+ "phi3:mini": "microsoft/Phi-3-mini-4k-instruct",
88
+ "gemma:7b": "google/gemma-7b-it",
89
+ }
90
+
91
+
92
+ def get_hf_model_for_ollama(ollama_model: str) -> Optional[str]:
93
+ """Get the HuggingFace model ID for an Ollama model"""
94
+ # Handle versioned models (e.g., "qwen2.5:7b-instruct" -> "qwen2.5:7b")
95
+ base_model = ollama_model.split("-")[0] if "-" in ollama_model else ollama_model
96
+
97
+ return OLLAMA_TO_HF_MAP.get(base_model)
98
+
99
+
100
+ def get_ollama_for_hf(hf_model: str) -> Optional[str]:
101
+ """Get the Ollama model for a HuggingFace model ID"""
102
+ reverse_map = {v: k for k, v in OLLAMA_TO_HF_MAP.items()}
103
+ return reverse_map.get(hf_model)
104
+
105
+
106
+ def test_financial_advisor_ollama(
107
+ model: str = "qwen2.5:7b",
108
+ ollama_client: Optional[OllamaClient] = None
109
+ ) -> str:
110
+ """Test a financial advisor model via Ollama"""
111
+
112
+ if ollama_client is None:
113
+ ollama_client = OllamaClient()
114
+
115
+ if not ollama_client.is_available():
116
+ return "Error: Ollama is not running. Please start Ollama first."
117
+
118
+ if not ollama_client.model_exists(model):
119
+ return f"Error: Model '{model}' is not available. Please pull it first with: ollama pull {model}"
120
+
121
+ # Test prompt
122
+ prompt = """You are a financial advisor. A client asks:
123
+
124
+ "I'm 35 years old with $50,000 in savings. Should I invest in stocks or bonds?"
125
+
126
+ Provide professional financial advice."""
127
+
128
+ result = ollama_client.generate(model, prompt)
129
+
130
+ if "error" in result:
131
+ return f"Error: {result['error']}"
132
+
133
+ return result.get("response", "No response")
134
+
135
+
136
+ # Global client instance
137
+ _client = None
138
+
139
+
140
+ def get_ollama_client() -> OllamaClient:
141
+ """Get the global Ollama client instance"""
142
+ global _client
143
+ if _client is None:
144
+ _client = OllamaClient()
145
+ return _client
requirements.txt ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # GUI Requirements
2
+ # Install with: pip install -r requirements_gui.txt
3
+
4
+ # Core training requirements
5
+ torch>=2.0.0
6
+ transformers>=4.46.0
7
+ peft>=0.4.0
8
+ accelerate>=0.20.0
9
+ bitsandbytes>=0.41.0
10
+ datasets>=2.12.0
11
+ trl>=0.12.0
12
+ sentencepiece>=0.1.99
13
+ protobuf>=3.20.0
14
+
15
+ # GUI framework
16
+ gradio>=4.0.0
17
+
18
+ # Security for API key storage
19
+ cryptography>=41.0.0
20
+
21
+ # Configuration
22
+ pyyaml>=6.0
23
+ python-dotenv>=1.0.0
24
+
25
+ # Utilities
26
+ tqdm>=4.65.0
27
+ numpy>=1.24.0
28
+
29
+ # API integrations for synthetic data generation
30
+ openai>=1.0.0
31
+ anthropic>=0.8.0
32
+
33
+ # Ollama integration
34
+ requests>=2.31.0
35
+
36
+ # RunPod cloud integration
37
+ paramiko>=3.0.0
38
+ scp>=0.14.0
secure_config.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Secure Configuration - Encrypted API key storage using Fernet encryption
3
+
4
+ Provides secure storage and retrieval of API keys with encryption at rest.
5
+ """
6
+
7
+ import os
8
+ import json
9
+ from pathlib import Path
10
+ from typing import Dict, Optional
11
+ from cryptography.fernet import Fernet
12
+
13
+
14
+ class SecureConfig:
15
+ """Manages encrypted API key storage"""
16
+
17
+ def __init__(self, secrets_dir: str = ".secrets"):
18
+ self.secrets_dir = Path(secrets_dir)
19
+ self.secrets_dir.mkdir(exist_ok=True)
20
+
21
+ self.key_file = self.secrets_dir / "encryption.key"
22
+ self.data_file = self.secrets_dir / "api_keys.enc"
23
+
24
+ self.fernet = self._load_or_create_key()
25
+
26
+ def _load_or_create_key(self) -> Fernet:
27
+ """Load existing encryption key or create new one"""
28
+ if self.key_file.exists():
29
+ with open(self.key_file, "rb") as f:
30
+ key = f.read()
31
+ else:
32
+ key = Fernet.generate_key()
33
+ with open(self.key_file, "wb") as f:
34
+ f.write(key)
35
+
36
+ return Fernet(key)
37
+
38
+ def save_keys(self, api_keys: Dict[str, str]):
39
+ """Save API keys with encryption"""
40
+ # Remove empty keys
41
+ api_keys = {k: v for k, v in api_keys.items() if v and v.strip()}
42
+
43
+ # Serialize to JSON
44
+ json_data = json.dumps(api_keys)
45
+
46
+ # Encrypt
47
+ encrypted_data = self.fernet.encrypt(json_data.encode())
48
+
49
+ # Save to file
50
+ with open(self.data_file, "wb") as f:
51
+ f.write(encrypted_data)
52
+
53
+ # Also set environment variables for current session
54
+ for key, value in api_keys.items():
55
+ os.environ[key] = value
56
+
57
+ def load_keys(self) -> Dict[str, str]:
58
+ """Load and decrypt API keys"""
59
+ if not self.data_file.exists():
60
+ return {}
61
+
62
+ try:
63
+ # Read encrypted data
64
+ with open(self.data_file, "rb") as f:
65
+ encrypted_data = f.read()
66
+
67
+ # Decrypt
68
+ decrypted_data = self.fernet.decrypt(encrypted_data)
69
+
70
+ # Deserialize
71
+ api_keys = json.loads(decrypted_data.decode())
72
+
73
+ # Set environment variables
74
+ for key, value in api_keys.items():
75
+ os.environ[key] = value
76
+
77
+ return api_keys
78
+
79
+ except Exception as e:
80
+ print(f"Error loading keys: {e}")
81
+ return {}
82
+
83
+ def get_key(self, key_name: str) -> Optional[str]:
84
+ """Get a specific API key"""
85
+ # First try environment variable
86
+ value = os.environ.get(key_name)
87
+ if value:
88
+ return value
89
+
90
+ # Then try loading from encrypted file
91
+ keys = self.load_keys()
92
+ return keys.get(key_name)
93
+
94
+ def get_masked_key(self, key_name: str) -> Optional[str]:
95
+ """Get a masked version of an API key for display"""
96
+ key = self.get_key(key_name)
97
+ if not key:
98
+ return None
99
+
100
+ # Show first 8 and last 4 characters
101
+ if len(key) > 20:
102
+ return f"{key[:8]}...{key[-4:]}"
103
+ else:
104
+ return f"{key[:4]}...{key[-2:]}"
105
+
106
+ def delete_keys(self):
107
+ """Delete all stored keys"""
108
+ if self.data_file.exists():
109
+ self.data_file.unlink()
110
+
111
+ # Also clear environment variables
112
+ env_keys = [
113
+ "HUGGINGFACE_TOKEN",
114
+ "OPENAI_API_KEY",
115
+ "ANTHROPIC_API_KEY",
116
+ "WANDB_API_KEY",
117
+ "ELEVEN_API_KEY",
118
+ "RUNPOD_API_KEY"
119
+ ]
120
+ for key in env_keys:
121
+ os.environ.pop(key, None)
122
+
123
+ def has_key(self, key_name: str) -> bool:
124
+ """Check if a key exists"""
125
+ return self.get_key(key_name) is not None
126
+
127
+
128
+ # Global instance
129
+ _config = None
130
+
131
+
132
+ def get_config() -> SecureConfig:
133
+ """Get the global secure config instance"""
134
+ global _config
135
+ if _config is None:
136
+ _config = SecureConfig()
137
+ return _config