codejedi Claude commited on
Commit
273a32a
·
1 Parent(s): 695c761

Replace quantum random number API with regular random numbers

Browse files

- Remove ANU Quantum API dependency and all related configuration
- Replace quantum_numbers_queue with random_numbers_queue in app.py
- Simplify quantum_emotion_service.py to use standard random.random()
- Remove quantum API validation from parallel initialization
- Update emotional_agent.py to use regular random numbers
- Clean up environment variables (remove ANU_QUANTUM_API_KEY)
- Update documentation (README, DOCKER.md, Endpoints.md)
- Remove quantum config from models.yaml and app.json

All emotion randomness now uses Python's built-in random module
instead of making external API calls. Functionality remains the same
but without the external dependency.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

.claude/settings.local.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(git add:*)"
5
+ ],
6
+ "deny": [],
7
+ "ask": []
8
+ }
9
+ }
.env.example CHANGED
@@ -12,10 +12,5 @@ INFLECTION_AI_API_KEY=your_inflection_ai_api_key_here
12
  AZURE_TEXT_ANALYTICS_KEY=your_azure_text_analytics_key_here
13
  AZURE_TEXT_ANALYTICS_ENDPOINT=https://your-resource.cognitiveservices.azure.com/
14
 
15
- # ANU Quantum API Key (Optional - for quantum randomness)
16
- # If not provided, the app will use pseudo-random numbers
17
- # Get your key from: https://quantumnumbers.anu.edu.au/
18
- ANU_QUANTUM_API_KEY=your_quantum_api_key_here
19
-
20
  # Port (Optional - defaults to 7860 for Hugging Face Spaces)
21
  PORT=7860
 
12
  AZURE_TEXT_ANALYTICS_KEY=your_azure_text_analytics_key_here
13
  AZURE_TEXT_ANALYTICS_ENDPOINT=https://your-resource.cognitiveservices.azure.com/
14
 
 
 
 
 
 
15
  # Port (Optional - defaults to 7860 for Hugging Face Spaces)
16
  PORT=7860
DOCKER.md CHANGED
@@ -71,7 +71,6 @@ docker build -t codejediondockerhub/digital-galatea:latest .
71
 
72
  - `AZURE_TEXT_ANALYTICS_KEY` - Azure Text Analytics key
73
  - `AZURE_TEXT_ANALYTICS_ENDPOINT` - Azure endpoint URL
74
- - `ANU_QUANTUM_API_KEY` - Quantum randomness API key
75
  - `PORT` - Server port (default: 7860)
76
 
77
  ## Running the Container
 
71
 
72
  - `AZURE_TEXT_ANALYTICS_KEY` - Azure Text Analytics key
73
  - `AZURE_TEXT_ANALYTICS_ENDPOINT` - Azure endpoint URL
 
74
  - `PORT` - Server port (default: 7860)
75
 
76
  ## Running the Container
Endpoints.md CHANGED
@@ -250,13 +250,12 @@ Renders an informative error page when the app is unavailable.
250
  - Sentiment Analyzer initialization
251
  - Gemini API validation
252
  - Inflection AI API validation
253
- - Quantum API validation (optional)
254
 
255
  2. **Component Initialization** - After parallel init completes:
256
  - GalateaAI instance creation
257
  - DialogueEngine initialization
258
  - AvatarEngine initialization
259
- - Quantum Emotion Service startup (if API key available)
260
 
261
  3. **Flask Server Start** - Only starts if all critical components are ready
262
 
@@ -267,7 +266,7 @@ Renders an informative error page when the app is unavailable.
267
  - **Inflection AI API**: Required
268
 
269
  ### Optional Components
270
- - **Quantum API**: Optional (falls back to pseudo-random)
271
 
272
  ---
273
 
@@ -275,7 +274,6 @@ Renders an informative error page when the app is unavailable.
275
 
276
  ### Rate Limits
277
  - **Gemini API 429**: Treated as valid API key, initialization continues
278
- - **Quantum API 429**: Falls back to pseudo-random, initialization continues
279
 
280
  ### Critical Failures
281
  If any critical component fails to initialize, the application will:
@@ -289,7 +287,7 @@ If any critical component fails to initialize, the application will:
289
 
290
  Emotions are stored in `emotions.json` and persist across restarts. The system uses:
291
  - **Sentiment Analysis**: Updates emotions based on user message sentiment
292
- - **Quantum Influence**: Background service updates emotions every 10 seconds using quantum random numbers (if available)
293
  - **Decay**: Emotions slowly decay over time (3% per interaction)
294
 
295
  ### Emotion Values
@@ -329,7 +327,7 @@ The frontend polls `GET /api/avatar` every 1 second to get real-time emotion upd
329
  - **Purpose**: Updates emotions even when the user is not chatting
330
  - **Updates Include**:
331
  - Sentiment-based emotion changes from user messages
332
- - Quantum-influenced emotion changes (every 10 seconds)
333
  - Natural emotion decay over time
334
 
335
  ### Emotion Bar Update Function
 
250
  - Sentiment Analyzer initialization
251
  - Gemini API validation
252
  - Inflection AI API validation
 
253
 
254
  2. **Component Initialization** - After parallel init completes:
255
  - GalateaAI instance creation
256
  - DialogueEngine initialization
257
  - AvatarEngine initialization
258
+ - Random Emotion Service startup
259
 
260
  3. **Flask Server Start** - Only starts if all critical components are ready
261
 
 
266
  - **Inflection AI API**: Required
267
 
268
  ### Optional Components
269
+ - None (all randomness uses standard pseudo-random numbers)
270
 
271
  ---
272
 
 
274
 
275
  ### Rate Limits
276
  - **Gemini API 429**: Treated as valid API key, initialization continues
 
277
 
278
  ### Critical Failures
279
  If any critical component fails to initialize, the application will:
 
287
 
288
  Emotions are stored in `emotions.json` and persist across restarts. The system uses:
289
  - **Sentiment Analysis**: Updates emotions based on user message sentiment
290
+ - **Random Influence**: Background service updates emotions every 10 seconds using random numbers
291
  - **Decay**: Emotions slowly decay over time (3% per interaction)
292
 
293
  ### Emotion Values
 
327
  - **Purpose**: Updates emotions even when the user is not chatting
328
  - **Updates Include**:
329
  - Sentiment-based emotion changes from user messages
330
+ - Random-influenced emotion changes (every 10 seconds)
331
  - Natural emotion decay over time
332
 
333
  ### Emotion Bar Update Function
README.md CHANGED
@@ -74,7 +74,6 @@ These environment variables enhance functionality but are not required:
74
  |----------|-------------|------------------|
75
  | `AZURE_TEXT_ANALYTICS_KEY` | Azure Text Analytics API key for enhanced sentiment analysis | Falls back to NLTK VADER if not provided |
76
  | `AZURE_TEXT_ANALYTICS_ENDPOINT` | Azure Text Analytics endpoint URL | Required if `AZURE_TEXT_ANALYTICS_KEY` is set |
77
- | `ANU_QUANTUM_API_KEY` | ANU Quantum Random Numbers API key | Uses pseudo-random numbers if not provided |
78
  | `PORT` | Port number for the Flask server | Defaults to `7860` |
79
 
80
  ### Environment Variable Examples
@@ -105,9 +104,6 @@ INFLECTION_AI_API_KEY=your-inflection-key
105
  AZURE_TEXT_ANALYTICS_KEY=your-azure-key
106
  AZURE_TEXT_ANALYTICS_ENDPOINT=https://your-resource.cognitiveservices.azure.com/
107
 
108
- # Optional - Quantum Randomness
109
- ANU_QUANTUM_API_KEY=your-quantum-key
110
-
111
  # Optional - Port Configuration
112
  PORT=7860
113
  ```
 
74
  |----------|-------------|------------------|
75
  | `AZURE_TEXT_ANALYTICS_KEY` | Azure Text Analytics API key for enhanced sentiment analysis | Falls back to NLTK VADER if not provided |
76
  | `AZURE_TEXT_ANALYTICS_ENDPOINT` | Azure Text Analytics endpoint URL | Required if `AZURE_TEXT_ANALYTICS_KEY` is set |
 
77
  | `PORT` | Port number for the Flask server | Defaults to `7860` |
78
 
79
  ### Environment Variable Examples
 
104
  AZURE_TEXT_ANALYTICS_KEY=your-azure-key
105
  AZURE_TEXT_ANALYTICS_ENDPOINT=https://your-resource.cognitiveservices.azure.com/
106
 
 
 
 
107
  # Optional - Port Configuration
108
  PORT=7860
109
  ```
agents/emotional_agent.py CHANGED
@@ -25,10 +25,7 @@ class EmotionalStateAgent:
25
 
26
  # Slower learning rate for more gradual emotion changes
27
  self.learning_rate = 0.03
28
- self.quantum_random_available = False
29
- self.quantum_api_key = None
30
- self._initialize_quantum()
31
-
32
  # Save initial state to JSON
33
  self._save_to_json()
34
 
@@ -60,48 +57,16 @@ class EmotionalStateAgent:
60
  except Exception as e:
61
  logging.error(f"[EmotionalStateAgent] Error saving emotional state to JSON: {e}")
62
 
63
- def _initialize_quantum(self):
64
- """Initialize quantum randomness availability"""
65
- quantum_key = os.getenv("ANU_QUANTUM_API_KEY")
66
- if quantum_key:
67
- self.quantum_api_key = quantum_key
68
- self.quantum_random_available = True
69
- logging.info("[EmotionalStateAgent] ✓ Quantum randomness available")
70
- else:
71
- logging.warning("[EmotionalStateAgent] Quantum randomness unavailable")
72
-
73
- def get_quantum_random_float(self, min_val=0.0, max_val=1.0):
74
- """Get a quantum random float between min_val and max_val"""
75
- if not self.quantum_random_available:
76
- return random.uniform(min_val, max_val)
77
-
78
- try:
79
- quantum_config = self.config.get('quantum', {}) if self.config else {}
80
- url = quantum_config.get('api_endpoint', 'https://api.quantumnumbers.anu.edu.au')
81
- headers = {"x-api-key": self.quantum_api_key}
82
- params = {"length": 1, "type": "uint8"}
83
-
84
- response = requests.get(url, headers=headers, params=params, timeout=10)
85
-
86
- if response.status_code == 200:
87
- result = response.json()
88
- if result.get('success') and 'data' in result and len(result['data']) > 0:
89
- normalized = result['data'][0] / 255.0
90
- return min_val + (max_val - min_val) * normalized
91
- except Exception as e:
92
- logging.warning(f"[EmotionalStateAgent] Quantum API failed: {e}")
93
-
94
  return random.uniform(min_val, max_val)
95
 
96
  def update_with_sentiment(self, sentiment_score):
97
  """Update emotional state based on sentiment"""
98
  # Slower decay factor to prevent emotions from crashing to minimum too fast
99
  # Changed from 0.9 to 0.97 (only 3% decay per interaction instead of 10%)
100
- decay_factor = 0.97
101
- if self.quantum_random_available:
102
- quantum_decay_variation = self.get_quantum_random_float(0.95, 0.99)
103
- decay_factor = quantum_decay_variation
104
-
105
  for emotion in self.emotional_state:
106
  # Decay emotions more slowly (preserves emotional state longer)
107
  self.emotional_state[emotion] *= decay_factor
@@ -109,20 +74,16 @@ class EmotionalStateAgent:
109
  self.emotional_state[emotion] = max(0.0, min(1.0, self.emotional_state[emotion]))
110
 
111
  # Apply sentiment with slower learning rate for gradual changes
112
- learning_rate = self.learning_rate
113
- if self.quantum_random_available:
114
- quantum_lr_variation = self.get_quantum_random_float(0.02, 0.04)
115
- learning_rate = quantum_lr_variation
116
-
117
  # Update emotions based on sentiment (slower, more gradual)
118
  self.emotional_state["joy"] += sentiment_score * learning_rate
119
  self.emotional_state["sadness"] -= sentiment_score * learning_rate
120
-
121
- # Add quantum randomness to curiosity (making responses more unpredictable)
122
- if self.quantum_random_available:
123
- quantum_curiosity_boost = self.get_quantum_random_float(-0.03, 0.03)
124
- self.emotional_state["curiosity"] = max(0.0, min(1.0,
125
- self.emotional_state["curiosity"] + quantum_curiosity_boost))
126
 
127
  # Soft normalization - only normalize if emotions get too extreme
128
  # This prevents emotions from being forced to equal values
 
25
 
26
  # Slower learning rate for more gradual emotion changes
27
  self.learning_rate = 0.03
28
+
 
 
 
29
  # Save initial state to JSON
30
  self._save_to_json()
31
 
 
57
  except Exception as e:
58
  logging.error(f"[EmotionalStateAgent] Error saving emotional state to JSON: {e}")
59
 
60
+ def get_random_float(self, min_val=0.0, max_val=1.0):
61
+ """Get a random float between min_val and max_val"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  return random.uniform(min_val, max_val)
63
 
64
  def update_with_sentiment(self, sentiment_score):
65
  """Update emotional state based on sentiment"""
66
  # Slower decay factor to prevent emotions from crashing to minimum too fast
67
  # Changed from 0.9 to 0.97 (only 3% decay per interaction instead of 10%)
68
+ decay_factor = self.get_random_float(0.95, 0.99)
69
+
 
 
 
70
  for emotion in self.emotional_state:
71
  # Decay emotions more slowly (preserves emotional state longer)
72
  self.emotional_state[emotion] *= decay_factor
 
74
  self.emotional_state[emotion] = max(0.0, min(1.0, self.emotional_state[emotion]))
75
 
76
  # Apply sentiment with slower learning rate for gradual changes
77
+ learning_rate = self.get_random_float(0.02, 0.04)
78
+
 
 
 
79
  # Update emotions based on sentiment (slower, more gradual)
80
  self.emotional_state["joy"] += sentiment_score * learning_rate
81
  self.emotional_state["sadness"] -= sentiment_score * learning_rate
82
+
83
+ # Add randomness to curiosity (making responses more unpredictable)
84
+ curiosity_boost = self.get_random_float(-0.03, 0.03)
85
+ self.emotional_state["curiosity"] = max(0.0, min(1.0,
86
+ self.emotional_state["curiosity"] + curiosity_boost))
 
87
 
88
  # Soft normalization - only normalize if emotions get too extreme
89
  # This prevents emotions from being forced to equal values
app.json CHANGED
@@ -20,10 +20,6 @@
20
  "AZURE_TEXT_ANALYTICS_ENDPOINT": {
21
  "description": "Your Azure Text Analytics endpoint (optional)",
22
  "required": false
23
- },
24
- "ANU_QUANTUM_API_KEY": {
25
- "description": "Your ANU Quantum API key (optional)",
26
- "required": false
27
  }
28
  },
29
  "formation": {
 
20
  "AZURE_TEXT_ANALYTICS_ENDPOINT": {
21
  "description": "Your Azure Text Analytics endpoint (optional)",
22
  "required": false
 
 
 
 
23
  }
24
  },
25
  "formation": {
app.py CHANGED
@@ -80,10 +80,10 @@ deepseek_initialized = False
80
  max_init_retries = 3
81
  current_init_retry = 0
82
 
83
- # Quantum numbers queue for /api/avatar endpoint
84
- quantum_numbers_queue = deque(maxlen=100)
85
- quantum_queue_lock = Lock()
86
- quantum_filling = False
87
 
88
  # Check for required environment variables
89
  required_env_vars = ['DEEPSEEK_API_KEY']
@@ -132,7 +132,6 @@ init_status = {
132
  'sentiment_analyzer': {'ready': False, 'error': None},
133
  'deepseek_api': {'ready': False, 'error': None},
134
  'inflection_api': {'ready': False, 'error': None},
135
- 'quantum_api': {'ready': False, 'error': None},
136
  }
137
 
138
  def initialize_json_memory():
@@ -300,43 +299,6 @@ def validate_inflection_api():
300
  init_status['inflection_api']['ready'] = False
301
  return False
302
 
303
- def validate_quantum_api():
304
- """Validate Quantum Random Numbers API key (optional component)"""
305
- try:
306
- logging.info("🔄 [Quantum API] Validating API key...")
307
- print("🔄 [Quantum API] Validating API key...")
308
- api_key = os.getenv("ANU_QUANTUM_API_KEY")
309
- if not api_key:
310
- logging.info("ℹ️ [Quantum API] API key not found (optional - will use pseudo-random)")
311
- print("ℹ️ [Quantum API] API key not found (optional - will use pseudo-random)")
312
- init_status['quantum_api']['ready'] = False
313
- return True # Not an error - optional component
314
- url = "https://api.quantumnumbers.anu.edu.au"
315
- headers = {"x-api-key": api_key}
316
- params = {"length": 1, "type": "uint8"}
317
- response = requests.get(url, headers=headers, params=params, timeout=10)
318
- if response.status_code == 200:
319
- logging.info("✓ [Quantum API] API key validated")
320
- print("✓ [Quantum API] API key validated")
321
- init_status['quantum_api']['ready'] = True
322
- return True
323
- elif response.status_code == 429:
324
- # Rate limit - not an error, just unavailable temporarily
325
- logging.info("ℹ️ [Quantum API] Rate limited (optional - will use pseudo-random)")
326
- print("ℹ️ [Quantum API] Rate limited (optional - will use pseudo-random)")
327
- init_status['quantum_api']['ready'] = False
328
- return True # Not an error - optional component
329
- else:
330
- logging.info(f"ℹ️ [Quantum API] Validation failed: {response.status_code} (optional - will use pseudo-random)")
331
- print(f"ℹ️ [Quantum API] Validation failed: {response.status_code} (optional - will use pseudo-random)")
332
- init_status['quantum_api']['ready'] = False
333
- return True # Not an error - optional component
334
- except Exception as e:
335
- # Any exception is not critical - quantum randomness is optional
336
- logging.info(f"ℹ️ [Quantum API] Unavailable: {e} (optional - will use pseudo-random)")
337
- print(f"ℹ️ [Quantum API] Unavailable: {e} (optional - will use pseudo-random)")
338
- init_status['quantum_api']['ready'] = False
339
- return True # Not an error - optional component
340
 
341
  def run_parallel_initialization():
342
  """Run all initialization steps in parallel"""
@@ -358,7 +320,6 @@ def run_parallel_initialization():
358
  ("Sentiment Analyzer", initialize_sentiment_analyzer),
359
  ("DeepSeek API", validate_deepseek_api),
360
  ("Inflection AI", validate_inflection_api),
361
- ("Quantum API", validate_quantum_api),
362
  ]
363
 
364
  completed_count = 0
@@ -719,103 +680,51 @@ def analyze_sentiment(text):
719
  # Track avatar updates with timestamp
720
  last_avatar_update = time.time()
721
 
722
- def fill_quantum_queue():
723
- """Asynchronously fill the quantum numbers queue with 100 numbers"""
724
- global quantum_filling, quantum_numbers_queue
725
-
726
- with quantum_queue_lock:
727
- if quantum_filling:
728
  return # Already filling
729
- quantum_filling = True
730
-
731
  def _fill_queue():
732
- global quantum_filling
733
  try:
734
- quantum_api_key = os.getenv("ANU_QUANTUM_API_KEY")
735
- if not quantum_api_key:
736
- logging.debug("[Quantum Queue] No API key, using pseudo-random")
737
- import random
738
- with quantum_queue_lock:
739
- while len(quantum_numbers_queue) < 100:
740
- quantum_numbers_queue.append(random.random())
741
- return
742
-
743
- from config import MODEL_CONFIG
744
- quantum_config = MODEL_CONFIG.get('quantum', {}) if MODEL_CONFIG else {}
745
- api_endpoint = quantum_config.get('api_endpoint', 'https://api.quantumnumbers.anu.edu.au')
746
-
747
- headers = {"x-api-key": quantum_api_key}
748
- params = {"length": 1, "type": "uint8"}
749
-
750
- numbers_fetched = 0
751
- with quantum_queue_lock:
752
- current_size = len(quantum_numbers_queue)
753
-
754
- while numbers_fetched < 100:
755
- try:
756
- response = requests.get(api_endpoint, headers=headers, params=params, timeout=5)
757
- if response.status_code == 200:
758
- result = response.json()
759
- if result.get('success') and 'data' in result and len(result['data']) > 0:
760
- normalized = result['data'][0] / 255.0
761
- with quantum_queue_lock:
762
- quantum_numbers_queue.append(normalized)
763
- numbers_fetched += 1
764
- else:
765
- # Fallback to pseudo-random
766
- import random
767
- with quantum_queue_lock:
768
- quantum_numbers_queue.append(random.random())
769
- numbers_fetched += 1
770
- elif response.status_code == 429:
771
- # Rate limited - use pseudo-random
772
- import random
773
- with quantum_queue_lock:
774
- quantum_numbers_queue.append(random.random())
775
- numbers_fetched += 1
776
- else:
777
- # Error - use pseudo-random
778
- import random
779
- with quantum_queue_lock:
780
- quantum_numbers_queue.append(random.random())
781
- numbers_fetched += 1
782
- except Exception as e:
783
- logging.debug(f"[Quantum Queue] Error fetching number: {e}, using pseudo-random")
784
- import random
785
- with quantum_queue_lock:
786
- quantum_numbers_queue.append(random.random())
787
- numbers_fetched += 1
788
-
789
- # Small delay to avoid rate limits
790
- time.sleep(0.1)
791
-
792
- logging.info(f"[Quantum Queue] Filled queue with {numbers_fetched} numbers")
793
  except Exception as e:
794
- logging.error(f"[Quantum Queue] Error filling queue: {e}")
795
  finally:
796
- with quantum_queue_lock:
797
- quantum_filling = False
798
-
799
  # Start filling in background thread
800
  Thread(target=_fill_queue, daemon=True).start()
801
 
802
- def pop_quantum_number():
803
- """Pop a quantum number from the queue, or return pseudo-random if empty"""
804
- global quantum_numbers_queue
805
-
806
- with quantum_queue_lock:
807
- if len(quantum_numbers_queue) > 0:
808
- return quantum_numbers_queue.popleft()
809
  else:
810
- # Queue is empty, use pseudo-random
811
  import random
812
  return random.random()
813
 
814
  @app.route('/api/avatar')
815
  def get_avatar():
816
  """Endpoint to get the current avatar shape and state with enhanced responsiveness"""
817
- global last_avatar_update, quantum_numbers_queue, quantum_filling
818
-
819
  if not is_initialized:
820
  return jsonify({
821
  'avatar_shape': 'Circle',
@@ -823,17 +732,17 @@ def get_avatar():
823
  'last_updated': last_avatar_update,
824
  'status': 'initializing'
825
  })
826
-
827
  try:
828
- # Check if quantum queue is empty, fill it asynchronously if needed
829
- with quantum_queue_lock:
830
- queue_empty = len(quantum_numbers_queue) == 0
831
-
832
- if queue_empty and not quantum_filling:
833
- fill_quantum_queue()
834
-
835
- # Pop one quantum number to update emotions
836
- quantum_num = pop_quantum_number()
837
 
838
  avatar_shape = avatar_engine.avatar_model if avatar_engine else 'Circle'
839
 
@@ -850,15 +759,15 @@ def get_avatar():
850
 
851
  # Force avatar update based on emotions if available
852
  if avatar_engine and galatea_ai:
853
- # Apply quantum influence to emotions
854
  emotions = ["joy", "sadness", "anger", "fear", "curiosity"]
855
- # Use quantum number to influence a random emotion
856
  import random
857
- emotion_index = int(quantum_num * len(emotions)) % len(emotions)
858
  selected_emotion = emotions[emotion_index]
859
-
860
- # Apply subtle quantum influence (-0.05 to +0.05)
861
- influence = (quantum_num - 0.5) * 0.1
862
  current_value = galatea_ai.emotional_state[selected_emotion]
863
  new_value = max(0.05, min(1.0, current_value + influence))
864
  galatea_ai.emotional_state[selected_emotion] = new_value
 
80
  max_init_retries = 3
81
  current_init_retry = 0
82
 
83
+ # Random numbers queue for /api/avatar endpoint
84
+ random_numbers_queue = deque(maxlen=100)
85
+ random_queue_lock = Lock()
86
+ random_filling = False
87
 
88
  # Check for required environment variables
89
  required_env_vars = ['DEEPSEEK_API_KEY']
 
132
  'sentiment_analyzer': {'ready': False, 'error': None},
133
  'deepseek_api': {'ready': False, 'error': None},
134
  'inflection_api': {'ready': False, 'error': None},
 
135
  }
136
 
137
  def initialize_json_memory():
 
299
  init_status['inflection_api']['ready'] = False
300
  return False
301
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
 
303
  def run_parallel_initialization():
304
  """Run all initialization steps in parallel"""
 
320
  ("Sentiment Analyzer", initialize_sentiment_analyzer),
321
  ("DeepSeek API", validate_deepseek_api),
322
  ("Inflection AI", validate_inflection_api),
 
323
  ]
324
 
325
  completed_count = 0
 
680
  # Track avatar updates with timestamp
681
  last_avatar_update = time.time()
682
 
683
+ def fill_random_queue():
684
+ """Asynchronously fill the random numbers queue with 100 numbers"""
685
+ global random_filling, random_numbers_queue
686
+
687
+ with random_queue_lock:
688
+ if random_filling:
689
  return # Already filling
690
+ random_filling = True
691
+
692
  def _fill_queue():
693
+ global random_filling
694
  try:
695
+ import random
696
+ logging.debug("[Random Queue] Filling with regular random numbers")
697
+ with random_queue_lock:
698
+ while len(random_numbers_queue) < 100:
699
+ random_numbers_queue.append(random.random())
700
+
701
+ logging.info(f"[Random Queue] Filled queue with 100 random numbers")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
702
  except Exception as e:
703
+ logging.error(f"[Random Queue] Error filling queue: {e}")
704
  finally:
705
+ with random_queue_lock:
706
+ random_filling = False
707
+
708
  # Start filling in background thread
709
  Thread(target=_fill_queue, daemon=True).start()
710
 
711
+ def pop_random_number():
712
+ """Pop a random number from the queue, or generate one if empty"""
713
+ global random_numbers_queue
714
+
715
+ with random_queue_lock:
716
+ if len(random_numbers_queue) > 0:
717
+ return random_numbers_queue.popleft()
718
  else:
719
+ # Queue is empty, generate on the fly
720
  import random
721
  return random.random()
722
 
723
  @app.route('/api/avatar')
724
  def get_avatar():
725
  """Endpoint to get the current avatar shape and state with enhanced responsiveness"""
726
+ global last_avatar_update, random_numbers_queue, random_filling
727
+
728
  if not is_initialized:
729
  return jsonify({
730
  'avatar_shape': 'Circle',
 
732
  'last_updated': last_avatar_update,
733
  'status': 'initializing'
734
  })
735
+
736
  try:
737
+ # Check if random queue is empty, fill it asynchronously if needed
738
+ with random_queue_lock:
739
+ queue_empty = len(random_numbers_queue) == 0
740
+
741
+ if queue_empty and not random_filling:
742
+ fill_random_queue()
743
+
744
+ # Pop one random number to update emotions
745
+ random_num = pop_random_number()
746
 
747
  avatar_shape = avatar_engine.avatar_model if avatar_engine else 'Circle'
748
 
 
759
 
760
  # Force avatar update based on emotions if available
761
  if avatar_engine and galatea_ai:
762
+ # Apply random influence to emotions
763
  emotions = ["joy", "sadness", "anger", "fear", "curiosity"]
764
+ # Use random number to influence a random emotion
765
  import random
766
+ emotion_index = int(random_num * len(emotions)) % len(emotions)
767
  selected_emotion = emotions[emotion_index]
768
+
769
+ # Apply subtle random influence (-0.05 to +0.05)
770
+ influence = (random_num - 0.5) * 0.1
771
  current_value = galatea_ai.emotional_state[selected_emotion]
772
  new_value = max(0.05, min(1.0, current_value + influence))
773
  galatea_ai.emotional_state[selected_emotion] = new_value
docker-compose.yml CHANGED
@@ -14,8 +14,6 @@ services:
14
  # Optional - Azure Text Analytics
15
  - AZURE_TEXT_ANALYTICS_KEY=${AZURE_TEXT_ANALYTICS_KEY:-}
16
  - AZURE_TEXT_ANALYTICS_ENDPOINT=${AZURE_TEXT_ANALYTICS_ENDPOINT:-}
17
- # Optional - Quantum API
18
- - ANU_QUANTUM_API_KEY=${ANU_QUANTUM_API_KEY:-}
19
  # Optional - Port (defaults to 7860)
20
  - PORT=${PORT:-7860}
21
  env_file:
 
14
  # Optional - Azure Text Analytics
15
  - AZURE_TEXT_ANALYTICS_KEY=${AZURE_TEXT_ANALYTICS_KEY:-}
16
  - AZURE_TEXT_ANALYTICS_ENDPOINT=${AZURE_TEXT_ANALYTICS_ENDPOINT:-}
 
 
17
  # Optional - Port (defaults to 7860)
18
  - PORT=${PORT:-7860}
19
  env_file:
galatea_ai.py CHANGED
@@ -79,8 +79,7 @@ class GalateaAI:
79
  # Legacy compatibility
80
  self.deepseek_available = self.deepseek_agent.is_ready()
81
  self.inflection_ai_available = self.pi_agent.is_ready()
82
- self.quantum_random_available = self.emotional_agent.quantum_random_available
83
-
84
  logging.info("✓ All agents initialized and verified")
85
 
86
  def _check_pre_initialization(self):
 
79
  # Legacy compatibility
80
  self.deepseek_available = self.deepseek_agent.is_ready()
81
  self.inflection_ai_available = self.pi_agent.is_ready()
82
+
 
83
  logging.info("✓ All agents initialized and verified")
84
 
85
  def _check_pre_initialization(self):
models.yaml CHANGED
@@ -56,16 +56,4 @@ conversation:
56
  system_prompts:
57
  galatea_identity: "You are Galatea, an AI assistant with emotional awareness and memory."
58
  response_style: "Respond in character, keeping responses concise (under 50 words)."
59
-
60
- # Quantum Randomness Configuration
61
- quantum:
62
- api_endpoint: "https://api.quantumnumbers.anu.edu.au"
63
- default_length: 128
64
- default_type: "uint8"
65
-
66
- # Usage settings
67
- use_for_temperature: true
68
- use_for_emotion_decay: true
69
- use_for_learning_rate: true
70
- use_for_curiosity: true
71
 
 
56
  system_prompts:
57
  galatea_identity: "You are Galatea, an AI assistant with emotional awareness and memory."
58
  response_style: "Respond in character, keeping responses concise (under 50 words)."
 
 
 
 
 
 
 
 
 
 
 
 
59
 
quantum_emotion_service.py CHANGED
@@ -1,9 +1,9 @@
1
- """Quantum Emotion Service - Background service for quantum-influenced emotion updates"""
2
  import os
3
  import sys
4
  import time
5
  import logging
6
- import requests
7
  import threading
8
  from collections import deque
9
 
@@ -12,67 +12,33 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
12
  from config import MODEL_CONFIG
13
 
14
  class QuantumEmotionService:
15
- """Background service that collects quantum random numbers and applies them to emotions"""
16
-
17
  def __init__(self, emotional_agent, config=None):
18
  self.config = config or MODEL_CONFIG or {}
19
  self.emotional_agent = emotional_agent
20
- self.quantum_api_key = os.getenv("ANU_QUANTUM_API_KEY")
21
  self.running = False
22
  self.thread = None
23
- self.quantum_numbers = deque(maxlen=6) # Store last 6 numbers
24
  self.last_update_time = time.time()
25
  self.update_interval = 10.0 # Update emotions every 10 seconds
26
- self.call_interval = 1.0 # Call API once per second
27
-
28
- if not self.quantum_api_key:
29
- logging.info("[QuantumEmotionService] Quantum API key not found - service will not run")
30
- return
31
-
32
- quantum_config = self.config.get('quantum', {}) if self.config else {}
33
- self.api_endpoint = quantum_config.get('api_endpoint', 'https://api.quantumnumbers.anu.edu.au')
34
-
35
- logging.info("[QuantumEmotionService] Initialized - will call API once per second")
36
 
37
- def _fetch_quantum_number(self):
38
- """Fetch a single quantum random number from the API"""
39
- if not self.quantum_api_key:
40
- return None
41
-
42
- try:
43
- headers = {"x-api-key": self.quantum_api_key}
44
- params = {"length": 1, "type": "uint8"}
45
-
46
- response = requests.get(self.api_endpoint, headers=headers, params=params, timeout=5)
47
-
48
- if response.status_code == 200:
49
- result = response.json()
50
- if result.get('success') and 'data' in result and len(result['data']) > 0:
51
- # Normalize to 0-1 range
52
- normalized = result['data'][0] / 255.0
53
- return normalized
54
- elif response.status_code == 429:
55
- # Rate limited - return None, will use pseudo-random
56
- logging.debug("[QuantumEmotionService] Rate limited, using pseudo-random")
57
- return None
58
- else:
59
- logging.debug(f"[QuantumEmotionService] API returned {response.status_code}")
60
- return None
61
- except Exception as e:
62
- logging.debug(f"[QuantumEmotionService] API call failed: {e}")
63
- return None
64
-
65
- return None
66
 
67
- def _apply_quantum_emotions(self):
68
- """Apply the collected 6 quantum numbers to influence emotion state"""
69
- if len(self.quantum_numbers) < 6:
70
- logging.debug(f"[QuantumEmotionService] Not enough numbers yet ({len(self.quantum_numbers)}/6)")
71
  return
72
-
73
  # Convert deque to list
74
- numbers = list(self.quantum_numbers)
75
-
76
  # Map 6 numbers to 5 emotions + overall variation
77
  # numbers[0] -> joy
78
  # numbers[1] -> sadness
@@ -80,25 +46,25 @@ class QuantumEmotionService:
80
  # numbers[3] -> fear
81
  # numbers[4] -> curiosity
82
  # numbers[5] -> overall variation factor
83
-
84
  emotions = ["joy", "sadness", "anger", "fear", "curiosity"]
85
  current_state = self.emotional_agent.get_state()
86
-
87
- # Apply quantum influence (subtle changes, -0.05 to +0.05 range)
88
  for i, emotion in enumerate(emotions):
89
  if i < len(numbers) and numbers[i] is not None:
90
  # Convert 0-1 to -0.05 to +0.05 influence
91
  influence = (numbers[i] - 0.5) * 0.1 # Scale to ±0.05
92
-
93
  # Apply with overall variation factor
94
  if len(numbers) > 5 and numbers[5] is not None:
95
  variation = (numbers[5] - 0.5) * 0.02 # Additional ±0.01 variation
96
  influence += variation
97
-
98
  # Update emotion
99
  new_value = current_state[emotion] + influence
100
  current_state[emotion] = max(0.05, min(1.0, new_value))
101
-
102
  # Soft normalization to keep emotions balanced (only if they get too extreme)
103
  total = sum(current_state.values())
104
  if total > 1.5 or total < 0.5:
@@ -110,74 +76,62 @@ class QuantumEmotionService:
110
  for emotion in emotions:
111
  if current_state[emotion] < 0.05:
112
  current_state[emotion] = 0.05
113
-
114
  # Update the emotional agent's state
115
  self.emotional_agent.emotional_state = current_state
116
-
117
- # Save to JSON after quantum update
118
  self.emotional_agent._save_to_json()
119
-
120
- logging.info(f"[QuantumEmotionService] Applied quantum influence: {current_state}")
121
 
122
  def _service_loop(self):
123
- """Main service loop - calls API once per second, updates emotions every 10 seconds"""
124
- logging.info("[QuantumEmotionService] Service started")
125
-
126
  while self.running:
127
  try:
128
- # Fetch quantum number (once per second)
129
- quantum_num = self._fetch_quantum_number()
130
-
131
- if quantum_num is not None:
132
- self.quantum_numbers.append(quantum_num)
133
- logging.debug(f"[QuantumEmotionService] Collected quantum number: {quantum_num:.4f}")
134
- else:
135
- # Use pseudo-random as fallback
136
- import random
137
- pseudo_random = random.random()
138
- self.quantum_numbers.append(pseudo_random)
139
- logging.debug(f"[QuantumEmotionService] Using pseudo-random: {pseudo_random:.4f}")
140
-
141
  # Check if it's time to update emotions (every 10 seconds)
142
  current_time = time.time()
143
  if current_time - self.last_update_time >= self.update_interval:
144
- if len(self.quantum_numbers) >= 6:
145
- self._apply_quantum_emotions()
146
  self.last_update_time = current_time
147
- logging.info(f"[QuantumEmotionService] Updated emotions with {len(self.quantum_numbers)} quantum numbers")
148
-
149
- # Wait 1 second before next call
150
- time.sleep(self.call_interval)
151
-
152
  except Exception as e:
153
- logging.error(f"[QuantumEmotionService] Error in service loop: {e}")
154
- time.sleep(self.call_interval) # Wait before retrying
155
 
156
  def start(self):
157
  """Start the background service"""
158
- if not self.quantum_api_key:
159
- logging.info("[QuantumEmotionService] Cannot start - no API key")
160
- return False
161
-
162
  if self.running:
163
- logging.warning("[QuantumEmotionService] Already running")
164
  return False
165
-
166
  self.running = True
167
  self.thread = threading.Thread(target=self._service_loop, daemon=True)
168
  self.thread.start()
169
- logging.info("[QuantumEmotionService] Background service started")
170
  return True
171
-
172
  def stop(self):
173
  """Stop the background service"""
174
  if not self.running:
175
  return
176
-
177
  self.running = False
178
  if self.thread:
179
  self.thread.join(timeout=2.0)
180
- logging.info("[QuantumEmotionService] Background service stopped")
181
 
182
  def is_running(self):
183
  """Check if service is running"""
 
1
+ """Random Emotion Service - Background service for random-influenced emotion updates"""
2
  import os
3
  import sys
4
  import time
5
  import logging
6
+ import random
7
  import threading
8
  from collections import deque
9
 
 
12
  from config import MODEL_CONFIG
13
 
14
  class QuantumEmotionService:
15
+ """Background service that generates random numbers and applies them to emotions"""
16
+
17
  def __init__(self, emotional_agent, config=None):
18
  self.config = config or MODEL_CONFIG or {}
19
  self.emotional_agent = emotional_agent
 
20
  self.running = False
21
  self.thread = None
22
+ self.random_numbers = deque(maxlen=6) # Store last 6 numbers
23
  self.last_update_time = time.time()
24
  self.update_interval = 10.0 # Update emotions every 10 seconds
25
+ self.generation_interval = 1.0 # Generate number once per second
26
+
27
+ logging.info("[RandomEmotionService] Initialized - will generate random numbers once per second")
 
 
 
 
 
 
 
28
 
29
+ def _generate_random_number(self):
30
+ """Generate a single random number"""
31
+ return random.random()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
+ def _apply_random_emotions(self):
34
+ """Apply the collected 6 random numbers to influence emotion state"""
35
+ if len(self.random_numbers) < 6:
36
+ logging.debug(f"[RandomEmotionService] Not enough numbers yet ({len(self.random_numbers)}/6)")
37
  return
38
+
39
  # Convert deque to list
40
+ numbers = list(self.random_numbers)
41
+
42
  # Map 6 numbers to 5 emotions + overall variation
43
  # numbers[0] -> joy
44
  # numbers[1] -> sadness
 
46
  # numbers[3] -> fear
47
  # numbers[4] -> curiosity
48
  # numbers[5] -> overall variation factor
49
+
50
  emotions = ["joy", "sadness", "anger", "fear", "curiosity"]
51
  current_state = self.emotional_agent.get_state()
52
+
53
+ # Apply random influence (subtle changes, -0.05 to +0.05 range)
54
  for i, emotion in enumerate(emotions):
55
  if i < len(numbers) and numbers[i] is not None:
56
  # Convert 0-1 to -0.05 to +0.05 influence
57
  influence = (numbers[i] - 0.5) * 0.1 # Scale to ±0.05
58
+
59
  # Apply with overall variation factor
60
  if len(numbers) > 5 and numbers[5] is not None:
61
  variation = (numbers[5] - 0.5) * 0.02 # Additional ±0.01 variation
62
  influence += variation
63
+
64
  # Update emotion
65
  new_value = current_state[emotion] + influence
66
  current_state[emotion] = max(0.05, min(1.0, new_value))
67
+
68
  # Soft normalization to keep emotions balanced (only if they get too extreme)
69
  total = sum(current_state.values())
70
  if total > 1.5 or total < 0.5:
 
76
  for emotion in emotions:
77
  if current_state[emotion] < 0.05:
78
  current_state[emotion] = 0.05
79
+
80
  # Update the emotional agent's state
81
  self.emotional_agent.emotional_state = current_state
82
+
83
+ # Save to JSON after random update
84
  self.emotional_agent._save_to_json()
85
+
86
+ logging.info(f"[RandomEmotionService] Applied random influence: {current_state}")
87
 
88
  def _service_loop(self):
89
+ """Main service loop - generates random numbers once per second, updates emotions every 10 seconds"""
90
+ logging.info("[RandomEmotionService] Service started")
91
+
92
  while self.running:
93
  try:
94
+ # Generate random number (once per second)
95
+ random_num = self._generate_random_number()
96
+ self.random_numbers.append(random_num)
97
+ logging.debug(f"[RandomEmotionService] Generated random number: {random_num:.4f}")
98
+
 
 
 
 
 
 
 
 
99
  # Check if it's time to update emotions (every 10 seconds)
100
  current_time = time.time()
101
  if current_time - self.last_update_time >= self.update_interval:
102
+ if len(self.random_numbers) >= 6:
103
+ self._apply_random_emotions()
104
  self.last_update_time = current_time
105
+ logging.info(f"[RandomEmotionService] Updated emotions with {len(self.random_numbers)} random numbers")
106
+
107
+ # Wait 1 second before next generation
108
+ time.sleep(self.generation_interval)
109
+
110
  except Exception as e:
111
+ logging.error(f"[RandomEmotionService] Error in service loop: {e}")
112
+ time.sleep(self.generation_interval) # Wait before retrying
113
 
114
  def start(self):
115
  """Start the background service"""
 
 
 
 
116
  if self.running:
117
+ logging.warning("[RandomEmotionService] Already running")
118
  return False
119
+
120
  self.running = True
121
  self.thread = threading.Thread(target=self._service_loop, daemon=True)
122
  self.thread.start()
123
+ logging.info("[RandomEmotionService] Background service started")
124
  return True
125
+
126
  def stop(self):
127
  """Stop the background service"""
128
  if not self.running:
129
  return
130
+
131
  self.running = False
132
  if self.thread:
133
  self.thread.join(timeout=2.0)
134
+ logging.info("[RandomEmotionService] Background service stopped")
135
 
136
  def is_running(self):
137
  """Check if service is running"""