Compare commits
19 Commits
6dc8672dd8
...
master_sna
| Author | SHA1 | Date | |
|---|---|---|---|
| 6879a75f09 | |||
| 18f2f6f461 | |||
| 928f08cc57 | |||
| da300f75fe | |||
| b0b343e009 | |||
| ffaa7c96ba | |||
| c5893a6de4 | |||
| 554efec086 | |||
| 4d089eeb88 | |||
| 06b6340731 | |||
| e4292478a1 | |||
| 16eaceb70a | |||
| 5e89e730ee | |||
| e42b749baf | |||
| 17b5646268 | |||
| d71883917c | |||
| 1222c4ab5a | |||
| 2a43b29ca9 | |||
| d29ba781ba |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,4 +1,6 @@
|
||||
.env
|
||||
venv
|
||||
__pycache__
|
||||
journal_prompt_*
|
||||
historic_prompts.json
|
||||
pool_prompts.json
|
||||
feedback_words.json
|
||||
|
||||
163
AGENTS.md
Normal file
163
AGENTS.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# Task: Combine pool and history stats into a single function and single menu item
|
||||
|
||||
## Changes Made
|
||||
|
||||
### 1. Created New Combined Stats Function
|
||||
- Added `show_combined_stats()` method to `JournalPromptGenerator` class
|
||||
- Combines both pool statistics and history statistics into a single function
|
||||
- Displays two tables: "Prompt Pool Statistics" and "Prompt History Statistics"
|
||||
|
||||
### 2. Updated Interactive Menu
|
||||
- Changed menu from 5 options to 4 options:
|
||||
- 1. Draw prompts from pool (no API call)
|
||||
- 2. Fill prompt pool using API
|
||||
- 3. View combined statistics (replaces separate pool and history stats)
|
||||
- 4. Exit
|
||||
- Updated menu handling logic to use the new combined stats function
|
||||
|
||||
### 3. Updated Command-Line Arguments
|
||||
- Removed `--pool-stats` argument
|
||||
- Updated `--stats` argument description to "Show combined statistics (pool and history)"
|
||||
- Updated main function logic to use `show_combined_stats()` instead of separate functions
|
||||
|
||||
### 4. Removed Old Stats Functions
|
||||
- Removed `show_pool_stats()` method
|
||||
- Removed `show_history_stats()` method
|
||||
- All functionality consolidated into `show_combined_stats()`
|
||||
|
||||
### 5. Code Cleanup
|
||||
- Removed unused imports and references to old stats functions
|
||||
- Ensured all menu options work correctly with the new combined stats
|
||||
|
||||
## Testing
|
||||
- Verified `--stats` command-line argument works correctly
|
||||
- Tested interactive mode shows updated menu
|
||||
- Confirmed combined stats display both pool and history information
|
||||
- Tested default mode (draw from pool) still works
|
||||
- Verified fill-pool option starts correctly
|
||||
|
||||
## Result
|
||||
Successfully combined pool and history statistics into a single function and single menu item, simplifying the user interface while maintaining all functionality.
|
||||
|
||||
---
|
||||
|
||||
# Task: Implement theme feedback words functionality with new menu item
|
||||
|
||||
## Changes Made
|
||||
|
||||
### 1. Added New Theme Feedback Words API Call
|
||||
- Created `generate_theme_feedback_words()` method that:
|
||||
- Loads `ds_feedback.txt` prompt template
|
||||
- Sends historic prompts to AI API for analysis
|
||||
- **INCLUDES current feedback words from `feedback_words.json` in the API payload**
|
||||
- Receives 6 theme words as JSON response
|
||||
- Parses and validates the response
|
||||
|
||||
### 2. Added User Rating System
|
||||
- Created `collect_feedback_ratings()` method that:
|
||||
- Presents each of the 6 theme words to the user
|
||||
- Collects ratings from 0-6 for each word
|
||||
- Creates structured feedback items with keys (feedback00-feedback05)
|
||||
- Includes weight values based on user ratings
|
||||
|
||||
### 3. Added Feedback Words Update System
|
||||
- Created `update_feedback_words()` method that:
|
||||
- Replaces existing feedback words with new ratings
|
||||
- Saves updated feedback words to `feedback_words.json`
|
||||
- Maintains the required JSON structure
|
||||
|
||||
### 4. Updated Interactive Menu
|
||||
- Expanded menu from 4 options to 5 options:
|
||||
- 1. Draw prompts from pool (no API call)
|
||||
- 2. Fill prompt pool using API
|
||||
- 3. View combined statistics
|
||||
- 4. Generate and rate theme feedback words (NEW)
|
||||
- 5. Exit
|
||||
- Added complete implementation for option 4
|
||||
|
||||
### 5. Enhanced Data Handling
|
||||
- Added `_save_feedback_words()` method for saving feedback data
|
||||
- Updated `_load_feedback_words()` to handle JSON structure properly
|
||||
- Ensured feedback words are included in AI prompts when generating new prompts
|
||||
|
||||
## Testing
|
||||
- Verified all new methods exist and have correct signatures
|
||||
- Confirmed `ds_feedback.txt` file exists and is readable
|
||||
- Tested feedback words JSON structure validation
|
||||
- Verified interactive menu displays new option correctly
|
||||
- Confirmed existing functionality remains intact
|
||||
|
||||
## Result
|
||||
Successfully implemented a new menu item and functionality for generating theme feedback words. The system now:
|
||||
1. Makes an API call with historic prompts and `ds_feedback.txt` template
|
||||
2. Receives 6 theme words from the AI
|
||||
3. Collects user ratings (0-6) for each word
|
||||
4. Updates `feedback_words.json` with the new ratings
|
||||
5. Integrates the feedback into future prompt generation
|
||||
|
||||
The implementation maintains backward compatibility while adding valuable feedback functionality to improve prompt generation quality over time.
|
||||
|
||||
Too many tests, so I moved all of them into the tests directory.
|
||||
|
||||
---
|
||||
|
||||
# Task: Implement feedback_historic.json cyclic buffer system (30 items)
|
||||
|
||||
## Changes Made
|
||||
|
||||
### 1. Added Feedback Historic System
|
||||
- Created `feedback_historic.json` file to store previous feedback words (without weights)
|
||||
- Implemented a cyclic buffer system with 30-item capacity (feedback00-feedback29)
|
||||
- When new feedback is generated (6 words), they become feedback00-feedback05
|
||||
- All existing items shift down by 6 positions
|
||||
- Items beyond feedback29 are discarded
|
||||
|
||||
### 2. Updated Class Initialization
|
||||
- Added `feedback_historic` attribute to `JournalPromptGenerator` class
|
||||
- Updated `__init__` method to load `feedback_historic.json`
|
||||
- Added `_load_feedback_historic()` method to load historic feedback words
|
||||
- Added `_save_feedback_historic()` method to save historic feedback words (keeping only first 30)
|
||||
|
||||
### 3. Enhanced Feedback Words Management
|
||||
- Updated `add_feedback_words_to_history()` method to:
|
||||
- Extract just the words from current feedback words (no weights)
|
||||
- Add 6 new words to the historic buffer
|
||||
- Shift all existing words down by 6 positions
|
||||
- Maintain 30-item limit by discarding oldest items
|
||||
- Updated `update_feedback_words()` to automatically call `add_feedback_words_to_history()`
|
||||
|
||||
### 4. Improved AI Prompt Generation
|
||||
- Updated `generate_theme_feedback_words()` method to include historic feedback words in API call
|
||||
- The prompt now includes three sections:
|
||||
1. Previous prompts (historic prompts)
|
||||
2. Current feedback themes (with weights)
|
||||
3. Historic feedback themes (just words, no weights)
|
||||
- This helps the AI avoid repeating previously used theme words
|
||||
|
||||
### 5. Data Structure Design
|
||||
- Historic feedback words are stored as a list of dictionaries with keys (feedback00, feedback01, etc.)
|
||||
- Each dictionary contains only the word (no weight field)
|
||||
- Structure mirrors `prompts_historic.json` but for feedback words
|
||||
- 30-item limit provides sufficient history while preventing excessive repetition
|
||||
|
||||
## Testing
|
||||
- Created comprehensive test to verify cyclic buffer functionality
|
||||
- Tested that new items are added at the beginning (feedback00-feedback05)
|
||||
- Verified that existing items shift down correctly
|
||||
- Confirmed 30-item limit is enforced (oldest items are dropped)
|
||||
- Tested that historic feedback words are included in AI prompts
|
||||
- Verified that weights are not stored in historic buffer (only words)
|
||||
|
||||
## Result
|
||||
Successfully implemented a feedback historic cyclic buffer system that:
|
||||
1. Stores previous feedback words in `feedback_historic.json` (30-item limit)
|
||||
2. Automatically adds new feedback words to history when they are updated
|
||||
3. Includes historic feedback words in AI prompts to avoid repetition
|
||||
4. Maintains consistent data structure with the rest of the system
|
||||
5. Provides a memory of previous theme words to improve AI suggestions over time
|
||||
|
||||
The system now has a complete feedback loop where:
|
||||
- Historic prompts and feedback words inform new theme word generation
|
||||
- New theme words are rated by users and become current feedback words
|
||||
- Current feedback words are added to the historic buffer
|
||||
- Historic feedback words help avoid repetition in future theme word generation
|
||||
66
README.md
66
README.md
@@ -67,9 +67,11 @@ daily-journal-prompt/
|
||||
├── test_project.py # Test suite for the project
|
||||
├── requirements.txt # Python dependencies
|
||||
├── ds_prompt.txt # AI prompt template for generating journal prompts
|
||||
├── historic_prompts.json # History of previous 60 prompts (JSON format)
|
||||
├── prompts_historic.json # History of previous 60 prompts (JSON format)
|
||||
├── prompts_pool.json # Pool of available prompts for selection (JSON format)
|
||||
├── example.env # Example environment configuration
|
||||
├── .env # Your actual environment configuration (gitignored)
|
||||
├── settings.cfg # Configuration file for prompt settings and pool size
|
||||
└── .gitignore # Git ignore rules
|
||||
```
|
||||
|
||||
@@ -80,10 +82,12 @@ daily-journal-prompt/
|
||||
- **run.sh**: Convenience bash script for easy execution
|
||||
- **test_project.py**: Test suite to verify project setup
|
||||
- **requirements.txt**: Python dependencies (openai, python-dotenv, rich)
|
||||
- **ds_prompt.txt**: The core prompt template that instructs the AI to generate 6 new journal prompts
|
||||
- **historic_prompts.json**: JSON array containing the last 60 generated prompts
|
||||
- **ds_prompt.txt**: The core prompt template that instructs the AI to generate new journal prompts
|
||||
- **prompts_historic.json**: JSON array containing the last 60 generated prompts (cyclic buffer)
|
||||
- **prompts_pool.json**: JSON array containing the pool of available prompts for selection
|
||||
- **example.env**: Template for your environment configuration
|
||||
- **.env**: Your actual environment variables (not tracked in git for security)
|
||||
- **settings.cfg**: Configuration file for prompt settings (length, count) and pool size
|
||||
|
||||
## 🎯 Quick Start
|
||||
|
||||
@@ -140,18 +144,60 @@ python test_project.py
|
||||
|
||||
## 🔧 Usage
|
||||
|
||||
### New Pool-Based System
|
||||
|
||||
The system now uses a two-step process:
|
||||
|
||||
1. **Fill the Prompt Pool**: Generate prompts using AI and add them to the pool
|
||||
2. **Draw from Pool**: Select prompts from the pool for journaling sessions
|
||||
|
||||
### Command Line Options
|
||||
|
||||
```bash
|
||||
# Default: Draw prompts from pool (no API call)
|
||||
python generate_prompts.py
|
||||
|
||||
# Interactive mode with menu
|
||||
python generate_prompts.py --interactive
|
||||
|
||||
# Fill the prompt pool using AI (makes API call)
|
||||
python generate_prompts.py --fill-pool
|
||||
|
||||
# Show pool statistics
|
||||
python generate_prompts.py --pool-stats
|
||||
|
||||
# Show history statistics
|
||||
python generate_prompts.py --stats
|
||||
|
||||
# Help
|
||||
python generate_prompts.py --help
|
||||
```
|
||||
|
||||
### Interactive Mode Options
|
||||
|
||||
1. **Draw prompts from pool (no API call)**: Displays and consumes prompts from the pool file
|
||||
2. **Fill prompt pool using API**: Generates new prompts using AI and adds them to pool
|
||||
3. **View pool statistics**: Shows pool size, target size, and available sessions
|
||||
4. **View history statistics**: Shows historic prompt count and capacity
|
||||
5. **Exit**: Quit the program
|
||||
|
||||
### Prompt Generation Process
|
||||
|
||||
1. The system reads the template from `ds_prompt.txt`
|
||||
2. It loads the previous 60 prompts from the fixed length cyclic buffer `historic_prompts.json`
|
||||
3. The AI generates 6 new prompts, attempting to minimize repetition
|
||||
4. Six new prompts are displayed.
|
||||
5. User selects one prompt for his/her journal writing session, which is added to the `historic_prompts.json` cyclic buffer.
|
||||
1. User chooses to fill the prompt pool.
|
||||
2. The system reads the template from `ds_prompt.txt`
|
||||
3. It loads the previous 60 prompts from the fixed length cyclic buffer `prompts_historic.json`
|
||||
4. The AI generates some number of new prompts, attempting to minimize repetition
|
||||
5. The new prompts are used to fill the prompt pool to the `settings.cfg` configured value.
|
||||
|
||||
### Prompt Selection Process
|
||||
|
||||
1. A `settings.cfg` configurable number of prompts are drawn from the prompt pool and displayed to the user.
|
||||
2. User selects one prompt for his/her journal writing session, which is added to the `prompts_historic.json` cyclic buffer.
|
||||
3. All prompts which were displayed are removed from the prompt pool permanently.
|
||||
|
||||
## 📝 Prompt Examples
|
||||
|
||||
The tool generates prompts like these (from `historic_prompts.json`):
|
||||
The tool generates prompts like these (from `prompts_historic.json`):
|
||||
|
||||
- **Memory-based**: "Describe a memory you have that is tied to a specific smell..."
|
||||
- **Creative Writing**: "Invent a mythological creature for a modern urban setting..."
|
||||
@@ -188,7 +234,7 @@ You can modify `ds_prompt.txt` to change the prompt generation parameters:
|
||||
|
||||
## 🔄 Maintaining Prompt History
|
||||
|
||||
The `historic_prompts.json` file maintains a rolling history of the last 60 prompts. This helps:
|
||||
The `prompts_historic.json` file maintains a rolling history of the last 60 prompts. This helps:
|
||||
|
||||
1. **Avoid repetition**: The AI references previous prompts to generate new, diverse topics
|
||||
2. **Track usage**: See what types of prompts have been generated
|
||||
|
||||
119
demonstrate_feedback_historic.py
Normal file
119
demonstrate_feedback_historic.py
Normal file
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Demonstration of the feedback_historic.json cyclic buffer system.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from generate_prompts import JournalPromptGenerator
|
||||
|
||||
def demonstrate_system():
|
||||
"""Demonstrate the feedback historic system."""
|
||||
print("="*70)
|
||||
print("DEMONSTRATION: Feedback Historic Cyclic Buffer System")
|
||||
print("="*70)
|
||||
|
||||
# Create a temporary .env file
|
||||
with open(".env.demo", "w") as f:
|
||||
f.write("DEEPSEEK_API_KEY=demo_key\n")
|
||||
f.write("API_BASE_URL=https://api.deepseek.com\n")
|
||||
f.write("MODEL=deepseek-chat\n")
|
||||
|
||||
# Initialize generator
|
||||
generator = JournalPromptGenerator(config_path=".env.demo")
|
||||
|
||||
print("\n1. Initial state:")
|
||||
print(f" - feedback_words: {len(generator.feedback_words)} items")
|
||||
print(f" - feedback_historic: {len(generator.feedback_historic)} items")
|
||||
|
||||
# Create some sample feedback words
|
||||
sample_words_batch1 = [
|
||||
{"feedback00": "memory", "weight": 5},
|
||||
{"feedback01": "time", "weight": 4},
|
||||
{"feedback02": "nature", "weight": 3},
|
||||
{"feedback03": "emotion", "weight": 6},
|
||||
{"feedback04": "change", "weight": 2},
|
||||
{"feedback05": "connection", "weight": 4}
|
||||
]
|
||||
|
||||
print("\n2. Adding first batch of feedback words...")
|
||||
generator.update_feedback_words(sample_words_batch1)
|
||||
print(f" - Added 6 feedback words")
|
||||
print(f" - feedback_historic now has: {len(generator.feedback_historic)} items")
|
||||
|
||||
# Show the historic items
|
||||
print("\n Historic feedback words (no weights):")
|
||||
for i, item in enumerate(generator.feedback_historic):
|
||||
key = list(item.keys())[0]
|
||||
print(f" {key}: {item[key]}")
|
||||
|
||||
# Add second batch
|
||||
sample_words_batch2 = [
|
||||
{"feedback00": "creativity", "weight": 5},
|
||||
{"feedback01": "reflection", "weight": 4},
|
||||
{"feedback02": "growth", "weight": 3},
|
||||
{"feedback03": "transformation", "weight": 6},
|
||||
{"feedback04": "journey", "weight": 2},
|
||||
{"feedback05": "discovery", "weight": 4}
|
||||
]
|
||||
|
||||
print("\n3. Adding second batch of feedback words...")
|
||||
generator.update_feedback_words(sample_words_batch2)
|
||||
print(f" - Added 6 more feedback words")
|
||||
print(f" - feedback_historic now has: {len(generator.feedback_historic)} items")
|
||||
|
||||
print("\n Historic feedback words after second batch:")
|
||||
print(" (New words at the top, old words shifted down)")
|
||||
for i, item in enumerate(generator.feedback_historic[:12]): # Show first 12
|
||||
key = list(item.keys())[0]
|
||||
print(f" {key}: {item[key]}")
|
||||
|
||||
# Demonstrate the cyclic buffer by adding more batches
|
||||
print("\n4. Demonstrating cyclic buffer (30 item limit)...")
|
||||
print(" Adding 5 more batches (30 more words total)...")
|
||||
|
||||
for batch_num in range(3, 8):
|
||||
batch_words = []
|
||||
for j in range(6):
|
||||
batch_words.append({f"feedback{j:02d}": f"batch{batch_num}_word{j+1}", "weight": 3})
|
||||
generator.update_feedback_words(batch_words)
|
||||
|
||||
print(f" - feedback_historic now has: {len(generator.feedback_historic)} items (max 30)")
|
||||
print(f" - Oldest items have been dropped to maintain 30-item limit")
|
||||
|
||||
# Show the structure
|
||||
print("\n5. Checking file structure...")
|
||||
if os.path.exists("feedback_historic.json"):
|
||||
with open("feedback_historic.json", "r") as f:
|
||||
data = json.load(f)
|
||||
print(f" - feedback_historic.json exists with {len(data)} items")
|
||||
print(f" - First item: {data[0]}")
|
||||
print(f" - Last item: {data[-1]}")
|
||||
print(f" - Items have keys (feedback00, feedback01, etc.) but no weights")
|
||||
|
||||
# Clean up
|
||||
os.remove(".env.demo")
|
||||
if os.path.exists("feedback_words.json"):
|
||||
os.remove("feedback_words.json")
|
||||
if os.path.exists("feedback_historic.json"):
|
||||
os.remove("feedback_historic.json")
|
||||
|
||||
print("\n" + "="*70)
|
||||
print("SUMMARY:")
|
||||
print("="*70)
|
||||
print("✓ feedback_historic.json stores previous feedback words (no weights)")
|
||||
print("✓ Maximum of 30 items (feedback00-feedback29)")
|
||||
print("✓ When new feedback is generated (6 words):")
|
||||
print(" - They become feedback00-feedback05 in the historic buffer")
|
||||
print(" - All existing items shift down by 6 positions")
|
||||
print(" - Items beyond feedback29 are discarded")
|
||||
print("✓ Historic feedback words are included in AI prompts for")
|
||||
print(" generate_theme_feedback_words() to avoid repetition")
|
||||
print("="*70)
|
||||
|
||||
if __name__ == "__main__":
|
||||
demonstrate_system()
|
||||
|
||||
20
ds_feedback.txt
Normal file
20
ds_feedback.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
Request for generation of writing prompts for journaling
|
||||
|
||||
Payload:
|
||||
The previous 60 prompts have been provided as a JSON array for reference.
|
||||
The current 6 feedback themes have been provided. You will not re-use any of these most-recently used words here.
|
||||
The previous 30 feedback themes are also provided. You should try to avoid re-using these unless it really makes sense to.
|
||||
|
||||
Guidelines:
|
||||
Using the attached JSON of writing prompts, you should try to pick out 4 unique and intentionally vague single-word themes that apply to some portion of the list. They can range from common to uncommon words.
|
||||
Then add 2 more single word divergent themes that are less related to the historic prompts and are somewhat different from the other 4 for a total of 6 words.
|
||||
These 2 divergent themes give the user the option to steer away from existing themes.
|
||||
Examples for the divergent themes could be the option to add a theme like technology when the other themes are related to beauty, or mortality when the other themes are very positive.
|
||||
Be creative, don't just use my example.
|
||||
A very high temperature AI response is warranted here to generate a large vocabulary.
|
||||
|
||||
Expected Output:
|
||||
Output as a JSON list with just the six words, in lowercase.
|
||||
Despite the provided history being a keyed list or dictionary, the expected return JSON will be a simple list with no keys.
|
||||
Respond ONLY with valid JSON. No explanations, no markdown, no backticks.
|
||||
|
||||
@@ -1,9 +1,26 @@
|
||||
Please generate 6 writing prompts in English with a few guidelines.
|
||||
These are meant to inspire one to two pages of writing in a journal as exercise.
|
||||
Topics can be diverse.
|
||||
The prompts should be between 500 and 1000 characters.
|
||||
The previous 60 prompts have been provided as a JSON array for reference. Attempt minimal repetition, but some thematic overlap is acceptable. Newly generated prompts should try extra hard to avoid repetition in previous prompts with the lowest indices.
|
||||
Request for generation of writing prompts for journaling
|
||||
|
||||
Output as a JSON array with key names from "newprompt0" to "newpromptN" where N is the number of prompts minus one.
|
||||
Payload:
|
||||
The previous 60 prompts have been provided as a JSON array for reference.
|
||||
Some vague feedback themes have been provided, each having a weight value from 0 to 6.
|
||||
|
||||
Guidelines:
|
||||
Please generate some number of individual writing prompts in English following these guidelines.
|
||||
Topics can be diverse, and the whole batch should have no outright repetition.
|
||||
These are meant to inspire one to two pages of writing in a journal as exercise.
|
||||
|
||||
Prompt History:
|
||||
The provided history brackets two mechanisms.
|
||||
The history will allow for reducing repetition, however some thematic overlap is acceptable. Try harder to avoid overlap with lower indices in the array.
|
||||
As the user discards prompts, the themes will be very slowly steered, so it's okay to take some inspiration from the history.
|
||||
|
||||
Feedback Themes:
|
||||
A JSON of single-word feedback themes is provided with each having a weight value from 0 to 6.
|
||||
Consider these weighted themes only rarely when creating a new writing prompt. Most prompts should be created with full creative freedom.
|
||||
Only gently influence writing prompts with these. It is better to have all generated prompts ignore a theme than have many reference a theme overtly.
|
||||
|
||||
Expected Output:
|
||||
Output as a JSON list with the requested number of elements.
|
||||
Despite the provided history being a keyed list or dictionary, the expected return JSON will be a simple list with no keys.
|
||||
Respond ONLY with valid JSON. No explanations, no markdown, no backticks.
|
||||
|
||||
|
||||
92
feedback_historic.json
Normal file
92
feedback_historic.json
Normal file
@@ -0,0 +1,92 @@
|
||||
[
|
||||
{
|
||||
"feedback00": "labyrinth"
|
||||
},
|
||||
{
|
||||
"feedback01": "residue"
|
||||
},
|
||||
{
|
||||
"feedback02": "tremor"
|
||||
},
|
||||
{
|
||||
"feedback03": "effigy"
|
||||
},
|
||||
{
|
||||
"feedback04": "quasar"
|
||||
},
|
||||
{
|
||||
"feedback05": "gossamer"
|
||||
},
|
||||
{
|
||||
"feedback06": "resonance"
|
||||
},
|
||||
{
|
||||
"feedback07": "erosion"
|
||||
},
|
||||
{
|
||||
"feedback08": "surrender"
|
||||
},
|
||||
{
|
||||
"feedback09": "excess"
|
||||
},
|
||||
{
|
||||
"feedback10": "chaos"
|
||||
},
|
||||
{
|
||||
"feedback11": "fabric"
|
||||
},
|
||||
{
|
||||
"feedback12": "palimpsest"
|
||||
},
|
||||
{
|
||||
"feedback13": "lacuna"
|
||||
},
|
||||
{
|
||||
"feedback14": "efflorescence"
|
||||
},
|
||||
{
|
||||
"feedback15": "tessellation"
|
||||
},
|
||||
{
|
||||
"feedback16": "sublimation"
|
||||
},
|
||||
{
|
||||
"feedback17": "vertigo"
|
||||
},
|
||||
{
|
||||
"feedback18": "artifact"
|
||||
},
|
||||
{
|
||||
"feedback19": "mycelium"
|
||||
},
|
||||
{
|
||||
"feedback20": "threshold"
|
||||
},
|
||||
{
|
||||
"feedback21": "cartography"
|
||||
},
|
||||
{
|
||||
"feedback22": "spectacle"
|
||||
},
|
||||
{
|
||||
"feedback23": "friction"
|
||||
},
|
||||
{
|
||||
"feedback24": "mutation"
|
||||
},
|
||||
{
|
||||
"feedback25": "echo"
|
||||
},
|
||||
{
|
||||
"feedback26": "repair"
|
||||
},
|
||||
{
|
||||
"feedback27": "velocity"
|
||||
},
|
||||
{
|
||||
"feedback28": "syntax"
|
||||
},
|
||||
{
|
||||
"feedback29": "divergence"
|
||||
}
|
||||
]
|
||||
26
feedback_words.json
Normal file
26
feedback_words.json
Normal file
@@ -0,0 +1,26 @@
|
||||
[
|
||||
{
|
||||
"feedback00": "labyrinth",
|
||||
"weight": 3
|
||||
},
|
||||
{
|
||||
"feedback01": "residue",
|
||||
"weight": 3
|
||||
},
|
||||
{
|
||||
"feedback02": "tremor",
|
||||
"weight": 3
|
||||
},
|
||||
{
|
||||
"feedback03": "effigy",
|
||||
"weight": 3
|
||||
},
|
||||
{
|
||||
"feedback04": "quasar",
|
||||
"weight": 3
|
||||
},
|
||||
{
|
||||
"feedback05": "gossamer",
|
||||
"weight": 3
|
||||
}
|
||||
]
|
||||
@@ -9,9 +9,7 @@ import json
|
||||
import sys
|
||||
import argparse
|
||||
import configparser
|
||||
from datetime import datetime
|
||||
from typing import List, Dict, Any, Optional
|
||||
from pathlib import Path
|
||||
|
||||
from openai import OpenAI
|
||||
from dotenv import load_dotenv
|
||||
@@ -31,6 +29,9 @@ class JournalPromptGenerator:
|
||||
self.config_path = config_path
|
||||
self.client = None
|
||||
self.historic_prompts = []
|
||||
self.pool_prompts = []
|
||||
self.feedback_words = []
|
||||
self.feedback_historic = []
|
||||
self.prompt_template = ""
|
||||
self.settings = {}
|
||||
|
||||
@@ -41,6 +42,9 @@ class JournalPromptGenerator:
|
||||
# Load data files
|
||||
self._load_prompt_template()
|
||||
self._load_historic_prompts()
|
||||
self._load_pool_prompts()
|
||||
self._load_feedback_words()
|
||||
self._load_feedback_historic()
|
||||
|
||||
def _load_config(self):
|
||||
"""Load configuration from environment file."""
|
||||
@@ -73,7 +77,8 @@ class JournalPromptGenerator:
|
||||
self.settings = {
|
||||
'min_length': 500,
|
||||
'max_length': 1000,
|
||||
'num_prompts': 6
|
||||
'num_prompts': 6,
|
||||
'cached_pool_volume': 20 # Default value
|
||||
}
|
||||
|
||||
try:
|
||||
@@ -94,6 +99,12 @@ class JournalPromptGenerator:
|
||||
if 'num_prompts' in prompts_section:
|
||||
self.settings['num_prompts'] = int(prompts_section['num_prompts'])
|
||||
|
||||
# Load cached_pool_volume from prefetch section
|
||||
if 'prefetch' in config:
|
||||
prefetch_section = config['prefetch']
|
||||
if 'cached_pool_volume' in prefetch_section:
|
||||
self.settings['cached_pool_volume'] = int(prefetch_section['cached_pool_volume'])
|
||||
|
||||
except FileNotFoundError:
|
||||
self.console.print("[yellow]Warning: settings.cfg not found, using default values[/yellow]")
|
||||
except ValueError as e:
|
||||
@@ -102,24 +113,10 @@ class JournalPromptGenerator:
|
||||
self.console.print(f"[yellow]Warning: Error reading settings.cfg: {e}, using default values[/yellow]")
|
||||
|
||||
def _load_prompt_template(self):
|
||||
"""Load the prompt template from ds_prompt.txt and update with config values."""
|
||||
"""Load the prompt template from ds_prompt.txt."""
|
||||
try:
|
||||
with open("ds_prompt.txt", "r") as f:
|
||||
template = f.read()
|
||||
|
||||
# Replace hardcoded values with config values
|
||||
template = template.replace(
|
||||
"between 500 and 1000 characters",
|
||||
f"between {self.settings['min_length']} and {self.settings['max_length']} characters"
|
||||
)
|
||||
|
||||
# Replace the number of prompts (6) with config value
|
||||
template = template.replace(
|
||||
"Please generate 6 writing prompts",
|
||||
f"Please generate {self.settings['num_prompts']} writing prompts"
|
||||
)
|
||||
|
||||
self.prompt_template = template
|
||||
self.prompt_template = f.read()
|
||||
except FileNotFoundError:
|
||||
self.console.print("[red]Error: ds_prompt.txt not found[/red]")
|
||||
sys.exit(1)
|
||||
@@ -127,13 +124,13 @@ class JournalPromptGenerator:
|
||||
def _load_historic_prompts(self):
|
||||
"""Load historic prompts from JSON file."""
|
||||
try:
|
||||
with open("historic_prompts.json", "r") as f:
|
||||
with open("prompts_historic.json", "r") as f:
|
||||
self.historic_prompts = json.load(f)
|
||||
except FileNotFoundError:
|
||||
self.console.print("[yellow]Warning: historic_prompts.json not found, starting with empty history[/yellow]")
|
||||
self.console.print("[yellow]Warning: prompts_historic.json not found, starting with empty history[/yellow]")
|
||||
self.historic_prompts = []
|
||||
except json.JSONDecodeError:
|
||||
self.console.print("[yellow]Warning: historic_prompts.json is corrupted, starting with empty history[/yellow]")
|
||||
self.console.print("[yellow]Warning: prompts_historic.json is corrupted, starting with empty history[/yellow]")
|
||||
self.historic_prompts = []
|
||||
|
||||
def _save_historic_prompts(self):
|
||||
@@ -142,24 +139,94 @@ class JournalPromptGenerator:
|
||||
if len(self.historic_prompts) > 60:
|
||||
self.historic_prompts = self.historic_prompts[:60]
|
||||
|
||||
with open("historic_prompts.json", "w") as f:
|
||||
with open("prompts_historic.json", "w") as f:
|
||||
json.dump(self.historic_prompts, f, indent=2)
|
||||
|
||||
def _renumber_prompts(self):
|
||||
"""Renumber all prompts to maintain prompt00-prompt59 range."""
|
||||
renumbered_prompts = []
|
||||
for i, prompt_dict in enumerate(self.historic_prompts):
|
||||
# Get the prompt text from the first key in the dictionary
|
||||
prompt_key = list(prompt_dict.keys())[0]
|
||||
prompt_text = prompt_dict[prompt_key]
|
||||
def _load_pool_prompts(self):
|
||||
"""Load pool prompts from JSON file."""
|
||||
try:
|
||||
with open("prompts_pool.json", "r") as f:
|
||||
self.pool_prompts = json.load(f)
|
||||
except FileNotFoundError:
|
||||
self.console.print("[yellow]Warning: prompts_pool.json not found, starting with empty pool[/yellow]")
|
||||
self.pool_prompts = []
|
||||
except json.JSONDecodeError:
|
||||
self.console.print("[yellow]Warning: prompts_pool.json is corrupted, starting with empty pool[/yellow]")
|
||||
self.pool_prompts = []
|
||||
|
||||
# Create new prompt with correct numbering
|
||||
new_prompt_key = f"prompt{i:02d}"
|
||||
renumbered_prompts.append({
|
||||
new_prompt_key: prompt_text
|
||||
})
|
||||
def _load_feedback_words(self):
|
||||
"""Load feedback words from JSON file."""
|
||||
try:
|
||||
with open("feedback_words.json", "r") as f:
|
||||
self.feedback_words = json.load(f)
|
||||
except FileNotFoundError:
|
||||
self.console.print("[yellow]Warning: feedback_words.json not found, starting with empty feedback words[/yellow]")
|
||||
self.feedback_words = []
|
||||
except json.JSONDecodeError:
|
||||
self.console.print("[yellow]Warning: feedback_words.json is corrupted, starting with empty feedback words[/yellow]")
|
||||
self.feedback_words = []
|
||||
|
||||
def _load_feedback_historic(self):
|
||||
"""Load historic feedback words from JSON file."""
|
||||
try:
|
||||
with open("feedback_historic.json", "r") as f:
|
||||
self.feedback_historic = json.load(f)
|
||||
except FileNotFoundError:
|
||||
self.console.print("[yellow]Warning: feedback_historic.json not found, starting with empty feedback history[/yellow]")
|
||||
self.feedback_historic = []
|
||||
except json.JSONDecodeError:
|
||||
self.console.print("[yellow]Warning: feedback_historic.json is corrupted, starting with empty feedback history[/yellow]")
|
||||
self.feedback_historic = []
|
||||
|
||||
def _save_feedback_words(self):
|
||||
"""Save feedback words to JSON file."""
|
||||
with open("feedback_words.json", "w") as f:
|
||||
json.dump(self.feedback_words, f, indent=2)
|
||||
|
||||
def _save_feedback_historic(self):
|
||||
"""Save historic feedback words to JSON file (keeping only first 30)."""
|
||||
# Keep only the first 30 feedback words (newest are at the beginning)
|
||||
if len(self.feedback_historic) > 30:
|
||||
self.feedback_historic = self.feedback_historic[:30]
|
||||
|
||||
with open("feedback_historic.json", "w") as f:
|
||||
json.dump(self.feedback_historic, f, indent=2)
|
||||
|
||||
def _save_pool_prompts(self):
|
||||
"""Save pool prompts to JSON file."""
|
||||
with open("prompts_pool.json", "w") as f:
|
||||
json.dump(self.pool_prompts, f, indent=2)
|
||||
|
||||
def add_prompts_to_pool(self, prompts: List[str]):
|
||||
"""Add generated prompts to the pool."""
|
||||
# Simply extend the pool with the new prompts (no keys)
|
||||
self.pool_prompts.extend(prompts)
|
||||
|
||||
self._save_pool_prompts()
|
||||
self.console.print(f"[green]Added {len(prompts)} prompts to pool[/green]")
|
||||
|
||||
def draw_prompts_from_pool(self, count: int = None) -> List[str]:
|
||||
"""Draw prompts from the pool (removes them from pool)."""
|
||||
if count is None:
|
||||
count = self.settings['num_prompts']
|
||||
|
||||
if len(self.pool_prompts) < count:
|
||||
self.console.print(f"[yellow]Warning: Pool only has {len(self.pool_prompts)} prompts, requested {count}[/yellow]")
|
||||
count = len(self.pool_prompts)
|
||||
|
||||
if count == 0:
|
||||
self.console.print("[red]Error: Pool is empty[/red]")
|
||||
return []
|
||||
|
||||
# Draw prompts from the beginning of the pool
|
||||
drawn_prompts = self.pool_prompts[:count]
|
||||
self.pool_prompts = self.pool_prompts[count:]
|
||||
|
||||
# Save updated pool
|
||||
self._save_pool_prompts()
|
||||
|
||||
return drawn_prompts
|
||||
|
||||
self.historic_prompts = renumbered_prompts
|
||||
|
||||
def add_prompt_to_history(self, prompt_text: str):
|
||||
"""
|
||||
@@ -193,44 +260,96 @@ class JournalPromptGenerator:
|
||||
self.historic_prompts = updated_prompts
|
||||
self._save_historic_prompts()
|
||||
|
||||
def _prepare_prompt(self) -> str:
|
||||
"""Prepare the full prompt with historic context."""
|
||||
# Format historic prompts for the AI
|
||||
if self.historic_prompts:
|
||||
historic_context = json.dumps(self.historic_prompts, indent=2)
|
||||
full_prompt = f"{self.prompt_template}\n\nPrevious prompts:\n{historic_context}"
|
||||
else:
|
||||
full_prompt = self.prompt_template
|
||||
def add_feedback_words_to_history(self):
|
||||
"""
|
||||
Add current feedback words to the historic feedback words cyclic buffer.
|
||||
The 6 new feedback words become feedback00-feedback05, all others shift down,
|
||||
and feedback29 is discarded (keeping only 30 items total).
|
||||
"""
|
||||
# Extract just the words from the current feedback words
|
||||
# Current feedback_words structure: [{"feedback00": "word", "weight": 3}, ...]
|
||||
new_feedback_words = []
|
||||
|
||||
return full_prompt
|
||||
for i, feedback_item in enumerate(self.feedback_words):
|
||||
# Get the word from the feedback item (key is feedback00, feedback01, etc.)
|
||||
feedback_key = f"feedback{i:02d}"
|
||||
if feedback_key in feedback_item:
|
||||
word = feedback_item[feedback_key]
|
||||
# Create new feedback word object with just the word (no weight)
|
||||
new_feedback_words.append({
|
||||
feedback_key: word
|
||||
})
|
||||
|
||||
def _parse_ai_response(self, response_content: str) -> List[Dict[str, str]]:
|
||||
# If we don't have 6 feedback words, we can't add them to history
|
||||
if len(new_feedback_words) != 6:
|
||||
self.console.print(f"[yellow]Warning: Expected 6 feedback words, got {len(new_feedback_words)}. Not adding to history.[/yellow]")
|
||||
return
|
||||
|
||||
# Shift all existing feedback words down by 6 positions
|
||||
# We'll create a new list starting with the 6 new feedback words
|
||||
updated_feedback_historic = new_feedback_words
|
||||
|
||||
# Add all existing feedback words, shifting their numbers down by 6
|
||||
for i, feedback_dict in enumerate(self.feedback_historic):
|
||||
if i >= 24: # We only keep 30 feedback words total (00-29), and we've already added 6
|
||||
break
|
||||
|
||||
# Get the feedback word
|
||||
feedback_key = list(feedback_dict.keys())[0]
|
||||
word = feedback_dict[feedback_key]
|
||||
|
||||
# Create feedback word with new number (shifted down by 6)
|
||||
new_feedback_key = f"feedback{i+6:02d}"
|
||||
updated_feedback_historic.append({
|
||||
new_feedback_key: word
|
||||
})
|
||||
|
||||
self.feedback_historic = updated_feedback_historic
|
||||
self._save_feedback_historic()
|
||||
self.console.print("[green]Added 6 feedback words to history[/green]")
|
||||
|
||||
def _parse_ai_response(self, response_content: str) -> List[str]:
|
||||
"""
|
||||
Parse the AI response to extract new prompts.
|
||||
Expected format: JSON array with keys "newprompt0" to "newpromptN" where N = num_prompts-1
|
||||
Expected format: JSON list/array of prompt strings
|
||||
|
||||
Handles DeepSeek API responses that may include backticks and leading "json" string.
|
||||
"""
|
||||
# First, try to clean up the response content
|
||||
cleaned_content = self._clean_ai_response(response_content)
|
||||
|
||||
try:
|
||||
# Try to parse as JSON
|
||||
data = json.loads(response_content)
|
||||
data = json.loads(cleaned_content)
|
||||
|
||||
# Convert to list of prompt dictionaries
|
||||
new_prompts = []
|
||||
for i in range(self.settings['num_prompts']):
|
||||
key = f"newprompt{i}"
|
||||
if key in data:
|
||||
prompt_text = data[key]
|
||||
prompt_obj = {
|
||||
f"prompt{len(self.historic_prompts) + i:02d}": prompt_text
|
||||
}
|
||||
new_prompts.append(prompt_obj)
|
||||
|
||||
return new_prompts
|
||||
# Check if data is a list
|
||||
if isinstance(data, list):
|
||||
# Return the list of prompt strings directly
|
||||
# Ensure we have the correct number of prompts
|
||||
if len(data) >= self.settings['num_prompts']:
|
||||
return data[:self.settings['num_prompts']]
|
||||
else:
|
||||
self.console.print(f"[yellow]Warning: AI returned {len(data)} prompts, expected {self.settings['num_prompts']}[/yellow]")
|
||||
return data
|
||||
elif isinstance(data, dict):
|
||||
# Fallback for old format: dictionary with newprompt0, newprompt1, etc.
|
||||
self.console.print("[yellow]Warning: AI returned dictionary format, expected list format[/yellow]")
|
||||
new_prompts = []
|
||||
for i in range(self.settings['num_prompts']):
|
||||
key = f"newprompt{i}"
|
||||
if key in data:
|
||||
new_prompts.append(data[key])
|
||||
return new_prompts
|
||||
else:
|
||||
self.console.print(f"[yellow]Warning: AI returned unexpected data type: {type(data)}[/yellow]")
|
||||
return []
|
||||
|
||||
except json.JSONDecodeError:
|
||||
# If not valid JSON, try to extract prompts from text
|
||||
self.console.print("[yellow]Warning: AI response is not valid JSON, attempting to extract prompts...[/yellow]")
|
||||
self.console.print(f"[yellow]Full response content for debugging:[/yellow]")
|
||||
self.console.print(f"[yellow]{response_content}[/yellow]")
|
||||
self.console.print(f"[yellow]Cleaned content: {cleaned_content}[/yellow]")
|
||||
|
||||
# Look for patterns in the text
|
||||
lines = response_content.strip().split('\n')
|
||||
@@ -239,19 +358,96 @@ class JournalPromptGenerator:
|
||||
for i, line in enumerate(lines[:self.settings['num_prompts']]): # Take first N non-empty lines
|
||||
line = line.strip()
|
||||
if line and len(line) > 50: # Reasonable minimum length for a prompt
|
||||
prompt_obj = {
|
||||
f"prompt{len(self.historic_prompts) + i:02d}": line
|
||||
}
|
||||
new_prompts.append(prompt_obj)
|
||||
new_prompts.append(line)
|
||||
|
||||
# If still no prompts could be parsed, provide detailed debug information
|
||||
if not new_prompts:
|
||||
self.console.print("\n[red]ERROR: Could not extract any prompts from AI response[/red]")
|
||||
self.console.print("[red]="*60 + "[/red]")
|
||||
self.console.print("[bold red]DEBUG INFORMATION:[/bold red]")
|
||||
self.console.print("[red]="*60 + "[/red]")
|
||||
|
||||
# Show response metadata
|
||||
self.console.print(f"[yellow]Response length: {len(response_content)} characters[/yellow]")
|
||||
self.console.print(f"[yellow]Expected number of prompts: {self.settings['num_prompts']}[/yellow]")
|
||||
|
||||
# Show first 500 characters of response
|
||||
preview = response_content[:500]
|
||||
if len(response_content) > 500:
|
||||
preview += "..."
|
||||
self.console.print(f"[yellow]Response preview (first 500 chars):[/yellow]")
|
||||
self.console.print(f"[yellow]{preview}[/yellow]")
|
||||
|
||||
# Show cleaned content analysis
|
||||
self.console.print(f"[yellow]Cleaned content length: {len(cleaned_content)} characters[/yellow]")
|
||||
self.console.print(f"[yellow]Cleaned content preview: {cleaned_content[:200]}...[/yellow]")
|
||||
|
||||
# Show line analysis
|
||||
self.console.print(f"[yellow]Number of lines in response: {len(lines)}[/yellow]")
|
||||
self.console.print(f"[yellow]First 5 lines:[/yellow]")
|
||||
for i, line in enumerate(lines[:5]):
|
||||
self.console.print(f"[yellow] Line {i+1}: {line[:100]}{'...' if len(line) > 100 else ''}[/yellow]")
|
||||
|
||||
# Show JSON parsing attempt details
|
||||
self.console.print(f"[yellow]JSON parsing attempted on cleaned content:[/yellow]")
|
||||
self.console.print(f"[yellow] Cleaned content starts with: {cleaned_content[:50]}...[/yellow]")
|
||||
|
||||
# Show full payload for debugging
|
||||
self.console.print("\n[bold red]FULL PAYLOAD DUMP:[/bold red]")
|
||||
self.console.print("[red]" + "="*60 + "[/red]")
|
||||
self.console.print(f"[red]{response_content}[/red]")
|
||||
self.console.print("[red]" + "="*60 + "[/red]")
|
||||
|
||||
return new_prompts
|
||||
|
||||
def generate_prompts(self) -> List[Dict[str, str]]:
|
||||
"""Generate new journal prompts using AI."""
|
||||
self.console.print("\n[cyan]Generating new journal prompts...[/cyan]")
|
||||
|
||||
# Prepare the prompt
|
||||
full_prompt = self._prepare_prompt()
|
||||
def _clean_ai_response(self, response_content: str) -> str:
|
||||
"""
|
||||
Clean up AI response content to handle common formatting issues from DeepSeek API.
|
||||
|
||||
Handles:
|
||||
1. Leading/trailing backticks (```json ... ```)
|
||||
2. Leading "json" string on its own line
|
||||
3. Extra whitespace and newlines
|
||||
"""
|
||||
content = response_content.strip()
|
||||
|
||||
# Remove leading/trailing backticks (```json ... ```)
|
||||
if content.startswith('```'):
|
||||
# Find the first newline after the opening backticks
|
||||
lines = content.split('\n')
|
||||
if len(lines) > 1:
|
||||
# Check if first line contains "json" or other language specifier
|
||||
first_line = lines[0].strip()
|
||||
if 'json' in first_line.lower() or first_line == '```':
|
||||
# Remove the first line (```json or ```)
|
||||
content = '\n'.join(lines[1:])
|
||||
|
||||
# Remove trailing backticks if present
|
||||
if content.endswith('```'):
|
||||
content = content[:-3].rstrip()
|
||||
|
||||
# Remove leading "json" string on its own line (case-insensitive)
|
||||
lines = content.split('\n')
|
||||
if len(lines) > 0:
|
||||
first_line = lines[0].strip().lower()
|
||||
if first_line == 'json':
|
||||
content = '\n'.join(lines[1:])
|
||||
|
||||
# Also handle the case where "json" might be at the beginning of the first line
|
||||
# but not the entire line (e.g., "json\n{...}")
|
||||
content = content.strip()
|
||||
if content.lower().startswith('json\n'):
|
||||
content = content[4:].strip()
|
||||
|
||||
return content.strip()
|
||||
|
||||
def generate_specific_number_of_prompts(self, count: int) -> List[str]:
|
||||
"""Generate a specific number of journal prompts using AI."""
|
||||
self.console.print(f"\n[cyan]Generating {count} new journal prompts...[/cyan]")
|
||||
|
||||
# Prepare the prompt with specific count
|
||||
full_prompt = self._prepare_prompt_with_count(count)
|
||||
|
||||
# Show progress
|
||||
with Progress(
|
||||
@@ -277,33 +473,285 @@ class JournalPromptGenerator:
|
||||
|
||||
except Exception as e:
|
||||
self.console.print(f"[red]Error calling AI API: {e}[/red]")
|
||||
self.console.print(f"[yellow]Full prompt sent to API (first 500 chars):[/yellow]")
|
||||
self.console.print(f"[yellow]{full_prompt[:500]}...[/yellow]")
|
||||
return []
|
||||
|
||||
# Parse the response
|
||||
new_prompts = self._parse_ai_response(response_content)
|
||||
new_prompts = self._parse_ai_response_with_count(response_content, count)
|
||||
|
||||
if not new_prompts:
|
||||
self.console.print("[red]Error: Could not parse any prompts from AI response[/red]")
|
||||
return []
|
||||
|
||||
# Note: Prompts are NOT added to historic_prompts here
|
||||
# They will be added only when the user chooses one in interactive mode
|
||||
# via the add_prompt_to_history() method
|
||||
|
||||
return new_prompts
|
||||
|
||||
def display_prompts(self, prompts: List[Dict[str, str]]):
|
||||
def _prepare_prompt_with_count(self, count: int) -> str:
|
||||
"""Prepare the full prompt with historic context and specific count."""
|
||||
# Start with the base template
|
||||
template = self.prompt_template
|
||||
|
||||
# Add the instruction for the specific number of prompts
|
||||
# This will be added to the prompt since it's being removed from ds_prompt.txt
|
||||
prompt_instruction = f"Please generate {count} writing prompts, each between {self.settings['min_length']} and {self.settings['max_length']} characters."
|
||||
|
||||
# Format historic prompts for the AI
|
||||
if self.historic_prompts:
|
||||
historic_context = json.dumps(self.historic_prompts, indent=2)
|
||||
full_prompt = f"{template}\n\n{prompt_instruction}\n\nPrevious prompts:\n{historic_context}"
|
||||
else:
|
||||
full_prompt = f"{template}\n\n{prompt_instruction}"
|
||||
|
||||
# Add feedback words if available
|
||||
if self.feedback_words:
|
||||
feedback_context = json.dumps(self.feedback_words, indent=2)
|
||||
full_prompt = f"{full_prompt}\n\nFeedback words:\n{feedback_context}"
|
||||
|
||||
return full_prompt
|
||||
|
||||
def _parse_ai_response_with_count(self, response_content: str, expected_count: int) -> List[str]:
|
||||
"""
|
||||
Parse the AI response to extract new prompts with specific expected count.
|
||||
"""
|
||||
# First, try to clean up the response content
|
||||
cleaned_content = self._clean_ai_response(response_content)
|
||||
|
||||
try:
|
||||
# Try to parse as JSON
|
||||
data = json.loads(cleaned_content)
|
||||
|
||||
# Check if data is a list
|
||||
if isinstance(data, list):
|
||||
# Return the list of prompt strings directly
|
||||
# Ensure we have the correct number of prompts
|
||||
if len(data) >= expected_count:
|
||||
return data[:expected_count]
|
||||
else:
|
||||
self.console.print(f"[yellow]Warning: AI returned {len(data)} prompts, expected {expected_count}[/yellow]")
|
||||
return data
|
||||
elif isinstance(data, dict):
|
||||
# Fallback for old format: dictionary with newprompt0, newprompt1, etc.
|
||||
self.console.print("[yellow]Warning: AI returned dictionary format, expected list format[/yellow]")
|
||||
new_prompts = []
|
||||
for i in range(expected_count):
|
||||
key = f"newprompt{i}"
|
||||
if key in data:
|
||||
new_prompts.append(data[key])
|
||||
return new_prompts
|
||||
else:
|
||||
self.console.print(f"[yellow]Warning: AI returned unexpected data type: {type(data)}[/yellow]")
|
||||
return []
|
||||
|
||||
except json.JSONDecodeError:
|
||||
# If not valid JSON, try to extract prompts from text
|
||||
self.console.print("[yellow]Warning: AI response is not valid JSON, attempting to extract prompts...[/yellow]")
|
||||
|
||||
# Look for patterns in the text
|
||||
lines = response_content.strip().split('\n')
|
||||
new_prompts = []
|
||||
|
||||
for i, line in enumerate(lines[:expected_count]): # Take first N non-empty lines
|
||||
line = line.strip()
|
||||
if line and len(line) > 50: # Reasonable minimum length for a prompt
|
||||
new_prompts.append(line)
|
||||
|
||||
return new_prompts
|
||||
|
||||
def fill_pool_to_target(self) -> int:
|
||||
"""Fill the prompt pool to reach cached_pool_volume target with a single API call."""
|
||||
target_volume = self.settings['cached_pool_volume']
|
||||
current_pool_size = len(self.pool_prompts)
|
||||
|
||||
if current_pool_size >= target_volume:
|
||||
self.console.print(f"[green]Pool already has {current_pool_size} prompts, target is {target_volume}[/green]")
|
||||
return 0
|
||||
|
||||
prompts_needed = target_volume - current_pool_size
|
||||
self.console.print(f"[cyan]Current pool size: {current_pool_size}[/cyan]")
|
||||
self.console.print(f"[cyan]Target pool size: {target_volume}[/cyan]")
|
||||
self.console.print(f"[cyan]Prompts needed: {prompts_needed}[/cyan]")
|
||||
|
||||
# Make a single API call to generate exactly the number of prompts needed
|
||||
self.console.print(f"\n[cyan]Making single API call to generate {prompts_needed} prompts...[/cyan]")
|
||||
new_prompts = self.generate_specific_number_of_prompts(prompts_needed)
|
||||
|
||||
if new_prompts:
|
||||
# Add all generated prompts to pool
|
||||
self.pool_prompts.extend(new_prompts)
|
||||
total_added = len(new_prompts)
|
||||
self.console.print(f"[green]Added {total_added} prompts to pool[/green]")
|
||||
|
||||
# Save the updated pool
|
||||
self._save_pool_prompts()
|
||||
return total_added
|
||||
else:
|
||||
self.console.print("[red]Failed to generate prompts[/red]")
|
||||
return 0
|
||||
|
||||
def generate_theme_feedback_words(self) -> List[str]:
|
||||
"""Generate 6 theme feedback words using AI based on historic prompts."""
|
||||
self.console.print("\n[cyan]Generating theme feedback words based on historic prompts...[/cyan]")
|
||||
|
||||
# Load the feedback prompt template
|
||||
try:
|
||||
with open("ds_feedback.txt", "r") as f:
|
||||
feedback_template = f.read()
|
||||
except FileNotFoundError:
|
||||
self.console.print("[red]Error: ds_feedback.txt not found[/red]")
|
||||
return []
|
||||
|
||||
# Prepare the full prompt with historic context and feedback words
|
||||
if self.historic_prompts:
|
||||
historic_context = json.dumps(self.historic_prompts, indent=2)
|
||||
full_prompt = f"{feedback_template}\n\nPrevious prompts:\n{historic_context}"
|
||||
|
||||
# Add current feedback words if available (with weights)
|
||||
if self.feedback_words:
|
||||
feedback_context = json.dumps(self.feedback_words, indent=2)
|
||||
full_prompt = f"{full_prompt}\n\nCurrent feedback themes (with weights):\n{feedback_context}"
|
||||
|
||||
# Add historic feedback words if available (just words, no weights)
|
||||
if self.feedback_historic:
|
||||
feedback_historic_context = json.dumps(self.feedback_historic, indent=2)
|
||||
full_prompt = f"{full_prompt}\n\nHistoric feedback themes (just words):\n{feedback_historic_context}"
|
||||
else:
|
||||
self.console.print("[yellow]Warning: No historic prompts available for feedback analysis[/yellow]")
|
||||
return []
|
||||
|
||||
# Show progress
|
||||
with Progress(
|
||||
SpinnerColumn(),
|
||||
TextColumn("[progress.description]{task.description}"),
|
||||
transient=True,
|
||||
) as progress:
|
||||
task = progress.add_task("Calling AI API for theme analysis...", total=None)
|
||||
|
||||
try:
|
||||
# Call the AI API
|
||||
response = self.client.chat.completions.create(
|
||||
model=self.model,
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a creative writing assistant that analyzes writing prompts. Always respond with valid JSON."},
|
||||
{"role": "user", "content": full_prompt}
|
||||
],
|
||||
temperature=0.7,
|
||||
max_tokens=1000
|
||||
)
|
||||
|
||||
response_content = response.choices[0].message.content
|
||||
|
||||
except Exception as e:
|
||||
self.console.print(f"[red]Error calling AI API: {e}[/red]")
|
||||
self.console.print(f"[yellow]Full prompt sent to API (first 500 chars):[/yellow]")
|
||||
self.console.print(f"[yellow]{full_prompt[:500]}...[/yellow]")
|
||||
return []
|
||||
|
||||
# Parse the response to get 6 theme words
|
||||
theme_words = self._parse_theme_words_response(response_content)
|
||||
|
||||
if not theme_words or len(theme_words) != 6:
|
||||
self.console.print(f"[red]Error: Expected 6 theme words, got {len(theme_words) if theme_words else 0}[/red]")
|
||||
return []
|
||||
|
||||
return theme_words
|
||||
|
||||
def _parse_theme_words_response(self, response_content: str) -> List[str]:
|
||||
"""
|
||||
Parse the AI response to extract 6 theme words.
|
||||
Expected format: JSON list of 6 lowercase words.
|
||||
"""
|
||||
# First, try to clean up the response content
|
||||
cleaned_content = self._clean_ai_response(response_content)
|
||||
|
||||
try:
|
||||
# Try to parse as JSON
|
||||
data = json.loads(cleaned_content)
|
||||
|
||||
# Check if data is a list
|
||||
if isinstance(data, list):
|
||||
# Ensure all items are strings and lowercase them
|
||||
theme_words = []
|
||||
for word in data:
|
||||
if isinstance(word, str):
|
||||
theme_words.append(word.lower().strip())
|
||||
else:
|
||||
theme_words.append(str(word).lower().strip())
|
||||
|
||||
return theme_words
|
||||
else:
|
||||
self.console.print(f"[yellow]Warning: AI returned unexpected data type: {type(data)}[/yellow]")
|
||||
return []
|
||||
|
||||
except json.JSONDecodeError:
|
||||
# If not valid JSON, try to extract words from text
|
||||
self.console.print("[yellow]Warning: AI response is not valid JSON, attempting to extract theme words...[/yellow]")
|
||||
|
||||
# Look for patterns in the text
|
||||
lines = response_content.strip().split('\n')
|
||||
theme_words = []
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if line and len(line) < 50: # Theme words should be short
|
||||
# Try to extract words (lowercase, no punctuation)
|
||||
words = [w.lower().strip('.,;:!?()[]{}"\'') for w in line.split()]
|
||||
theme_words.extend(words)
|
||||
|
||||
if len(theme_words) >= 6:
|
||||
break
|
||||
|
||||
return theme_words[:6]
|
||||
|
||||
def collect_feedback_ratings(self, theme_words: List[str]) -> List[Dict[str, Any]]:
|
||||
"""Collect user ratings (0-6) for each theme word and return structured feedback."""
|
||||
self.console.print("\n[bold]Please rate each theme word from 0 to 6:[/bold]")
|
||||
self.console.print("[dim]0 = Not relevant, 6 = Very relevant[/dim]\n")
|
||||
|
||||
feedback_items = []
|
||||
|
||||
for i, word in enumerate(theme_words):
|
||||
while True:
|
||||
try:
|
||||
rating = Prompt.ask(
|
||||
f"[bold]Word {i+1}: {word}[/bold]",
|
||||
choices=[str(x) for x in range(0, 7)], # 0-6 inclusive
|
||||
default="3"
|
||||
)
|
||||
rating_int = int(rating)
|
||||
|
||||
if 0 <= rating_int <= 6:
|
||||
# Create feedback item with key (feedback00, feedback01, etc.)
|
||||
feedback_key = f"feedback{i:02d}"
|
||||
feedback_items.append({
|
||||
feedback_key: word,
|
||||
"weight": rating_int
|
||||
})
|
||||
break
|
||||
else:
|
||||
self.console.print("[yellow]Please enter a number between 0 and 6[/yellow]")
|
||||
except ValueError:
|
||||
self.console.print("[yellow]Please enter a valid number[/yellow]")
|
||||
|
||||
return feedback_items
|
||||
|
||||
def update_feedback_words(self, new_feedback_items: List[Dict[str, Any]]):
|
||||
"""Update feedback words with new ratings."""
|
||||
# Replace existing feedback words with new ones
|
||||
self.feedback_words = new_feedback_items
|
||||
self._save_feedback_words()
|
||||
self.console.print(f"[green]Updated feedback words with {len(new_feedback_items)} items[/green]")
|
||||
|
||||
# Also add the new feedback words to the historic buffer
|
||||
self.add_feedback_words_to_history()
|
||||
|
||||
def display_prompts(self, prompts: List[str]):
|
||||
"""Display generated prompts in a nice format."""
|
||||
self.console.print("\n" + "="*60)
|
||||
self.console.print("[bold green]✨ NEW JOURNAL PROMPTS GENERATED ✨[/bold green]")
|
||||
self.console.print("[bold green]✨ READING FROM POOL ✨[/bold green]")
|
||||
self.console.print("="*60 + "\n")
|
||||
|
||||
for i, prompt_dict in enumerate(prompts, 1):
|
||||
# Extract prompt text (key is like "prompt60", "prompt61", etc.)
|
||||
prompt_key = list(prompt_dict.keys())[0]
|
||||
prompt_text = prompt_dict[prompt_key]
|
||||
|
||||
# Create a panel for each prompt
|
||||
for i, prompt_text in enumerate(prompts, 1):
|
||||
# Create a panel for each prompt with UI numbering
|
||||
panel = Panel(
|
||||
f"[cyan]{prompt_text}[/cyan]",
|
||||
title=f"[bold]Prompt #{i}[/bold]",
|
||||
@@ -313,19 +761,33 @@ class JournalPromptGenerator:
|
||||
self.console.print(panel)
|
||||
self.console.print() # Empty line between prompts
|
||||
|
||||
def show_history_stats(self):
|
||||
"""Show statistics about prompt history."""
|
||||
total_prompts = len(self.historic_prompts)
|
||||
def show_combined_stats(self):
|
||||
"""Show combined statistics about both prompt pool and history."""
|
||||
# Pool statistics
|
||||
total_pool_prompts = len(self.pool_prompts)
|
||||
pool_table = Table(title="Prompt Pool Statistics")
|
||||
pool_table.add_column("Metric", style="cyan")
|
||||
pool_table.add_column("Value", style="green")
|
||||
|
||||
table = Table(title="Prompt History Statistics")
|
||||
table.add_column("Metric", style="cyan")
|
||||
table.add_column("Value", style="green")
|
||||
pool_table.add_row("Prompts in pool", str(total_pool_prompts))
|
||||
pool_table.add_row("Prompts per session", str(self.settings['num_prompts']))
|
||||
pool_table.add_row("Target pool size", str(self.settings['cached_pool_volume']))
|
||||
pool_table.add_row("Available sessions", str(total_pool_prompts // self.settings['num_prompts']))
|
||||
|
||||
table.add_row("Total prompts in history", str(total_prompts))
|
||||
table.add_row("History capacity", "60 prompts")
|
||||
table.add_row("Available slots", str(max(0, 60 - total_prompts)))
|
||||
# History statistics
|
||||
total_history_prompts = len(self.historic_prompts)
|
||||
history_table = Table(title="Prompt History Statistics")
|
||||
history_table.add_column("Metric", style="cyan")
|
||||
history_table.add_column("Value", style="green")
|
||||
|
||||
self.console.print(table)
|
||||
history_table.add_row("Total prompts in history", str(total_history_prompts))
|
||||
history_table.add_row("History capacity", "60 prompts")
|
||||
history_table.add_row("Available slots", str(max(0, 60 - total_history_prompts)))
|
||||
|
||||
# Display both tables
|
||||
self.console.print(pool_table)
|
||||
self.console.print() # Empty line between tables
|
||||
self.console.print(history_table)
|
||||
|
||||
def interactive_mode(self):
|
||||
"""Run in interactive mode with user prompts."""
|
||||
@@ -336,52 +798,68 @@ class JournalPromptGenerator:
|
||||
))
|
||||
|
||||
while True:
|
||||
self.console.print("\n[bold]Options:[/bold]")
|
||||
self.console.print("1. Generate new prompts")
|
||||
self.console.print("2. View history statistics")
|
||||
self.console.print("3. Exit")
|
||||
# Display most recent prompt from history if available
|
||||
if self.historic_prompts:
|
||||
most_recent_prompt = self.historic_prompts[0] # prompt00 is always first
|
||||
prompt_key = list(most_recent_prompt.keys())[0]
|
||||
prompt_text = most_recent_prompt[prompt_key]
|
||||
|
||||
choice = Prompt.ask("\nEnter your choice", choices=["1", "2", "3"], default="1")
|
||||
self.console.print("\n" + "="*60)
|
||||
self.console.print("[bold cyan]📝 CURRENT PROMPT 📝[/bold cyan]")
|
||||
self.console.print("="*60)
|
||||
self.console.print(f"\n[cyan]{prompt_text}[/cyan]\n")
|
||||
self.console.print("="*60 + "\n")
|
||||
|
||||
self.console.print("\n[bold]Options:[/bold]")
|
||||
self.console.print("1. Draw prompts from pool (no API call)")
|
||||
self.console.print("2. Fill prompt pool using API")
|
||||
self.console.print("3. View combined statistics")
|
||||
self.console.print("4. Generate and rate theme feedback words")
|
||||
self.console.print("5. Exit")
|
||||
|
||||
choice = Prompt.ask("\nEnter your choice", choices=["1", "2", "3", "4", "5"], default="1")
|
||||
|
||||
if choice == "1":
|
||||
new_prompts = self.generate_prompts()
|
||||
if new_prompts:
|
||||
self.display_prompts(new_prompts)
|
||||
# Draw prompts from pool
|
||||
drawn_prompts = self.draw_prompts_from_pool()
|
||||
if drawn_prompts:
|
||||
self.display_prompts(drawn_prompts)
|
||||
|
||||
# Ask if user wants to save a prompt
|
||||
if Confirm.ask("\nWould you like to save one of these prompts to a file?"):
|
||||
prompt_num = Prompt.ask(
|
||||
"Which prompt number would you like to save?",
|
||||
choices=[str(i) for i in range(1, len(new_prompts) + 1)],
|
||||
default="1"
|
||||
)
|
||||
# Ask which prompt to add to history
|
||||
prompt_num = Prompt.ask(
|
||||
"\nWhich prompt number would you like to add to history?",
|
||||
choices=[str(i) for i in range(1, len(drawn_prompts) + 1)],
|
||||
default="1"
|
||||
)
|
||||
|
||||
prompt_idx = int(prompt_num) - 1
|
||||
prompt_dict = new_prompts[prompt_idx]
|
||||
prompt_key = list(prompt_dict.keys())[0]
|
||||
prompt_text = prompt_dict[prompt_key]
|
||||
prompt_idx = int(prompt_num) - 1
|
||||
prompt_text = drawn_prompts[prompt_idx]
|
||||
|
||||
# Save to file
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
filename = f"journal_prompt_{timestamp}.txt"
|
||||
|
||||
with open(filename, "w") as f:
|
||||
f.write(f"Journal Prompt - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
||||
f.write("="*50 + "\n\n")
|
||||
f.write(prompt_text)
|
||||
f.write("\n\n" + "="*50 + "\n")
|
||||
f.write("Happy writing! ✍️\n")
|
||||
|
||||
self.console.print(f"[green]Prompt saved to {filename}[/green]")
|
||||
|
||||
# Add the chosen prompt to historic prompts cyclic buffer
|
||||
self.add_prompt_to_history(prompt_text)
|
||||
self.console.print(f"[green]Prompt added to history as prompt00[/green]")
|
||||
# Add the chosen prompt to historic prompts cyclic buffer
|
||||
self.add_prompt_to_history(prompt_text)
|
||||
self.console.print(f"[green]Prompt added to history as prompt00[/green]")
|
||||
|
||||
elif choice == "2":
|
||||
self.show_history_stats()
|
||||
# Fill prompt pool to target volume using API
|
||||
total_added = self.fill_pool_to_target()
|
||||
if total_added > 0:
|
||||
self.console.print(f"[green]Successfully added {total_added} prompts to pool[/green]")
|
||||
else:
|
||||
self.console.print("[yellow]No prompts were added to pool[/yellow]")
|
||||
|
||||
elif choice == "3":
|
||||
self.show_combined_stats()
|
||||
|
||||
elif choice == "4":
|
||||
# Generate and rate theme feedback words
|
||||
theme_words = self.generate_theme_feedback_words()
|
||||
if theme_words:
|
||||
feedback_items = self.collect_feedback_ratings(theme_words)
|
||||
self.update_feedback_words(feedback_items)
|
||||
else:
|
||||
self.console.print("[yellow]No theme words were generated[/yellow]")
|
||||
|
||||
elif choice == "5":
|
||||
self.console.print("[green]Goodbye! Happy journaling! 📓[/green]")
|
||||
break
|
||||
|
||||
@@ -402,7 +880,12 @@ def main():
|
||||
parser.add_argument(
|
||||
"--stats", "-s",
|
||||
action="store_true",
|
||||
help="Show history statistics"
|
||||
help="Show combined statistics (pool and history)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--fill-pool", "-f",
|
||||
action="store_true",
|
||||
help="Fill prompt pool using API"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
@@ -411,14 +894,22 @@ def main():
|
||||
generator = JournalPromptGenerator(config_path=args.config)
|
||||
|
||||
if args.stats:
|
||||
generator.show_history_stats()
|
||||
generator.show_combined_stats()
|
||||
elif args.fill_pool:
|
||||
# Fill prompt pool to target volume using API
|
||||
total_added = generator.fill_pool_to_target()
|
||||
if total_added > 0:
|
||||
generator.console.print(f"[green]Successfully added {total_added} prompts to pool[/green]")
|
||||
else:
|
||||
generator.console.print("[yellow]No prompts were added to pool[/yellow]")
|
||||
elif args.interactive:
|
||||
generator.interactive_mode()
|
||||
else:
|
||||
# Default: generate and display prompts
|
||||
new_prompts = generator.generate_prompts()
|
||||
if new_prompts:
|
||||
generator.display_prompts(new_prompts)
|
||||
# Default: draw prompts from pool (no API call)
|
||||
drawn_prompts = generator.draw_prompts_from_pool()
|
||||
if drawn_prompts:
|
||||
generator.display_prompts(drawn_prompts)
|
||||
generator.console.print("[yellow]Note: These prompts were drawn from the pool. Use --fill-pool to add more prompts.[/yellow]")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
[
|
||||
{
|
||||
"prompt00": "\"newprompt3\": \"Recall a teacher, mentor, or elder who said something to you in passing that you have never forgotten. It might have been a compliment, a criticism, or an offhand observation. Reconstruct the scene. Why did their words carry such weight? How have you turned them over in your mind since? Explore the power of brief, seemingly casual utterances to shape a person's self-concept.\","
|
||||
},
|
||||
{
|
||||
"prompt01": "\"newprompt0\": \"Write a detailed portrait of a tree you know well\u2014not just its appearance, but its history in that spot, the way its branches move in different winds, the creatures that inhabit it, the shadows it casts at various hours. Imagine its perspective across seasons and years. What has it witnessed? What would it say about change, resilience, or stillness if it could speak? Let the tree become a mirror for your own sense of place and time.\","
|
||||
},
|
||||
{
|
||||
"prompt02": "Describe a memory you have that is tied to a specific smell. Don't just tell the story of the event; focus on describing the scent itself in as much detail as possible\u2014its texture, its weight in the air, its nuances. How does conjuring that smell now make you feel in your body? Let the description of the aroma lead you back into the memory's landscape."
|
||||
},
|
||||
{
|
||||
"prompt03": "Write a letter to your 15-year-old self. Be kind, be blunt, be humorous, or be stern. What do you know now that you desperately needed to hear then? What mystery about your future life could you tantalizingly hint at without giving it all away? Don't just give advice; try to capture the voice and tone you wish an older, wiser person had used with you."
|
||||
},
|
||||
{
|
||||
"prompt04": "You find a forgotten door in a place you know well\u2014your home, your workplace, your daily park. It wasn't there yesterday. You open it. Describe what is on the other side using only sensory details: sight, sound, temperature, smell. Do not explain its purpose or origin. Simply document the experience of crossing that threshold."
|
||||
},
|
||||
{
|
||||
"prompt05": "Make a list of ten tiny, perfect moments from the past month that no one else probably noticed or would remember. The way light fell on a spoon, a stranger's half-smile, the sound of rain stopping. Elaborate on at least three of them, expanding them into full vignettes. Why did these micro-moments stick with you?"
|
||||
},
|
||||
{
|
||||
"prompt06": "Invent a mythological creature for a modern urban setting. What does it look like? What is its behavior and habitat (e.g., subway tunnels, server farms, air vents)? What folklore do people whisper about it? What does it symbolize\u2014anxiety, forgotten connections, hope? Describe a recent 'sighting' of this creature in vivid detail."
|
||||
},
|
||||
{
|
||||
"prompt07": "Choose an object in your immediate line of sight that is not electronic. Write its biography. Where was it made? Who owned it before you? What conversations has it overheard? What secrets does it hold? What small damages or wear marks does it have, and what story does each tell? Give this ordinary item an epic history."
|
||||
},
|
||||
{
|
||||
"prompt08": "Describe your current emotional state as a weather system. Is it a still, high-pressure fog? A sudden, sharp hailstorm? A lingering, humid drizzle? Map its boundaries, its intensity, its forecast. What terrain does it move over\u2014the mountains of your responsibilities, the plains of your routine? How does it affect your internal climate?"
|
||||
},
|
||||
{
|
||||
"prompt09": "Recall a time you were deeply embarrassed. Write about it from the perspective of a sympathetic observer who was there\u2014or invent one. How might they have perceived the event? What context or kindness might they have seen that you, in your self-focused shame, completely missed? Reframe the memory through their eyes."
|
||||
},
|
||||
{
|
||||
"prompt10": "What skill or craft have you always wanted to learn but haven't? Immerse yourself in a detailed fantasy of mastering it. Describe the feel of the tools in your hands, the initial frustrations, the first small success, the growing muscle memory. What does the final, perfected product of your labor look or feel like? Live in that imagined\u6210\u5c31\u611f."
|
||||
},
|
||||
{
|
||||
"prompt11": "Write a dialogue between two aspects of yourself (e.g., Your Ambitious Self and Your Tired Self; Your Cynical Self and Your Hopeful Self). Give them distinct voices. What are they arguing about, negotiating, or planning? Don't just state positions; let them bicker, persuade, or sit in silence together. See where the conversation goes."
|
||||
},
|
||||
{
|
||||
"prompt12": "Describe your childhood home from the perspective of a small animal (a mouse, a squirrel, a bird) that lived there concurrently with you. What did this creature notice about your family's rhythms, the layout, the dangers, and the treasures (crumbs, cozy materials)? How did it perceive you, the giant human child?"
|
||||
},
|
||||
{
|
||||
"prompt13": "List five paths your life could have taken if you'd made one different choice. Briefly outline each alternate reality. Then, choose one and dive deep: write a journal entry from that version of you today. What are their worries, joys, and regrets? How is their voice similar to or different from your own?"
|
||||
},
|
||||
{
|
||||
"prompt14": "Think of a person you see regularly but do not know (a barista, a neighbor, a commuter). Invent a rich, secret inner life for them. What profound private mission are they on? What hidden talent do they possess? What great sorrow or hope are they carrying today as they serve your coffee or stand on the platform? Write from their perspective."
|
||||
},
|
||||
{
|
||||
"prompt15": "What is a belief you held strongly five or ten years ago that you have since questioned or abandoned? Trace the evolution of that change. Was it a sudden shattering or a slow erosion? What person, experience, or piece of information was the catalyst? Describe the feeling of the ground shifting under that particular piece of your worldview."
|
||||
},
|
||||
{
|
||||
"prompt16": "Describe a common, mundane process (making tea, tying your shoes, doing laundry) in extreme, almost absurdly epic detail, as if you were writing a sacred manual or a scientific treatise for an alien civilization. Break down every micro-action, every sensation, every potential variable. Find the profound in the procedural."
|
||||
},
|
||||
{
|
||||
"prompt17": "You are given a suitcase and told you must leave your home in one hour, not knowing if or when you'll return. You can only take what fits in the case. Describe, in real-time, the frantic and deliberate process of choosing. What practical items make the cut? What irreplaceable tokens? What do you leave behind, and what does that feel like?"
|
||||
},
|
||||
{
|
||||
"prompt18": "Write about water in three different forms: as a memory involving a body of water (ocean, river, bath), as a description of drinking a glass of water right now, and as a metaphor for an emotion. Move seamlessly between these three aspects. Let the fluidity of the theme connect them."
|
||||
},
|
||||
{
|
||||
"prompt19": "What does silence sound like in your current environment? Don't just say 'quiet.' Describe the layers of sound that actually constitute the silence\u2014the hums, ticks, distant rumbles, the sound of your own body. Now, project what this same space sounded like 100 years ago, and what it might sound like 100 years from now."
|
||||
},
|
||||
{
|
||||
"prompt20": "Create a recipe for a dish that represents your current life phase. List ingredients (e.g., \"two cups of transition,\" \"a pinch of anxiety,\" \"a steady base of routine\"). Write the instructions, including the method, cooking time, and necessary equipment. Describe the final product's taste, texture, and who it should be shared with."
|
||||
},
|
||||
{
|
||||
"prompt21": "Recall a dream from the past week, however fragmentary. Don't interpret it. Instead, expand it. Continue the narrative from where it left off. Describe the dream logic, the landscape, the characters. Let it become a story. Where does your dreaming mind take you when given free rein on the page?"
|
||||
},
|
||||
{
|
||||
"prompt22": "Make a list of everything that is blue in your immediate environment. Describe each shade specifically (slate, cobalt, robin's egg, faded denim). Then, choose one blue object and write about its journey to being here, in this blue state, in front of you. How did it get its color? What has it reflected?"
|
||||
},
|
||||
{
|
||||
"prompt23": "Write a eulogy for something you've lost that isn't a person\u2014a habit, a version of a city, a relationship dynamic, a part of your identity. Acknowledge its virtues and its flaws. Say goodbye properly, with humor, regret, and gratitude. What did it give you? What space has its departure created?"
|
||||
},
|
||||
{
|
||||
"prompt24": "Describe your hands. Not just their appearance, but their capabilities, their scars, their memories. What have they held, built, comforted, or torn down? What do their specific aches and strengths tell you about the life you've lived so far? If your hands could speak, what would they say they want to do next?"
|
||||
},
|
||||
{
|
||||
"prompt25": "Imagine you can overhear the conversation of the people at the table next to you in a caf\u00e9, but they are speaking in a language you don't understand. Based on their tone, gestures, pauses, and expressions, invent the dialogue. What crucial, funny, or tragic misunderstanding are they having? What are they *really* talking about?"
|
||||
},
|
||||
{
|
||||
"prompt26": "What is a piece of art (a song, painting, film, book) that fundamentally moved you? Describe the first time you encountered it. Don't just analyze why it's good; describe the physical and emotional reaction it provoked. Has its meaning changed for you over time? How does it live inside you now?"
|
||||
},
|
||||
{
|
||||
"prompt27": "You have one day completely alone, with no obligations and no possibility of communication. The power and internet are out. How do you spend the hours from waking to sleeping? Detail the rituals, the wanderings, the thoughts, the meals. Do you enjoy the solitude or chafe against it? What arises in the quiet?"
|
||||
},
|
||||
{
|
||||
"prompt28": "Personify a negative emotion you've been feeling lately (e.g., anxiety, envy, restlessness). Give it a name, a form, a voice. Write a character profile of it. What does it want? What does it fear? What flawed logic does it operate under? Then, write a short scene of you having a cup of tea with it, listening to its perspective."
|
||||
},
|
||||
{
|
||||
"prompt29": "Describe a city you've never been to, based solely on the stories, images, and snippets you've absorbed about it. Build it from imagination and second-hand clues. Then, contrast that with a description of your own street, seen with the hyper-attentive eyes of a first-time visitor. Make the familiar alien, and the alien familiar."
|
||||
},
|
||||
{
|
||||
"prompt30": "Think of a crossroads in your past. Now, imagine you see a ghost of your former self standing there, frozen in that moment of decision. What would you want to say to that ghost? Would you offer comfort, a warning, or just silent companionship? Write the encounter. Does the ghost speak back?"
|
||||
},
|
||||
{
|
||||
"prompt31": "What is a tradition in your family or community\u2014big or small\u2014that you find meaningful? Describe its sensory details, its rhythms, its players. Now, trace its origin. How did it start? Has it mutated over time? What does its continued practice say about what your family values, fears, or hopes for?"
|
||||
},
|
||||
{
|
||||
"prompt32": "Choose a year from your past. Catalog the soundtrack of that year: songs on the radio, albums you loved, jingles, background music. For each, describe a specific memory or feeling it evokes. How does the music of that time period color your memory of the entire era? What does it sound like to you now?"
|
||||
},
|
||||
{
|
||||
"prompt33": "Write instructions for a stranger on how to be you for a day. Include the essential routines, the internal dialogues to expect, the things to avoid, the small comforts to lean on, and the passwords to your various anxieties. Be brutally honest and surprisingly practical. What would they find hardest to mimic?"
|
||||
},
|
||||
{
|
||||
"prompt34": "Describe a moment of unexpected kindness, either given or received. Don't frame it as a grand gesture. Focus on a small, almost invisible act. What were the circumstances? Why was it so potent? How did it ripple out, changing the temperature of your day or your perception of someone?"
|
||||
},
|
||||
{
|
||||
"prompt35": "You discover you have a superpower, but it is frustratingly mundane and specific (e.g., the ability to always know exactly what time it is without a clock, to perfectly fold fitted sheets, to find lost buttons). Explore the practical uses, the minor heroics, the unexpected downsides, and the peculiar loneliness of this unique gift."
|
||||
},
|
||||
{
|
||||
"prompt36": "Go to a window. Describe the view in extreme detail, as if painting it with words, for five minutes. Then, close your eyes and describe the view from a window that was significant to you in the past (your childhood bedroom, a previous office, a grandparent's house). Juxtapose the two landscapes on the page."
|
||||
},
|
||||
{
|
||||
"prompt37": "What is a question you are tired of being asked? Write a rant about why it's so irritating, reductive, or painful. Then, flip it: write the question you wish people would ask you instead. Answer that new question fully and generously."
|
||||
},
|
||||
{
|
||||
"prompt38": "Describe a hobby or interest you have from the perspective of someone who finds it utterly baffling and boring. Then, defend it with the passionate zeal of a true devotee. Try to convey its magic and depth to this imagined skeptic. What is the core beauty you see that they miss?"
|
||||
},
|
||||
{
|
||||
"prompt39": "List ten things you would do if you were not afraid. They can be grand (quit my job) or small (sing karaoke). Choose one and vividly imagine doing it. Walk through every step, from decision to action to aftermath. How does the air feel different on the other side of that fear?"
|
||||
},
|
||||
{
|
||||
"prompt40": "Write about a time you got exactly what you wanted\u2014and it was not what you expected. Describe the desire, the anticipation, the moment of attainment, and the subtle (or not-so-subtle) disappointment or confusion that followed. What did that experience teach you about wanting?"
|
||||
},
|
||||
{
|
||||
"prompt41": "Personify your favorite season. Give it a personality, a wardrobe, a way of speaking, a scent. Write a monologue from its perspective. What does it think of humans? What is its purpose? What does it love and resent about its place in the yearly cycle? Let it defend itself against its critics."
|
||||
},
|
||||
{
|
||||
"prompt42": "Describe a room after a significant emotional event has occurred (a fight, a celebration, a profound conversation), but without mentioning the event or the people. Let the objects, the disarray, the quality of light, the lingering smells tell the story. What is the room holding now?"
|
||||
},
|
||||
{
|
||||
"prompt43": "What is a rule you live by? It can be profound ('assume good intent') or practical ('never grocery shop hungry'). Explain its origin story. When have you broken it, and what were the consequences? How has following it shaped your life? Argue for its universal adoption."
|
||||
},
|
||||
{
|
||||
"prompt44": "Imagine you can send a message of exactly 100 words into the future, to be read by you in ten years. What do you say? What do you ask? What current worry do you want to soothe, or what current joy do you want future-you to remember? Write the message. Then, write a 100-word message from you-ten-years-ago to present-you."
|
||||
},
|
||||
{
|
||||
"prompt45": "Describe a color to someone who has been blind from birth. Use metaphor, texture, temperature, sound, emotion, and memory. Don't rely on sight-based comparisons ('like the sky'). Try to make them *feel* the essence of 'red' or 'blue' or 'gold' in their bones."
|
||||
},
|
||||
{
|
||||
"prompt46": "Write about a book that changed your mind. Not just one you loved, but one that actively altered your perspective on a topic. Describe your stance before, the unsettling process of reading it, and the new landscape of thought afterwards. What made you vulnerable to its argument?"
|
||||
},
|
||||
{
|
||||
"prompt47": "Catalog the contents of a junk drawer or a cluttered bag\u2014yours or an imagined one. Describe each item with care, as an archaeologist might. Then, construct a biography of the drawer's owner based solely on these artifacts. What story do these mundane relics tell?"
|
||||
},
|
||||
{
|
||||
"prompt48": "What does your body know how to do that your mind often forgets? (e.g., breathe deeply, relax into sleep, find rhythm in a walk). Write a series of instructions from your body to your mind, teaching it this skill again. Use the language of sensation, not thought."
|
||||
},
|
||||
{
|
||||
"prompt49": "Describe a journey you take every day (commute, walk the dog, trip to the kitchen) as a epic quest. Cast yourself as the hero, the mundane obstacles as trials (the traffic dragon, the staircase mountain), and the destination as a holy grail. Infuse the ordinary with mythic significance."
|
||||
},
|
||||
{
|
||||
"prompt50": "Write a review of today, as if it were a product, a performance, or a restaurant. Give it star ratings in different categories (Productivity, Serendipity, Comfort, Learning). Write the critic's summary. Be witty, be harsh, be fair. What was the highlight? The biggest flaw? Would you recommend today to a friend?"
|
||||
},
|
||||
{
|
||||
"prompt51": "Think of a word you love the sound of. Write it at the top of the page. Now, free-associate from that word\u2014not just meanings, but sounds, memories, images. Let it spin a web of connections. Where does it lead you? Try to capture the aesthetic and emotional resonance of the word itself."
|
||||
},
|
||||
{
|
||||
"prompt52": "Describe a piece of technology you use daily from the perspective of a person from 200 years ago. Have them stumble upon it and try to deduce its purpose, its magic, and its societal implications from its form and behavior. What would they find beautiful, terrifying, or absurd?"
|
||||
},
|
||||
{
|
||||
"prompt53": "Write a gratitude list, but for difficult things. Be specific. Not just 'my job,' but 'the particular frustration of my project that forced me to learn a new skill.' Find the hidden gift in the challenge, the strength forged in the irritation, the clarity born of the sorrow."
|
||||
},
|
||||
{
|
||||
"prompt54": "You meet a version of yourself from a parallel universe where one key life detail is different (you chose a different career, never moved, stayed with a different partner). Have a conversation with them. What do you envy? What do you pity? What fundamental part of 'you' remains the same?"
|
||||
},
|
||||
{
|
||||
"prompt55": "Describe a taste from your childhood that you can almost, but not quite, recall\u2014a specific candy, a grandparent's dish, a seasonal treat. Try to chase the memory with all your senses. What else was happening when you tasted it? Who was there? Why is it lost? The struggle to remember is the story."
|
||||
},
|
||||
{
|
||||
"prompt56": "What is a place that feels like a sanctuary to you? Describe it not just physically, but atmospherically. What rules (spoken or unspoken) govern it? What happens to your internal noise when you enter? What would it mean to carry the essence of that place with you into busier, louder realms?"
|
||||
},
|
||||
{
|
||||
"prompt57": "Write about waiting. Describe a specific time you were waiting for something (big or small). Capture the texture of the time itself\u2014how it stretched, what you noticed, the cycles of hope and boredom. What did you learn in the suspension? The action is in the inaction."
|
||||
},
|
||||
{
|
||||
"prompt58": "Compose a manifesto for a very small, personal revolution. What outdated pattern, fear, or limitation are you overthrowing? What are your guiding principles? What is your first, tiny act of rebellion? Use bold, declarative language. Declare your independence from something that no longer serves you."
|
||||
},
|
||||
{
|
||||
"prompt59": "Describe a conflict you witnessed but were not part of\u2014between strangers, friends, or family members. Focus on the subtext: the body language, the words not said, the history simmering beneath the surface. What did you understand about each person from this glimpse? Why has this particular conflict stuck with you?"
|
||||
}
|
||||
]
|
||||
182
prompts_historic.json
Normal file
182
prompts_historic.json
Normal file
@@ -0,0 +1,182 @@
|
||||
[
|
||||
{
|
||||
"prompt00": "Choose a common phrase you use often (e.g., \"I'm fine,\" \"Just a minute,\" \"Don't worry about it\"). Dissect it. What does it truly mean when you say it? What does it conceal? What convenience does it provide? Now, for one day, vow not to use it. Chronicle the conversations that become longer, more awkward, or more honest as a result."
|
||||
},
|
||||
{
|
||||
"prompt01": "Recall a time you received a gift that was perfectly, inexplicably right for you. Describe the gift and the giver. What made it so resonant? Was it an understanding of a secret wish, a reflection of an unseen part of you, or a tool you didn't know you needed? Explore the magic of being seen and understood through the medium of an object."
|
||||
},
|
||||
{
|
||||
"prompt02": "Map a friendship as a shared garden. What did each of you plant in the initial soil? What has grown wild? What requires regular tending? Have there been seasons of drought or frost? Are there any beautiful, stubborn weeds? Write a gardener's diary entry about the current state of this plot, reflecting on its history and future."
|
||||
},
|
||||
{
|
||||
"prompt03": "Describe a skill you have that is entirely non-verbal\u2014perhaps riding a bike, kneading dough, tuning an instrument by ear. Attempt to write a manual for this skill using only metaphors and physical sensations. Avoid technical terms. Can you translate embodied knowledge into prose? What is lost, and what is poetically gained?"
|
||||
},
|
||||
{
|
||||
"prompt04": "Recall a scent that acts as a master key, unlocking a flood of specific, detailed memories. Describe the scent in non-scent words: is it sharp, round, velvety, brittle? Now, follow the key into the memory palace it opens. Don't just describe the memory; describe the architecture of the connection itself. How is scent wired so directly to the past?"
|
||||
},
|
||||
{
|
||||
"prompt05": "Imagine you are a translator for a species that communicates through subtle shifts in temperature. Describe a recent emotional experience as a thermal map. Where in your body did the warmth of joy concentrate? Where did the cold front of anxiety settle? How would you translate this silent, somatic language into words for someone who only understands degrees and gradients?"
|
||||
},
|
||||
{
|
||||
"prompt06": "Find a surface covered in a fine layer of dust\u2014a windowsill, an old book, a forgotten picture frame. Describe this 'residue' of time and neglect. What stories does the pattern of settlement tell? Write about the act of wiping it away. Is it an erasure of history or a renewal? What clean surface is revealed, and does it feel like a loss or a gain?"
|
||||
},
|
||||
{
|
||||
"prompt07": "Build a 'gossamer' bridge in your mind between two seemingly disconnected concepts: for example, baking bread and forgiveness, or traffic patterns and anxiety. Describe the fragile, translucent strands of logic or metaphor you use to connect them. Walk across this bridge. What new landscape do you find on the other side? Does the bridge hold, or dissolve after use?"
|
||||
},
|
||||
{
|
||||
"prompt08": "Map a personal 'labyrinth' of procrastination or avoidance. What are its enticing entryways (\"I'll just check...\")? Its circular corridors of rationalization? Its terrifying center (the task itself)? Describe one recent journey into this maze. What finally provided the thread to lead you out, or what made you decide to sit in the center and confront the Minotaur?"
|
||||
},
|
||||
{
|
||||
"prompt09": "Craft a mental 'effigy' of a piece of advice you were given that you've chosen to ignore. Give it form and substance. Do you keep it on a shelf, bury it, or ritually dismantle it? Write about the act of holding this representation of rejected wisdom. Does making it concrete help you understand your refusal, or simply honor the intention of the giver?"
|
||||
},
|
||||
{
|
||||
"prompt10": "Recall a decision point that felt like standing at the mouth of a 'labyrinth,' with multiple winding paths ahead. Describe the initial confusion and the method you used to choose an entrance (logic, intuition, chance). Now, with hindsight, map the path you actually took. Were there dead ends or unexpected centers? Did the labyrinth lead you out, or deeper into understanding?"
|
||||
},
|
||||
{
|
||||
"prompt11": "Contemplate a 'quasar'\u2014an immensely luminous, distant celestial object. Use it as a metaphor for a source of guidance or inspiration in your life that feels both incredibly powerful and remote. Who or what is this distant beacon? Describe the 'light' it emits and the long journey it takes to reach you. How do you navigate by this ancient, brilliant, but fundamentally untouchable signal?"
|
||||
},
|
||||
{
|
||||
"prompt12": "Describe a piece of music that left a 'residue' in your mind\u2014a melody that loops unbidden, a lyric that sticks, a rhythm that syncs with your heartbeat. How does this auditory artifact resurface during quiet moments? What emotional or memory-laden dust has it collected? Write about the process of this mental replay, and whether you seek to amplify it or gently brush it away."
|
||||
},
|
||||
{
|
||||
"prompt13": "Recall a 'failed' experiment from your past\u2014a recipe that flopped, a project abandoned, a relationship that didn't work. Instead of framing it as a mistake, analyze it as a valuable trial that produced data. What did you learn about the materials, the process, or yourself? How did the outcome diverge from your hypothesis? Write a lab report for this experiment, focusing on the insights gained rather than the desired product. How does this reframe 'failure'?"
|
||||
},
|
||||
{
|
||||
"prompt14": "Chronicle the life cycle of a rumor or piece of gossip that reached you. Where did you first hear it? How did it mutate as it passed to you? What was your role\u2014conduit, amplifier, skeptic, terminator? Analyze the social algorithm that governs such information transfer. What need did this rumor feed in its listeners? Write about the velocity and distortion of unverified stories through a community."
|
||||
},
|
||||
{
|
||||
"prompt15": "Recall a time you had to translate\u2014not between languages, but between contexts: explaining a job to family, describing an emotion to someone who doesn't share it, making a technical concept accessible. Describe the words that failed you and the metaphors you crafted to bridge the gap. What was lost in translation? What was surprisingly clarified? Explore the act of building temporary, fragile bridges of understanding between internal and external worlds."
|
||||
},
|
||||
{
|
||||
"prompt16": "You discover a forgotten corner of a digital space you own\u2014an old blog draft, a buried folder of photos, an abandoned social media profile. Explore this digital artifact as an archaeologist would a physical site. What does the layout, the language, the imagery tell you about a past self? Reconstruct the mindset of the person who created it. How does this digital echo compare to your current identity? Is it a charming relic or an unsettling ghost?"
|
||||
},
|
||||
{
|
||||
"prompt17": "You are tasked with archiving a sound that is becoming obsolete\u2014the click of a rotary phone, the chirp of a specific bird whose habitat is shrinking, the particular hum of an old appliance. Record a detailed description of this sound as if for a future museum. What are its frequencies, its rhythms, its emotional connotations? Now, imagine the silence that will exist in its place. What other, newer sounds will fill that auditory niche? Write an elegy for a vanishing sonic fingerprint."
|
||||
},
|
||||
{
|
||||
"prompt18": "Craft a mental effigy of a habit, fear, or desire you wish to understand better. Describe this symbolic representation in detail\u2014its materials, its posture, its expression. Now, perform a symbolic action upon it: you might place it in a drawer, bury it in the garden of your mind, or set it adrift on an imaginary river. Chronicle this ritual. Does the act of creating and addressing the effigy change your relationship to the thing it represents, or does it merely make its presence more tangible?"
|
||||
},
|
||||
{
|
||||
"prompt19": "Describe a labyrinth you have constructed in your own mind\u2014not a physical maze, but a complex, recurring thought pattern or emotional state you find yourself navigating. What are its winding corridors (rationalizations), its dead ends (frustrations), and its potential center (understanding or acceptance)? Map one recent journey through this internal labyrinth. What subtle tremor of insight or fear guided your turns? How do you find your way out, or do you choose to remain within, exploring its familiar, intricate paths?"
|
||||
},
|
||||
{
|
||||
"prompt20": "Examine a family tradition or ritual as if it were an ancient artifact. Break down its syntax: the required steps, the symbolic objects, the spoken phrases. Who are the keepers of this tradition? How has it mutated or diverged over generations? Participate in or recall this ritual with fresh eyes. What unspoken values and histories are encoded within its performance? What would be lost if it faded into oblivion?"
|
||||
},
|
||||
{
|
||||
"prompt21": "Observe a plant growing in an unexpected place\u2014a crack in the sidewalk, a gutter, a wall. Chronicle its struggle and persistence. Imagine the velocity of its growth against all odds. Write from the plant's perspective about its daily existence: the foot traffic, the weather, the search for sustenance. What can this resilient life form teach you about finding footholds and thriving in inhospitable environments?"
|
||||
},
|
||||
{
|
||||
"prompt22": "Imagine your creative process as a room with many thresholds. Describe the room where you generate raw ideas\u2014its mess, its energy. Then, describe the act of crossing the threshold into the room where you refine and edit. What changes in the atmosphere? What do you leave behind at the door, and what must you carry with you? Write about the architecture of your own creativity."
|
||||
},
|
||||
{
|
||||
"prompt23": "You are given a seed. It is not a magical seed, but an ordinary one from a fruit you ate. Instead of planting it, you decide to carry it with you for a week as a silent companion. Describe its presence in your pocket or bag. How does knowing it is there, a compact potential for an entire mycelial network of roots and a tree, subtly influence your days? Write about the weight of unactivated futures."
|
||||
},
|
||||
{
|
||||
"prompt24": "Recall a time you had to learn a new system or language quickly\u2014a job, a software, a social circle. Describe the initial phase of feeling like an outsider, decoding the basic algorithms of behavior. Then, focus on the precise moment you felt you crossed the threshold from outsider to competent insider. What was the catalyst? A piece of understood jargon? A successfully completed task? Explore the subtle architecture of belonging."
|
||||
},
|
||||
{
|
||||
"prompt25": "You find an old, annotated map\u2014perhaps in a book, or a tourist pamphlet from a trip long ago. Study the marks: circled sites, crossed-out routes, notes in the margin. Reconstruct the journey of the person who held this map. Where did they plan to go? Where did they actually go, based on the evidence? Write the travelogue of that forgotten expedition, blending the cartographic intention with the likely reality."
|
||||
},
|
||||
{
|
||||
"prompt26": "You encounter a door that is usually locked, but today it is slightly ajar. This is not a grand, mysterious portal, but an ordinary door\u2014to a storage closet, a rooftop, a neighbor's garden gate. Write about the potent allure of this minor threshold. Do you push it open? What mundane or profound discovery lies on the other side? Explore the magnetism of accessible secrets in a world of usual boundaries."
|
||||
},
|
||||
{
|
||||
"prompt27": "Recall a piece of practical advice you received that functioned like a simple life algorithm: 'When X happens, do Y.' Examine a recent situation where you deliberately chose not to follow that algorithm. What prompted the deviation? What was the outcome? Describe the feeling of operating outside of a previously trusted internal program. Did the mutation feel like a mistake or an evolution?"
|
||||
},
|
||||
{
|
||||
"prompt28": "Describe a piece of clothing you own that has been altered or mended multiple times. Trace the history of each repair. Who performed them, and under what circumstances? How does the garment's story of damage and restoration mirror larger cycles of wear and renewal in your own life? What does its continued use, despite its patched state, say about your relationship with impermanence and care?"
|
||||
},
|
||||
{
|
||||
"prompt29": "You find an old, hand-drawn map that leads to a place in your neighborhood. Follow it. Does it lead you to a spot that still exists, or to a location now utterly changed? Describe the journey of reconciling the cartography of the past with the terrain of the present. What has been erased? What endures? What ghosts of previous journeys do you feel along the way?"
|
||||
},
|
||||
{
|
||||
"prompt30": "Consider a skill you are learning. Break down its initial algorithm\u2014the basic, rigid steps you must follow. Now, describe the moment when practice leads to mutation: the algorithm begins to dissolve into intuition, muscle memory, or personal style. Where are you in this process? Can you feel the old, clunky code still running beneath the new, fluid performance? Write about the uncomfortable, fruitful space between competence and mastery."
|
||||
},
|
||||
{
|
||||
"prompt31": "Analyze the unspoken social algorithm of a group you belong to\u2014your family, your friend circle, your coworkers. What are the input rules (jokes that are allowed, topics to avoid)? What are the output expectations (laughter, support, problem-solving)? Now, imagine introducing a mutation: you break a minor, unwritten rule. Chronicle the system's response. Does it self-correct, reject the input, or adapt?"
|
||||
},
|
||||
{
|
||||
"prompt32": "Imagine your daily routine is a genetic sequence. Identify a habitual behavior that feels like a dominant gene. Now, imagine a spontaneous mutation occurring in this sequence\u2014one small, random change in the order or execution of your day. Follow the consequences. Does this mutation prove beneficial, harmful, or neutral? Does it replicate and become part of your new code? Write about the evolution of a personal habit through chance."
|
||||
},
|
||||
{
|
||||
"prompt33": "Your memory is a vast, dark archive. Choose a specific memory and imagine you are its archivist. Describe the process of retrieving it: locating the correct catalog number, the feel of the storage medium, the quality of the playback. Now, describe the process of conservation\u2014what elements are fragile and in need of repair? Do you restore it to its original clarity, or preserve its current, faded state? What is the ethical duty of a self-archivist?"
|
||||
},
|
||||
{
|
||||
"prompt34": "Examine a mended object in your possession\u2014a book with tape, a garment with a patch, a glued-together mug. Describe the repair not as a flaw, but as a new feature, a record of care and continuity. Write the history of its breaking and its fixing. Who performed the repair, and what was their state of mind? How does the object's value now reside in its visible history of damage and healing?"
|
||||
},
|
||||
{
|
||||
"prompt35": "Imagine you are a cartographer of sound. Map the auditory landscape of your current environment. Label the persistent drones, the intermittent rhythms, the sudden percussive events. What are the quiet zones? Where do sounds overlap to create new harmonies or dissonances? Now, imagine mutating one sound source\u2014silencing a hum, amplifying a whisper, changing a rhythm. How does this single alteration redraw the entire sonic map and your emotional response to the space?"
|
||||
},
|
||||
{
|
||||
"prompt36": "Contemplate the concept of a 'watershed'\u2014a geographical dividing line. Now, identify a watershed moment in your own life: a decision, an event, or a realization that divided your experience into 'before' and 'after.' Describe the landscape of the 'before.' Then, detail the moment of the divide itself. Finally, look out over the 'after' territory. How did the paths available to you fundamentally diverge at that ridge line? What rivers of consequence began to flow in new directions?"
|
||||
},
|
||||
{
|
||||
"prompt37": "Observe a spiderweb, a bird's nest, or another intricate natural construction. Describe it not as a static object, but as the recorded evidence of a process\u2014a series of deliberate actions repeated to create a functional whole. Imagine you are an archaeologist from another planet discovering this artifact. What hypotheses would you form about the builder's intelligence, needs, and methods? Write your field report."
|
||||
},
|
||||
{
|
||||
"prompt38": "Walk through a familiar indoor space (your home, your office) in complete darkness, or with your eyes closed if safe. Navigate by touch, memory, and sound alone. Describe the experience. Which objects and spaces feel different? What details do you notice that vision usually overrides? Write about the knowledge held in your hands and feet, and the temporary oblivion of the visual world. How does this shift in primary sense redefine your understanding of the space?"
|
||||
},
|
||||
{
|
||||
"prompt39": "You discover a single, worn-out glove lying on a park bench. Describe it in detail\u2014its color, material, signs of wear. Write a speculative history for this artifact. Who owned it? How was it lost? From the glove's perspective, narrate its journey from a department store shelf to this moment of abandonment. What human warmth did it hold, and what does its solitary state signify about loss and separation?"
|
||||
},
|
||||
{
|
||||
"prompt40": "Find a body of water\u2014a puddle after rain, a pond, a riverbank. Look at your reflection, then disturb the surface with a touch or a thrown pebble. Watch the image shatter and slowly reform. Use this as a metaphor for a period of personal disruption in your life. Describe the 'shattering' event, the chaotic ripple period, and the gradual, never-quite-identical reformation of your sense of self. What was lost in the distortion, and what new facets were revealed?"
|
||||
},
|
||||
{
|
||||
"prompt41": "You are handed a map of a city you know well, but it is from a century ago. Compare it to the modern layout. Which streets have vanished into oblivion, paved over or renamed? Which buildings are ghosts on the page? Choose one lost place and imagine walking its forgotten route today. What echoes of its past life\u2014sounds, smells, activities\u2014can you almost perceive beneath the contemporary surface? Write about the layers of history that coexist in a single geographic space."
|
||||
},
|
||||
{
|
||||
"prompt42": "What is something you've been putting off and why?"
|
||||
},
|
||||
{
|
||||
"prompt43": "Recall a piece of art\u2014a painting, song, film\u2014that initially confused or repelled you, but that you later came to appreciate or love. Describe your first, negative reaction in detail. Then, trace the journey to understanding. What changed in you or your context that allowed a new interpretation? Write about the value of sitting with discomfort and the rewards of having your internal syntax for beauty challenged and expanded."
|
||||
},
|
||||
{
|
||||
"prompt44": "Imagine your life as a vast, intricate tapestry. Describe the overall scene it depicts. Now, find a single, loose thread\u2014a small regret, an unresolved question, a path not taken. Write about gently pulling on that thread. What part of the tapestry begins to unravel? What new pattern or image is revealed\u2014or destroyed\u2014by following this divergence? Is the act one of repair or deconstruction?"
|
||||
},
|
||||
{
|
||||
"prompt45": "Recall a dream that felt more real than waking life. Describe its internal logic, its emotional palette, and its lingering aftertaste. Now, write a 'practical guide' for navigating that specific dreamscape, as if for a tourist. What are the rules? What should one avoid? What treasures might be found? By treating the dream as a tangible place, what insights do you gain about the concerns of your subconscious?"
|
||||
},
|
||||
{
|
||||
"prompt46": "Describe a public space you frequent (a library, a cafe, a park) at the exact moment it opens or closes. Capture the transition from emptiness to potential, or from activity to stillness. Focus on the staff or custodians who facilitate this transition\u2014the unseen architects of these daily cycles. Write from the perspective of the space itself as it breathes in or out its human occupants. What residue of the day does it hold in the quiet?"
|
||||
},
|
||||
{
|
||||
"prompt47": "Listen to a piece of music you know well, but focus exclusively on a single instrument or voice that usually resides in the background. Follow its thread through the entire composition. Describe its journey: when does it lead, when does it harmonize, when does it fall silent? Now, write a short story where this supporting element is the main character. How does shifting your auditory focus create a new narrative from familiar material?"
|
||||
},
|
||||
{
|
||||
"prompt48": "Describe your reflection in a window at night, with the interior light creating a double exposure of your face and the dark world outside. What two versions of yourself are superimposed? Write a conversation between the 'inside' self, defined by your private space, and the 'outside' self, defined by the anonymous night. What do they want from each other? How does this liminal artifact\u2014the glass\u2014both separate and connect these identities?"
|
||||
},
|
||||
{
|
||||
"prompt49": "Imagine you are a diver exploring the deep ocean of your own memory. Choose a specific, vivid memory and describe it as a submerged landscape. What creatures (emotions) swim there? What is the water pressure (emotional weight) like? Now, imagine a small, deliberate act of forgetting\u2014letting a single detail of that memory dissolve into the murk. How does this selective oblivion change the entire ecosystem of that recollection? Does it create space for new growth, or does it feel like a loss of truth?"
|
||||
},
|
||||
{
|
||||
"prompt50": "Recall a conversation that ended in a misunderstanding that was never resolved. Re-write the exchange, but introduce a single point of divergence\u2014one person says something slightly different, or pauses a moment longer. How does this tiny change alter the entire trajectory of the conversation and potentially the relationship? Explore the butterfly effect in human dialogue."
|
||||
},
|
||||
{
|
||||
"prompt51": "Spend 15 minutes in complete silence, actively listening for the absence of a specific sound that is usually present (e.g., traffic, refrigerator hum, birds). Describe the quality of this crafted silence. What smaller sounds emerge in the void? How does your mind and body react to the deliberate removal of this sonic artifact? Explore the concept of oblivion as an active, perceptible state rather than a mere lack."
|
||||
},
|
||||
{
|
||||
"prompt52": "Describe a skill or talent you possess that feels like it's fading from lack of use\u2014a language getting rusty, a sport you no longer play, an instrument gathering dust. Perform or practice it now, even if clumsily. Chronicle the physical and mental sensations of re-engagement. What echoes of proficiency remain? Is the knowledge truly gone, or merely dormant? Write about the relationship between mastery and oblivion."
|
||||
},
|
||||
{
|
||||
"prompt53": "Choose a common word (e.g., 'home,' 'work,' 'friend') and dissect its personal syntax. What rules, associations, and exceptions have you built around its meaning? Now, deliberately break one of those rules. Use the word in a context or with a definition that feels wrong to you. Write a paragraph that forces this new usage. How does corrupting your own internal language create space for new understanding?"
|
||||
},
|
||||
{
|
||||
"prompt54": "Contemplate a personal habit or pattern you wish to change. Instead of focusing on breaking it, imagine it diverging\u2014mutating into a new, slightly different pattern. Describe the old habit in detail, then design its evolved form. What small, intentional twist could redirect its energy? Write about a day living with this divergent habit. How does a shift in perspective, rather than eradication, alter your relationship to it?"
|
||||
},
|
||||
{
|
||||
"prompt55": "Describe a routine journey you make (a commute, a walk to the store) but narrate it as if you are a traveler in a foreign, slightly surreal land. Give fantastical names to ordinary landmarks. Interpret mundane events as portents or rituals. What hidden narrative or mythic structure can you impose on this familiar path? How does this reframing reveal the magic latent in the everyday?"
|
||||
},
|
||||
{
|
||||
"prompt56": "Imagine a place from your childhood that no longer exists in its original form\u2014a demolished building, a paved-over field, a renovated room. Reconstruct it from memory with all its sensory details. Now, write about the process of its erasure. Who decided it should change? What was lost in the transition, and what, if anything, was gained? How does the ghost of that place still influence the geography of your memory?"
|
||||
},
|
||||
{
|
||||
"prompt57": "You find an old, functional algorithm\u2014a recipe card, a knitting pattern, a set of instructions for assembling furniture. Follow it to the letter, but with a new, meditative attention to each step. Describe the process not as a means to an end, but as a ritual in itself. What resonance does this deliberate, prescribed action have? Does the final product matter, or has the value been in the structured journey?"
|
||||
},
|
||||
{
|
||||
"prompt58": "Imagine knowledge and ideas spread through a community not like a virus, but like a mycelium\u2014subterranean, cooperative, nutrient-sharing. Recall a time you learned something profound from an unexpected or unofficial source. Trace the hidden network that brought that wisdom to you. How many people and experiences were unknowingly part of that fruiting? Write a thank you to this invisible web."
|
||||
},
|
||||
{
|
||||
"prompt59": "Imagine your creative or problem-solving process is a mycelial network. A question or idea is dropped like a spore onto this vast, hidden web. Describe the journey of this spore as it sends out filaments, connects with distant nodes of memory and knowledge, and eventually fruits as an 'aha' moment or a new creation. How does this model differ from a linear, step-by-step algorithm? What does it teach you about patience and indirect growth?"
|
||||
}
|
||||
]
|
||||
4
prompts_pool.json
Normal file
4
prompts_pool.json
Normal file
@@ -0,0 +1,4 @@
|
||||
[
|
||||
"Describe preparing and eating a meal alone with the attention of a sacred ritual. Focus on each step: selecting ingredients, the sound of chopping, the aromas, the arrangement on the plate, the first bite. Write about the difference between eating for fuel and eating as an act of communion with yourself. What thoughts arise in the space of this deliberate solitude?",
|
||||
"Recall a rule you were taught as a child\u2014a practical safety rule, a social manner, a household edict. Examine its original purpose. Now, trace how your relationship to that rule has evolved. Do you follow it rigidly, have you modified it, or do you ignore it entirely? Write about the journey from external imposition to internalized (or rejected) law."
|
||||
]
|
||||
127
run.sh
127
run.sh
@@ -7,95 +7,94 @@ echo "📓 Daily Journal Prompt Generator"
|
||||
echo "================================="
|
||||
|
||||
# Check if Python is installed
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
echo "❌ Python 3 is required but not installed."
|
||||
exit 1
|
||||
if ! command -v python3 &>/dev/null; then
|
||||
echo "❌ Python 3 is required but not installed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if .env file exists
|
||||
if [ ! -f ".env" ]; then
|
||||
echo "⚠️ .env file not found. Creating from example..."
|
||||
if [ -f "example.env" ]; then
|
||||
cp example.env .env
|
||||
echo "✅ Created .env file from example."
|
||||
echo " Please edit .env and add your API key."
|
||||
exit 1
|
||||
else
|
||||
echo "❌ example.env not found either."
|
||||
exit 1
|
||||
fi
|
||||
echo "⚠️ .env file not found. Creating from example..."
|
||||
if [ -f "example.env" ]; then
|
||||
cp example.env .env
|
||||
echo "✅ Created .env file from example."
|
||||
echo " Please edit .env and add your API key."
|
||||
exit 1
|
||||
else
|
||||
echo "❌ example.env not found either."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if dependencies are installed
|
||||
echo "🔍 Checking dependencies..."
|
||||
if ! python3 -c "import openai, dotenv" &> /dev/null; then
|
||||
echo "📦 Installing dependencies..."
|
||||
pip install -r requirements.txt
|
||||
if ! python3 -c "import openai, dotenv" &>/dev/null; then
|
||||
echo "📦 Installing dependencies..."
|
||||
pip install -r requirements.txt
|
||||
fi
|
||||
|
||||
# Parse command line arguments
|
||||
INTERACTIVE=false
|
||||
SIMPLE=false
|
||||
STATS=false
|
||||
FILL_POOL=false
|
||||
HELP=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-i|--interactive)
|
||||
INTERACTIVE=true
|
||||
shift
|
||||
;;
|
||||
-s|--simple)
|
||||
SIMPLE=true
|
||||
shift
|
||||
;;
|
||||
--stats)
|
||||
STATS=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
HELP=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "❌ Unknown option: $1"
|
||||
echo " Use -h for help"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
case $1 in
|
||||
-i | --interactive)
|
||||
INTERACTIVE=true
|
||||
shift
|
||||
;;
|
||||
--stats)
|
||||
STATS=true
|
||||
shift
|
||||
;;
|
||||
--fill-pool)
|
||||
FILL_POOL=true
|
||||
shift
|
||||
;;
|
||||
-h | --help)
|
||||
HELP=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "❌ Unknown option: $1"
|
||||
echo " Use -h for help"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$HELP" = true ]; then
|
||||
echo "Usage: ./run.sh [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -i, --interactive Run in interactive mode (with rich interface)"
|
||||
echo " -s, --simple Run simple version (no rich dependency)"
|
||||
echo " --stats Show prompt history statistics"
|
||||
echo " -h, --help Show this help message"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " ./run.sh # Generate prompts (default)"
|
||||
echo " ./run.sh -i # Interactive mode"
|
||||
echo " ./run.sh -s # Simple version"
|
||||
echo " ./run.sh --stats # Show statistics"
|
||||
exit 0
|
||||
echo "Usage: ./run.sh [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -i, --interactive Run in interactive mode (with rich interface)"
|
||||
echo " --stats Show combined statistics (pool and history)"
|
||||
echo " --fill-pool Fill prompt pool using AI (makes API call)"
|
||||
echo " -h, --help Show this help message"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " ./run.sh # Draw prompts from pool (default)"
|
||||
echo " ./run.sh -i # Interactive mode"
|
||||
echo " ./run.sh --stats # Show combined statistics"
|
||||
echo " ./run.sh --fill-pool # Fill prompt pool using AI"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$STATS" = true ]; then
|
||||
echo "📊 Showing statistics..."
|
||||
python3 generate_prompts.py --stats
|
||||
echo "📊 Showing combined statistics..."
|
||||
python3 generate_prompts.py --stats
|
||||
elif [ "$FILL_POOL" = true ]; then
|
||||
echo "🔄 Filling prompt pool using AI..."
|
||||
python3 generate_prompts.py --fill-pool
|
||||
elif [ "$INTERACTIVE" = true ]; then
|
||||
echo "🎮 Starting interactive mode..."
|
||||
python3 generate_prompts.py --interactive
|
||||
elif [ "$SIMPLE" = true ]; then
|
||||
echo "⚡ Running simple version..."
|
||||
python3 simple_generate.py
|
||||
echo "🎮 Starting interactive mode..."
|
||||
python3 generate_prompts.py --interactive
|
||||
else
|
||||
echo "✨ Generating prompts..."
|
||||
python3 generate_prompts.py
|
||||
echo "✨ Drawing prompts from pool..."
|
||||
python3 generate_prompts.py
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "✅ Done! Happy journaling! 📓"
|
||||
|
||||
|
||||
10
settings.cfg
10
settings.cfg
@@ -1,12 +1,12 @@
|
||||
# settings.cfg
|
||||
# This controls how many prompts are presented, as well as how deep to pre-cache.
|
||||
# Obviously, the depth causes exponential growth with prompts^days, so be conservative.
|
||||
# This controls how many prompts are presented and consumed from the pool, as well as how much to pre-cache.
|
||||
# This is used to maintain functionality offline for some number of iterations.
|
||||
|
||||
[prompts]
|
||||
min_length = 500
|
||||
max_length = 1000
|
||||
num_prompts = 8
|
||||
num_prompts = 3
|
||||
|
||||
# Prefetch not yet implmented
|
||||
# Pool size can affect the prompts if is too high. Default 20.
|
||||
[prefetch]
|
||||
prompt_cache_depth = 3
|
||||
cached_pool_volume = 20
|
||||
|
||||
@@ -1,314 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple Daily Journal Prompt Generator
|
||||
A lightweight version without rich dependency.
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import sys
|
||||
import argparse
|
||||
import configparser
|
||||
from datetime import datetime
|
||||
from typing import List, Dict, Any
|
||||
|
||||
from openai import OpenAI
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
class SimplePromptGenerator:
|
||||
"""Simple version without rich dependency."""
|
||||
|
||||
def __init__(self, config_path: str = ".env"):
|
||||
"""Initialize the generator with configuration."""
|
||||
self.config_path = config_path
|
||||
self.client = None
|
||||
self.historic_prompts = []
|
||||
self.prompt_template = ""
|
||||
self.settings = {}
|
||||
|
||||
# Load configuration
|
||||
self._load_config()
|
||||
self._load_settings()
|
||||
|
||||
# Load data files
|
||||
self._load_prompt_template()
|
||||
self._load_historic_prompts()
|
||||
|
||||
def _load_config(self):
|
||||
"""Load configuration from environment file."""
|
||||
load_dotenv(self.config_path)
|
||||
|
||||
# Get API key
|
||||
self.api_key = os.getenv("DEEPSEEK_API_KEY") or os.getenv("OPENAI_API_KEY")
|
||||
if not self.api_key:
|
||||
print("Error: No API key found in .env file")
|
||||
print("Please add DEEPSEEK_API_KEY or OPENAI_API_KEY to your .env file")
|
||||
sys.exit(1)
|
||||
|
||||
# Get API base URL (default to DeepSeek)
|
||||
self.base_url = os.getenv("API_BASE_URL", "https://api.deepseek.com")
|
||||
|
||||
# Get model (default to deepseek-chat)
|
||||
self.model = os.getenv("MODEL", "deepseek-chat")
|
||||
|
||||
# Initialize OpenAI client
|
||||
self.client = OpenAI(
|
||||
api_key=self.api_key,
|
||||
base_url=self.base_url
|
||||
)
|
||||
|
||||
def _load_settings(self):
|
||||
"""Load settings from settings.cfg configuration file."""
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
# Set default values
|
||||
self.settings = {
|
||||
'min_length': 500,
|
||||
'max_length': 1000,
|
||||
'num_prompts': 6
|
||||
}
|
||||
|
||||
try:
|
||||
config.read('settings.cfg')
|
||||
|
||||
if 'prompts' in config:
|
||||
prompts_section = config['prompts']
|
||||
|
||||
# Load min_length
|
||||
if 'min_length' in prompts_section:
|
||||
self.settings['min_length'] = int(prompts_section['min_length'])
|
||||
|
||||
# Load max_length
|
||||
if 'max_length' in prompts_section:
|
||||
self.settings['max_length'] = int(prompts_section['max_length'])
|
||||
|
||||
# Load num_prompts
|
||||
if 'num_prompts' in prompts_section:
|
||||
self.settings['num_prompts'] = int(prompts_section['num_prompts'])
|
||||
|
||||
except FileNotFoundError:
|
||||
print("Warning: settings.cfg not found, using default values")
|
||||
except ValueError as e:
|
||||
print(f"Warning: Invalid value in settings.cfg: {e}, using default values")
|
||||
except Exception as e:
|
||||
print(f"Warning: Error reading settings.cfg: {e}, using default values")
|
||||
|
||||
def _load_prompt_template(self):
|
||||
"""Load the prompt template from ds_prompt.txt and update with config values."""
|
||||
try:
|
||||
with open("ds_prompt.txt", "r") as f:
|
||||
template = f.read()
|
||||
|
||||
# Replace hardcoded values with config values
|
||||
template = template.replace(
|
||||
"between 500 and 1000 characters",
|
||||
f"between {self.settings['min_length']} and {self.settings['max_length']} characters"
|
||||
)
|
||||
|
||||
# Replace the number of prompts (6) with config value
|
||||
template = template.replace(
|
||||
"Please generate 6 writing prompts",
|
||||
f"Please generate {self.settings['num_prompts']} writing prompts"
|
||||
)
|
||||
|
||||
self.prompt_template = template
|
||||
except FileNotFoundError:
|
||||
print("Error: ds_prompt.txt not found")
|
||||
sys.exit(1)
|
||||
|
||||
def _load_historic_prompts(self):
|
||||
"""Load historic prompts from JSON file."""
|
||||
try:
|
||||
with open("historic_prompts.json", "r") as f:
|
||||
self.historic_prompts = json.load(f)
|
||||
except (FileNotFoundError, json.JSONDecodeError):
|
||||
print("Warning: Starting with empty prompt history")
|
||||
self.historic_prompts = []
|
||||
|
||||
def _save_historic_prompts(self):
|
||||
"""Save historic prompts to JSON file (keeping only last 60)."""
|
||||
# Keep only the last 60 prompts
|
||||
if len(self.historic_prompts) > 60:
|
||||
self.historic_prompts = self.historic_prompts[-60:]
|
||||
|
||||
with open("historic_prompts.json", "w") as f:
|
||||
json.dump(self.historic_prompts, f, indent=2)
|
||||
|
||||
def _prepare_prompt(self) -> str:
|
||||
"""Prepare the full prompt with historic context."""
|
||||
if self.historic_prompts:
|
||||
historic_context = json.dumps(self.historic_prompts, indent=2)
|
||||
full_prompt = f"{self.prompt_template}\n\nPrevious prompts:\n{historic_context}"
|
||||
else:
|
||||
full_prompt = self.prompt_template
|
||||
|
||||
return full_prompt
|
||||
|
||||
def _parse_ai_response(self, response_content: str) -> List[Dict[str, str]]:
|
||||
"""Parse the AI response to extract new prompts."""
|
||||
try:
|
||||
# Try to parse as JSON
|
||||
data = json.loads(response_content)
|
||||
|
||||
# Convert to list of prompt dictionaries
|
||||
new_prompts = []
|
||||
for i in range(self.settings['num_prompts']):
|
||||
key = f"newprompt{i}"
|
||||
if key in data:
|
||||
prompt_text = data[key]
|
||||
prompt_obj = {
|
||||
f"prompt{len(self.historic_prompts) + i:02d}": prompt_text
|
||||
}
|
||||
new_prompts.append(prompt_obj)
|
||||
|
||||
return new_prompts
|
||||
|
||||
except json.JSONDecodeError:
|
||||
# If not valid JSON, try to extract prompts from text
|
||||
print("Warning: AI response is not valid JSON, attempting to extract prompts...")
|
||||
|
||||
# Look for patterns in the text
|
||||
lines = response_content.strip().split('\n')
|
||||
new_prompts = []
|
||||
|
||||
for i, line in enumerate(lines[:self.settings['num_prompts']]):
|
||||
line = line.strip()
|
||||
if line and len(line) > 50:
|
||||
prompt_obj = {
|
||||
f"prompt{len(self.historic_prompts) + i:02d}": line
|
||||
}
|
||||
new_prompts.append(prompt_obj)
|
||||
|
||||
return new_prompts
|
||||
|
||||
def generate_prompts(self) -> List[Dict[str, str]]:
|
||||
"""Generate new journal prompts using AI."""
|
||||
print("\nGenerating new journal prompts...")
|
||||
|
||||
# Prepare the prompt
|
||||
full_prompt = self._prepare_prompt()
|
||||
|
||||
try:
|
||||
# Call the AI API
|
||||
print("Calling AI API...")
|
||||
response = self.client.chat.completions.create(
|
||||
model=self.model,
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a creative writing assistant that generates journal prompts. Always respond with valid JSON."},
|
||||
{"role": "user", "content": full_prompt}
|
||||
],
|
||||
temperature=0.7,
|
||||
max_tokens=2000
|
||||
)
|
||||
|
||||
response_content = response.choices[0].message.content
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error calling AI API: {e}")
|
||||
return []
|
||||
|
||||
# Parse the response
|
||||
new_prompts = self._parse_ai_response(response_content)
|
||||
|
||||
if not new_prompts:
|
||||
print("Error: Could not parse any prompts from AI response")
|
||||
return []
|
||||
|
||||
# Add to historic prompts
|
||||
self.historic_prompts.extend(new_prompts)
|
||||
|
||||
# Save updated history
|
||||
self._save_historic_prompts()
|
||||
|
||||
return new_prompts
|
||||
|
||||
def display_prompts(self, prompts: List[Dict[str, str]]):
|
||||
"""Display generated prompts in a simple format."""
|
||||
print("\n" + "="*60)
|
||||
print("✨ NEW JOURNAL PROMPTS GENERATED ✨")
|
||||
print("="*60 + "\n")
|
||||
|
||||
for i, prompt_dict in enumerate(prompts, 1):
|
||||
# Extract prompt text
|
||||
prompt_key = list(prompt_dict.keys())[0]
|
||||
prompt_text = prompt_dict[prompt_key]
|
||||
|
||||
print(f"Prompt #{i}:")
|
||||
print("-" * 40)
|
||||
print(prompt_text)
|
||||
print("-" * 40 + "\n")
|
||||
|
||||
def show_history_stats(self):
|
||||
"""Show statistics about prompt history."""
|
||||
total_prompts = len(self.historic_prompts)
|
||||
|
||||
print("\nPrompt History Statistics:")
|
||||
print("-" * 30)
|
||||
print(f"Total prompts in history: {total_prompts}")
|
||||
print(f"History capacity: 60 prompts")
|
||||
print(f"Available slots: {max(0, 60 - total_prompts)}")
|
||||
|
||||
def save_prompt_to_file(self, prompt_dict: Dict[str, str], filename: str = None):
|
||||
"""Save a prompt to a text file."""
|
||||
prompt_key = list(prompt_dict.keys())[0]
|
||||
prompt_text = prompt_dict[prompt_key]
|
||||
|
||||
if not filename:
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
filename = f"journal_prompt_{timestamp}.txt"
|
||||
|
||||
with open(filename, "w") as f:
|
||||
f.write(f"Journal Prompt - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
||||
f.write("="*50 + "\n\n")
|
||||
f.write(prompt_text)
|
||||
f.write("\n\n" + "="*50 + "\n")
|
||||
f.write("Happy writing! ✍️\n")
|
||||
|
||||
print(f"Prompt saved to {filename}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point for the simple script."""
|
||||
parser = argparse.ArgumentParser(description="Generate journal prompts using AI (simple version)")
|
||||
parser.add_argument(
|
||||
"--stats", "-s",
|
||||
action="store_true",
|
||||
help="Show history statistics"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--save", "-S",
|
||||
type=int,
|
||||
help="Save a specific prompt number to file"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--config", "-c",
|
||||
default=".env",
|
||||
help="Path to configuration file (default: .env)"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Initialize generator
|
||||
generator = SimplePromptGenerator(config_path=args.config)
|
||||
|
||||
if args.stats:
|
||||
generator.show_history_stats()
|
||||
else:
|
||||
# Generate prompts
|
||||
new_prompts = generator.generate_prompts()
|
||||
if new_prompts:
|
||||
generator.display_prompts(new_prompts)
|
||||
|
||||
# Save specific prompt if requested
|
||||
if args.save:
|
||||
prompt_num = args.save
|
||||
if 1 <= prompt_num <= len(new_prompts):
|
||||
generator.save_prompt_to_file(new_prompts[prompt_num - 1])
|
||||
else:
|
||||
print(f"Error: Prompt number must be between 1 and {len(new_prompts)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script to verify the prompt numbering logic.
|
||||
"""
|
||||
|
||||
import json
|
||||
import configparser
|
||||
from datetime import datetime
|
||||
|
||||
def get_num_prompts():
|
||||
"""Get the number of prompts from settings.cfg or default."""
|
||||
config = configparser.ConfigParser()
|
||||
num_prompts = 6 # Default value
|
||||
|
||||
try:
|
||||
config.read('settings.cfg')
|
||||
if 'prompts' in config and 'num_prompts' in config['prompts']:
|
||||
num_prompts = int(config['prompts']['num_prompts'])
|
||||
except (FileNotFoundError, ValueError):
|
||||
pass
|
||||
|
||||
return num_prompts
|
||||
|
||||
def test_renumbering():
|
||||
"""Test the renumbering logic."""
|
||||
|
||||
# Get number of prompts from config
|
||||
num_prompts = get_num_prompts()
|
||||
|
||||
# Create a sample historic prompts list
|
||||
historic_prompts = []
|
||||
for i in range(60):
|
||||
historic_prompts.append({
|
||||
f"prompt{i:02d}": f"Old prompt {i}"
|
||||
})
|
||||
|
||||
print(f"Original prompts: {len(historic_prompts)}")
|
||||
print(f"First prompt key: {list(historic_prompts[0].keys())[0]}")
|
||||
print(f"Last prompt key: {list(historic_prompts[-1].keys())[0]}")
|
||||
print(f"Number of prompts from config: {num_prompts}")
|
||||
|
||||
# Simulate adding new prompts (as the current code would create them)
|
||||
new_prompts = []
|
||||
for i in range(num_prompts):
|
||||
new_prompts.append({
|
||||
f"prompt{len(historic_prompts) + i:02d}": f"New prompt {i}"
|
||||
})
|
||||
|
||||
print(f"\nNew prompts to add: {len(new_prompts)}")
|
||||
for i, prompt in enumerate(new_prompts):
|
||||
print(f" New prompt {i}: {list(prompt.keys())[0]}")
|
||||
|
||||
# Prepend new prompts (reverse to maintain order)
|
||||
for prompt in reversed(new_prompts):
|
||||
historic_prompts.insert(0, prompt)
|
||||
|
||||
print(f"\nAfter prepending: {len(historic_prompts)} prompts")
|
||||
print(f"First 3 prompts keys:")
|
||||
for i in range(3):
|
||||
print(f" {i}: {list(historic_prompts[i].keys())[0]}")
|
||||
|
||||
# Renumber all prompts
|
||||
renumbered_prompts = []
|
||||
for i, prompt_dict in enumerate(historic_prompts):
|
||||
prompt_key = list(prompt_dict.keys())[0]
|
||||
prompt_text = prompt_dict[prompt_key]
|
||||
|
||||
new_prompt_key = f"prompt{i:02d}"
|
||||
renumbered_prompts.append({
|
||||
new_prompt_key: prompt_text
|
||||
})
|
||||
|
||||
print(f"\nAfter renumbering: {len(renumbered_prompts)} prompts")
|
||||
print(f"First 10 prompts keys:")
|
||||
for i in range(10):
|
||||
print(f" prompt{i:02d}: {list(renumbered_prompts[i].keys())[0]} = {renumbered_prompts[i][f'prompt{i:02d}'][:30]}...")
|
||||
|
||||
# Keep only first 60
|
||||
if len(renumbered_prompts) > 60:
|
||||
renumbered_prompts = renumbered_prompts[:60]
|
||||
|
||||
print(f"\nAfter keeping only first 60: {len(renumbered_prompts)} prompts")
|
||||
print(f"First prompt: {list(renumbered_prompts[0].keys())[0]} = {renumbered_prompts[0]['prompt00'][:30]}...")
|
||||
print(f"Last prompt: {list(renumbered_prompts[-1].keys())[0]} = {renumbered_prompts[-1]['prompt59'][:30]}...")
|
||||
|
||||
# Verify the range
|
||||
for i in range(60):
|
||||
expected_key = f"prompt{i:02d}"
|
||||
actual_key = list(renumbered_prompts[i].keys())[0]
|
||||
if expected_key != actual_key:
|
||||
print(f"ERROR: Expected {expected_key}, got {actual_key}")
|
||||
return False
|
||||
|
||||
print("\n✅ All tests passed! Prompt numbering is correct.")
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_renumbering()
|
||||
|
||||
82
tests/test_end_to_end.py
Normal file
82
tests/test_end_to_end.py
Normal file
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
End-to-end test to verify prompts are stored as simple strings without keys.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add the parent directory to the Python path
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from generate_prompts import JournalPromptGenerator
|
||||
|
||||
def test_end_to_end():
|
||||
"""Test the complete flow from parsing to pool management."""
|
||||
print("Testing end-to-end flow...")
|
||||
print("="*60)
|
||||
|
||||
# Create a generator instance
|
||||
generator = JournalPromptGenerator(config_path=".env")
|
||||
|
||||
# Clear the pool first
|
||||
generator.pool_prompts = []
|
||||
generator._save_pool_prompts()
|
||||
|
||||
# Test 1: Simulate AI response (JSON list format)
|
||||
print("\n1. Simulating AI response (JSON list format)...")
|
||||
mock_ai_response = json.dumps([
|
||||
"Write about a childhood memory that still makes you smile.",
|
||||
"Describe your perfect day from start to finish.",
|
||||
"What is something you've been putting off and why?",
|
||||
"Imagine you could have a conversation with any historical figure."
|
||||
])
|
||||
|
||||
# Parse the response
|
||||
parsed_prompts = generator._parse_ai_response(mock_ai_response)
|
||||
print(f" Parsed {len(parsed_prompts)} prompts")
|
||||
print(f" All prompts are strings: {all(isinstance(p, str) for p in parsed_prompts)}")
|
||||
print(f" No prompts have keys: {all(not isinstance(p, dict) for p in parsed_prompts)}")
|
||||
|
||||
# Test 2: Add to pool
|
||||
print("\n2. Adding prompts to pool...")
|
||||
generator.add_prompts_to_pool(parsed_prompts)
|
||||
print(f" Pool now has {len(generator.pool_prompts)} prompts")
|
||||
print(f" All pool prompts are strings: {all(isinstance(p, str) for p in generator.pool_prompts)}")
|
||||
|
||||
# Test 3: Draw from pool
|
||||
print("\n3. Drawing prompts from pool...")
|
||||
drawn_prompts = generator.draw_prompts_from_pool(count=2)
|
||||
print(f" Drew {len(drawn_prompts)} prompts")
|
||||
print(f" Drawn prompts are strings: {all(isinstance(p, str) for p in drawn_prompts)}")
|
||||
print(f" Pool now has {len(generator.pool_prompts)} prompts remaining")
|
||||
|
||||
# Test 4: Save and load pool
|
||||
print("\n4. Testing pool persistence...")
|
||||
# Save current pool
|
||||
generator._save_pool_prompts()
|
||||
|
||||
# Create a new generator instance to load the pool
|
||||
generator2 = JournalPromptGenerator(config_path=".env")
|
||||
print(f" New instance loaded {len(generator2.pool_prompts)} prompts from pool_prompts.json")
|
||||
print(f" Loaded prompts are strings: {all(isinstance(p, str) for p in generator2.pool_prompts)}")
|
||||
|
||||
# Test 5: Check pool_prompts.json content
|
||||
print("\n5. Checking pool_prompts.json file content...")
|
||||
with open("pool_prompts.json", "r") as f:
|
||||
pool_content = json.load(f)
|
||||
print(f" File contains {len(pool_content)} items")
|
||||
print(f" First item type: {type(pool_content[0])}")
|
||||
print(f" First item is string: {isinstance(pool_content[0], str)}")
|
||||
print(f" First item value: {pool_content[0][:50]}...")
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("✅ All tests passed! Prompts are stored as simple strings without keys.")
|
||||
print("="*60)
|
||||
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_end_to_end()
|
||||
|
||||
55
tests/test_feedback_integration.py
Normal file
55
tests/test_feedback_integration.py
Normal file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script to verify feedback_words integration
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add the parent directory to the Python path
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from generate_prompts import JournalPromptGenerator
|
||||
|
||||
def test_feedback_words_loading():
|
||||
"""Test that feedback_words are loaded correctly."""
|
||||
print("Testing feedback_words integration...")
|
||||
|
||||
try:
|
||||
# Initialize the generator
|
||||
generator = JournalPromptGenerator()
|
||||
|
||||
# Check if feedback_words were loaded
|
||||
print(f"Number of feedback words loaded: {len(generator.feedback_words)}")
|
||||
|
||||
if generator.feedback_words:
|
||||
print("Feedback words loaded successfully:")
|
||||
for i, feedback in enumerate(generator.feedback_words):
|
||||
print(f" {i+1}. {feedback}")
|
||||
else:
|
||||
print("No feedback words loaded (this might be expected if file is empty)")
|
||||
|
||||
# Test _prepare_prompt_with_count method
|
||||
print("\nTesting _prepare_prompt_with_count method...")
|
||||
prompt_with_count = generator._prepare_prompt_with_count(3)
|
||||
print(f"Prompt with count length: {len(prompt_with_count)} characters")
|
||||
|
||||
# Check if feedback words are included in the prompt with count
|
||||
if generator.feedback_words and "Feedback words:" in prompt_with_count:
|
||||
print("✓ Feedback words are included in the prompt with count")
|
||||
else:
|
||||
print("✗ Feedback words are NOT included in the prompt with count")
|
||||
|
||||
print("\n✅ All tests passed!")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ Error during testing: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = test_feedback_words_loading()
|
||||
sys.exit(0 if success else 1)
|
||||
|
||||
@@ -8,20 +8,19 @@ import json
|
||||
import os
|
||||
import sys
|
||||
from unittest.mock import Mock, patch
|
||||
from datetime import datetime
|
||||
|
||||
# Add current directory to path to import our modules
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
# Mock response for testing
|
||||
MOCK_AI_RESPONSE = '''{
|
||||
"newprompt0": "Describe a place from your childhood that no longer exists. What made it special? What sounds, smells, and textures do you remember?",
|
||||
"newprompt1": "Write a letter to your future self 10 years from now. What hopes, fears, and questions do you want to share?",
|
||||
"newprompt2": "Imagine you wake up with a new superpower that only works on Tuesdays. What is it and how do you use it?",
|
||||
"newprompt3": "Describe a meal that represents your cultural heritage. Who taught you to make it? What memories are tied to it?",
|
||||
"newprompt4": "Write about a time you got lost, literally or metaphorically. What did you discover along the way?",
|
||||
"newprompt5": "Create a dialogue between your current self and your teenage self. What would you talk about?"
|
||||
}'''
|
||||
# Mock response for testing (new list format)
|
||||
MOCK_AI_RESPONSE = '''[
|
||||
"Describe a place from your childhood that no longer exists. What made it special? What sounds, smells, and textures do you remember?",
|
||||
"Write a letter to your future self 10 years from now. What hopes, fears, and questions do you want to share?",
|
||||
"Imagine you wake up with a new superpower that only works on Tuesdays. What is it and how do you use it?",
|
||||
"Describe a meal that represents your cultural heritage. Who taught you to make it? What memories are tied to it?",
|
||||
"Write about a time you got lost, literally or metaphorically. What did you discover along the way?",
|
||||
"Create a dialogue between your current self and your teenage self. What would you talk about?"
|
||||
]'''
|
||||
|
||||
|
||||
def test_file_structure():
|
||||
@@ -110,20 +109,20 @@ def test_mock_ai_response():
|
||||
# Test JSON parsing
|
||||
data = json.loads(MOCK_AI_RESPONSE)
|
||||
|
||||
# Check structure
|
||||
expected_keys = [f"newprompt{i}" for i in range(6)]
|
||||
missing_keys = [key for key in expected_keys if key not in data]
|
||||
# Check structure - should be a list
|
||||
if not isinstance(data, list):
|
||||
print(f" ✗ Mock response is not a list, got {type(data)}")
|
||||
return False
|
||||
|
||||
if missing_keys:
|
||||
print(f" ✗ Missing keys in mock response: {missing_keys}")
|
||||
if len(data) != 6:
|
||||
print(f" ✗ Mock response has {len(data)} items, expected 6")
|
||||
return False
|
||||
|
||||
print(f" ✓ Mock response parsed successfully")
|
||||
print(f" ✓ Contains all 6 expected prompts")
|
||||
|
||||
# Check prompt lengths
|
||||
for i in range(6):
|
||||
prompt = data[f"newprompt{i}"]
|
||||
for i, prompt in enumerate(data):
|
||||
if len(prompt) < 50:
|
||||
print(f" ⚠ Prompt {i} is very short ({len(prompt)} characters)")
|
||||
|
||||
@@ -202,7 +201,7 @@ def test_python_scripts():
|
||||
"""Test that Python scripts are syntactically valid."""
|
||||
print("\nTesting Python scripts...")
|
||||
|
||||
scripts_to_test = ["generate_prompts.py", "simple_generate.py"]
|
||||
scripts_to_test = ["generate_prompts.py"]
|
||||
all_valid = True
|
||||
|
||||
for script in scripts_to_test:
|
||||
@@ -281,3 +280,4 @@ if __name__ == "__main__":
|
||||
success = main()
|
||||
sys.exit(0 if success else 1)
|
||||
|
||||
|
||||
67
tests/test_valid_response.py
Normal file
67
tests/test_valid_response.py
Normal file
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test the error handling with a valid response.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
|
||||
# Add the parent directory to the Python path
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from generate_prompts import JournalPromptGenerator
|
||||
|
||||
def test_valid_response():
|
||||
"""Test with a valid JSON response."""
|
||||
|
||||
# Create a mock generator
|
||||
generator = JournalPromptGenerator(config_path=".env")
|
||||
|
||||
# Create a valid response with 4 prompts as a list (new format)
|
||||
valid_response = [
|
||||
"Write about a time when you felt truly at peace.",
|
||||
"Describe your ideal morning routine in detail.",
|
||||
"What are three things you're grateful for today?",
|
||||
"Reflect on a recent challenge and what you learned from it."
|
||||
]
|
||||
|
||||
# Convert to JSON string
|
||||
json_response = json.dumps(valid_response)
|
||||
|
||||
print("\n=== Test: Valid JSON response (list format) ===")
|
||||
result = generator._parse_ai_response(json_response)
|
||||
print(f"Number of prompts extracted: {len(result)}")
|
||||
print(f"Type of result: {type(result)}")
|
||||
|
||||
for i, prompt_text in enumerate(result):
|
||||
print(f"Prompt {i+1}: {prompt_text[:50]}...")
|
||||
|
||||
# Test with backticks
|
||||
print("\n=== Test: Valid JSON response with backticks ===")
|
||||
backticks_response = f"```json\n{json_response}\n```"
|
||||
result = generator._parse_ai_response(backticks_response)
|
||||
print(f"Number of prompts extracted: {len(result)}")
|
||||
|
||||
# Test with "json" prefix
|
||||
print("\n=== Test: Valid JSON response with 'json' prefix ===")
|
||||
json_prefix_response = f"json\n{json_response}"
|
||||
result = generator._parse_ai_response(json_prefix_response)
|
||||
print(f"Number of prompts extracted: {len(result)}")
|
||||
|
||||
# Test fallback for old dictionary format
|
||||
print("\n=== Test: Fallback for old dictionary format ===")
|
||||
old_format_response = {
|
||||
"newprompt0": "Write about a time when you felt truly at peace.",
|
||||
"newprompt1": "Describe your ideal morning routine in detail.",
|
||||
"newprompt2": "What are three things you're grateful for today?",
|
||||
"newprompt3": "Reflect on a recent challenge and what you learned from it."
|
||||
}
|
||||
json_old_response = json.dumps(old_format_response)
|
||||
result = generator._parse_ai_response(json_old_response)
|
||||
print(f"Number of prompts extracted: {len(result)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_valid_response()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user