Rafs-an09002 commited on
Commit
2cf8d14
Β·
verified Β·
1 Parent(s): 805aa65

sync: backend from GitHub Actions

Browse files
Files changed (1) hide show
  1. core/inference_engine.py +14 -45
core/inference_engine.py CHANGED
@@ -1,14 +1,5 @@
1
  """
2
  Inference Engine β€” Akinator-style, context-aware, guaranteed-to-terminate AI.
3
-
4
- Fixes in this version:
5
- - get_next_question() returns None when question_selector finds nothing useful
6
- (active-item grounding removes impossible questions like "Russia border?" after
7
- Indian Subcontinent confirmed). This immediately triggers ready_to_guess=True
8
- in app.py, ending the game β€” no infinite loop.
9
- - _should_stop_asking() is the single source-of-truth for stopping; it is called
10
- both after process_answer() and inside get_next_question().
11
- - soft_filter() no longer takes top_k from GAME_CONFIG (removed aggressive top_k=3).
12
  """
13
 
14
  import logging
@@ -22,7 +13,7 @@ from algorithms.information_gain import InformationGain
22
  from algorithms.bayesian_network import BayesianNetwork
23
  from models.game_state import GameState
24
  from models.item_model import Item
25
- from backend.config import GAME_CONFIG
26
  from services.firebase_service import FirebaseService
27
 
28
  logger = logging.getLogger(__name__)
@@ -32,17 +23,17 @@ class InferenceEngine:
32
  """Main AI Engine β€” Akinator-style, guaranteed termination."""
33
 
34
  def __init__(self):
35
- self.question_selector = QuestionSelector()
36
- self.probability_manager = ProbabilityManager()
37
  self.confidence_calculator = ConfidenceCalculator()
38
- self.information_gain = InformationGain()
39
- self.bayesian_network = BayesianNetwork()
40
- self.firebase_service = FirebaseService()
41
  self.active_games: Dict[str, GameState] = {}
42
  self.session_stats = {
43
- 'games_played': 0,
44
  'successful_guesses': 0,
45
- 'average_questions': 0,
46
  }
47
  logger.info("InferenceEngine ready (v3.2 β€” context-aware, guaranteed termination)")
48
 
@@ -88,14 +79,6 @@ class InferenceEngine:
88
  # ── Question flow ─────────────────────────────────────────────────────────
89
 
90
  def get_next_question(self, game_state: GameState) -> Optional[Dict]:
91
- """
92
- Returns the best next question, or None if the game should end.
93
- None can mean:
94
- (a) confidence/item-count threshold reached β†’ ready to guess
95
- (b) no useful question left after active-item grounding β†’ ready to guess
96
- Both cases set ready_to_guess=True in app.py.
97
- """
98
- # Check stopping conditions first
99
  if self._should_stop_asking(game_state):
100
  logger.info(f"[{game_state.session_id}] Stop condition met before question select.")
101
  return None
@@ -114,11 +97,6 @@ class InferenceEngine:
114
  game_state_history=game_state.answer_history,
115
  )
116
 
117
- # ── KEY FIX: selector returned None β†’ no useful question remains ─────
118
- # This happens when all remaining questions are about attributes that
119
- # none of the active items actually have (e.g., "Russia border?" when
120
- # only Indian Subcontinent countries are left).
121
- # Returning None here makes app.py set ready_to_guess=True immediately.
122
  if question is None:
123
  logger.info(
124
  f"[{game_state.session_id}] Selector found no useful question "
@@ -143,7 +121,6 @@ class InferenceEngine:
143
  question = game_state.current_question
144
  game_state.record_answer(answer)
145
 
146
- # Bayesian probability update
147
  active_items = game_state.get_active_items()
148
  for item in active_items:
149
  item.probability = self.probability_manager.update_item_probability(
@@ -151,7 +128,6 @@ class InferenceEngine:
151
  )
152
 
153
  self.probability_manager.normalize_probabilities(game_state.items)
154
- # Conservative soft filter β€” keeps top-20, threshold 0.1% of top item
155
  self.probability_manager.soft_filter(game_state.items)
156
  self.bayesian_network.update_beliefs(question, answer, game_state.items)
157
 
@@ -168,10 +144,10 @@ class InferenceEngine:
168
  )
169
 
170
  return {
171
- 'confidence': confidence,
172
  'active_items_count': len(current_active),
173
- 'top_prediction': top_item.to_dict() if top_item else None,
174
- 'should_stop': should_stop,
175
  }
176
 
177
  # ── Final prediction ──────────────────────────────────────────────────────
@@ -208,14 +184,9 @@ class InferenceEngine:
208
  # ── Stopping logic ────────────────────────────────────────────────────────
209
 
210
  def _should_stop_asking(self, game_state: GameState) -> bool:
211
- """
212
- Single source of truth for stopping.
213
- Returns True when the engine should make its final guess.
214
- """
215
  active_items = game_state.get_active_items()
216
  active_count = len(active_items)
217
 
218
- # 1. Only 1 or 2 items remain β†’ guess immediately
219
  force_at = GAME_CONFIG.get('force_guess_at_items', 2)
220
  if active_count <= force_at:
221
  logger.info(
@@ -224,12 +195,10 @@ class InferenceEngine:
224
  )
225
  return True
226
 
227
- # 2. No questions left at all (all asked)
228
  if not game_state.get_available_questions():
229
  logger.info(f"[{game_state.session_id}] No available questions left.")
230
  return True
231
 
232
- # 3. Adaptive confidence threshold
233
  confidence = self.confidence_calculator.calculate(active_items)
234
  return self.confidence_calculator.should_make_guess(
235
  confidence,
@@ -252,8 +221,8 @@ class InferenceEngine:
252
  games = self.session_stats['games_played']
253
  success = self.session_stats['successful_guesses']
254
  return {
255
- 'games_played': games,
256
  'successful_guesses': success,
257
- 'success_rate': (success / games * 100) if games > 0 else 0,
258
- 'average_questions': self.session_stats['average_questions'],
259
  }
 
1
  """
2
  Inference Engine β€” Akinator-style, context-aware, guaranteed-to-terminate AI.
 
 
 
 
 
 
 
 
 
3
  """
4
 
5
  import logging
 
13
  from algorithms.bayesian_network import BayesianNetwork
14
  from models.game_state import GameState
15
  from models.item_model import Item
16
+ from config import GAME_CONFIG
17
  from services.firebase_service import FirebaseService
18
 
19
  logger = logging.getLogger(__name__)
 
23
  """Main AI Engine β€” Akinator-style, guaranteed termination."""
24
 
25
  def __init__(self):
26
+ self.question_selector = QuestionSelector()
27
+ self.probability_manager = ProbabilityManager()
28
  self.confidence_calculator = ConfidenceCalculator()
29
+ self.information_gain = InformationGain()
30
+ self.bayesian_network = BayesianNetwork()
31
+ self.firebase_service = FirebaseService()
32
  self.active_games: Dict[str, GameState] = {}
33
  self.session_stats = {
34
+ 'games_played': 0,
35
  'successful_guesses': 0,
36
+ 'average_questions': 0,
37
  }
38
  logger.info("InferenceEngine ready (v3.2 β€” context-aware, guaranteed termination)")
39
 
 
79
  # ── Question flow ─────────────────────────────────────────────────────────
80
 
81
  def get_next_question(self, game_state: GameState) -> Optional[Dict]:
 
 
 
 
 
 
 
 
82
  if self._should_stop_asking(game_state):
83
  logger.info(f"[{game_state.session_id}] Stop condition met before question select.")
84
  return None
 
97
  game_state_history=game_state.answer_history,
98
  )
99
 
 
 
 
 
 
100
  if question is None:
101
  logger.info(
102
  f"[{game_state.session_id}] Selector found no useful question "
 
121
  question = game_state.current_question
122
  game_state.record_answer(answer)
123
 
 
124
  active_items = game_state.get_active_items()
125
  for item in active_items:
126
  item.probability = self.probability_manager.update_item_probability(
 
128
  )
129
 
130
  self.probability_manager.normalize_probabilities(game_state.items)
 
131
  self.probability_manager.soft_filter(game_state.items)
132
  self.bayesian_network.update_beliefs(question, answer, game_state.items)
133
 
 
144
  )
145
 
146
  return {
147
+ 'confidence': confidence,
148
  'active_items_count': len(current_active),
149
+ 'top_prediction': top_item.to_dict() if top_item else None,
150
+ 'should_stop': should_stop,
151
  }
152
 
153
  # ── Final prediction ──────────────────────────────────────────────────────
 
184
  # ── Stopping logic ────────────────────────────────────────────────────────
185
 
186
  def _should_stop_asking(self, game_state: GameState) -> bool:
 
 
 
 
187
  active_items = game_state.get_active_items()
188
  active_count = len(active_items)
189
 
 
190
  force_at = GAME_CONFIG.get('force_guess_at_items', 2)
191
  if active_count <= force_at:
192
  logger.info(
 
195
  )
196
  return True
197
 
 
198
  if not game_state.get_available_questions():
199
  logger.info(f"[{game_state.session_id}] No available questions left.")
200
  return True
201
 
 
202
  confidence = self.confidence_calculator.calculate(active_items)
203
  return self.confidence_calculator.should_make_guess(
204
  confidence,
 
221
  games = self.session_stats['games_played']
222
  success = self.session_stats['successful_guesses']
223
  return {
224
+ 'games_played': games,
225
  'successful_guesses': success,
226
+ 'success_rate': (success / games * 100) if games > 0 else 0,
227
+ 'average_questions': self.session_stats['average_questions'],
228
  }