| 383 | |
| 384 | @dataclass |
| 385 | class UserFeedbackState: |
| 386 | profile: Dict[str, Any] |
| 387 | profile_text: str |
| 388 | profile_vector: Dict[str, float] = field(default_factory=dict) |
| 389 | positive_sum: Dict[str, float] = field(default_factory=dict) |
| 390 | negative_sum: Dict[str, float] = field(default_factory=dict) |
| 391 | positive_examples: List[Tuple[str, float]] = field(default_factory=list) |
| 392 | negative_examples: List[Tuple[str, float]] = field(default_factory=list) |
| 393 | logistic_weights: Dict[str, float] = field(default_factory=dict) |
| 394 | logistic_bias: float = 0.0 |
| 395 | logistic_trained: bool = False |
| 396 | positive_count: int = 0 |
| 397 | negative_count: int = 0 |
| 398 | |
| 399 | @property |
| 400 | def uses_feedback_classifier(self) -> bool: |
| 401 | return self.positive_count >= MIN_CLASSIFIER_POSITIVES and self.negative_count >= MIN_CLASSIFIER_NEGATIVES |
| 402 | |
| 403 | def positive_centroid(self) -> Dict[str, float]: |
| 404 | return normalize_vector(self.positive_sum) |
| 405 | |
| 406 | def negative_centroid(self) -> Dict[str, float]: |
| 407 | return normalize_vector(self.negative_sum) |
| 408 | |
| 409 | def prepare_vectors(self, idf: Dict[str, float]) -> None: |
| 410 | self.profile_vector = vectorize_text(self.profile_text, idf) |
| 411 | self.positive_sum = {} |
| 412 | self.negative_sum = {} |
| 413 | training_samples: List[Tuple[Dict[str, float], int, float]] = [] |
| 414 | for text, weight in self.positive_examples: |
| 415 | vector = vectorize_text(text, idf) |
| 416 | add_weighted_vector(self.positive_sum, vector, weight) |
| 417 | training_samples.append((vector, 1, weight)) |
| 418 | for text, weight in self.negative_examples: |
| 419 | vector = vectorize_text(text, idf) |
| 420 | add_weighted_vector(self.negative_sum, vector, weight) |
| 421 | training_samples.append((vector, 0, weight)) |
| 422 | if self.uses_feedback_classifier: |
| 423 | self.logistic_weights, self.logistic_bias = train_weighted_logistic_regression(training_samples) |
| 424 | self.logistic_trained = bool(self.logistic_weights) |
| 425 | else: |
| 426 | self.logistic_weights = {} |
| 427 | self.logistic_bias = 0.0 |
| 428 | self.logistic_trained = False |
| 429 | |
| 430 | def feedback_texts(self) -> List[str]: |
| 431 | return [text for text, _ in self.positive_examples + self.negative_examples] |
| 432 | |
| 433 | def update_positive_text(self, text: str, weight: float = 1.0) -> None: |
| 434 | self.positive_examples.append((text, weight)) |
| 435 | self.positive_count += 1 |
| 436 | |
| 437 | def update_negative_text(self, text: str, weight: float = 0.35) -> None: |
| 438 | self.negative_examples.append((text, weight)) |
| 439 | self.negative_count += 1 |
| 440 | |
| 441 | def predict_logistic(self, vector: Dict[str, float]) -> float: |
| 442 | if not self.logistic_trained: |
no outgoing calls
no test coverage detected