Upload folder using huggingface_hub
Browse files- .generation_progress.json +0 -0
- .gitattributes +1 -0
- __pycache__/dataset_generator.cpython-311.pyc +0 -0
- bazi_training_data.jsonl +3 -0
- dataset_generator.py +1072 -0
- test_format.py +70 -0
- test_gemini25.py +51 -0
- test_gen.py +77 -0
.generation_progress.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
.gitattributes
CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
bazi_training_data.jsonl filter=lfs diff=lfs merge=lfs -text
|
__pycache__/dataset_generator.cpython-311.pyc
ADDED
Binary file (22.7 kB). View file
|
|
bazi_training_data.jsonl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:77ab7c88badabeabf1c9141ccfcd0377a1746abcadf87c16a9420c68c0f9c591
|
3 |
+
size 1186265441
|
dataset_generator.py
ADDED
@@ -0,0 +1,1072 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""
|
3 |
+
Dataset Generator for BaZi Fortune Telling
|
4 |
+
Uses Google Gemini Pro 2.5 with thinking to generate high-quality training data
|
5 |
+
Supports multithreaded processing for faster generation
|
6 |
+
"""
|
7 |
+
|
8 |
+
import json
|
9 |
+
import random
|
10 |
+
import time
|
11 |
+
from datetime import datetime, timedelta
|
12 |
+
from typing import Dict, List, Optional, Tuple
|
13 |
+
import os
|
14 |
+
import sys
|
15 |
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
16 |
+
from threading import Lock
|
17 |
+
import threading
|
18 |
+
|
19 |
+
# Add parent directory to path to import modules
|
20 |
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
21 |
+
|
22 |
+
from bazi_calculator import BaziCalculator, compute_birth_pillars
|
23 |
+
from bazi_favorable_elements import calculate_favorable_elements
|
24 |
+
|
25 |
+
# Import Google Gemini
|
26 |
+
try:
|
27 |
+
from google import genai
|
28 |
+
from google.genai import types
|
29 |
+
except ImportError:
|
30 |
+
print("Please install google-genai: pip install google-genai")
|
31 |
+
sys.exit(1)
|
32 |
+
|
33 |
+
# Gemini API Configuration
|
34 |
+
GEMINI_API_KEY = "AIzaSyCqe3vjvPlo1lt_hpQ4nqAC0-_1omva1oc"
|
35 |
+
os.environ["GEMINI_API_KEY"] = GEMINI_API_KEY
|
36 |
+
|
37 |
+
# Configuration for multithreading
|
38 |
+
MAX_WORKERS = 10 # Number of concurrent threads for API calls
|
39 |
+
RATE_LIMIT_DELAY = 0.5 # Delay between API calls to avoid rate limiting
|
40 |
+
|
41 |
+
# Thread-safe locks
|
42 |
+
file_lock = Lock()
|
43 |
+
progress_lock = Lock()
|
44 |
+
print_lock = Lock()
|
45 |
+
api_call_lock = Lock()
|
46 |
+
last_api_call_time = 0
|
47 |
+
|
48 |
+
# Global context cache for system prompt
|
49 |
+
context_cache = None
|
50 |
+
cache_lock = Lock()
|
51 |
+
|
52 |
+
|
53 |
+
def format_bazi_production_style(person: Dict, bazi_data: Dict) -> str:
|
54 |
+
"""Format BaZi data in production website style"""
|
55 |
+
lines = []
|
56 |
+
birth_date = bazi_data['birth_date']
|
57 |
+
pillars = bazi_data['birth_pillars']
|
58 |
+
fav_elems = bazi_data['favorable_elements']
|
59 |
+
future_data = bazi_data['future_data']
|
60 |
+
calculator = bazi_data['calculator']
|
61 |
+
|
62 |
+
# Current date for query
|
63 |
+
current_date = datetime.now()
|
64 |
+
|
65 |
+
# Header
|
66 |
+
lines.append("=== BaZi Data ===")
|
67 |
+
lines.append(f"Name: {person['name']}")
|
68 |
+
lines.append(f"Birth: {birth_date.strftime('%Y-%m-%d %H:%M:%S')}")
|
69 |
+
lines.append(f"Gender: {person['gender']}")
|
70 |
+
lines.append(f"Query: {current_date.strftime('%Y-%m-%d %H:%M')}")
|
71 |
+
|
72 |
+
# Calculate age
|
73 |
+
age = current_date.year - birth_date.year
|
74 |
+
if current_date.month < birth_date.month or (current_date.month == birth_date.month and current_date.day < birth_date.day):
|
75 |
+
age -= 1
|
76 |
+
lines.append(f"Age: {age}")
|
77 |
+
|
78 |
+
# Life Stem (Day Master)
|
79 |
+
day_stem = pillars['day'][0]
|
80 |
+
# Determine strength (simplified)
|
81 |
+
stem_strength = "Strong" if len(fav_elems['favorable']) > len(fav_elems['unfavorable']) else "Weak"
|
82 |
+
lines.append(f"Life Stem: {day_stem} ({stem_strength})")
|
83 |
+
|
84 |
+
# Favorable/Unfavorable elements
|
85 |
+
lines.append(f"Favorable: {', '.join(fav_elems['favorable'])}")
|
86 |
+
lines.append(f"Unfavorable: {', '.join(fav_elems['unfavorable'])}")
|
87 |
+
|
88 |
+
# Four Pillars with hidden stems
|
89 |
+
lines.append("\n=== Four Pillars ===")
|
90 |
+
|
91 |
+
# Get hidden stems
|
92 |
+
from bazi_calculator import BaziCalculator
|
93 |
+
calc_temp = BaziCalculator(birth_date, person['gender'].lower(), pillars)
|
94 |
+
|
95 |
+
# Year pillar
|
96 |
+
year_hidden = calc_temp.BRANCH_HIDDEN_STEMS.get(pillars['year'][1], [])
|
97 |
+
lines.append(f"Year : {pillars['year'][0]}{pillars['year'][1]} (hidden: {', '.join(year_hidden)})")
|
98 |
+
|
99 |
+
# Month pillar
|
100 |
+
month_hidden = calc_temp.BRANCH_HIDDEN_STEMS.get(pillars['month'][1], [])
|
101 |
+
lines.append(f"Month: {pillars['month'][0]}{pillars['month'][1]} (hidden: {', '.join(month_hidden)})")
|
102 |
+
|
103 |
+
# Day pillar
|
104 |
+
day_hidden = calc_temp.BRANCH_HIDDEN_STEMS.get(pillars['day'][1], [])
|
105 |
+
lines.append(f"Day : {pillars['day'][0]}{pillars['day'][1]} (hidden: {', '.join(day_hidden)})")
|
106 |
+
|
107 |
+
# Hour pillar
|
108 |
+
hour_hidden = calc_temp.BRANCH_HIDDEN_STEMS.get(pillars['hour'][1], [])
|
109 |
+
lines.append(f"Hour : {pillars['hour'][0]}{pillars['hour'][1]} (hidden: {', '.join(hour_hidden)})")
|
110 |
+
|
111 |
+
# Current Luck Pillar
|
112 |
+
lines.append("\n=== Current Luck Pillar ===")
|
113 |
+
for lp in future_data['luck_pillars']:
|
114 |
+
if lp['start_age'] <= age <= lp['end_age']:
|
115 |
+
lines.append(f"Age {age}: {lp['heaven']}{lp['earth']}")
|
116 |
+
break
|
117 |
+
|
118 |
+
# Current Period
|
119 |
+
lines.append("\n=== Current Period ===")
|
120 |
+
|
121 |
+
# Annual
|
122 |
+
for ap in future_data['annual_pillars']:
|
123 |
+
if ap['year'] == current_date.year:
|
124 |
+
lines.append(f"Annual: {ap['heaven']}{ap['earth']}")
|
125 |
+
break
|
126 |
+
|
127 |
+
# Monthly
|
128 |
+
current_month_key = f"{current_date.year}-{current_date.month:02d}"
|
129 |
+
if current_month_key in future_data['monthly_pillars']:
|
130 |
+
mp = future_data['monthly_pillars'][current_month_key]
|
131 |
+
lines.append(f"Monthly: {mp['heaven']}{mp['earth']}")
|
132 |
+
|
133 |
+
# Daily
|
134 |
+
current_day_key = current_date.strftime("%Y-%m-%d")
|
135 |
+
if current_day_key in future_data['daily_pillars']:
|
136 |
+
dp = future_data['daily_pillars'][current_day_key]
|
137 |
+
lines.append(f"Daily: {dp['heaven']}{dp['earth']}")
|
138 |
+
|
139 |
+
# Hourly
|
140 |
+
current_hour = current_date.hour
|
141 |
+
# Map to Chinese hour
|
142 |
+
if current_hour == 0:
|
143 |
+
chinese_hour = 23
|
144 |
+
elif current_hour % 2 == 0:
|
145 |
+
chinese_hour = current_hour - 1
|
146 |
+
else:
|
147 |
+
chinese_hour = current_hour
|
148 |
+
|
149 |
+
if chinese_hour in future_data['hourly_pillars']:
|
150 |
+
hp = future_data['hourly_pillars'][chinese_hour]
|
151 |
+
lines.append(f"Hourly: {hp['heaven']}{hp['earth']}")
|
152 |
+
|
153 |
+
# Future 100 Years
|
154 |
+
lines.append("\n=== Future 100 Years ===")
|
155 |
+
|
156 |
+
# Luck Pillars
|
157 |
+
lines.append("\n[Luck Pillars]")
|
158 |
+
for lp in future_data['luck_pillars']:
|
159 |
+
lines.append(f"Age {lp['start_age']}-{lp['end_age']}: {lp['heaven']}{lp['earth']}")
|
160 |
+
|
161 |
+
# Annual Pillars - ALL 100 years
|
162 |
+
lines.append("\n[Annual Pillars]")
|
163 |
+
for ap in future_data['annual_pillars']:
|
164 |
+
lines.append(f"{ap['year']}: {ap['heaven']}{ap['earth']}")
|
165 |
+
|
166 |
+
# Monthly Pillars - Show many months
|
167 |
+
lines.append("\n[Monthly Pillars]")
|
168 |
+
monthly_keys = sorted(list(future_data['monthly_pillars'].keys()))
|
169 |
+
for key in monthly_keys:
|
170 |
+
mp = future_data['monthly_pillars'][key]
|
171 |
+
lines.append(f"{key}: {mp['heaven']}{mp['earth']}")
|
172 |
+
|
173 |
+
# Daily Pillars - Show many days
|
174 |
+
lines.append("\n[Daily Pillars]")
|
175 |
+
daily_keys = sorted(list(future_data['daily_pillars'].keys()))
|
176 |
+
for key in daily_keys:
|
177 |
+
dp = future_data['daily_pillars'][key]
|
178 |
+
lines.append(f"{key}: {dp['heaven']}{dp['earth']}")
|
179 |
+
|
180 |
+
lines.append("\n[Loaded with AiMing v3]")
|
181 |
+
|
182 |
+
return '\n'.join(lines)
|
183 |
+
|
184 |
+
# Load system prompt
|
185 |
+
def load_system_prompt() -> str:
|
186 |
+
"""Load the system prompt from prompt-v3.txt"""
|
187 |
+
script_dir = os.path.dirname(os.path.abspath(__file__))
|
188 |
+
parent_dir = os.path.dirname(script_dir)
|
189 |
+
prompt_path = os.path.join(parent_dir, 'prompt-v3.txt')
|
190 |
+
with open(prompt_path, 'r', encoding='utf-8') as f:
|
191 |
+
return f.read()
|
192 |
+
|
193 |
+
def generate_random_birthdays(count: int = 10) -> List[Dict]:
|
194 |
+
"""Generate random birthdays with varied demographics
|
195 |
+
|
196 |
+
Args:
|
197 |
+
count: Number of random birthdays to generate
|
198 |
+
"""
|
199 |
+
birthdays = []
|
200 |
+
|
201 |
+
# Define name pools - expanded for more variety
|
202 |
+
male_names = ["สมชาย", "วิชัย", "ประยุทธ์", "สมศักดิ์", "วีระ", "ณัฐวุฒิ", "ธนากร", "พงษ์ศักดิ์", "อภิชาติ", "จักรพันธ์",
|
203 |
+
"สุรชัย", "ประสิทธิ์", "สมพงษ์", "วิทยา", "นิพนธ์", "สุทธิพงษ์", "อนันต์", "ชัยวัฒน์", "ธีระ", "พิชัย"]
|
204 |
+
female_names = ["สมหญิง", "มาลี", "สุดาพร", "วิไลวรรณ", "พรทิพย์", "นันทนา", "จิราภรณ์", "ศิริพร", "อรวรรณ", "ปิยะนุช",
|
205 |
+
"วรรณา", "สุภาพร", "รัตนา", "อารีย์", "ปาริชาติ", "สุนิสา", "กาญจนา", "ดวงใจ", "ชนิดา", "พิมพ์ใจ"]
|
206 |
+
|
207 |
+
# Generate diverse birth years (1960-2005)
|
208 |
+
# If count > 46 (years available), allow duplicates
|
209 |
+
year_range = list(range(1960, 2006))
|
210 |
+
if count <= len(year_range):
|
211 |
+
years = random.sample(year_range, count)
|
212 |
+
else:
|
213 |
+
# For counts > 46, we allow some duplicate years
|
214 |
+
years = random.choices(year_range, k=count)
|
215 |
+
|
216 |
+
for i in range(count):
|
217 |
+
gender = random.choice(["Male", "Female"])
|
218 |
+
name = random.choice(male_names if gender == "Male" else female_names)
|
219 |
+
|
220 |
+
# Random month and day
|
221 |
+
month = random.randint(1, 12)
|
222 |
+
if month in [1, 3, 5, 7, 8, 10, 12]:
|
223 |
+
day = random.randint(1, 31)
|
224 |
+
elif month in [4, 6, 9, 11]:
|
225 |
+
day = random.randint(1, 30)
|
226 |
+
else: # February
|
227 |
+
day = random.randint(1, 28)
|
228 |
+
|
229 |
+
# Random time (distributed across all 12 Chinese hours)
|
230 |
+
hour = random.randint(0, 23)
|
231 |
+
minute = random.randint(0, 59)
|
232 |
+
|
233 |
+
birthdays.append({
|
234 |
+
"name": f"{name}_{i+1}",
|
235 |
+
"gender": gender,
|
236 |
+
"year": years[i],
|
237 |
+
"month": month,
|
238 |
+
"day": day,
|
239 |
+
"hour": hour,
|
240 |
+
"minute": minute,
|
241 |
+
"is_twin": False
|
242 |
+
})
|
243 |
+
|
244 |
+
return birthdays
|
245 |
+
|
246 |
+
def generate_questions() -> List[str]:
|
247 |
+
"""Generate 10 diverse questions for BaZi consultation"""
|
248 |
+
question_templates = [
|
249 |
+
# General fortune (expanded)
|
250 |
+
"ดวงนี้เป็นอย่างไร",
|
251 |
+
"ช่วงไหนในชีวิตจะประสบความสำเร็จ",
|
252 |
+
"มีอุปสรรคอะไรที่ต้องระวังบ้าง",
|
253 |
+
"ดวงชะตาโดยรวมปีนี้เป็นอย่างไร",
|
254 |
+
"ชีวิตโดยรวมจะเป็นอย่างไร",
|
255 |
+
"อนาคตของฉันจะเป็นอย่างไร",
|
256 |
+
"ปีนี้จะเป็นปีที่ดีไหม",
|
257 |
+
"ช่วงชีวิตไหนจะเจอปัญหามาก",
|
258 |
+
"จะมีชีวิตที่ดีขึ้นเมื่อไหร่",
|
259 |
+
"ดวงชะตาในระยะยาวเป็นอย่างไร",
|
260 |
+
"ชีวิตจะมีความสุขไหม",
|
261 |
+
"จะพบเจอสิ่งดีๆ เมื่อไหร่",
|
262 |
+
"มีเคราะห์อะไรรออยู่ไหม",
|
263 |
+
"ดวงจะดีขึ้นเมื่อไหร่",
|
264 |
+
"ชีวิตจะราบรื่นไหม",
|
265 |
+
"ปีไหนจะเป็นปีทอง",
|
266 |
+
"จะมีโชคเมื่อไหร่",
|
267 |
+
"ควรระวังอะไรในชีวิต",
|
268 |
+
"จะมีอะไรเปลี่ยนแปลงไหม",
|
269 |
+
"ดวงชะตาตอนแก่เป็นอย่างไร",
|
270 |
+
|
271 |
+
# Career questions (expanded)
|
272 |
+
"ช่วงนี้งานการเป็นอย่างไรบ้าง มีโอกาสก้าวหน้าไหม",
|
273 |
+
"อยากเปลี่ยนงานใหม่ ช่วงไหนเหมาะสม",
|
274 |
+
"อยากทำธุรกิจส่วนตัว เหมาะไหม ควรทำธุรกิจประเภทไหน",
|
275 |
+
"จะได้เลื่อนตำแหน่งไหม",
|
276 |
+
"งานที่ทำอยู่จะมั่นคงไหม",
|
277 |
+
"ควรลาออกจากงานไหม",
|
278 |
+
"จะตกงานไหม",
|
279 |
+
"อาชีพอะไรเหมาะกับดวงนี้",
|
280 |
+
"ทำงานกับคนอื่นดีหรือทำงานคนเดียวดี",
|
281 |
+
"จะประสบความสำเร็จในอาชีพไหม",
|
282 |
+
"เจ้านายเมตตาไหม",
|
283 |
+
"ควรทำงานรับราชการหรือเอกชน",
|
284 |
+
"จะมีคนอิจฉาในที่ทำงานไหม",
|
285 |
+
"ควรเปลี่ยนสายงานไหม",
|
286 |
+
"จะได้ทำงานที่ชอบไหม",
|
287 |
+
"หุ้นส่วนธุรกิจจะซื่อสัตย์ไหม",
|
288 |
+
"ควรทำงานในประเทศหรือต่างประเทศ",
|
289 |
+
"จะมีปัญหาในที่ทำงานไหม",
|
290 |
+
"ควรรับงานฟรีแลนซ์ไหม",
|
291 |
+
"ธุรกิจจะอยู่รอดไหม",
|
292 |
+
"ควรขยายธุรกิจไหม",
|
293 |
+
"จะมีคู่แข่งทางธุรกิจไหม",
|
294 |
+
"ลูกค้าจะพอใจไหม",
|
295 |
+
"ควรร่วมทุนกับใครไหม",
|
296 |
+
"จะได้โบนัสไหม",
|
297 |
+
"งานที่สมัครจะได้ไหม",
|
298 |
+
"ควรทำอาชีพเสริมไหม",
|
299 |
+
"จะเป็นผู้บริหารไหม",
|
300 |
+
"ควรลงทุนในธุรกิจอะไร",
|
301 |
+
"จะมีรายได้เสริมไหม",
|
302 |
+
|
303 |
+
# Love and relationships (expanded)
|
304 |
+
"เรื่องความรักเป็นอย่างไรบ้าง จะเจอคนที่ใช่เมื่อไหร่",
|
305 |
+
"ความรักกับแฟนปัจจุบันจะไปได้ไกลไหม",
|
306 |
+
"ปีนี้มีโอกาสแต่งงานไหม",
|
307 |
+
"จะได้เจอแฟนไหม เมื่อไหร่",
|
308 |
+
"แฟนจะนอกใจไหม",
|
309 |
+
"จะมีคนมาชอบไหม",
|
310 |
+
"จะเลิกกับแฟนไหม",
|
311 |
+
"ความรักจะลงเอยด้วยดีไหม",
|
312 |
+
"คู่ที่เหมาะสมเป็นคนแบบไหน",
|
313 |
+
"จะมีกี่ครั้งในชีวิตที่รักจริง",
|
314 |
+
"จะโสดไปตลอดชีวิตไหม",
|
315 |
+
"ควรแต่งงานกับคนนี้ไหม",
|
316 |
+
"จะหย่าร้างไหม",
|
317 |
+
"จะกลับมาคืนดีกันไหม",
|
318 |
+
"คนที่แอบชอบจะมาบอกรักไหม",
|
319 |
+
"จะมีคนที่สามไหม",
|
320 |
+
"ควรรอคนเก่าไหม",
|
321 |
+
"จะเ��อรักแท้ไหม",
|
322 |
+
"แฟนจะดีกับเราไหม",
|
323 |
+
"จะมีปัญหาเรื่องความรักไหม",
|
324 |
+
"ควรเปิดใจให้คนใหม่ไหม",
|
325 |
+
"จะอกหักไหม",
|
326 |
+
"คนที่ชอบจะชอบเราไหม",
|
327 |
+
"จะแต่งงานกี่ครั้ง",
|
328 |
+
"คู่ชีวิตจะมาจากไหน",
|
329 |
+
"จะมีลูกกับคนรักไหม",
|
330 |
+
"ความรักระยะไกลจะสำเร็จไหม",
|
331 |
+
"จะเจอคนรักที่ทำงานไหม",
|
332 |
+
"ควรคบคนอายุเท่าไหร่",
|
333 |
+
"จะมีความรักออนไลน์ไหม",
|
334 |
+
|
335 |
+
# Health (expanded)
|
336 |
+
"สุขภาพช่วงนี้ต้องระวังอะไรบ้าง",
|
337 |
+
"มีปัญหาสุขภาพเรื้อรัง จะหายไหม",
|
338 |
+
"ปีนี้จะป่วยไหม",
|
339 |
+
"เดือนนี้อาการป่วยจะดีขึ้นไหม",
|
340 |
+
"ดวงนี้ป่วยเป็นโรคอะไร",
|
341 |
+
"จะมีอายุยืนไหม",
|
342 |
+
"ควรระวังโรคอะไรเป็นพิเศษ",
|
343 |
+
"การผ่าตัดจะสำเร็จไหม",
|
344 |
+
"ควรไปหาหมอไหม",
|
345 |
+
"สุขภาพจิตจะแข็งแรงไหม",
|
346 |
+
"จะมีอุบัติเหตุไหม",
|
347 |
+
"จะมีปัญหาเรื่องน้ำหนักไหม",
|
348 |
+
"ควรออกกำลังกายแบบไหน",
|
349 |
+
"จะมีปัญหาการนอนไหม",
|
350 |
+
"สายตาจะมีปัญหาไหม",
|
351 |
+
"ฟันจะมีปัญหาไหม",
|
352 |
+
"จะมีอาการแพ้ไหม",
|
353 |
+
"ควรกินอาหารแบบไหน",
|
354 |
+
"จะเครียดมากไหม",
|
355 |
+
"จะมีปัญหาผิวหนังไหม",
|
356 |
+
"ระบบย่อยอาหารจะมีปัญหาไหม",
|
357 |
+
"จะปวดหัวบ่อยไหม",
|
358 |
+
"จะมีปัญหากระดูกไหม",
|
359 |
+
"หัวใจจะแข็งแรงไหม",
|
360 |
+
"จะมีปัญหาความดันไหม",
|
361 |
+
"จะเป็นโรคติดต่อไหม",
|
362 |
+
"จะมีปัญหาฮอร์โมนไหม",
|
363 |
+
"ควรตรวจสุขภาพเมื่อไหร่",
|
364 |
+
"จะติดเชื้อไหม",
|
365 |
+
"จะมีปัญหาการหายใจไหม",
|
366 |
+
|
367 |
+
# Wealth and finance (expanded)
|
368 |
+
"การเงินปีนี้เป็นอย่างไร จะมีโชคลาภไหม",
|
369 |
+
"อยากลงทุนหุ้น/คริปโต เหมาะไหม",
|
370 |
+
"หนี้สินเยอะ จะหมดเมื่อไหร่",
|
371 |
+
"ดวงนี้รวยเมื่อไหร่",
|
372 |
+
"ดวงนี้รวยไหม",
|
373 |
+
"จะถูกหวยไหม",
|
374 |
+
"ควรลงทุนอะไร",
|
375 |
+
"จะล้มละลายไหม",
|
376 |
+
"เงินจะพอใช้ไหม",
|
377 |
+
"จะมีคนมาหลอกเงินไหม",
|
378 |
+
"ควรให้คนอื่นยืมเงินไหม",
|
379 |
+
"จะได้มรดกไหม",
|
380 |
+
"ควรซื้อบ้านหรือเช่าบ้าน",
|
381 |
+
"การลงทุนจะได้ผลตอบแทนดีไหม",
|
382 |
+
"จะมีเงินเก็บไหม",
|
383 |
+
"ควรฝากเงินหรือลงทุน",
|
384 |
+
"จะได้เงินคืนจากลูกหนี้ไหม",
|
385 |
+
"ควรซื้อทองไหม",
|
386 |
+
"จะมีรายได้พิเศษไหม",
|
387 |
+
"ควรเล่นพนันไหม",
|
388 |
+
"จะขาดทุนไหม",
|
389 |
+
"ควรกู้เงินไหม",
|
390 |
+
"จะมีเงินใช้ตอนแก่ไหม",
|
391 |
+
"ควรทำประกันไหม",
|
392 |
+
"จะมีปัญหาภาษีไหม",
|
393 |
+
"ควรลงทุนอสังหาริมทรัพย์ไหม",
|
394 |
+
"จะได้โบนัสพิเศษไหม",
|
395 |
+
"ควรเก็บเงินสดไหม",
|
396 |
+
"จะมีหนี้นอกระบบไหม",
|
397 |
+
"ควรขายทรัพย์สินไหม",
|
398 |
+
|
399 |
+
# Family (expanded)
|
400 |
+
"ลูกๆ จะเรียนหนังสือเก่งไหม",
|
401 |
+
"ความสัมพันธ์ในครอบครัวจะราบรื่นไหม",
|
402 |
+
"จะมีลูกไหม",
|
403 |
+
"พ่อแม่จะมีสุขภาพดีไหม",
|
404 |
+
"พี่น้องจะสามัคคีกันไหม",
|
405 |
+
"จะมีปัญหากับญาติไหม",
|
406 |
+
"ลูกจะกตัญญูไหม",
|
407 |
+
"ควรมีลูกกี่คน",
|
408 |
+
"จะได้อยู่กับครอบครัวไหม",
|
409 |
+
"จะมีปัญหากับพ่อตาแม่ยายไหม",
|
410 |
+
"ลูกจะประสบความสำเร็จไหม",
|
411 |
+
"จะต้องดูแลพ่อแม่ไหม",
|
412 |
+
"พี่น้องจะช่วยเหลือไหม",
|
413 |
+
"จะมีปัญหาเรื่องมรดกไหม",
|
414 |
+
"ครอบครัวจะอบอุ่นไหม",
|
415 |
+
"จะมีใครในครอบครัวป่วยไหม",
|
416 |
+
"ลูกจะได้เรียนสูงไหม",
|
417 |
+
"จะมีปัญหากับคู่สมรสของลูกไหม",
|
418 |
+
"หลานจะน่ารักไหม",
|
419 |
+
"จะได้เลี้ยงหลานไหม",
|
420 |
+
|
421 |
+
# Education and learning (expanded)
|
422 |
+
"จะสอบติดไหม",
|
423 |
+
"ควรเรียนต่อไหม",
|
424 |
+
"สาขาอะไรเหมาะกับฉัน",
|
425 |
+
"จะจบการศึกษาไหม",
|
426 |
+
"จะได้ทุนการศึกษาไหม",
|
427 |
+
"จะเรียนเก่งไหม",
|
428 |
+
"ควรเรียนในประเทศหรือต่างประเทศ",
|
429 |
+
"จะสอบตกไหม",
|
430 |
+
"ควรเรียนพิเศษไหม",
|
431 |
+
"จะมีปัญหากับอาจารย์ไหม",
|
432 |
+
"จะมีเพื่อนที่ดีไหม",
|
433 |
+
"ควรย้ายโรงเรียนไหม",
|
434 |
+
"จะได้เกียรตินิยมไหม",
|
435 |
+
"ควรเรียนภาษาอะไร",
|
436 |
+
"จะมีปัญหาการเรียนไหม",
|
437 |
+
"ควรเรียนปริญญาโทไหม",
|
438 |
+
"จะได้ไปแลกเปลี่ยนไหม",
|
439 |
+
"ควรเรียนออนไลน์ไหม",
|
440 |
+
"จะมีปัญหากับเพื่อนร่วมชั้นไหม",
|
441 |
+
"ควรเข้ามหาวิทยาลัยไหน",
|
442 |
+
|
443 |
+
# Specific timing (expanded)
|
444 |
+
"เดือนหน้าจะมีเรื่องดีๆ เข้ามาไหม",
|
445 |
+
"ปีหน้าควรระวังเรื่องอะไรบ้าง",
|
446 |
+
"วันไหนในเดือนนี้เป็นวันมงคล",
|
447 |
+
"พรุ่งนี้จะเป็นวันที่ดีไหม",
|
448 |
+
"สัปดาห์นี้จะมีข่าวดีไหม",
|
449 |
+
"ช่วงไหนของปีจะดีที่สุด",
|
450 |
+
"เดือนไหนควรระมัดระวังเป็นพิเศษ",
|
451 |
+
"วันนี้ควรทำอะไร",
|
452 |
+
"คืนนี้จะนอนหลับสบายไหม",
|
453 |
+
"เช้านี้จะมีเรื่องดีไหม",
|
454 |
+
"บ่ายนี้ควรระวังอะไร",
|
455 |
+
"เย็นนี้จะเจอใคร",
|
456 |
+
"วันจันทร์จะเป็นอย่างไร",
|
457 |
+
"สุดสัปดาห์นี้จะสนุกไหม",
|
458 |
+
"เดือนนี้จะได้เงินไหม",
|
459 |
+
"ปีนี้จะมีอะไรเปลี่ยนแปลง",
|
460 |
+
"ไตรมาสนี้ธุรกิจจะดีไหม",
|
461 |
+
"ฤดูนี้สุขภาพจะเป็นอย่างไร",
|
462 |
+
"ช่วงปิดเทอมจะไปไหนไหม",
|
463 |
+
"วันเกิดปีนี้จะมีความสุขไหม",
|
464 |
+
|
465 |
+
# Decision making (expanded)
|
466 |
+
"กำลังตัดสินใจเรื่องสำคัญ ควรเลือกทางไหน",
|
467 |
+
"จะย้ายบ้านดีไหม ช่วงไหนเหมาะสม",
|
468 |
+
"อยากไปเรียนต่อต่างประเทศ เหมาะไหม",
|
469 |
+
"ควรซื้อรถไหม",
|
470 |
+
"ควรขาย��ี่ดินไหม",
|
471 |
+
"ควรเริ่มทำอะไรใหม่ๆ ไหม",
|
472 |
+
"ควรรับข้อเสนอนี้ไหม",
|
473 |
+
"ควรไปหรือไม่ไป",
|
474 |
+
"ควรพูดความจริงไหม",
|
475 |
+
"ควรให้อภัยไหม",
|
476 |
+
"ควรเสี่ยงไหม",
|
477 |
+
"ควรรอหรือรีบทำ",
|
478 |
+
"ควรเลือกข้อ A หรือ B",
|
479 |
+
"ควรลงทะเบียนไหม",
|
480 |
+
"ควรเซ็นสัญญาไหม",
|
481 |
+
"ควรไว้ใจคนนี้ไหม",
|
482 |
+
"ควรบอกเลิกไหม",
|
483 |
+
"ควรเริ่มใหม่ไหม",
|
484 |
+
"ควรยอมรับไหม",
|
485 |
+
"ควรปฏิเสธไหม",
|
486 |
+
|
487 |
+
# Travel and relocation (expanded)
|
488 |
+
"จะได้ไปต่างประเทศไหม",
|
489 |
+
"ควรย้ายไปอยู่ที่อื่นไหม",
|
490 |
+
"การเดินทางจะปลอดภัยไหม",
|
491 |
+
"ทิศไหนเหมาะกับดวง",
|
492 |
+
"ควรอยู่ภาคไหนของประเทศ",
|
493 |
+
"จะได้ไปเที่ยวไหม",
|
494 |
+
"ควรไปเที่ยวประเทศไหน",
|
495 |
+
"จะมีปัญหาระหว่างเดินทางไหม",
|
496 |
+
"ควรเดินทางคนเดียวหรือกับคนอื่น",
|
497 |
+
"จะพลาดเที่ยวบินไหม",
|
498 |
+
"จะทำหนังสือเดินทางหายไหม",
|
499 |
+
"ควรพักโรงแรมหรือบ้านเพื่อน",
|
500 |
+
"จะมีอุบัติเหตุทางรถไหม",
|
501 |
+
"ควรขับรถเองหรือนั่งรถคนอื่น",
|
502 |
+
"จะติดอยู่ต่างประเทศไหม",
|
503 |
+
|
504 |
+
# Legal and disputes (expanded)
|
505 |
+
"คดีความจะชนะไหม",
|
506 |
+
"จะมีปัญหาทางกฎหมายไหม",
|
507 |
+
"ควรฟ้องร้องไหม",
|
508 |
+
"จะเจอคนโกงไหม",
|
509 |
+
"จะมีปัญหากับเจ้าหน้าที่ไหม",
|
510 |
+
"จะถูกฟ้องไหม",
|
511 |
+
"ควรยอมความไหม",
|
512 |
+
"จะมีปัญหาเอกสารไหม",
|
513 |
+
"ทนายความจะช่วยได้ไหม",
|
514 |
+
"จะต้องขึ้นศาลไหม",
|
515 |
+
"จะมีปัญหาสัญญาไหม",
|
516 |
+
"จะถูกหลอกลวงไหม",
|
517 |
+
"จะมีคนมาเอาเปรียบไหม",
|
518 |
+
"ควรแจ้งความไหม",
|
519 |
+
"จะได้ความยุติธรรมไหม",
|
520 |
+
|
521 |
+
# Spiritual and luck (expanded)
|
522 |
+
"ธาตุอะไรเสริมดวง ควรใส่สีอะไร",
|
523 |
+
"เลขอะไรเป็นเลขมงคล",
|
524 |
+
"ควรบูชาพระเครื่องไหม",
|
525 |
+
"ควรทำบุญอะไร",
|
526 |
+
"มีกรรมเก่าอะไรติดตัวไหม",
|
527 |
+
"ควรแก้ชงอย่างไร",
|
528 |
+
"วันไหนเป็นวันดีของฉัน",
|
529 |
+
"ควรตั้งชื่อลูกว่าอะไร",
|
530 |
+
"ควรไหว้พระวันไหน",
|
531 |
+
"ควรสวดมนต์ไหม",
|
532 |
+
"จะมีสิ่งศักดิ์สิทธิ์คุ้มครองไหม",
|
533 |
+
"ควรใส่เครื่องรางไหม",
|
534 |
+
"ควรไปวัดไหนทำบุญ",
|
535 |
+
"จะมีบุญจากชาติก่อนไหม",
|
536 |
+
"ควรถือศีลไหม",
|
537 |
+
"จะมีเทพเจ้าช่วยไหม",
|
538 |
+
"ควรบนบานไหม",
|
539 |
+
"จะสมหวังที่บนไว้ไหม",
|
540 |
+
"ควรเลี้ยงสัตว์อะไร",
|
541 |
+
"ต้นไม้อะไรเสริมดวง",
|
542 |
+
|
543 |
+
# Personal development (expanded)
|
544 |
+
"จะพัฒนาตัวเองได้อย่างไร",
|
545 |
+
"จุดแข็งของฉันคืออะไร",
|
546 |
+
"จุดอ่อนที่ควรแก้ไขคืออะไร",
|
547 |
+
"จะมีชื่อเสียงไหม",
|
548 |
+
"จะประสบความสำเร็จในชีวิตไหม",
|
549 |
+
"ควรทำอะไรเพื่อเปลี่ยนชีว��ต",
|
550 |
+
"จะเป็นคนสำคัญไหม",
|
551 |
+
"จะมีคนนับถือไหม",
|
552 |
+
"ควรเรียนรู้อะไรเพิ่ม",
|
553 |
+
"จะมีความสามารถพิเศษไหม",
|
554 |
+
"ควรฝึกทักษะอะไร",
|
555 |
+
"จะเป็นผู้นำไหม",
|
556 |
+
"จะมีคนตามไหม",
|
557 |
+
"ควรเปลี่ยนนิสัยอะไร",
|
558 |
+
"จะมีความคิดสร้างสรรค์ไหม",
|
559 |
+
"ควรทำงานอดิเรกอะไร",
|
560 |
+
"จะเก่งด้านไหน",
|
561 |
+
"ควรออกจากคอมฟอร์ทโซนไหม",
|
562 |
+
"จะมีพรสวรรค์ด้านไหน",
|
563 |
+
"ควรฝึกสมาธิไหม",
|
564 |
+
|
565 |
+
# Crisis and problems (expanded)
|
566 |
+
"จะผ่านวิกฤตนี้ไปได้ไหม",
|
567 |
+
"ปัญหาจะจบเมื่อไหร่",
|
568 |
+
"จะมีคนช่วยเหลือไหม",
|
569 |
+
"ควรขอความช่วยเหลือจากใคร",
|
570 |
+
"สิ่งที่กังวลจะเกิดขึ้นจริงไหม",
|
571 |
+
"จะแก้ปัญหาได้ไหม",
|
572 |
+
"ควรหนีปัญหาไหม",
|
573 |
+
"จะมีทางออกไหม",
|
574 |
+
"ปัญหาจะซ้ำไหม",
|
575 |
+
"จะมีปัญหาใหม่ไหม",
|
576 |
+
"ควรเผชิญหน้ากับปัญหาไหม",
|
577 |
+
"จะมีคนมาสร้างปัญหาไหม",
|
578 |
+
"ปัญหาจะใหญ่ขึ้นไหม",
|
579 |
+
"ควรปล่อยวางไหม",
|
580 |
+
"จะเครียดมากไหม",
|
581 |
+
|
582 |
+
# Business specific
|
583 |
+
"ควรเปิดร้านอาหารไหม",
|
584 |
+
"ธุรกิจออนไลน์จะสำเร็จไหม",
|
585 |
+
"ควรขายของผ่านโซเชียลไหม",
|
586 |
+
"จะมีลูกค้าเยอะไหม",
|
587 |
+
"คู่แข่งจะมากไหม",
|
588 |
+
"ควรทำธุรกิจครอบครัวไหม",
|
589 |
+
"จะขาดทุนในปีแรกไหม",
|
590 |
+
"ควรขอสินเชื่อธุรกิจไหม",
|
591 |
+
"พนักงานจะซื่อสัตย์ไหม",
|
592 |
+
"ควรเปิดสาขาไหม",
|
593 |
+
|
594 |
+
# Technology and modern life
|
595 |
+
"ควรเรียนเขียนโปรแกรมไหม",
|
596 |
+
"จะประสบความสำเร็จในสายไอทีไหม",
|
597 |
+
"ควรทำช่อง YouTube ไหม",
|
598 |
+
"จะดังในโซเชียลมีเดียไหม",
|
599 |
+
"ควรเป็นอินฟลูเอนเซอร์ไหม",
|
600 |
+
"จะถูกแฮกข้อมูลไหม",
|
601 |
+
"ควรลงทุนใน NFT ไหม",
|
602 |
+
"จะติดเกมไหม",
|
603 |
+
"ควรทำงาน Work from home ไหม",
|
604 |
+
"จะมีปัญหากับเทคโนโลยีไหม",
|
605 |
+
|
606 |
+
# Lifestyle choices
|
607 |
+
"ควรเป็นมังสวิรัติไหม",
|
608 |
+
"ควรงดเหล้าไหม",
|
609 |
+
"ควรเลิกบุหรี่ไหม",
|
610 |
+
"ควรทำศัลยกรรมไหม",
|
611 |
+
"ควรลดน้ำหนักไหม",
|
612 |
+
"ควรย้อมผมไหม",
|
613 |
+
"ควรเลี้ยงสัตว์ไหม",
|
614 |
+
"ควรอยู่คอนโดหรือบ้าน",
|
615 |
+
"ควรมีรถไหม",
|
616 |
+
"ควรใช้ชีวิตแบบมินิมอลไหม",
|
617 |
+
|
618 |
+
# Social relationships
|
619 |
+
"จะมีเพื่อนแท้ไหม",
|
620 |
+
"เพื่อนจะทรยศไหม",
|
621 |
+
"ควรตัดขาดกับเพื่อนคนนี้ไหม",
|
622 |
+
"จะมีศัตรูไหม",
|
623 |
+
"คนรอบข้างจริงใจไหม",
|
624 |
+
"จะถูกนินทาไหม",
|
625 |
+
"จะมีคนอิจฉาไหม",
|
626 |
+
"ควรให้โอกาสเพื่อนไหม",
|
627 |
+
"จะโดนหักหลังไหม",
|
628 |
+
"ควรไว้ใจใครไหม",
|
629 |
+
|
630 |
+
# Special occasions
|
631 |
+
"งานแต่งจะสำเร็จไหม",
|
632 |
+
"ควรจัดงานใหญ่หรือเล็ก",
|
633 |
+
"จะมีคนมาร่วมงานเยอะไหม",
|
634 |
+
"ควรเลื��อนงานไหม",
|
635 |
+
"งานบวชจะราบรื่นไหม",
|
636 |
+
"ควรทำพิธีขึ้นบ้านใหม่ไหม",
|
637 |
+
"จะได้รับเชิญไปงานไหม",
|
638 |
+
"ควรไปงานศพไหม",
|
639 |
+
"วันเกิดปีนี้จะพิเศษไหม",
|
640 |
+
"ควรฉลองอะไรไหม",
|
641 |
+
|
642 |
+
# Retirement and elderly
|
643 |
+
"จะเกษียณสุขสบายไหม",
|
644 |
+
"ควรเกษียณเมื่อไหร่",
|
645 |
+
"จะมีเงินพอใช้ตอนแก่ไหม",
|
646 |
+
"ลูกจะดูแลไหม",
|
647 |
+
"ควรอยู่บ้านพักคนชราไหม",
|
648 |
+
"จะแก่อย่างมีความสุขไหม",
|
649 |
+
"จะมีโรคประจำตัวไหม",
|
650 |
+
"ควรทำพินัยกรรมไหม",
|
651 |
+
"จะได้เห็นหลานโตไหม",
|
652 |
+
"ควรย้ายไปอยู่ต่างจังหวัดไหม",
|
653 |
+
]
|
654 |
+
|
655 |
+
# Randomly select 3 questions
|
656 |
+
return random.sample(question_templates, 4)
|
657 |
+
|
658 |
+
def get_bazi_data(person: Dict) -> Dict:
|
659 |
+
"""Calculate BaZi data manually"""
|
660 |
+
|
661 |
+
# Prepare birth datetime
|
662 |
+
birth_date = datetime(
|
663 |
+
person['year'], person['month'], person['day'],
|
664 |
+
person['hour'], person['minute']
|
665 |
+
)
|
666 |
+
|
667 |
+
# Calculate BaZi pillars manually
|
668 |
+
birth_pillars = compute_birth_pillars(birth_date)
|
669 |
+
|
670 |
+
# Calculate favorable elements
|
671 |
+
fav_elements = calculate_favorable_elements(birth_pillars)
|
672 |
+
|
673 |
+
# Generate future BaZi data
|
674 |
+
calculator = BaziCalculator(
|
675 |
+
birth_datetime=birth_date,
|
676 |
+
gender=person['gender'],
|
677 |
+
birth_pillars=birth_pillars
|
678 |
+
)
|
679 |
+
|
680 |
+
# Import the generate function from api_v3
|
681 |
+
from api_v3 import generate_future_bazi_data
|
682 |
+
future_data = generate_future_bazi_data(calculator, years_ahead=100)
|
683 |
+
|
684 |
+
return {
|
685 |
+
'birth_pillars': birth_pillars,
|
686 |
+
'favorable_elements': fav_elements,
|
687 |
+
'future_data': future_data,
|
688 |
+
'birth_date': birth_date,
|
689 |
+
'calculator': calculator
|
690 |
+
}
|
691 |
+
|
692 |
+
def create_or_get_context_cache(system_prompt: str) -> str:
|
693 |
+
"""Create or retrieve context cache for the system prompt
|
694 |
+
|
695 |
+
Returns:
|
696 |
+
cache_name: The name/ID of the cached context
|
697 |
+
"""
|
698 |
+
global context_cache
|
699 |
+
|
700 |
+
with cache_lock:
|
701 |
+
if context_cache is not None:
|
702 |
+
with print_lock:
|
703 |
+
print(" Using existing context cache for system prompt")
|
704 |
+
return context_cache
|
705 |
+
|
706 |
+
try:
|
707 |
+
client = genai.Client(api_key=GEMINI_API_KEY)
|
708 |
+
|
709 |
+
# Create context cache with the system prompt
|
710 |
+
with print_lock:
|
711 |
+
print(" Creating context cache for system prompt (this saves API costs)...")
|
712 |
+
|
713 |
+
# Create cache with system instruction using the correct API format
|
714 |
+
# Contents is required even when using system_instruction
|
715 |
+
cache = client.caches.create(
|
716 |
+
model="gemini-2.5-pro", # Use explicit version with context caching support
|
717 |
+
config=types.CreateCachedContentConfig(
|
718 |
+
display_name='bazi_system_prompt', # Identifier for the cache
|
719 |
+
system_instruction=system_prompt, # The long system prompt to cache
|
720 |
+
contents=[
|
721 |
+
types.Content(
|
722 |
+
role="user",
|
723 |
+
parts=[types.Part(text="System prompt loaded.")],
|
724 |
+
)
|
725 |
+
], # Minimal content required by API
|
726 |
+
ttl="72000s", # Cache for 20 hour (in seconds string format)
|
727 |
+
)
|
728 |
+
)
|
729 |
+
|
730 |
+
context_cache = cache.name
|
731 |
+
|
732 |
+
with print_lock:
|
733 |
+
print(f" Context cache created: {context_cache}")
|
734 |
+
print(f" Cache will save ~{len(system_prompt)//4} tokens per request")
|
735 |
+
|
736 |
+
return context_cache
|
737 |
+
|
738 |
+
except Exception as e:
|
739 |
+
with print_lock:
|
740 |
+
print(f" Warning: Could not create context cache: {e}")
|
741 |
+
print(" Falling back to regular API calls (higher cost)")
|
742 |
+
return None
|
743 |
+
|
744 |
+
def call_gemini_api_with_cache(user_prompt: str, cache_name: str = None, system_prompt: str = None, max_retries: int = 3) -> Tuple[str, Optional[str]]:
|
745 |
+
"""Call Google Gemini using context caching for cost savings
|
746 |
+
|
747 |
+
Returns:
|
748 |
+
Tuple of (response_text, new_cache_name or None)
|
749 |
+
"""
|
750 |
+
client = genai.Client(api_key=GEMINI_API_KEY)
|
751 |
+
|
752 |
+
for attempt in range(max_retries):
|
753 |
+
try:
|
754 |
+
# Generate response using cached context if available
|
755 |
+
if cache_name:
|
756 |
+
try:
|
757 |
+
# Use cached context - must use same model as cache
|
758 |
+
model = "gemini-2.5-pro"
|
759 |
+
|
760 |
+
# Prepare content for the user prompt
|
761 |
+
contents = [
|
762 |
+
types.Content(
|
763 |
+
role="user",
|
764 |
+
parts=[types.Part(text=user_prompt)],
|
765 |
+
)
|
766 |
+
]
|
767 |
+
|
768 |
+
config = types.GenerateContentConfig(
|
769 |
+
temperature=0.7,
|
770 |
+
top_k=40,
|
771 |
+
top_p=0.95,
|
772 |
+
max_output_tokens=7500,
|
773 |
+
cached_content=cache_name # Reference the cached content
|
774 |
+
)
|
775 |
+
|
776 |
+
# Generate with cached context
|
777 |
+
response = client.models.generate_content(
|
778 |
+
model=model,
|
779 |
+
contents=contents, # Use Content object format
|
780 |
+
config=config
|
781 |
+
)
|
782 |
+
|
783 |
+
# Get the text from response
|
784 |
+
if response.text:
|
785 |
+
return response.text, cache_name # Return existing cache name
|
786 |
+
else:
|
787 |
+
raise Exception("No response text generated")
|
788 |
+
|
789 |
+
except Exception as cache_error:
|
790 |
+
# If cache fails, try to recreate it
|
791 |
+
if "PERMISSION_DENIED" in str(cache_error) or "not found" in str(cache_error).lower():
|
792 |
+
with print_lock:
|
793 |
+
print(f" Cache expired or not found, recreating cache...")
|
794 |
+
|
795 |
+
# Try to recreate cache if system_prompt is provided
|
796 |
+
if system_prompt:
|
797 |
+
new_cache = create_or_get_context_cache(system_prompt)
|
798 |
+
if new_cache:
|
799 |
+
cache_name = new_cache
|
800 |
+
with print_lock:
|
801 |
+
print(f" Cache recreated successfully: {new_cache}")
|
802 |
+
# Retry with new cache
|
803 |
+
continue
|
804 |
+
|
805 |
+
# If no system_prompt or cache creation failed, use regular API
|
806 |
+
cache_name = None
|
807 |
+
else:
|
808 |
+
raise cache_error
|
809 |
+
|
810 |
+
# If cache_name is None or cache failed, use regular API
|
811 |
+
if not cache_name:
|
812 |
+
# Fallback to regular call without caching
|
813 |
+
contents = [
|
814 |
+
types.Content(
|
815 |
+
role="user",
|
816 |
+
parts=[
|
817 |
+
types.Part(text=user_prompt),
|
818 |
+
],
|
819 |
+
),
|
820 |
+
]
|
821 |
+
|
822 |
+
config = types.GenerateContentConfig(
|
823 |
+
temperature=0.7,
|
824 |
+
top_k=40,
|
825 |
+
top_p=0.95,
|
826 |
+
max_output_tokens=7500,
|
827 |
+
)
|
828 |
+
|
829 |
+
# Generate response (collecting all chunks)
|
830 |
+
response_text = ""
|
831 |
+
for chunk in client.models.generate_content_stream(
|
832 |
+
model="gemini-2.5-pro", # Use 2.5 pro for non-cached
|
833 |
+
contents=contents,
|
834 |
+
config=config,
|
835 |
+
):
|
836 |
+
if chunk.text:
|
837 |
+
response_text += chunk.text
|
838 |
+
|
839 |
+
if response_text:
|
840 |
+
return response_text, None # No cache used
|
841 |
+
else:
|
842 |
+
raise Exception("No response text generated")
|
843 |
+
|
844 |
+
except Exception as e:
|
845 |
+
error_str = str(e)
|
846 |
+
if attempt < max_retries - 1 and ("503" in error_str or "overloaded" in error_str.lower()):
|
847 |
+
wait_time = (attempt + 1) * 2
|
848 |
+
print(f" API overloaded. Retrying in {wait_time} seconds...")
|
849 |
+
import time
|
850 |
+
time.sleep(wait_time)
|
851 |
+
else:
|
852 |
+
raise Exception(f"Gemini API error: {e}")
|
853 |
+
|
854 |
+
# Keep old function for compatibility
|
855 |
+
def call_gemini_api(prompt: str, max_retries: int = 3) -> str:
|
856 |
+
"""Legacy function - calls the new cached version"""
|
857 |
+
response, _ = call_gemini_api_with_cache(prompt, None, None, max_retries)
|
858 |
+
return response
|
859 |
+
|
860 |
+
def generate_dataset_entry(person: Dict, question: str, system_prompt: str, entry_id: str, current: int, total: int, cache_name: str = None) -> Tuple[str, Optional[Dict]]:
|
861 |
+
"""Generate a single dataset entry (thread-safe) with context caching
|
862 |
+
|
863 |
+
Returns:
|
864 |
+
Tuple of (entry_id, entry_dict or None)
|
865 |
+
"""
|
866 |
+
global last_api_call_time, context_cache
|
867 |
+
|
868 |
+
try:
|
869 |
+
with print_lock:
|
870 |
+
print(f" [{current}/{total}] Thread-{threading.current_thread().name}: Processing {question[:50]}...")
|
871 |
+
|
872 |
+
# Get BaZi data (this is computational, no API call)
|
873 |
+
bazi_data = get_bazi_data(person)
|
874 |
+
|
875 |
+
# Format BaZi data in production style
|
876 |
+
bazi_formatted = format_bazi_production_style(person, bazi_data)
|
877 |
+
|
878 |
+
# Format user prompt in production style
|
879 |
+
user_prompt = f"""{bazi_formatted}
|
880 |
+
|
881 |
+
Question: {question}"""
|
882 |
+
|
883 |
+
# Rate limiting for API calls
|
884 |
+
with api_call_lock:
|
885 |
+
current_time = time.time()
|
886 |
+
if last_api_call_time > 0:
|
887 |
+
elapsed = current_time - last_api_call_time
|
888 |
+
if elapsed < RATE_LIMIT_DELAY:
|
889 |
+
time.sleep(RATE_LIMIT_DELAY - elapsed)
|
890 |
+
last_api_call_time = time.time()
|
891 |
+
|
892 |
+
# Get response from Gemini using context caching
|
893 |
+
if cache_name:
|
894 |
+
# When using cache, only send the user prompt
|
895 |
+
response, new_cache = call_gemini_api_with_cache(user_prompt, cache_name, system_prompt)
|
896 |
+
# Update global cache if it was recreated
|
897 |
+
if new_cache and new_cache != cache_name:
|
898 |
+
with cache_lock:
|
899 |
+
context_cache = new_cache
|
900 |
+
with print_lock:
|
901 |
+
print(f" Updated global cache to: {new_cache}")
|
902 |
+
else:
|
903 |
+
# Fallback to combined prompt
|
904 |
+
full_prompt = f"{system_prompt}\n\n---\n\n{user_prompt}"
|
905 |
+
response, _ = call_gemini_api_with_cache(full_prompt, None, None)
|
906 |
+
|
907 |
+
with print_lock:
|
908 |
+
print(f" Thread-{threading.current_thread().name}: Successfully generated response")
|
909 |
+
|
910 |
+
# Format as OpenAI training data
|
911 |
+
return (entry_id, {
|
912 |
+
"messages": [
|
913 |
+
{"role": "system", "content": system_prompt},
|
914 |
+
{"role": "user", "content": user_prompt},
|
915 |
+
{"role": "assistant", "content": response}
|
916 |
+
]
|
917 |
+
})
|
918 |
+
|
919 |
+
except Exception as e:
|
920 |
+
with print_lock:
|
921 |
+
print(f" Thread-{threading.current_thread().name}: Error - {str(e)}")
|
922 |
+
return (entry_id, None)
|
923 |
+
|
924 |
+
def write_entry_to_file(output_file: str, entry: Dict) -> None:
|
925 |
+
"""Thread-safe writing to file"""
|
926 |
+
with file_lock:
|
927 |
+
with open(output_file, 'a', encoding='utf-8') as f:
|
928 |
+
f.write(json.dumps(entry, ensure_ascii=False) + '\n')
|
929 |
+
f.flush()
|
930 |
+
|
931 |
+
def save_progress(progress_file: str, processed_entries: set) -> None:
|
932 |
+
"""Thread-safe progress saving"""
|
933 |
+
with progress_lock:
|
934 |
+
with open(progress_file, 'w') as pf:
|
935 |
+
json.dump({'processed': list(processed_entries)}, pf)
|
936 |
+
|
937 |
+
def main():
|
938 |
+
"""Main function to generate the dataset with multithreading and context caching"""
|
939 |
+
print(f"Starting dataset generation with {MAX_WORKERS} worker threads...")
|
940 |
+
print(f"Using Google Gemini Context Caching to reduce API costs\n")
|
941 |
+
|
942 |
+
# Load system prompt
|
943 |
+
system_prompt = load_system_prompt()
|
944 |
+
print(f"System prompt loaded: {len(system_prompt)} characters")
|
945 |
+
|
946 |
+
# Create context cache for the system prompt to save costs
|
947 |
+
cache_name = create_or_get_context_cache(system_prompt)
|
948 |
+
if cache_name:
|
949 |
+
print(f"✓ Context caching enabled - will save ~{len(system_prompt)//4} tokens per request\n")
|
950 |
+
else:
|
951 |
+
print("⚠ Context caching disabled - using regular API calls\n")
|
952 |
+
|
953 |
+
# Output file - use absolute paths based on script location
|
954 |
+
script_dir = os.path.dirname(os.path.abspath(__file__))
|
955 |
+
output_file = os.path.join(script_dir, "bazi_training_data.jsonl")
|
956 |
+
progress_file = os.path.join(script_dir, ".generation_progress.json")
|
957 |
+
|
958 |
+
# Load progress if exists
|
959 |
+
processed_entries = set()
|
960 |
+
if os.path.exists(progress_file):
|
961 |
+
try:
|
962 |
+
with open(progress_file, 'r') as f:
|
963 |
+
progress_data = json.load(f)
|
964 |
+
processed_entries = set(progress_data.get('processed', []))
|
965 |
+
print(f"Resuming from previous run. Already processed: {len(processed_entries)} entries")
|
966 |
+
except:
|
967 |
+
processed_entries = set()
|
968 |
+
|
969 |
+
# Count existing entries in JSONL
|
970 |
+
existing_count = 0
|
971 |
+
if os.path.exists(output_file):
|
972 |
+
with open(output_file, 'r', encoding='utf-8') as f:
|
973 |
+
existing_count = sum(1 for line in f if line.strip())
|
974 |
+
print(f"Found {existing_count} existing entries in {output_file}")
|
975 |
+
|
976 |
+
# Generate random birthdays with seed for consistency
|
977 |
+
random.seed(18) # Fixed seed for reproducible birthdays
|
978 |
+
birthdays = generate_random_birthdays(500)
|
979 |
+
print(f"Generated {len(birthdays)} random birthdays")
|
980 |
+
|
981 |
+
# Prepare all tasks
|
982 |
+
tasks = []
|
983 |
+
current = 0
|
984 |
+
total = len(birthdays) * 4 # 4 questions per birthday
|
985 |
+
|
986 |
+
for person in birthdays:
|
987 |
+
# Use seed based on person for consistent questions
|
988 |
+
person_seed = hash(f"{person['name']}_{person['year']}_{person['month']}_{person['day']}")
|
989 |
+
random.seed(person_seed)
|
990 |
+
questions = generate_questions()
|
991 |
+
|
992 |
+
for question in questions:
|
993 |
+
current += 1
|
994 |
+
# Create unique ID for this entry
|
995 |
+
entry_id = f"{person['name']}_{person['year']}{person['month']:02d}{person['day']:02d}_{hash(question)}"
|
996 |
+
|
997 |
+
# Skip if already processed
|
998 |
+
if entry_id not in processed_entries:
|
999 |
+
tasks.append((person, question, system_prompt, entry_id, current, total))
|
1000 |
+
|
1001 |
+
print(f"\nTotal tasks to process: {len(tasks)}")
|
1002 |
+
print(f"Already processed: {len(processed_entries)}")
|
1003 |
+
print(f"Remaining: {len(tasks)}")
|
1004 |
+
|
1005 |
+
if not tasks:
|
1006 |
+
print("No new tasks to process. Exiting.")
|
1007 |
+
return
|
1008 |
+
|
1009 |
+
# Process tasks with thread pool
|
1010 |
+
generated_count = 0
|
1011 |
+
failed_count = 0
|
1012 |
+
|
1013 |
+
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
|
1014 |
+
# Submit all tasks
|
1015 |
+
future_to_task = {}
|
1016 |
+
for task in tasks:
|
1017 |
+
person, question, system_prompt, entry_id, idx, total = task
|
1018 |
+
# Use the latest cache_name (might be updated by other threads)
|
1019 |
+
current_cache = context_cache if context_cache else cache_name
|
1020 |
+
future = executor.submit(generate_dataset_entry, person, question, system_prompt, entry_id, idx, total, current_cache)
|
1021 |
+
future_to_task[future] = (person, question, entry_id)
|
1022 |
+
|
1023 |
+
# Process completed tasks
|
1024 |
+
for future in as_completed(future_to_task):
|
1025 |
+
person, question, entry_id = future_to_task[future]
|
1026 |
+
|
1027 |
+
try:
|
1028 |
+
result_entry_id, entry = future.result(timeout=600) # 10 minute timeout per task
|
1029 |
+
|
1030 |
+
if entry:
|
1031 |
+
# Write to file
|
1032 |
+
write_entry_to_file(output_file, entry)
|
1033 |
+
|
1034 |
+
# Update progress
|
1035 |
+
with progress_lock:
|
1036 |
+
processed_entries.add(result_entry_id)
|
1037 |
+
generated_count += 1
|
1038 |
+
|
1039 |
+
# Save progress after EVERY entry (not just every 10)
|
1040 |
+
save_progress(progress_file, processed_entries)
|
1041 |
+
|
1042 |
+
with print_lock:
|
1043 |
+
print(f"✓ Saved entry #{existing_count + generated_count}: {person['name']} - {question[:30]}...")
|
1044 |
+
else:
|
1045 |
+
failed_count += 1
|
1046 |
+
with print_lock:
|
1047 |
+
print(f"✗ Failed: {person['name']} - {question[:30]}...")
|
1048 |
+
|
1049 |
+
except Exception as e:
|
1050 |
+
failed_count += 1
|
1051 |
+
with print_lock:
|
1052 |
+
print(f"✗ Error processing {person['name']}: {str(e)}")
|
1053 |
+
|
1054 |
+
# Final progress save
|
1055 |
+
if generated_count > 0:
|
1056 |
+
save_progress(progress_file, processed_entries)
|
1057 |
+
|
1058 |
+
print(f"\n" + "="*60)
|
1059 |
+
print(f"Dataset generation complete!")
|
1060 |
+
print(f"Generated: {generated_count} new entries")
|
1061 |
+
print(f"Failed: {failed_count} entries")
|
1062 |
+
print(f"Total entries in file: {existing_count + generated_count}")
|
1063 |
+
print(f"Saved to: {output_file}")
|
1064 |
+
|
1065 |
+
# Clean up progress file if completed successfully
|
1066 |
+
if len(processed_entries) >= total:
|
1067 |
+
if os.path.exists(progress_file):
|
1068 |
+
os.remove(progress_file)
|
1069 |
+
print("Cleaned up progress file (generation complete)")
|
1070 |
+
|
1071 |
+
if __name__ == "__main__":
|
1072 |
+
main()
|
test_format.py
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""Test BaZi format generation"""
|
3 |
+
|
4 |
+
import sys
|
5 |
+
import os
|
6 |
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
7 |
+
|
8 |
+
from datetime import datetime
|
9 |
+
from bazi_calculator import BaziCalculator, compute_birth_pillars
|
10 |
+
from bazi_favorable_elements import calculate_favorable_elements
|
11 |
+
from api_v3 import generate_future_bazi_data
|
12 |
+
|
13 |
+
# Test person
|
14 |
+
person = {
|
15 |
+
'name': 'TestUser',
|
16 |
+
'gender': 'Male',
|
17 |
+
'year': 1990,
|
18 |
+
'month': 5,
|
19 |
+
'day': 15,
|
20 |
+
'hour': 10,
|
21 |
+
'minute': 30,
|
22 |
+
'is_twin': False
|
23 |
+
}
|
24 |
+
|
25 |
+
# Prepare birth datetime
|
26 |
+
birth_date = datetime(
|
27 |
+
person['year'], person['month'], person['day'],
|
28 |
+
person['hour'], person['minute']
|
29 |
+
)
|
30 |
+
|
31 |
+
print(f"Birth date: {birth_date}")
|
32 |
+
|
33 |
+
# Calculate BaZi pillars manually
|
34 |
+
birth_pillars = compute_birth_pillars(birth_date)
|
35 |
+
print(f"Birth pillars: {birth_pillars}")
|
36 |
+
|
37 |
+
# Calculate favorable elements
|
38 |
+
fav_elements = calculate_favorable_elements(birth_pillars)
|
39 |
+
print(f"Favorable elements: {fav_elements}")
|
40 |
+
|
41 |
+
# Generate future BaZi data
|
42 |
+
calculator = BaziCalculator(
|
43 |
+
birth_datetime=birth_date,
|
44 |
+
gender=person['gender'],
|
45 |
+
birth_pillars=birth_pillars
|
46 |
+
)
|
47 |
+
|
48 |
+
print("Generating future data...")
|
49 |
+
future_data = generate_future_bazi_data(calculator, years_ahead=100)
|
50 |
+
|
51 |
+
print(f"Generated {len(future_data['luck_pillars'])} luck pillars")
|
52 |
+
print(f"Generated {len(future_data['annual_pillars'])} annual pillars")
|
53 |
+
print(f"Generated {len(future_data['monthly_pillars'])} monthly pillars")
|
54 |
+
print(f"Generated {len(future_data['daily_pillars'])} daily pillars")
|
55 |
+
|
56 |
+
# Now format it
|
57 |
+
from dataset.dataset_generator import format_bazi_production_style
|
58 |
+
|
59 |
+
bazi_data = {
|
60 |
+
'birth_pillars': birth_pillars,
|
61 |
+
'favorable_elements': fav_elements,
|
62 |
+
'future_data': future_data,
|
63 |
+
'birth_date': birth_date,
|
64 |
+
'calculator': calculator
|
65 |
+
}
|
66 |
+
|
67 |
+
formatted = format_bazi_production_style(person, bazi_data)
|
68 |
+
print("\n\nFormatted output:")
|
69 |
+
print("=" * 50)
|
70 |
+
print(formatted)
|
test_gemini25.py
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""Test Gemini 2.5 Pro with google-genai"""
|
3 |
+
|
4 |
+
import os
|
5 |
+
from google import genai
|
6 |
+
from google.genai import types
|
7 |
+
|
8 |
+
# Set API key
|
9 |
+
os.environ["GEMINI_API_KEY"] = "AIzaSyCqe3vjvPlo1lt_hpQ4nqAC0-_1omva1oc"
|
10 |
+
|
11 |
+
def test_generate():
|
12 |
+
print("Testing Gemini 2.5 Pro...")
|
13 |
+
|
14 |
+
client = genai.Client(
|
15 |
+
api_key=os.environ.get("GEMINI_API_KEY"),
|
16 |
+
)
|
17 |
+
|
18 |
+
model = "gemini-2.5-pro"
|
19 |
+
contents = [
|
20 |
+
types.Content(
|
21 |
+
role="user",
|
22 |
+
parts=[
|
23 |
+
types.Part.from_text(text="Say hello in exactly 5 words"),
|
24 |
+
],
|
25 |
+
),
|
26 |
+
]
|
27 |
+
|
28 |
+
generate_content_config = types.GenerateContentConfig(
|
29 |
+
temperature=0.7,
|
30 |
+
max_output_tokens=100,
|
31 |
+
)
|
32 |
+
|
33 |
+
print(f"Calling {model}...")
|
34 |
+
response_text = ""
|
35 |
+
|
36 |
+
try:
|
37 |
+
for chunk in client.models.generate_content_stream(
|
38 |
+
model=model,
|
39 |
+
contents=contents,
|
40 |
+
config=generate_content_config,
|
41 |
+
):
|
42 |
+
if chunk.text:
|
43 |
+
response_text += chunk.text
|
44 |
+
print(f"Chunk: {chunk.text}")
|
45 |
+
|
46 |
+
print(f"\nFull response: {response_text}")
|
47 |
+
except Exception as e:
|
48 |
+
print(f"Error: {e}")
|
49 |
+
|
50 |
+
if __name__ == "__main__":
|
51 |
+
test_generate()
|
test_gen.py
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""Test generation to see where it hangs"""
|
3 |
+
|
4 |
+
import sys
|
5 |
+
import os
|
6 |
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
7 |
+
|
8 |
+
import asyncio
|
9 |
+
from datetime import datetime
|
10 |
+
import json
|
11 |
+
import httpx
|
12 |
+
|
13 |
+
# Gemini API Configuration
|
14 |
+
GEMINI_API_KEY = "AIzaSyCqe3vjvPlo1lt_hpQ4nqAC0-_1omva1oc"
|
15 |
+
GEMINI_ENDPOINT = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent"
|
16 |
+
|
17 |
+
async def test_gemini():
|
18 |
+
"""Test Gemini API call"""
|
19 |
+
print("Testing Gemini API...")
|
20 |
+
|
21 |
+
prompt = "Say hello in 5 words"
|
22 |
+
|
23 |
+
headers = {
|
24 |
+
"Content-Type": "application/json"
|
25 |
+
}
|
26 |
+
|
27 |
+
data = {
|
28 |
+
"contents": [{
|
29 |
+
"parts": [{
|
30 |
+
"text": prompt
|
31 |
+
}]
|
32 |
+
}],
|
33 |
+
"generationConfig": {
|
34 |
+
"temperature": 0.7,
|
35 |
+
"topK": 40,
|
36 |
+
"topP": 0.95,
|
37 |
+
"maxOutputTokens": 100,
|
38 |
+
},
|
39 |
+
"safetySettings": [
|
40 |
+
{"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"},
|
41 |
+
{"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"},
|
42 |
+
{"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_NONE"},
|
43 |
+
{"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"}
|
44 |
+
]
|
45 |
+
}
|
46 |
+
|
47 |
+
print(f"Calling Gemini at: {GEMINI_ENDPOINT}")
|
48 |
+
|
49 |
+
try:
|
50 |
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
51 |
+
response = await client.post(
|
52 |
+
f"{GEMINI_ENDPOINT}?key={GEMINI_API_KEY}",
|
53 |
+
headers=headers,
|
54 |
+
json=data
|
55 |
+
)
|
56 |
+
|
57 |
+
print(f"Response status: {response.status_code}")
|
58 |
+
|
59 |
+
if response.status_code == 200:
|
60 |
+
result = response.json()
|
61 |
+
print(f"Full response: {json.dumps(result, indent=2)}")
|
62 |
+
if 'candidates' in result and len(result['candidates']) > 0:
|
63 |
+
candidate = result['candidates'][0]
|
64 |
+
if 'content' in candidate and 'parts' in candidate['content']:
|
65 |
+
text = candidate['content']['parts'][0]['text']
|
66 |
+
print(f"Extracted text: {text}")
|
67 |
+
else:
|
68 |
+
print(f"No content/parts in candidate: {candidate}")
|
69 |
+
else:
|
70 |
+
print(f"No candidates in result")
|
71 |
+
else:
|
72 |
+
print(f"Error: {response.text}")
|
73 |
+
except Exception as e:
|
74 |
+
print(f"Exception: {e}")
|
75 |
+
|
76 |
+
if __name__ == "__main__":
|
77 |
+
asyncio.run(test_gemini())
|