| 500 | return embedding |
| 501 | |
| 502 | def embed_batch(self, texts: List[str], batch_size: int = 32) -> List[List[float]]: |
| 503 | normalized_texts = [self._normalize_text(text) for text in texts] |
| 504 | results: List[Optional[List[float]]] = [None] * len(normalized_texts) |
| 505 | missing_indices = [] |
| 506 | |
| 507 | for index, text in enumerate(normalized_texts): |
| 508 | if not text: |
| 509 | results[index] = [0.0] * self.dimensions |
| 510 | continue |
| 511 | cached = self._load_cached(text) |
| 512 | if cached is not None: |
| 513 | results[index] = cached |
| 514 | else: |
| 515 | missing_indices.append(index) |
| 516 | |
| 517 | if missing_indices and self.provider == "openai" and self.client is not None: |
| 518 | try: |
| 519 | for batch_start in range(0, len(missing_indices), batch_size): |
| 520 | batch_indices = missing_indices[batch_start:batch_start + batch_size] |
| 521 | batch_texts = [normalized_texts[index] for index in batch_indices] |
| 522 | kwargs: Dict[str, Any] = {"model": self.model, "input": batch_texts} |
| 523 | if self.model.startswith("text-embedding-3"): |
| 524 | kwargs["dimensions"] = self.dimensions |
| 525 | response = self.client.embeddings.create(**kwargs) |
| 526 | for index, item in zip(batch_indices, response.data): |
| 527 | embedding = self._resize_vector(list(item.embedding)) |
| 528 | results[index] = embedding |
| 529 | self._save_cached(normalized_texts[index], embedding) |
| 530 | except Exception as exc: |
| 531 | print(f"Batch embedding error ({self.provider}:{self.model}): {exc}") |
| 532 | if _looks_like_auth_error(exc): |
| 533 | print( |
| 534 | "Embedding auth failed. Check OPENAI_API_KEY / DASHSCOPE_API_KEY " |
| 535 | "and verify the key is valid for the configured DashScope endpoint." |
| 536 | ) |
| 537 | self.provider = "hash" |
| 538 | self.model = "hash" |
| 539 | for index in missing_indices: |
| 540 | results[index] = self._get_hash_embedding(normalized_texts[index]) |
| 541 | |
| 542 | for index in missing_indices: |
| 543 | if results[index] is None: |
| 544 | results[index] = self.embed_text(normalized_texts[index]) |
| 545 | |
| 546 | return [embedding if embedding is not None else [0.0] * self.dimensions for embedding in results] |
| 547 | |
| 548 | def cosine_similarity(self, vector1: List[float], vector2: List[float]) -> float: |
| 549 | dot_product = sum(a * b for a, b in zip(vector1, vector2)) |