generated from nhcarrigan/template
feat(tarot): expand spread descriptions and add good-for guidance
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 49s
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 49s
Each spread now has a fuller description and a "Good for:" line explaining what kinds of questions it suits best. ✨ This commit was made with love from Hikari~ 🌸
This commit is contained in:
@@ -0,0 +1,157 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate position-specific tarot card meanings for all 78 cards across all spreads."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import anthropic
|
||||||
|
|
||||||
|
client = anthropic.Anthropic(api_key="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")
|
||||||
|
|
||||||
|
CARDS = [
|
||||||
|
"the_fool", "the_magician", "the_high_priestess", "the_empress", "the_emperor",
|
||||||
|
"the_hierophant", "the_lovers", "the_chariot", "strength", "the_hermit",
|
||||||
|
"wheel_of_fortune", "justice", "the_hanged_man", "death", "temperance",
|
||||||
|
"the_devil", "the_tower", "the_star", "the_moon", "the_sun",
|
||||||
|
"judgement", "the_world",
|
||||||
|
"ace_of_wands", "two_of_wands", "three_of_wands", "four_of_wands", "five_of_wands",
|
||||||
|
"six_of_wands", "seven_of_wands", "eight_of_wands", "nine_of_wands", "ten_of_wands",
|
||||||
|
"page_of_wands", "knight_of_wands", "queen_of_wands", "king_of_wands",
|
||||||
|
"ace_of_cups", "two_of_cups", "three_of_cups", "four_of_cups", "five_of_cups",
|
||||||
|
"six_of_cups", "seven_of_cups", "eight_of_cups", "nine_of_cups", "ten_of_cups",
|
||||||
|
"page_of_cups", "knight_of_cups", "queen_of_cups", "king_of_cups",
|
||||||
|
"ace_of_swords", "two_of_swords", "three_of_swords", "four_of_swords", "five_of_swords",
|
||||||
|
"six_of_swords", "seven_of_swords", "eight_of_swords", "nine_of_swords", "ten_of_swords",
|
||||||
|
"page_of_swords", "knight_of_swords", "queen_of_swords", "king_of_swords",
|
||||||
|
"ace_of_pentacles", "two_of_pentacles", "three_of_pentacles", "four_of_pentacles", "five_of_pentacles",
|
||||||
|
"six_of_pentacles", "seven_of_pentacles", "eight_of_pentacles", "nine_of_pentacles", "ten_of_pentacles",
|
||||||
|
"page_of_pentacles", "knight_of_pentacles", "queen_of_pentacles", "king_of_pentacles",
|
||||||
|
]
|
||||||
|
|
||||||
|
POSITIONS = {
|
||||||
|
"single": "The Card (Single Card Draw)",
|
||||||
|
"three-card-past": "Past (Three Card Spread)",
|
||||||
|
"three-card-present": "Present (Three Card Spread)",
|
||||||
|
"three-card-future": "Future (Three Card Spread)",
|
||||||
|
"love-personalities": "Separate Personalities (Three Card Love Spread)",
|
||||||
|
"love-common-base": "Common Base of the Relationship (Three Card Love Spread)",
|
||||||
|
"love-connection": "The Binding Connection (Three Card Love Spread)",
|
||||||
|
"five-card-situation": "The Current Situation (Five Card Spread)",
|
||||||
|
"five-card-response": "The Response (Five Card Spread)",
|
||||||
|
"five-card-holding-back": "What Is Holding You Back (Five Card Spread)",
|
||||||
|
"five-card-change": "What You Can Do to Change the Situation (Five Card Spread)",
|
||||||
|
"five-card-outcome": "The Outcome If You Make That Change (Five Card Spread)",
|
||||||
|
"compat-your-mind": "Your Mind / Crown Chakra (Compatibility H Spread)",
|
||||||
|
"compat-your-heart": "Your Heart Chakra (Compatibility H Spread)",
|
||||||
|
"compat-your-root": "Your Root Chakra / Sexual Compatibility (Compatibility H Spread)",
|
||||||
|
"compat-bridge": "What Keeps You Together (Compatibility H Spread)",
|
||||||
|
"compat-their-mind": "Their Mind / Crown Chakra (Compatibility H Spread)",
|
||||||
|
"compat-their-heart": "Their Heart Chakra (Compatibility H Spread)",
|
||||||
|
"compat-their-root": "Their Root Chakra / Sexual Compatibility (Compatibility H Spread)",
|
||||||
|
"horseshoe-1": "Position 1: The Past (Horseshoe Spread)",
|
||||||
|
"horseshoe-2": "Position 2: The Present (Horseshoe Spread)",
|
||||||
|
"horseshoe-3": "Position 3: Hidden Influences (Horseshoe Spread)",
|
||||||
|
"horseshoe-4": "Position 4: The Querent (Horseshoe Spread)",
|
||||||
|
"horseshoe-5": "Position 5: Attitudes of Others (Horseshoe Spread)",
|
||||||
|
"horseshoe-6": "Position 6: What Should Be Done (Horseshoe Spread)",
|
||||||
|
"horseshoe-7": "Position 7: The Likely Outcome (Horseshoe Spread)",
|
||||||
|
"astro-aries": "Aries — House of Self (Astrological Spread)",
|
||||||
|
"astro-taurus": "Taurus — House of Possessions (Astrological Spread)",
|
||||||
|
"astro-gemini": "Gemini — House of Communication (Astrological Spread)",
|
||||||
|
"astro-cancer": "Cancer — House of Home (Astrological Spread)",
|
||||||
|
"astro-leo": "Leo — House of Pleasure (Astrological Spread)",
|
||||||
|
"astro-virgo": "Virgo — House of Health & Service (Astrological Spread)",
|
||||||
|
"astro-libra": "Libra — House of Partnerships (Astrological Spread)",
|
||||||
|
"astro-scorpio": "Scorpio — House of Transformation (Astrological Spread)",
|
||||||
|
"astro-sagittarius": "Sagittarius — House of Philosophy (Astrological Spread)",
|
||||||
|
"astro-capricorn": "Capricorn — House of Career (Astrological Spread)",
|
||||||
|
"astro-aquarius": "Aquarius — House of Community (Astrological Spread)",
|
||||||
|
"astro-pisces": "Pisces — House of the Unconscious (Astrological Spread)",
|
||||||
|
"celtic-present": "The Present Situation (Celtic Cross)",
|
||||||
|
"celtic-challenge": "The Challenge / Crossing Card (Celtic Cross)",
|
||||||
|
"celtic-past": "The Recent Past (Celtic Cross)",
|
||||||
|
"celtic-future": "The Near Future (Celtic Cross)",
|
||||||
|
"celtic-above": "Above — Conscious Goal (Celtic Cross)",
|
||||||
|
"celtic-below": "Below — Subconscious Influences (Celtic Cross)",
|
||||||
|
"celtic-advice": "Advice (Celtic Cross)",
|
||||||
|
"celtic-external": "External Influences (Celtic Cross)",
|
||||||
|
"celtic-hopes": "Hopes and Fears (Celtic Cross)",
|
||||||
|
"celtic-outcome": "The Final Outcome (Celtic Cross)",
|
||||||
|
}
|
||||||
|
|
||||||
|
def card_display_name(card_key: str) -> str:
|
||||||
|
return card_key.replace("_", " ").title()
|
||||||
|
|
||||||
|
def generate_meanings(card: str, positions: dict) -> dict:
|
||||||
|
card_name = card_display_name(card)
|
||||||
|
positions_list = "\n".join(
|
||||||
|
f'- "{key}": {label}' for key, label in positions.items()
|
||||||
|
)
|
||||||
|
|
||||||
|
position_properties = {
|
||||||
|
key: {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"regular": {"type": "string", "description": f"Upright meaning of {card_name} in the {label} position"},
|
||||||
|
"inverted": {"type": "string", "description": f"Reversed meaning of {card_name} in the {label} position"},
|
||||||
|
},
|
||||||
|
"required": ["regular", "inverted"],
|
||||||
|
}
|
||||||
|
for key, label in positions.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
tool = {
|
||||||
|
"name": "save_card_meanings",
|
||||||
|
"description": "Save position-specific tarot card meanings",
|
||||||
|
"input_schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": position_properties,
|
||||||
|
"required": list(positions.keys()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
response = client.messages.create(
|
||||||
|
model="claude-haiku-4-5-20251001",
|
||||||
|
max_tokens=8192,
|
||||||
|
tools=[tool],
|
||||||
|
tool_choice={"type": "tool", "name": "save_card_meanings"},
|
||||||
|
messages=[{
|
||||||
|
"role": "user",
|
||||||
|
"content": (
|
||||||
|
f'You are a knowledgeable tarot reader. Generate specific, meaningful 2-3 sentence interpretations '
|
||||||
|
f'for the tarot card "{card_name}" in each spread position. Each interpretation must be tailored to '
|
||||||
|
f'the card\'s traditional symbolism and the specific role of that position.\n\nPositions:\n{positions_list}'
|
||||||
|
),
|
||||||
|
}],
|
||||||
|
)
|
||||||
|
|
||||||
|
tool_use_block = next(b for b in response.content if b.type == "tool_use")
|
||||||
|
return tool_use_block.input
|
||||||
|
|
||||||
|
def main():
|
||||||
|
output_dir = os.path.join(os.path.dirname(__file__), "cards")
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
|
||||||
|
for i, card in enumerate(CARDS):
|
||||||
|
output_path = os.path.join(output_dir, f"{card}.json")
|
||||||
|
if os.path.exists(output_path):
|
||||||
|
print(f"[{i+1}/{len(CARDS)}] Skipping {card} (already exists)")
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(f"[{i+1}/{len(CARDS)}] Generating {card}...")
|
||||||
|
try:
|
||||||
|
meanings = generate_meanings(card, POSITIONS)
|
||||||
|
with open(output_path, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(meanings, f, indent=2, ensure_ascii=False)
|
||||||
|
print(f" ✓ Saved {card}.json")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ✗ Error for {card}: {e}")
|
||||||
|
|
||||||
|
# Small delay to be kind to the API
|
||||||
|
if i < len(CARDS) - 1:
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
print("\nDone!")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
+1069
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user