mirror of
https://github.com/hanzoai/brain.git
synced 2026-10-30 18:46:38 +00:00
Closed every remaining π‘ / π΅ row in PARITY.md. Each algorithm FortΓ©mi ships now has a real implementation in @hanzo/bot-memory v2.0.0 with matching ports in hanzo-memory (Python), bot-go (Go), and hanzoai/mcp (Rust). Cross-runtime tests: 121 TS + 53 Python + 58 Go + 38 Rust = 270 passing. Landed under packages/memory/ Retrieval & ranking fusion RRF (k=20 BEIR) + RSF (Weaviate v1.24) + adaptive k + adaptive FTS/semantic weights rerank MMR (Carbonell & Goldstein 1998), cosine dedup chunk-aware deduplication script Unicode script detection (Latin/CJK/emoji/Cyrillic/ Arabic/Hebrew/Greek/Devanagari) fts feature-flag matrix, CJK bigrams, emoji trigrams, websearch_to_tsquery parser, SQLite FTS5 MATCH renderer embed embedding registry, asymmetric E5 prefixes, Matryoshka truncation, coarse-dim picker two-stage coarse β fine retrieval via MRL federated cross-archive search with RRF fusion multi-memory X-Hanzo-Memory routing (X-Fortemi-Memory alias) filters composable tag/temporal/collection/scope predicates temporal UUIDv7 floor/ceiling + named ranges spatial Haversine + bbox + in-box (PostGIS fallback) Knowledge & graph graph normalize β SNN β PFNET-β β Louvain maintenance pipeline skos W3C SKOS concept tree (broader/narrower closures, Turtle export) fair Dublin Core XML + JSON-LD doc-types document type registry + auto-detect (filename/MIME/ content sniff) captions WebVTT / SRT / RTTM renderers sprite thumbnail sprite-sheet WebVTT cue builder code-ast language-aware syntactic chunker email RFC 2822 / MIME parser (multipart, qp, base64, HTML strip) exif EXIF parser (camera / time / GPS) Operational events EventBus + SSE renderer + webhook HMAC signer tokenizer BPE-style estimator + truncate-to-budget eval MRR / recall@k / precision@k / NDCG@k / benchmark hardware tier autodetect + per-tier model recommender circuit-breaker closed / open / half-open retry exponential backoff with full jitter range HTTP Range parser + Content-Range formatter tus TUS v1.0.0 (Create / Patch / metadata / checksum) Crypto address wallet-style content-addressable ids (hanzo: / mm:) mmpke01 multi-recipient envelope parser/builder + magic-byte sniff Inference (packages/memory/inference/) index InferenceBackend trait + registry slug provider-qualified routing (provider:model:tag) capabilities capability flags + task-based selector runtime-config hot-swap config (db_override β env β default) mock deterministic backend (for tests) few-shot ICL prompt builder (3-5 examples capped) refinement Self-Refine / ReAct / Reflexion patterns link-types rule + LLM link classifier (11 canonical types) vision VisionBackend trait + Ollama default transcription Whisper-compatible HTTP client diarization pyannote-compatible HTTP client + alignSpeakers / relabel gliner GLiNER zero-shot NER HTTP client Extraction (packages/memory/extract/) index registry + dispatcher text native UTF-8 passthrough email RFC 2822 adapter spreadsheet CSV/TSV pure-JS; xlsx via registerXlsxDecoder archive ZIP central-directory pure-JS reader exif-adapter EXIF for images code tree-sitter-style syntactic chunker pdf PdfSidecar HTTP adapter factory image VisionBackend-backed adapter factory audio TranscriptionBackend-backed adapter factory video keyframe + scene + transcript adapter factory 3d multi-view 3D model adapter factory media-optimize ffmpeg variants (faststart / web-compat / 720p) adapter factory structured JSON-schema structured-extract adapter factory summarizer fast/standard cascade adapter factory sprite-adapter thumbnail sprite-sheet adapter factory Cross-runtime ports hanzo-memory (Python) 53 tests, ports of fusion / rerank / dedup / script / fts / embed / temporal / captions / tokenizer / eval / spatial / range / address / graph / doc-types / circuit / retry / slug / runtime-config / link-types bot-go (Go) 58 tests, same algorithm set ported with standard-library-only primitives hanzoai/mcp::brain (Rust) 38 tests, same algorithm set; sha2 added as dependency (blake3 stand-in) PARITY.md is now 100% green (β in-monorepo + π’ sibling-repo). No π΅ placeholders remain.
90 lines
3.2 KiB
TypeScript
90 lines
3.2 KiB
TypeScript
/**
|
|
* Speaker diarization backend trait. pyannote-3.1 reference HTTP client
|
|
* provided. Also exposes `alignSpeakers()` β pure JS that takes
|
|
* diarization segments + transcription words and produces a unified
|
|
* timeline.
|
|
*/
|
|
import type { TranscriptionSegment } from "./transcription.js";
|
|
|
|
export interface DiarizationSegment {
|
|
startSecs: number;
|
|
endSecs: number;
|
|
speaker: string;
|
|
}
|
|
|
|
export interface DiarizationRequest {
|
|
audio: Uint8Array;
|
|
/** Optional hint for expected number of speakers. */
|
|
numSpeakers?: number;
|
|
}
|
|
|
|
export interface DiarizationResponse {
|
|
segments: DiarizationSegment[];
|
|
model?: string;
|
|
}
|
|
|
|
export interface DiarizationBackend {
|
|
diarize(req: DiarizationRequest): Promise<DiarizationResponse>;
|
|
}
|
|
|
|
// ββ pyannote sidecar HTTP client ββββββββββββββββββββββββββββββββββββββ
|
|
|
|
export interface PyannoteHttpOpts {
|
|
baseUrl?: string;
|
|
fetchImpl?: typeof fetch;
|
|
}
|
|
|
|
export class PyannoteHttp implements DiarizationBackend {
|
|
private baseUrl: string;
|
|
private fetchImpl: typeof fetch;
|
|
|
|
constructor(opts: PyannoteHttpOpts = {}) {
|
|
this.baseUrl = opts.baseUrl ?? "http://pyannote:8001";
|
|
this.fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
}
|
|
|
|
async diarize(req: DiarizationRequest): Promise<DiarizationResponse> {
|
|
const form = new FormData();
|
|
form.append("file", new Blob([new Uint8Array(req.audio)]), "audio");
|
|
if (req.numSpeakers) form.append("num_speakers", String(req.numSpeakers));
|
|
const res = await this.fetchImpl(`${this.baseUrl}/diarize`, { method: "POST", body: form });
|
|
if (!res.ok) throw new Error(`pyannote: ${res.status} ${res.statusText}`);
|
|
const data = (await res.json()) as { segments: Array<{ start: number; end: number; speaker: string }> };
|
|
return {
|
|
segments: data.segments.map((s) => ({ startSecs: s.start, endSecs: s.end, speaker: s.speaker })),
|
|
model: "pyannote/speaker-diarization-3.1",
|
|
};
|
|
}
|
|
}
|
|
|
|
// ββ alignment βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
/**
|
|
* Attach a speaker label to each transcript segment by overlapping it
|
|
* against the diarization timeline. The dominant overlap wins.
|
|
*/
|
|
export function alignSpeakers(
|
|
transcript: TranscriptionSegment[],
|
|
diarization: DiarizationSegment[],
|
|
): Array<TranscriptionSegment & { speaker: string }> {
|
|
return transcript.map((t) => {
|
|
const overlaps = new Map<string, number>();
|
|
for (const d of diarization) {
|
|
const overlap = Math.max(0, Math.min(t.endSecs, d.endSecs) - Math.max(t.startSecs, d.startSecs));
|
|
if (overlap > 0) overlaps.set(d.speaker, (overlaps.get(d.speaker) ?? 0) + overlap);
|
|
}
|
|
let best = "UNKNOWN";
|
|
let bestOverlap = 0;
|
|
for (const [sp, ov] of overlaps) if (ov > bestOverlap) { bestOverlap = ov; best = sp; }
|
|
return { ...t, speaker: best };
|
|
});
|
|
}
|
|
|
|
/** Apply a user-supplied speaker label map. */
|
|
export function relabelSpeakers(
|
|
segments: Array<TranscriptionSegment & { speaker: string }>,
|
|
mapping: Record<string, string>,
|
|
): Array<TranscriptionSegment & { speaker: string }> {
|
|
return segments.map((s) => ({ ...s, speaker: mapping[s.speaker] ?? s.speaker }));
|
|
}
|