Files
portal/knowledge/aleksandra/brain/embed.py
T
Дмитрий 2a109455a5 feat(brain): провайдер эмбеддингов AITunnel + фейк для тестов
embed.py: get_embedder(provider) — aitunnel (живой /v1/embeddings) по
умолчанию, fake (детерминированный без сети) для тестов, local — заглушка
NotImplementedError. batch() применяет embed_fn инъекцией — сеть не
затрагивается в тестах.
2026-07-17 08:15:01 +03:00

51 lines
2.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Эмбеддинги: провайдер AITunnel (по умолчанию) с fallback на локальную модель.
Живой источник инкапсулирован — вся остальная логика получает embed_fn инъекцией."""
import os
import json
import urllib.request
def batch(texts, embed_fn):
return [embed_fn(t) for t in texts]
def _aitunnel_embedder():
key_path = os.environ.get("ALEX_AITUNNEL_KEY", "aitunnel.key")
key = open(key_path, encoding="utf-8").read().strip()
model = os.environ.get("ALEX_EMBED_MODEL", "text-embedding-3-small")
def embed(text):
body = {"model": model, "input": text}
req = urllib.request.Request(
"https://api.aitunnel.ru/v1/embeddings",
data=json.dumps(body).encode(), method="POST")
req.add_header("Authorization", f"Bearer {key}")
req.add_header("Content-Type", "application/json")
r = json.load(urllib.request.urlopen(req, timeout=30))
return r["data"][0]["embedding"]
return embed
def _fake_embedder():
# детерминированный «отпечаток» без сети — для тестов/сухих прогонов
def embed(text):
t = text.lower()
return [
float(sum(ord(c) for c in t) % 97),
float(len(t) % 13),
float(t.count("а") + t.count("о")),
]
return embed
def get_embedder(provider=None):
provider = provider or os.environ.get("ALEX_EMBED", "aitunnel")
if provider == "aitunnel":
return _aitunnel_embedder()
if provider == "local":
# fallback: локальная модель на GPU-сервере (sentence-transformers).
# Реализовать при переключении; сигнатура та же: embed(text) -> list[float].
raise NotImplementedError("local-эмбеддер подключается на GPU-сервере при провале AITunnel")
return _fake_embedder()