kousthubh commited on
Commit
9801786
·
verified ·
1 Parent(s): 7645de7

Final Commit

Browse files
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2024 PSYCHEPLOT
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README.md CHANGED
@@ -1,14 +1,28 @@
1
- ---
2
- title: PSYCHEPLOT
3
- emoji: 🏢
4
- colorFrom: pink
5
- colorTo: blue
6
- sdk: streamlit
7
- sdk_version: 1.44.1
8
- app_file: app.py
9
- pinned: false
10
- license: mit
11
- short_description: Personality Detection through Interactive Narrative Gen
12
- ---
13
-
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # PSYCHEPLOT
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
+
5
+ Interactive storytelling application with personality analysis.
6
+
7
+ ## Setup
8
+ 1. Clone the repository
9
+ 2. Install dependencies: `pip install -r requirements.txt`
10
+ 3. Add GROQ API key to Hugging Face Spaces secrets
11
+
12
+ ## Files
13
+ - `app.py`: Main Streamlit application
14
+ - `story_engine.py`: Story generation logic
15
+ - `model.py`: Personality prediction model
16
+ - `personality_profiles.json`: Personality type definitions
17
+
18
+ ## Models
19
+ Make sure these files are included in the repository:
20
+ - personality_rf_model.joblib
21
+ - tfidf_vectorizer.joblib
22
+ - label_encoder.joblib
23
+
24
+ ## Features
25
+ - Multiple story genres
26
+ - Dynamic story generation
27
+ - Personality analysis
28
+ - Real-time trait tracking
app.py ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import os
3
+ from dotenv import load_dotenv
4
+ from story_engine import generate_story, continue_story
5
+ from model import predict_personality
6
+
7
+ # Load environment variables and set up API key first
8
+ load_dotenv()
9
+
10
+ # Get API key - handle both local development and Hugging Face Spaces
11
+ try:
12
+ GROQ_API_KEY = st.secrets["GROQ_API_KEY"]
13
+ except:
14
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY")
15
+
16
+ if not GROQ_API_KEY:
17
+ st.error("GROQ API Key not found. Please check your environment configuration.")
18
+ st.stop()
19
+
20
+ os.environ["GROQ_API_KEY"] = GROQ_API_KEY
21
+ os.environ["STREAMLIT_WATCHER_TYPE"] = "none"
22
+
23
+ # Initialize session state variables
24
+ if "selected_genre" not in st.session_state:
25
+ st.session_state.selected_genre = None
26
+ if "story_history" not in st.session_state:
27
+ st.session_state.story_history = []
28
+ if "story_options" not in st.session_state:
29
+ st.session_state.story_options = []
30
+ if "choice_history" not in st.session_state:
31
+ st.session_state.choice_history = []
32
+
33
+ # Track trait counts across choices
34
+ if "trait_counts" not in st.session_state:
35
+ st.session_state.trait_counts = {
36
+ "O": 0, # Openness
37
+ "C": 0, # Conscientiousness
38
+ "E": 0, # Extraversion
39
+ "A": 0, # Agreeableness
40
+ "N": 0 # Neuroticism
41
+ }
42
+
43
+ # Track personality predictions for each step
44
+ if "personality_history" not in st.session_state:
45
+ st.session_state.personality_history = []
46
+
47
+ st.set_page_config(layout="wide", page_title="PSYCHEPLOT", page_icon="👾")
48
+
49
+ st.title("👾 PSYCHEPLOT")
50
+ st.header("Select your genre")
51
+
52
+ if st.session_state.selected_genre is None:
53
+ col1, col2, col3, col4 = st.columns(4)
54
+ with col1:
55
+ if st.button("Crime"):
56
+ st.session_state.selected_genre = "Crime"
57
+ story_segment, options = generate_story("Crime", st.session_state.story_history)
58
+ st.session_state.story_options = options
59
+ with col2:
60
+ if st.button("Comedy"):
61
+ st.session_state.selected_genre = "Comedy"
62
+ story_segment, options = generate_story("Comedy", st.session_state.story_history)
63
+ st.session_state.story_options = options
64
+ with col3:
65
+ if st.button("Dark"):
66
+ st.session_state.selected_genre = "Dark"
67
+ story_segment, options = generate_story("Dark", st.session_state.story_history)
68
+ st.session_state.story_options = options
69
+ with col4:
70
+ if st.button("Educational"):
71
+ st.session_state.selected_genre = "Educational"
72
+ story_segment, options = generate_story("Educational", st.session_state.story_history)
73
+ st.session_state.story_options = options
74
+
75
+ if "step_count" not in st.session_state:
76
+ st.session_state.step_count = 0
77
+ if "chosen_options" not in st.session_state:
78
+ st.session_state.chosen_options = []
79
+
80
+ def update_personality_traits(choice_text):
81
+ """Update trait counts based on the current choice"""
82
+ result = predict_personality(choice_text)
83
+ st.session_state.personality_history.append(result)
84
+
85
+ # Custom thresholds for each trait
86
+ thresholds = {
87
+ "O": 0.20, # Openness
88
+ "C": 0.20, # Conscientiousness
89
+ "E": 0.15, # Extraversion
90
+ "A": 0.15, # Agreeableness
91
+ "N": 0.30 # Higher threshold for Neuroticism
92
+ }
93
+
94
+ # Update trait counts with custom thresholds
95
+ for trait, score in result["traits"].items():
96
+ if score >= thresholds[trait]:
97
+ st.session_state.trait_counts[trait] += 1
98
+
99
+ return result
100
+
101
+ def get_final_personality():
102
+ """Calculate final personality based on accumulated trait counts"""
103
+ from model import PERSONALITY_MAP, DOMINANT_TYPES
104
+ traits = ["O", "C", "E", "A", "N"]
105
+ trait_scores = {}
106
+
107
+ # Calculate scores
108
+ for trait in traits:
109
+ count = st.session_state.trait_counts[trait]
110
+ score = count / st.session_state.step_count
111
+ trait_scores[trait] = score
112
+
113
+ # Find potential dominant traits
114
+ max_score = max(trait_scores.values())
115
+ dominant_candidates = [
116
+ trait for trait, score in trait_scores.items()
117
+ if abs(score - max_score) < 0.01
118
+ ]
119
+
120
+ # Check if there's a clear dominant trait
121
+ if len(dominant_candidates) == 1:
122
+ dominant_trait = dominant_candidates[0]
123
+ other_scores = [score for trait, score in trait_scores.items()
124
+ if trait != dominant_trait]
125
+ max_other = max(other_scores)
126
+
127
+ if max_score >= 0.5 and (max_score - max_other) >= 0.2:
128
+ profile = DOMINANT_TYPES[dominant_trait]
129
+ return {
130
+ "type": dominant_trait,
131
+ "category": "Dominant Trait",
132
+ "label": profile["label"],
133
+ "description": profile["description"],
134
+ "traits": trait_scores
135
+ }
136
+
137
+ # If no clear dominant trait, create binary code and use personality map
138
+ binary_code = "".join(["H" if trait_scores[trait] > 0.3 else "L"
139
+ for trait in traits])
140
+
141
+ profile = PERSONALITY_MAP.get(binary_code, {
142
+ "label": "Unique Profile",
143
+ "description": "A distinctive combination of personality traits that creates a unique character profile."
144
+ })
145
+
146
+ return {
147
+ "type": binary_code,
148
+ "category": "Mixed Profile",
149
+ "label": profile["label"],
150
+ "description": profile["description"],
151
+ "traits": trait_scores
152
+ }
153
+
154
+ if st.session_state.selected_genre:
155
+ st.subheader(f"📖 Story Begins ({st.session_state.selected_genre} story):")
156
+ for i, segment in enumerate(st.session_state.story_history):
157
+ cleaned_segment = "\n".join([line for line in segment.split("\n") if not line.startswith(("1.", "2.", "3.", "4."))])
158
+ st.write(cleaned_segment)
159
+ if i < len(st.session_state.story_history) - 1:
160
+ st.divider()
161
+
162
+ if st.session_state.step_count < 6:
163
+ st.subheader("🔮 Choose the next step:")
164
+ for option in st.session_state.story_options:
165
+ if st.button(option):
166
+ story_segment, options = continue_story(st.session_state.story_history, option)
167
+ st.session_state.story_options = options
168
+
169
+ # Store the choice
170
+ st.session_state.choice_history.append({
171
+ "step": st.session_state.step_count + 1,
172
+ "chosen_option": option
173
+ })
174
+ st.session_state.chosen_options.append(option)
175
+
176
+ # Update personality prediction after this choice
177
+ choice_text = f"Step {st.session_state.step_count + 1}: {option}"
178
+ update_personality_traits(choice_text)
179
+
180
+ st.session_state.step_count += 1
181
+ st.rerun()
182
+ else:
183
+ # Final personality calculation based on accumulated trait counts
184
+ result = get_final_personality()
185
+
186
+ st.subheader("🧠 Final Personality Assessment:")
187
+ st.markdown(f"**Type:** `{result['type']}` ({result['category']})")
188
+ st.markdown(f"**Label:** {result['label']}")
189
+ st.markdown("**Description:**")
190
+ st.write(result['description'])
191
+
192
+ st.divider()
193
+ st.markdown("### 📊 Trait Breakdown")
194
+ for trait, score in result["traits"].items():
195
+ count = st.session_state.trait_counts[trait]
196
+ st.progress(score, text=f"{trait}: {score:.2f} ({count}/{st.session_state.step_count} choices)")
197
+
198
+ # Show choices
199
+ st.divider()
200
+ with st.expander("📜 Your Story Choices"):
201
+ choices_text = "\n".join([f"Step {choice['step']}: {choice['chosen_option']}" for choice in st.session_state.choice_history])
202
+ st.text(choices_text)
203
+
204
+ # Show personality evolution
205
+ with st.expander("📈 Personality Evolution"):
206
+ for i, personality in enumerate(st.session_state.personality_history):
207
+ st.markdown(f"**Step {i+1}**: {personality['type']} - {personality['label']}")
208
+ cols = st.columns(5)
209
+ for j, (trait, score) in enumerate(personality['traits'].items()):
210
+ with cols[j]:
211
+ st.progress(score, text=f"{trait}: {score:.2f}")
label_encoder.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:55e7a831ac825d0ba41c0e985ab58cb8127232091c76d229de76d1197398e5ae
3
+ size 615
model.py ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import joblib
2
+ import json
3
+ import re
4
+
5
+ # Load the models
6
+ rf_model = joblib.load("personality_rf_model.joblib")
7
+ vectorizer = joblib.load("tfidf_vectorizer.joblib")
8
+ label_encoder = joblib.load("label_encoder.joblib")
9
+
10
+ # Load personality profiles
11
+ with open("personality_profiles.json", "r") as f:
12
+ PERSONALITY_MAP = json.load(f)
13
+
14
+ # Define dominant types
15
+ DOMINANT_TYPES = {
16
+ "O": {
17
+ "label": "High Openness",
18
+ "description": "Curious, imaginative, and open to new experiences. Thrives in creative and unconventional environments."
19
+ },
20
+ "C": {
21
+ "label": "High Conscientiousness",
22
+ "description": "Organized, dependable, and goal-oriented. Strong sense of duty and self-discipline."
23
+ },
24
+ "E": {
25
+ "label": "High Extraversion",
26
+ "description": "Energetic, outgoing, and thrives on social interaction. Feeds off external stimulation."
27
+ },
28
+ "A": {
29
+ "label": "High Agreeableness",
30
+ "description": "Kind-hearted, empathetic, and cooperative. Seeks harmony and avoids conflict."
31
+ },
32
+ "N": {
33
+ "label": "High Neuroticism",
34
+ "description": "Emotionally sensitive, reactive, and prone to mood swings. Deeply introspective."
35
+ }
36
+ }
37
+
38
+ def preprocess_text(text):
39
+ """Preprocess text for prediction"""
40
+ text = text.lower()
41
+ text = re.sub(r'[^a-zA-Z\s]', '', text)
42
+ text = re.sub(r'\s+', ' ', text)
43
+ return text.strip()
44
+
45
+ def predict_personality(choices_text):
46
+ """Predict personality based on choices text"""
47
+ try:
48
+ # Extract choice numbers from the text
49
+ choices = re.findall(r'Step \d+: (\d+)\.', choices_text)
50
+
51
+ # Define trait mapping for each option number
52
+ trait_mapping = {
53
+ "1": "O", # Openness
54
+ "2": "C", # Conscientiousness
55
+ "3": "E", # Extraversion/Agreeableness
56
+ "4": "N" # Neuroticism
57
+ }
58
+
59
+ # Initialize trait counts
60
+ trait_counts = {"O": 0, "C": 0, "E": 0, "A": 0, "N": 0}
61
+ total_choices = len(choices)
62
+
63
+ # Count traits based on choices
64
+ for choice in choices:
65
+ if choice in trait_mapping:
66
+ trait = trait_mapping[choice]
67
+ trait_counts[trait] += 1
68
+ # Special case: option 3 contributes to both E and A
69
+ if choice == "3":
70
+ trait_counts["A"] += 0.5 # Reduced weight for secondary trait
71
+
72
+ # Calculate trait scores as percentages
73
+ trait_scores = {
74
+ trait: count/total_choices
75
+ for trait, count in trait_counts.items()
76
+ } if total_choices > 0 else trait_counts
77
+
78
+ # Find dominant trait(s) with stricter criteria
79
+ max_score = max(trait_scores.values())
80
+
81
+ # Count traits that share the max score
82
+ max_score_traits = [
83
+ trait for trait, score in trait_scores.items()
84
+ if abs(score - max_score) < 0.01 # Account for floating point comparison
85
+ ]
86
+
87
+ # Only consider dominant if:
88
+ # 1. Single trait has highest score
89
+ # 2. Score is significantly higher than others (>= 0.5)
90
+ # 3. No other trait is close to the max score
91
+ if len(max_score_traits) == 1 and max_score >= 0.5:
92
+ dominant_trait = max_score_traits[0]
93
+ other_scores = [score for trait, score in trait_scores.items()
94
+ if trait != dominant_trait]
95
+ max_other = max(other_scores) if other_scores else 0
96
+
97
+ if max_score - max_other >= 0.2: # Must be significantly higher
98
+ profile = DOMINANT_TYPES[dominant_trait]
99
+ return {
100
+ "type": dominant_trait,
101
+ "category": "Dominant Trait",
102
+ "label": profile["label"],
103
+ "description": profile["description"],
104
+ "traits": trait_scores
105
+ }
106
+
107
+ # If no clear dominant trait, use binary code
108
+ binary_code = "".join(["H" if trait_scores[trait] > 0.3 else "L"
109
+ for trait in ["O", "C", "E", "A", "N"]]) # Fixed order
110
+
111
+ # Get profile from personality map
112
+ profile = PERSONALITY_MAP.get(binary_code, {
113
+ "label": "Mixed Profile",
114
+ "description": "A balanced combination of different personality traits."
115
+ })
116
+
117
+ return {
118
+ "type": binary_code,
119
+ "category": "Mixed Profile",
120
+ "label": profile["label"],
121
+ "description": profile["description"],
122
+ "traits": trait_scores
123
+ }
124
+
125
+ except Exception as e:
126
+ print(f"Error during prediction: {e}")
127
+ return {
128
+ "type": "ERROR",
129
+ "category": "Error",
130
+ "label": "Prediction Error",
131
+ "description": str(e),
132
+ "traits": {}
133
+ }
134
+
135
+ # if __name__ == "__main__":
136
+ # example_text = "I enjoy meeting new people and trying new experiences"
137
+ # result = predict_personality(example_text)
138
+ # print(json.dumps(result, indent=2))
personality_profiles.json ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "HHHHH": {
3
+ "label": "The Enlightened Leader",
4
+ "description": "Curious, disciplined, outgoing, kind, and emotionally aware. A natural leader with high emotional intelligence."
5
+ },
6
+ "HHHHL": {
7
+ "label": "The Visionary Diplomat",
8
+ "description": "Inventive, reliable, socially skilled, cooperative, and emotionally stable. Great at guiding others and building bridges."
9
+ },
10
+ "HHHLH": {
11
+ "label": "The Passionate Performer",
12
+ "description": "Creative, organized, charismatic, assertive, and emotionally expressive. Thrives in dynamic, public environments."
13
+ },
14
+ "HHHLL": {
15
+ "label": "The Independent Idealist",
16
+ "description": "Imaginative and structured, yet introverted and blunt. Strives for truth over harmony."
17
+ },
18
+ "HHLHH": {
19
+ "label": "The Charismatic Reactor",
20
+ "description": "Thoughtful and unconventional, struggles with emotional turbulence but excels at inspiring others."
21
+ },
22
+ "HHLHL": {
23
+ "label": "The Unpredictable Artist",
24
+ "description": "Highly creative and principled, but emotionally steady and often distant. Expresses through abstract forms."
25
+ },
26
+ "HHLLH": {
27
+ "label": "The Analytical Rebel",
28
+ "description": "Innovative and introverted, resistant to rules but deeply sensitive inside."
29
+ },
30
+ "HHLLL": {
31
+ "label": "The Detached Visionary",
32
+ "description": "Independent, logical, and emotionally distant. Values thought over connection."
33
+ },
34
+ "HLHHH": {
35
+ "label": "The Fiery Advocate",
36
+ "description": "Empathetic yet impulsive. Low on structure but high on social drive and emotional intensity."
37
+ },
38
+ "HLHHL": {
39
+ "label": "The Vibrant Connector",
40
+ "description": "Warm, spontaneous, and full of life. Prefers chaos over routine but deeply values harmony."
41
+ },
42
+ "HLHLH": {
43
+ "label": "The Empathetic Strategist",
44
+ "description": "Creative and emotionally insightful, yet assertive and unafraid of conflict. Balances heart and head."
45
+ },
46
+ "HLHLL": {
47
+ "label": "The Expressive Maverick",
48
+ "description": "Energetic and persuasive, but emotionally stable and unapologetically individualistic."
49
+ },
50
+ "HLLHH": {
51
+ "label": "The Sensitive Idealist",
52
+ "description": "Kind-hearted and emotionally intense, yet often disorganized and introspective."
53
+ },
54
+ "HLLHL": {
55
+ "label": "The Intuitive Wanderer",
56
+ "description": "Unconventional and curious with a soft demeanor. Dislikes routine and avoids conflict."
57
+ },
58
+ "HLLLH": {
59
+ "label": "The Lone Visionary",
60
+ "description": "Quiet, original, sensitive, and slightly chaotic. Expresses through solitude and deep thought."
61
+ },
62
+ "HLLLL": {
63
+ "label": "The Elusive Thinker",
64
+ "description": "Detached from structure and low in sociability, yet highly imaginative and philosophical."
65
+ },
66
+ "LHHHH": {
67
+ "label": "The Loyal Realist",
68
+ "description": "Grounded, dependable, and sensitive. Seeks harmony and stability in a chaotic world."
69
+ },
70
+ "LHHHL": {
71
+ "label": "The Social Strategist",
72
+ "description": "Disciplined, sociable, and emotionally grounded. Excels in managing group dynamics."
73
+ },
74
+ "LHHLH": {
75
+ "label": "The Compassionate Achiever",
76
+ "description": "Structured, ambitious, and sensitive to others’ needs. Driven by purpose and empathy."
77
+ },
78
+ "LHHLL": {
79
+ "label": "The Grounded Realist",
80
+ "description": "Practical and persistent. Values results, avoids drama, and prefers predictability."
81
+ },
82
+ "LHLHH": {
83
+ "label": "The Expressive Sentinel",
84
+ "description": "Emotionally in tune and supportive, but avoids chaos and prefers routine."
85
+ },
86
+ "LHLHL": {
87
+ "label": "The Warm Minimalist",
88
+ "description": "Reserved and steady, but deeply caring. Keeps things simple and values deep connections."
89
+ },
90
+ "LHLLH": {
91
+ "label": "The Pragmatic Thinker",
92
+ "description": "Introverted but deeply rational. Balanced emotionality and a strong moral compass."
93
+ },
94
+ "LHLLL": {
95
+ "label": "The Structured Observer",
96
+ "description": "Detail-oriented and organized, with a tendency toward solitude."
97
+ },
98
+ "LLHHH": {
99
+ "label": "The Tender Explorer",
100
+ "description": "Emotionally rich and adventurous, but often lacks follow-through. Guided by heart and curiosity."
101
+ },
102
+ "LLHHL": {
103
+ "label": "The Relational Drifter",
104
+ "description": "Compassionate and engaging, but scattered. Thrives in the moment, avoids hard rules."
105
+ },
106
+ "LLHLH": {
107
+ "label": "The Emotional Realist",
108
+ "description": "Feels deeply and sees clearly. Honest and loyal, but struggles with emotional regulation."
109
+ },
110
+ "LLHLL": {
111
+ "label": "The Gentle Disruptor",
112
+ "description": "Quietly rebellious, kind, and skeptical of systems. Doesn’t like to be boxed in."
113
+ },
114
+ "LLLHH": {
115
+ "label": "The Chaotic Thinker",
116
+ "description": "Unstructured, sensitive, and highly perceptive. Often lost in thought or feeling."
117
+ },
118
+ "LLLHL": {
119
+ "label": "The Quiet Helper",
120
+ "description": "Introverted and steady, avoids confrontation. Gentle and observant, a calming presence."
121
+ },
122
+ "LLLLH": {
123
+ "label": "The Internal Storm",
124
+ "description": "Low in structure and sociability, but emotionally intense. Often misunderstood but deeply insightful."
125
+ },
126
+ "LLLLL": {
127
+ "label": "The Detached Observer",
128
+ "description": "Low across the board. Independent and rational, but emotionally distant and withdrawn."
129
+ }
130
+ }
131
+
personality_rf_model.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e1a8e8bca637b5fbf193ed70ca19b5deb38aa2248a98c426b3cc095d69f81fc3
3
+ size 11635057
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ streamlit
2
+ groq
3
+ python-dotenv
4
+ joblib
5
+ scikit-learn
6
+ watchdog
story_engine.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import os
3
+ from groq import Groq
4
+ from dotenv import load_dotenv
5
+ import streamlit as st
6
+
7
+ # Load environment variables
8
+ load_dotenv()
9
+
10
+ # Get API key - handle both local development and Hugging Face Spaces
11
+ try:
12
+ # Try to get from Streamlit secrets (Hugging Face Spaces)
13
+ GROQ_API_KEY = st.secrets["GROQ_API_KEY"]
14
+ except:
15
+ # Fallback to local .env file
16
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY")
17
+
18
+ if not GROQ_API_KEY:
19
+ raise Exception("GROQ API Key not found. Please check your environment configuration.")
20
+
21
+ # Initialize Groq client
22
+ client = Groq(api_key=GROQ_API_KEY)
23
+
24
+ # Different story starts for each genre
25
+ GENRE_STARTS = {
26
+ "Crime": "It's 11:00 pm on a winter night, you are standing next to a phone booth, you are witnessing a murder scene, and then the murderers see you standing behind the booth and watching the scene.",
27
+ "Comedy": "You accidentally walk into a top-secret government meeting, but instead of being arrested, they mistake you for their new intern. Now, you must pretend to know what you're doing.",
28
+ "Dark": "The last thing you remember is falling asleep in your bed, but now you wake up in an abandoned hospital with flickering lights and a note in your hand that says 'RUN'.",
29
+ "Educational": "A renowned scientist just discovered a way to time travel, and you're the first volunteer to test the machine. However, something goes wrong, and you end up in the year 3024."
30
+ }
31
+
32
+ def generate_story(genre, story_history):
33
+ try:
34
+ start_story = GENRE_STARTS.get(genre, "A new adventure begins...")
35
+ prompt = f"""
36
+ Write a story continuation and provide 4 options that map to specific personality traits:
37
+ Story start: {start_story}
38
+
39
+ Format:
40
+ [STORY]
41
+ Write the story continuation here (max 150 words)
42
+
43
+ [OPTIONS]
44
+ 1. An option showing openness (creative, exploratory approach)
45
+ 2. An option showing conscientiousness (methodical, careful approach)
46
+ 3. An option showing extraversion and agreeableness (social, cooperative approach)
47
+ 4. An option showing neuroticism (emotional, cautious approach)
48
+
49
+ Important: Present the options naturally without mentioning these traits explicitly.
50
+ Each option should be a natural story choice while subtly reflecting its trait.
51
+ """
52
+
53
+ response = client.chat.completions.create(
54
+ messages=[{"role": "user", "content": prompt}],
55
+ model="meta-llama/llama-4-scout-17b-16e-instruct",
56
+ temperature=0.7,
57
+ max_tokens=800
58
+ )
59
+
60
+ content = response.choices[0].message.content
61
+
62
+ # Split content into story and options
63
+ parts = content.split("[OPTIONS]")
64
+ if len(parts) == 2:
65
+ story = parts[0].replace("[STORY]", "").strip()
66
+ options_text = parts[1].strip()
67
+ options = [line.strip() for line in options_text.split("\n") if line.strip().startswith(("1.", "2.", "3.", "4."))]
68
+ else:
69
+ story = content
70
+ options = ["Continue the story", "Ask for help", "Take a different path", "Wait and observe"]
71
+
72
+ story_history.append(story)
73
+ return story, options
74
+
75
+ except Exception as e:
76
+ print(f"Error generating story: {e}")
77
+ return "An error occurred while generating the story. Please try again.", ["Retry", "Choose different genre", "Start over", "Get help"]
78
+
79
+ def continue_story(story_history, selected_option):
80
+ try:
81
+ full_story = "\n".join(story_history)
82
+ prompt = f"""
83
+ Previous story: {full_story}
84
+ Chosen action: {selected_option}
85
+
86
+ Continue the story based on the chosen action.
87
+ Provide 4 new options that map to:
88
+ - Option 1: Openness (innovative, creative solutions)
89
+ - Option 2: Conscientiousness (careful planning, responsibility)
90
+ - Option 3: Extraversion/Agreeableness (social interaction, cooperation)
91
+ - Option 4: Neuroticism (emotional awareness, caution)
92
+
93
+ Format:
94
+ [STORY]
95
+ Write the continuation here (max 150 words)
96
+
97
+ [OPTIONS]
98
+ Present 4 natural story options that subtly reflect these traits without explicitly mentioning them.
99
+ Make each option feel like a natural choice in the story.
100
+ """
101
+
102
+ response = client.chat.completions.create(
103
+ model="meta-llama/llama-4-scout-17b-16e-instruct",
104
+ messages=[{"role": "user", "content": prompt}],
105
+ temperature=0.7,
106
+ max_tokens=800
107
+ )
108
+
109
+ content = response.choices[0].message.content
110
+
111
+ # Split content into story and options
112
+ parts = content.split("[OPTIONS]")
113
+ if len(parts) == 2:
114
+ story = parts[0].replace("[STORY]", "").strip()
115
+ options_text = parts[1].strip()
116
+ options = [line.strip() for line in options_text.split("\n") if line.strip().startswith(("1.", "2.", "3.", "4."))]
117
+ else:
118
+ story = content
119
+ options = ["Continue forward", "Take a different approach", "Reconsider the situation", "Try something new"]
120
+
121
+ story_history.append(story)
122
+ return story, options
123
+
124
+ except Exception as e:
125
+ print(f"Error continuing story: {e}")
126
+ return "An error occurred while continuing the story. Please try again.", ["Retry", "Go back", "Choose different option", "Start over"]
127
+
128
+ # def predict_personality(choices_text):
129
+ # prompt = f"""
130
+ # Analyze the following choices made by the user and determine their personality type:
131
+
132
+ # Choices:
133
+ # {choices_text}
134
+
135
+ # The choices are labeled as follows:
136
+ # 1. Emotional
137
+ # 2. Rational
138
+ # 3. Diplomatic
139
+ # 4. Angry
140
+
141
+ # Based on the frequency and pattern of choices, predict the user's personality type.
142
+ # Give a short summary of their personality traits.
143
+
144
+ # ### Give me only output
145
+ # """
146
+
147
+ # response = client.chat.completions.create(
148
+ # model="llama3-70b-8192",
149
+ # messages=[{"role": "user", "content": prompt}],
150
+ # temperature=0.3,
151
+ # max_tokens=300
152
+ # )
153
+
154
+ # return response.choices[0].message.content
155
+
156
+
tfidf_vectorizer.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bdcdf34dc746af15d6a03516ab5e655e7a3a9b4b05508d1fe3bf7b7bc11cb1d3
3
+ size 196257