Skip to content

Commit aa26e68

Browse files
committed
add missing libs, add show-details-buttons for procedural memory, add more instructions regarding MIRIX_API_KEY and model api keys, fix some dashboard login issues
1 parent 2b4b6c2 commit aa26e68

File tree

20 files changed

+290
-552
lines changed

20 files changed

+290
-552
lines changed

README.md

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ docker compose up -d --pull always
2525
- Dashboard: http://localhost:5173
2626
- API: http://localhost:8531
2727

28-
**Step 2: Create an API key in the dashboard (http://localhost:5173) and set as the environmental variable.**
28+
**Step 2: Create an API key in the dashboard (http://localhost:5173) and set as the environmental variable `MIRIX_API_KEY`.**
2929

3030
**Step 3: Client (Python, `mirix-client`, https://pypi.org/project/mirix-client/):**
3131
```
@@ -41,18 +41,49 @@ client = MirixClient(
4141
base_url="http://localhost:8531",
4242
)
4343

44-
meta = client.initialize_meta_agent(
45-
config={"llm_config": {"model": "gemini-2.0-flash"}}
44+
client.initialize_meta_agent(
45+
config={
46+
"llm_config": {
47+
"model": "gemini-2.0-flash",
48+
"model_endpoint_type": "google_ai",
49+
"api_key": "your-api-key-here",
50+
"model_endpoint": "https://generativelanguage.googleapis.com",
51+
"context_window": 1_000_000,
52+
},
53+
"embedding_config": {
54+
"embedding_model": "text-embedding-004",
55+
"embedding_endpoint_type": "google_ai",
56+
"api_key": "your-api-key-here",
57+
"embedding_endpoint": "https://generativelanguage.googleapis.com",
58+
"embedding_dim": 768,
59+
},
60+
"meta_agent_config": {
61+
"agents": [
62+
{
63+
"core_memory_agent": {
64+
"blocks": [
65+
{"label": "human", "value": ""},
66+
{"label": "persona", "value": "I am a helpful assistant."},
67+
]
68+
}
69+
},
70+
"resource_memory_agent",
71+
"semantic_memory_agent",
72+
"episodic_memory_agent",
73+
"procedural_memory_agent",
74+
"knowledge_vault_memory_agent",
75+
],
76+
},
77+
}
4678
)
4779

48-
resp = client.add(
80+
client.add(
4981
user_id="demo-user",
5082
messages=[
5183
{"role": "user", "content": [{"type": "text", "text": "The moon now has a president."}]},
5284
{"role": "assistant", "content": [{"type": "text", "text": "Noted."}]},
5385
],
5486
)
55-
print(resp)
5687

5788
memories = client.retrieve_with_conversation(
5889
user_id="demo-user",
@@ -63,8 +94,7 @@ memories = client.retrieve_with_conversation(
6394
)
6495
print(memories)
6596
```
66-
For more API examples, see `samples/remote_client_example.py`.
67-
97+
For more API examples, see `samples/run_client.py`.
6898

6999
## License
70100

dashboard/src/api/client.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ apiClient.interceptors.request.use((config) => {
1818
apiClient.interceptors.response.use(
1919
(response) => response,
2020
(error) => {
21-
if (error.response?.status === 401) {
21+
const requestUrl = error.response?.config?.url;
22+
const isAuthPath = requestUrl === '/admin/auth/login' || requestUrl === '/admin/auth/register';
23+
24+
if (error.response?.status === 401 && !isAuthPath) {
2225
localStorage.removeItem('token');
2326
window.location.href = '/login';
2427
}
@@ -27,4 +30,3 @@ apiClient.interceptors.response.use(
2730
);
2831

2932
export default apiClient;
30-

dashboard/src/pages/Login.tsx

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useState } from 'react';
2+
import axios from 'axios';
23
import { Link } from 'react-router-dom';
34
import { useAuth } from '@/contexts/AuthContext';
45
import apiClient from '@/api/client';
@@ -8,6 +9,22 @@ import { Label } from '@/components/ui/label';
89
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
910
import logoImg from '@/assets/logo.png';
1011

12+
const parseErrorDetail = (detail: unknown, fallback: string) => {
13+
if (typeof detail === 'string') return detail;
14+
if (Array.isArray(detail)) {
15+
const messages = detail
16+
.map((item: any) => {
17+
const field = Array.isArray(item?.loc) ? item.loc[item.loc.length - 1] : undefined;
18+
const msg = item?.msg || item?.message;
19+
if (!msg) return null;
20+
return field ? `${field}: ${msg}` : msg;
21+
})
22+
.filter(Boolean);
23+
if (messages.length) return messages.join(', ');
24+
}
25+
return fallback;
26+
};
27+
1128
export const Login: React.FC = () => {
1229
const [email, setEmail] = useState('');
1330
const [password, setPassword] = useState('');
@@ -28,8 +45,18 @@ export const Login: React.FC = () => {
2845

2946
const { access_token, client } = response.data;
3047
login(access_token, client);
31-
} catch (err: any) {
32-
setError(err.response?.data?.detail || 'Failed to login');
48+
} catch (err) {
49+
if (axios.isAxiosError(err)) {
50+
if (err.response?.status === 404) {
51+
setError(parseErrorDetail(err.response?.data?.detail, 'Account does not exist. Please create an account.'));
52+
} else if (err.response?.status === 401) {
53+
setError(parseErrorDetail(err.response?.data?.detail, 'Invalid credentials. Please try again.'));
54+
} else {
55+
setError(parseErrorDetail(err.response?.data?.detail, 'Failed to login'));
56+
}
57+
} else {
58+
setError('Failed to login');
59+
}
3360
} finally {
3461
setLoading(false);
3562
}
@@ -88,4 +115,3 @@ export const Login: React.FC = () => {
88115
</div>
89116
);
90117
};
91-

dashboard/src/pages/Register.tsx

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useState } from 'react';
2+
import axios from 'axios';
23
import { Link } from 'react-router-dom';
34
import { useAuth } from '@/contexts/AuthContext';
45
import apiClient from '@/api/client';
@@ -8,6 +9,22 @@ import { Label } from '@/components/ui/label';
89
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
910
import logoImg from '@/assets/logo.png';
1011

12+
const parseErrorDetail = (detail: unknown, fallback: string) => {
13+
if (typeof detail === 'string') return detail;
14+
if (Array.isArray(detail)) {
15+
const messages = detail
16+
.map((item: any) => {
17+
const field = Array.isArray(item?.loc) ? item.loc[item.loc.length - 1] : undefined;
18+
const msg = item?.msg || item?.message;
19+
if (!msg) return null;
20+
return field ? `${field}: ${msg}` : msg;
21+
})
22+
.filter(Boolean);
23+
if (messages.length) return messages.join(', ');
24+
}
25+
return fallback;
26+
};
27+
1128
export const Register: React.FC = () => {
1229
const [name, setName] = useState('');
1330
const [email, setEmail] = useState('');
@@ -30,8 +47,13 @@ export const Register: React.FC = () => {
3047

3148
const { access_token, client } = response.data;
3249
login(access_token, client);
33-
} catch (err: any) {
34-
setError(err.response?.data?.detail || 'Failed to register');
50+
} catch (err) {
51+
if (axios.isAxiosError(err)) {
52+
const message = parseErrorDetail(err.response?.data?.detail, 'Failed to register');
53+
setError(message);
54+
} else {
55+
setError('Failed to register');
56+
}
3557
} finally {
3658
setLoading(false);
3759
}
@@ -80,7 +102,6 @@ export const Register: React.FC = () => {
80102
required
81103
value={password}
82104
onChange={(e) => setPassword(e.target.value)}
83-
minLength={8}
84105
/>
85106
</div>
86107
{error && <div className="text-sm text-red-500">{error}</div>}
@@ -99,4 +120,3 @@ export const Register: React.FC = () => {
99120
</div>
100121
);
101122
};
102-

dashboard/src/pages/dashboard/ApiKeys.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ export const ApiKeys: React.FC = () => {
284284

285285
{/* Success Dialog (Show created key) */}
286286
<Dialog open={successDialogOpen} onOpenChange={handleSuccessDialogClose}>
287-
<DialogContent className="sm:max-w-[500px]">
287+
<DialogContent className="sm:max-w-[560px]">
288288
<DialogHeader>
289289
<DialogTitle>Save your key</DialogTitle>
290290
<DialogDescription className="pt-2">
@@ -296,8 +296,8 @@ export const ApiKeys: React.FC = () => {
296296
</DialogHeader>
297297

298298
<div className="py-4">
299-
<div className="flex items-center gap-2 p-3 bg-muted rounded-md">
300-
<code className="flex-1 text-sm font-mono truncate">
299+
<div className="flex items-center gap-2 p-3 bg-muted rounded-md min-w-0">
300+
<code className="flex-1 min-w-0 text-sm font-mono truncate">
301301
{createdKey?.api_key}
302302
</code>
303303
<Button

dashboard/src/pages/dashboard/Memories.tsx

Lines changed: 95 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ export const Memories: React.FC = () => {
138138
const [memoryError, setMemoryError] = useState<string | null>(null);
139139
const [expandedEpisodic, setExpandedEpisodic] = useState<Record<string, boolean>>({});
140140
const [expandedSemantic, setExpandedSemantic] = useState<Record<string, boolean>>({});
141+
const [expandedProcedural, setExpandedProcedural] = useState<Record<string, boolean>>({});
142+
const [expandedResource, setExpandedResource] = useState<Record<string, boolean>>({});
143+
const [expandedKnowledge, setExpandedKnowledge] = useState<Record<string, boolean>>({});
141144
const [fieldsByType, setFieldsByType] = useState<Record<MemoryTypeFilter, string[]>>(DEFAULT_FIELDS);
142145
const prevUserIdRef = useRef<string | null>(null);
143146

@@ -237,6 +240,9 @@ export const Memories: React.FC = () => {
237240
setMemoryTab('episodic');
238241
setExpandedEpisodic({});
239242
setExpandedSemantic({});
243+
setExpandedProcedural({});
244+
setExpandedResource({});
245+
setExpandedKnowledge({});
240246
setMemoryTypeFilter('all');
241247
setSearchField('null');
242248
setSearchMethod('bm25');
@@ -254,6 +260,18 @@ export const Memories: React.FC = () => {
254260
setExpandedSemantic((prev) => ({ ...prev, [id]: !prev[id] }));
255261
};
256262

263+
const toggleProcedural = (id: string) => {
264+
setExpandedProcedural((prev) => ({ ...prev, [id]: !prev[id] }));
265+
};
266+
267+
const toggleResource = (id: string) => {
268+
setExpandedResource((prev) => ({ ...prev, [id]: !prev[id] }));
269+
};
270+
271+
const toggleKnowledge = (id: string) => {
272+
setExpandedKnowledge((prev) => ({ ...prev, [id]: !prev[id] }));
273+
};
274+
257275
// Keep search_field sensible when filters change
258276
useEffect(() => {
259277
if (memoryTypeFilter === 'all') {
@@ -429,13 +447,34 @@ export const Memories: React.FC = () => {
429447
</span>
430448
{item.created_at && <span>{formatDate(item.created_at)}</span>}
431449
</div>
432-
<div className="font-semibold text-base">{item.summary || 'No summary'}</div>
450+
<div className="flex items-start justify-between gap-2">
451+
<div className="font-semibold text-base">{item.summary || 'No summary'}</div>
452+
{item.steps && item.steps.length > 0 && (
453+
<button
454+
type="button"
455+
onClick={() => toggleProcedural(item.id)}
456+
className="flex items-center text-xs text-muted-foreground hover:text-foreground transition-colors"
457+
>
458+
{expandedProcedural[item.id] ? (
459+
<>
460+
Hide details <ChevronUp className="ml-1 h-4 w-4" />
461+
</>
462+
) : (
463+
<>
464+
Show details <ChevronDown className="ml-1 h-4 w-4" />
465+
</>
466+
)}
467+
</button>
468+
)}
469+
</div>
433470
{item.steps && item.steps.length > 0 ? (
434-
<ul className="list-decimal space-y-1 pl-5 text-sm text-muted-foreground">
435-
{item.steps.map((step, idx) => (
436-
<li key={`${item.id}-step-${idx}`}>{step}</li>
437-
))}
438-
</ul>
471+
expandedProcedural[item.id] ? (
472+
<ul className="list-decimal space-y-1 pl-5 text-sm text-muted-foreground">
473+
{item.steps.map((step, idx) => (
474+
<li key={`${item.id}-step-${idx}`}>{step}</li>
475+
))}
476+
</ul>
477+
) : null
439478
) : (
440479
<p className="text-sm text-muted-foreground">No steps recorded.</p>
441480
)}
@@ -463,10 +502,35 @@ export const Memories: React.FC = () => {
463502
{item.created_at && <span>{formatDate(item.created_at)}</span>}
464503
</div>
465504
<div className="font-semibold text-base">{item.title || 'Untitled'}</div>
466-
<p className="text-sm text-muted-foreground">{item.summary || 'No summary provided.'}</p>
467-
<p className="text-sm text-muted-foreground whitespace-pre-wrap">
468-
{item.content || 'No content stored.'}
469-
</p>
505+
<div className="flex items-start justify-between gap-2">
506+
<p className="text-sm text-muted-foreground">{item.summary || 'No summary provided.'}</p>
507+
{item.content && (
508+
<button
509+
type="button"
510+
onClick={() => toggleResource(item.id)}
511+
className="flex items-center text-xs text-muted-foreground hover:text-foreground transition-colors"
512+
>
513+
{expandedResource[item.id] ? (
514+
<>
515+
Hide details <ChevronUp className="ml-1 h-4 w-4" />
516+
</>
517+
) : (
518+
<>
519+
Show details <ChevronDown className="ml-1 h-4 w-4" />
520+
</>
521+
)}
522+
</button>
523+
)}
524+
</div>
525+
{item.content ? (
526+
expandedResource[item.id] ? (
527+
<p className="text-sm text-muted-foreground whitespace-pre-wrap">
528+
{item.content}
529+
</p>
530+
) : null
531+
) : (
532+
<p className="text-sm text-muted-foreground">No content stored.</p>
533+
)}
470534
</CardContent>
471535
</Card>
472536
))}
@@ -490,11 +554,30 @@ export const Memories: React.FC = () => {
490554
</span>
491555
{item.created_at && <span>{formatDate(item.created_at)}</span>}
492556
</div>
493-
<div className="font-semibold text-base">{item.caption || 'No caption'}</div>
557+
<div className="flex items-start justify-between gap-2">
558+
<div className="font-semibold text-base">{item.caption || 'No caption'}</div>
559+
{item.secret_value && (
560+
<button
561+
type="button"
562+
onClick={() => toggleKnowledge(item.id)}
563+
className="flex items-center text-xs text-muted-foreground hover:text-foreground transition-colors"
564+
>
565+
{expandedKnowledge[item.id] ? (
566+
<>
567+
Hide details <ChevronUp className="ml-1 h-4 w-4" />
568+
</>
569+
) : (
570+
<>
571+
Show details <ChevronDown className="ml-1 h-4 w-4" />
572+
</>
573+
)}
574+
</button>
575+
)}
576+
</div>
494577
<p className="text-sm text-muted-foreground">
495578
Source: {item.source || 'Unknown'} | Sensitivity: {item.sensitivity || 'Unspecified'}
496579
</p>
497-
{item.secret_value && (
580+
{item.secret_value && expandedKnowledge[item.id] && (
498581
<div className="rounded bg-muted px-3 py-2 text-sm font-mono break-all">
499582
{item.secret_value}
500583
</div>
@@ -715,4 +798,3 @@ export const Memories: React.FC = () => {
715798
);
716799
};
717800

718-

mirix/configs/examples/mirix_gemini.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ embedding_config:
1313
embedding_endpoint: "https://generativelanguage.googleapis.com"
1414
embedding_dim: 768
1515

16-
1716
meta_agent_config:
1817
system_prompts_folder: mirix\prompts\system\base
1918
agents:

0 commit comments

Comments
 (0)