Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4138f65
feat(memory-tree): typed pipeline-failure model + job failure columns…
May 30, 2026
d9a9e44
feat(memory-queue): fail-fast on unrecoverable embed failures (T012)
May 30, 2026
d7649e2
feat(memory-tree): skip embedding instead of zero-vectors when no pro…
May 30, 2026
7f2ebad
feat(memory-tree): honest pipeline status — degraded signal + first b…
May 30, 2026
c1f51a3
feat(intelligence): surface first-blocking-cause + degraded badges in…
May 30, 2026
bfbda53
feat(memory): one-shot pipeline doctor — RPC + agent tool + CLI (T023…
May 30, 2026
b16e53d
feat(memory-tree): summarizer falls back to cloud provider when local…
May 31, 2026
76d5cbd
feat(memory-tree): honour user OpenAI/custom embeddings + send dimens…
May 31, 2026
42bf06a
feat(memory-tree): summarizer partial success — one node's failure no…
May 31, 2026
1571785
feat(memory): auto-retry failed jobs on sync + manual retry_failed RP…
May 31, 2026
8d9be40
feat(memory): extraction coverage metric — backend + status/doctor + …
May 31, 2026
797e825
docs(about_app): register the Memory Pipeline Doctor capability (T039)
May 31, 2026
0d205f7
style(memory): cargo fmt the #002 memory-pipeline files
May 31, 2026
fea5394
fix(memory): address CodeRabbit review on #002 pipeline hardening
May 31, 2026
0b5f355
test(memory): lock FR-015 OpenAI routing at the factory level
May 31, 2026
6915e48
fix(memory): address 2nd CodeRabbit review on #002 pipeline hardening
May 31, 2026
286e639
chore(memory): complete rebase onto upstream/main (#002 pipeline hard…
May 31, 2026
8bbc677
fix(memory): log ancillary status-query failures before dropping them
May 31, 2026
192dba0
test(memory): fix integration-test callers of summarizer engine signa…
May 31, 2026
6ad9b9a
fix(memory): re-embed backfill must skip, not zero-vector, when no pr…
Jun 1, 2026
9583d88
docs(memory): stop claiming an auto-retry-on-sync that doesn't exist …
Jun 1, 2026
2b77a5c
i18n(memory): translate the #002 status + remediation strings into al…
Jun 1, 2026
dc8a099
fix(memory): address sanil-23 + graycyrus review on #002 pipeline har…
Jun 1, 2026
597f2dd
fix(memory): add OPENHUMAN_MEMORY_TREE_CLOUD_SUMMARIZATION env overri…
Jun 1, 2026
c382a8d
fix(memory): only flag pipeline status 'degraded' when chunks are pre…
Jun 1, 2026
16dc173
Merge remote-tracking branch 'upstream/main' into pr/memory-pipeline-…
Jun 1, 2026
5e3391b
Merge branch 'main' into pr/3076
senamakel Jun 2, 2026
5dd43b4
Merge remote-tracking branch 'upstream/main' into pr/3076
senamakel Jun 2, 2026
b43c516
fix(i18n): localize 'Embeddings' in remediation strings across all lo…
senamakel Jun 2, 2026
d57f3b4
fix(memory): add remediation key fallback + nullable extraction_cover…
senamakel Jun 2, 2026
ddb241d
fix(config): reuse parse_env_bool for OPENHUMAN_MEMORY_TREE_CLOUD_SUM…
senamakel Jun 2, 2026
af01596
test(memory): match summarizer guard assertion to reworded provider e…
Jun 2, 2026
4dfb431
fix(memory): address CodeRabbit review (safe subset) on #002 pipeline…
Jun 2, 2026
6f8399c
Merge remote-tracking branch 'upstream/main' into pr/memory-pipeline-…
senamakel Jun 2, 2026
8a41bed
fix(memory): address remaining CodeRabbit review feedback (#3076)
senamakel Jun 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions app/src/components/intelligence/MemoryTreeStatusPanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,69 @@ describe('<MemoryTreeStatusPanel />', () => {
);
});
});

// ── #002 (T018): degraded status + first-blocking-cause banner ──────────

it('renders the first-blocking-cause remediation banner with a degraded recall badge', async () => {
mockPipelineStatus.mockResolvedValueOnce(
payload({
status: 'degraded',
reason: 'semantic recall disabled',
first_blocking_cause: {
code: 'embeddings_unconfigured',
class: 'unrecoverable',
remediation_key: 'memory.health.remediation.embeddings_unconfigured',
},
degraded: { semantic_recall: true, structure: false },
})
);
render(<MemoryTreeStatusPanel />);

await waitFor(() => {
expect(screen.getByTestId('memory-tree-status-label')).toHaveTextContent(/degraded/i);
});

// The remediation text comes from the i18n key the core supplied.
const remediation = screen.getByTestId('memory-tree-blocking-cause-remediation');
expect(remediation).toHaveTextContent(/embeddings provider is configured/i);
// Recall badge present, structure badge absent.
expect(screen.getByTestId('memory-tree-badge-recall')).toBeInTheDocument();
expect(screen.queryByTestId('memory-tree-badge-structure')).not.toBeInTheDocument();
});

it('shows the structure badge when only extraction is degraded', async () => {
mockPipelineStatus.mockResolvedValueOnce(
payload({
status: 'degraded',
first_blocking_cause: {
code: 'extraction_timeout',
class: 'unrecoverable',
remediation_key: 'memory.health.remediation.extraction_timeout',
},
degraded: { semantic_recall: false, structure: true },
})
);
render(<MemoryTreeStatusPanel />);

await waitFor(() => {
expect(screen.getByTestId('memory-tree-blocking-cause')).toBeInTheDocument();
});
expect(screen.getByTestId('memory-tree-badge-structure')).toBeInTheDocument();
expect(screen.queryByTestId('memory-tree-badge-recall')).not.toBeInTheDocument();
expect(screen.getByTestId('memory-tree-blocking-cause-remediation')).toHaveTextContent(
/extraction model is timing out/i
);
});

it('does not render the blocking-cause banner on a healthy pipeline', async () => {
mockPipelineStatus.mockResolvedValueOnce(payload({ status: 'running' }));
render(<MemoryTreeStatusPanel />);

await waitFor(() => {
expect(screen.getByTestId('memory-tree-status-label')).toHaveTextContent(/running/i);
});
expect(screen.queryByTestId('memory-tree-blocking-cause')).not.toBeInTheDocument();
});
});

describe('integration health helpers', () => {
Expand Down
57 changes: 57 additions & 0 deletions app/src/components/intelligence/MemoryTreeStatusPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ function statusDotClass(kind: MemoryTreePipelineStatus['status']): string {
return 'bg-stone-400 dark:bg-neutral-500';
case 'error':
return 'bg-coral-500';
case 'degraded':
// Amber: the pipeline is running but recall/structure is reduced.
return 'bg-amber-500';
case 'idle':
default:
return 'bg-stone-400 dark:bg-neutral-500';
Expand Down Expand Up @@ -360,12 +363,21 @@ export function MemoryTreeStatusPanel({ onToast }: MemoryTreeStatusPanelProps) {
return t('memoryTree.status.statusSyncing');
case 'error':
return t('memoryTree.status.statusError');
case 'degraded':
return t('memoryTree.status.statusDegraded');
case 'idle':
default:
return t('memoryTree.status.statusIdle');
}
})();

// #002 (FR-004): the single first blocking cause, rendered verbatim with a
// localized remediation. Prefer the explicit `first_blocking_cause`; fall
// back to the active degradation cause so older payload shapes still surface
// something actionable.
const blockingCause = status?.first_blocking_cause ?? status?.degraded?.cause ?? null;
const degraded = status?.degraded;

const checked = !(status?.is_paused ?? false);

const tileClass =
Expand Down Expand Up @@ -400,6 +412,37 @@ export function MemoryTreeStatusPanel({ onToast }: MemoryTreeStatusPanelProps) {
</div>
) : null}

{/* #002 (FR-004): actionable first-blocking-cause banner. Shown when the
core reports a typed cause — names the problem + the fix instead of a
generic "error". Degraded badges below distinguish recall vs structure. */}
{!loading && blockingCause ? (
<div
className="rounded-lg border border-amber-200 dark:border-amber-500/30 bg-amber-50 dark:bg-amber-500/10 px-3 py-2 text-sm text-amber-800 dark:text-amber-200"
data-testid="memory-tree-blocking-cause">
<div className="font-medium" data-testid="memory-tree-blocking-cause-remediation">
{t(blockingCause.remediation_key, t('memory.health.remediation.unknown'))}
</div>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
{degraded?.semantic_recall || degraded?.structure ? (
<div className="mt-1 flex flex-wrap gap-1.5" data-testid="memory-tree-degraded-badges">
{degraded?.semantic_recall ? (
<span
className="inline-flex items-center rounded-full bg-amber-100 dark:bg-amber-500/20 px-2 py-0.5 text-[11px] font-medium text-amber-800 dark:text-amber-200"
data-testid="memory-tree-badge-recall">
{t('memoryTree.status.degradedRecall')}
</span>
) : null}
{degraded?.structure ? (
<span
className="inline-flex items-center rounded-full bg-amber-100 dark:bg-amber-500/20 px-2 py-0.5 text-[11px] font-medium text-amber-800 dark:text-amber-200"
data-testid="memory-tree-badge-structure">
{t('memoryTree.status.degradedStructure')}
</span>
) : null}
</div>
) : null}
</div>
) : null}

<div className="grid grid-cols-2 sm:grid-cols-4 gap-3" data-testid="memory-tree-status-tiles">
{/* Status tile ── color-coded pill */}
<div className={tileClass}>
Expand Down Expand Up @@ -463,6 +506,20 @@ export function MemoryTreeStatusPanel({ onToast }: MemoryTreeStatusPanelProps) {
</div>
</div>

{/* #002 (FR-010 / US5): extraction coverage. Only meaningful once chunks
exist; near-0% with chunks present means the wiki is built but has no
structure (the extraction model is failing). */}
{!loading && status && status.total_chunks > 0 && status.extraction_coverage != null ? (
<div
className="text-xs text-stone-500 dark:text-neutral-400"
data-testid="memory-tree-extraction-coverage">
{t('memoryTree.status.extractionCoverage').replace(
'{pct}',
String(Math.round((status.extraction_coverage ?? 0) * 100))
)}
</div>
) : null}

<IntegrationHealthStrip integrations={integrations} loading={loading} t={t} />

{/* Auto-sync toggle row — markup mirrors AIPanel's inline ToggleRow */}
Expand Down
25 changes: 25 additions & 0 deletions app/src/lib/i18n/ar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4390,6 +4390,31 @@ const messages: TranslationMap = {
'keyring.settings.revokeConsent': 'رفض التخزين المحلي',
'pages.settings.account.security': 'الأمان',
'pages.settings.account.securityDesc': 'وضع تخزين الأسرار وحالة سلسلة المفاتيح',
// #002 memory-pipeline-hardening: degraded badges + typed remediation.
'memoryTree.status.statusDegraded': 'متدهور',
'memoryTree.status.degradedRecall': 'الاسترجاع الدلالي معطّل',
'memoryTree.status.degradedStructure': 'بنية الويكي غير مكتملة',
'memoryTree.status.extractionCoverage': 'تغطية الاستخراج: {pct}% من الأجزاء لها بنية',
'memory.health.remediation.budget_exhausted':
'استنفدت تضمينات الذاكرة الميزانية المُدارة. أعدّ تضمينات Ollama المحلية (الإعدادات → الذكاء الاصطناعي → التضمينات) أو أضف مفتاح API الخاص بك للتضمينات لمواصلة بناء الذاكرة.',
'memory.health.remediation.auth_missing':
'لم يتم العثور على بيانات اعتماد التضمينات. سجّل الدخول إلى OpenHuman، أو أعدّ تضمينات Ollama المحلية في الإعدادات → الذكاء الاصطناعي → التضمينات.',
'memory.health.remediation.auth_invalid':
'تم رفض بيانات اعتماد التضمينات الخاصة بك. أعد المصادقة، أو بدّل إلى تضمينات Ollama المحلية في الإعدادات → الذكاء الاصطناعي → التضمينات.',
'memory.health.remediation.embeddings_unconfigured':
'لم يتم تكوين أي مزوّد تضمينات، لذا فإن الاسترجاع الدلالي معطّل. أعدّ تضمينات Ollama المحلية (موصى به) أو أضف مفتاح تضمينات في الإعدادات → الذكاء الاصطناعي → التضمينات.',
'memory.health.remediation.embedding_dim_mismatch':
'يعيد نموذج التضمين حجم متجه خاطئًا (تتوقع الذاكرة 1024 بُعدًا). اختر نموذجًا بـ 1024 بُعدًا، أو اطلب 1024 بُعدًا من مزوّدك.',
'memory.health.remediation.local_model_unavailable':
'نموذج محلي مطلوب غير متوفر. ثبّت/شغّل Ollama ونزّل النموذج، أو بدّل هذا الحِمل إلى مزوّد سحابي في الإعدادات → الذكاء الاصطناعي.',
'memory.health.remediation.extraction_timeout':
'يتجاوز نموذج استخراج الذاكرة المهلة الزمنية، لذا فإن بنية الويكي قليلة. بدّل نموذج استخراج الذاكرة إلى نموذج أسرع في الإعدادات → الذكاء الاصطناعي.',
'memory.health.remediation.summarizer_unavailable':
'لا يتوفر مزوّد تلخيص لميزة إنشاء أشجار التلخيص. فعّل الذكاء الاصطناعي المحلي (Ollama)، أو فعّل تلخيص السحابة في الإعدادات → الذكاء الاصطناعي → الذاكرة.',
'memory.health.remediation.transient':
'حدث خطأ مؤقت أدى إلى مقاطعة معالجة الذاكرة. ستتم إعادة المحاولة تلقائيًا.',
'memory.health.remediation.unknown':
'واجهت معالجة الذاكرة مشكلة. تحقق من الإعدادات → الذكاء الاصطناعي للتكوين.',
// Chat — agent-generated artifacts (#2779)
'chat.artifact.aria': 'الملف: {title}',
'chat.artifact.generating': 'جارٍ إنشاء {kind}…',
Expand Down
25 changes: 25 additions & 0 deletions app/src/lib/i18n/bn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4468,6 +4468,31 @@ const messages: TranslationMap = {
'keyring.settings.revokeConsent': 'স্থানীয় সঞ্চয়স্থান প্রত্যাখ্যান করুন',
'pages.settings.account.security': 'নিরাপত্তা',
'pages.settings.account.securityDesc': 'গোপনীয়তা সঞ্চয়স্থান মোড এবং কিচেন অবস্থা',
// #002 memory-pipeline-hardening: degraded badges + typed remediation.
'memoryTree.status.statusDegraded': 'অবনমিত',
'memoryTree.status.degradedRecall': 'সিম্যান্টিক রিকল নিষ্ক্রিয়',
'memoryTree.status.degradedStructure': 'উইকি কাঠামো অসম্পূর্ণ',
'memoryTree.status.extractionCoverage': 'এক্সট্র্যাকশন কভারেজ: {pct}% অংশের কাঠামো আছে',
'memory.health.remediation.budget_exhausted':
'মেমরি এমবেডিং পরিচালিত বাজেটে পৌঁছেছে। স্থানীয় Ollama এমবেডিং সেট আপ করুন (সেটিংস → AI → এমবেডিংস) অথবা মেমরি তৈরি চালিয়ে যেতে আপনার নিজস্ব এমবেডিং API কী যোগ করুন।',
'memory.health.remediation.auth_missing':
'কোনও এমবেডিং শংসাপত্র পাওয়া যায়নি। OpenHuman-এ লগ ইন করুন, অথবা সেটিংস → AI → এমবেডিংস-এ স্থানীয় Ollama এমবেডিং সেট আপ করুন।',
'memory.health.remediation.auth_invalid':
'আপনার এমবেডিং শংসাপত্র প্রত্যাখ্যাত হয়েছে। পুনরায় প্রমাণীকরণ করুন, অথবা সেটিংস → AI → এমবেডিংস-এ স্থানীয় Ollama এমবেডিং-এ স্যুইচ করুন।',
'memory.health.remediation.embeddings_unconfigured':
'কোনও এমবেডিং প্রদানকারী কনফিগার করা নেই, তাই সিম্যান্টিক রিকল বন্ধ। স্থানীয় Ollama এমবেডিং সেট আপ করুন (প্রস্তাবিত) অথবা সেটিংস → AI → এমবেডিংস-এ একটি এমবেডিং কী যোগ করুন।',
'memory.health.remediation.embedding_dim_mismatch':
'এমবেডিং মডেল ভুল ভেক্টর আকার ফেরত দেয় (মেমরি 1024 মাত্রা প্রত্যাশা করে)। 1024-মাত্রার একটি মডেল বেছে নিন, অথবা আপনার প্রদানকারীর কাছে 1024 মাত্রা অনুরোধ করুন।',
'memory.health.remediation.local_model_unavailable':
'একটি প্রয়োজনীয় স্থানীয় মডেল উপলব্ধ নেই। Ollama ইনস্টল/চালু করুন এবং মডেলটি ডাউনলোড করুন, অথবা সেটিংস → AI-তে এই কাজের চাপ একটি ক্লাউড প্রদানকারীতে স্যুইচ করুন।',
'memory.health.remediation.extraction_timeout':
'মেমরি এক্সট্র্যাকশন মডেল টাইম আউট হচ্ছে, তাই উইকিতে সামান্য কাঠামো আছে। সেটিংস → AI-তে মেমরি এক্সট্র্যাকশন মডেল একটি দ্রুততর মডেলে পরিবর্তন করুন।',
'memory.health.remediation.summarizer_unavailable':
'সারাংশ ট্রি তৈরির জন্য কোনও সারাংশ প্রদানকারী উপলব্ধ নেই। স্থানীয় AI (Ollama) সক্ষম করুন, অথবা সেটিংস → AI → মেমরিতে ক্লাউড সারাংশ সক্ষম করুন।',
'memory.health.remediation.transient':
'একটি অস্থায়ী ত্রুটি মেমরি প্রক্রিয়াকরণে বাধা দিয়েছে। স্বয়ংক্রিয়ভাবে পুনরায় চেষ্টা করা হবে।',
'memory.health.remediation.unknown':
'মেমরি প্রক্রিয়াকরণে একটি সমস্যা হয়েছে। কনফিগারেশনের জন্য সেটিংস → AI পরীক্ষা করুন।',
// Chat — agent-generated artifacts (#2779)
'chat.artifact.aria': 'আর্টিফ্যাক্ট: {title}',
'chat.artifact.generating': '{kind} তৈরি হচ্ছে…',
Expand Down
26 changes: 26 additions & 0 deletions app/src/lib/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4585,6 +4585,32 @@ const messages: TranslationMap = {
'keyring.settings.revokeConsent': 'Lokalen Speicher ablehnen',
'pages.settings.account.security': 'Sicherheit',
'pages.settings.account.securityDesc': 'Geheimnisspeicher-Modus und Schlüsselbund-Status',
// #002 memory-pipeline-hardening: degraded badges + typed remediation.
'memoryTree.status.statusDegraded': 'Eingeschränkt',
'memoryTree.status.degradedRecall': 'Semantische Suche deaktiviert',
'memoryTree.status.degradedStructure': 'Wiki-Struktur unvollständig',
'memoryTree.status.extractionCoverage':
'Extraktionsabdeckung: {pct}% der Abschnitte haben Struktur',
'memory.health.remediation.budget_exhausted':
'Die Speicher-Embeddings haben das verwaltete Budget erreicht. Richte lokale Ollama-Embeddings ein (Einstellungen → KI → Einbettungen) oder füge deinen eigenen Embeddings-API-Schlüssel hinzu, um den Speicher weiter aufzubauen.',
'memory.health.remediation.auth_missing':
'Keine Embeddings-Anmeldedaten gefunden. Melde dich bei OpenHuman an oder richte lokale Ollama-Embeddings unter Einstellungen → KI → Einbettungen ein.',
'memory.health.remediation.auth_invalid':
'Deine Embeddings-Anmeldedaten wurden abgelehnt. Authentifiziere dich erneut oder wechsle unter Einstellungen → KI → Einbettungen zu lokalen Ollama-Embeddings.',
'memory.health.remediation.embeddings_unconfigured':
'Es ist kein Embeddings-Anbieter konfiguriert, daher ist die semantische Suche deaktiviert. Richte lokale Ollama-Embeddings ein (empfohlen) oder füge unter Einstellungen → KI → Einbettungen einen Embeddings-Schlüssel hinzu.',
'memory.health.remediation.embedding_dim_mismatch':
'Das Embedding-Modell liefert die falsche Vektorgröße (der Speicher erwartet 1024 Dimensionen). Wähle ein Modell mit 1024 Dimensionen oder fordere 1024 Dimensionen von deinem Anbieter an.',
'memory.health.remediation.local_model_unavailable':
'Ein erforderliches lokales Modell ist nicht verfügbar. Installiere/starte Ollama und lade das Modell herunter, oder wechsle diese Arbeitslast unter Einstellungen → KI zu einem Cloud-Anbieter.',
'memory.health.remediation.extraction_timeout':
'Das Modell zur Speicherextraktion überschreitet die Zeit, daher hat das Wiki wenig Struktur. Wechsle das Modell für die Speicherextraktion unter Einstellungen → KI zu einem schnelleren.',
'memory.health.remediation.summarizer_unavailable':
'Für „Zusammenfassungsbäume erstellen” ist kein Zusammenfassungsanbieter verfügbar. Aktiviere die lokale KI (Ollama) oder aktiviere die Cloud-Zusammenfassung unter Einstellungen → KI → Speicher.',
'memory.health.remediation.transient':
'Ein vorübergehender Fehler hat die Speicherverarbeitung unterbrochen. Es wird automatisch erneut versucht.',
'memory.health.remediation.unknown':
'Bei der Speicherverarbeitung ist ein Problem aufgetreten. Überprüfe Einstellungen → KI für die Konfiguration.',
// Chat — agent-generated artifacts (#2779)
'chat.artifact.aria': 'Artefakt: {title}',
'chat.artifact.generating': 'Erstelle {kind}…',
Expand Down
Loading
Loading