@duckmind/deepquark-darwin-arm64 0.9.83 → 0.9.90

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/.deepquark/skills/bundled/knowledge-graph/SKILL.md +385 -0
  2. package/.deepquark/skills/bundled/knowledge-graph/STANDARDS.md +461 -0
  3. package/.deepquark/skills/bundled/knowledge-graph/lib/cli.ts +588 -0
  4. package/.deepquark/skills/bundled/knowledge-graph/lib/config.ts +630 -0
  5. package/.deepquark/skills/bundled/knowledge-graph/lib/connection-profile.ts +629 -0
  6. package/.deepquark/skills/bundled/knowledge-graph/lib/container.ts +756 -0
  7. package/.deepquark/skills/bundled/knowledge-graph/lib/mcp-client.ts +1310 -0
  8. package/.deepquark/skills/bundled/knowledge-graph/lib/output-formatter.ts +997 -0
  9. package/.deepquark/skills/bundled/knowledge-graph/lib/token-metrics.ts +335 -0
  10. package/.deepquark/skills/bundled/knowledge-graph/lib/transformation-log.ts +137 -0
  11. package/.deepquark/skills/bundled/knowledge-graph/lib/wrapper-config.ts +113 -0
  12. package/.deepquark/skills/bundled/knowledge-graph/server/.env.example +129 -0
  13. package/.deepquark/skills/bundled/knowledge-graph/server/compare-embeddings.ts +175 -0
  14. package/.deepquark/skills/bundled/knowledge-graph/server/config-falkordb.yaml +108 -0
  15. package/.deepquark/skills/bundled/knowledge-graph/server/config-neo4j.yaml +111 -0
  16. package/.deepquark/skills/bundled/knowledge-graph/server/diagnose.ts +483 -0
  17. package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-falkordb-dev.yml +146 -0
  18. package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-falkordb.yml +151 -0
  19. package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-neo4j-dev-local.yml +161 -0
  20. package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-neo4j-dev.yml +161 -0
  21. package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-neo4j.yml +169 -0
  22. package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-production.yml +128 -0
  23. package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-test.yml +10 -0
  24. package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose.yml +84 -0
  25. package/.deepquark/skills/bundled/knowledge-graph/server/entrypoint.sh +40 -0
  26. package/.deepquark/skills/bundled/knowledge-graph/server/install.ts +2054 -0
  27. package/.deepquark/skills/bundled/knowledge-graph/server/podman-compose-falkordb.yml +78 -0
  28. package/.deepquark/skills/bundled/knowledge-graph/server/podman-compose-neo4j.yml +88 -0
  29. package/.deepquark/skills/bundled/knowledge-graph/server/podman-compose.yml +83 -0
  30. package/.deepquark/skills/bundled/knowledge-graph/server/test-all-llms-mcp.ts +387 -0
  31. package/.deepquark/skills/bundled/knowledge-graph/server/test-embedding-models.ts +201 -0
  32. package/.deepquark/skills/bundled/knowledge-graph/server/test-embedding-providers.ts +641 -0
  33. package/.deepquark/skills/bundled/knowledge-graph/server/test-graphiti-model.ts +217 -0
  34. package/.deepquark/skills/bundled/knowledge-graph/server/test-grok-correct.ts +141 -0
  35. package/.deepquark/skills/bundled/knowledge-graph/server/test-grok-llms-mcp.ts +386 -0
  36. package/.deepquark/skills/bundled/knowledge-graph/server/test-grok-models.ts +173 -0
  37. package/.deepquark/skills/bundled/knowledge-graph/server/test-llama-extraction.ts +188 -0
  38. package/.deepquark/skills/bundled/knowledge-graph/server/test-mcp-final.ts +240 -0
  39. package/.deepquark/skills/bundled/knowledge-graph/server/test-mcp-live.ts +187 -0
  40. package/.deepquark/skills/bundled/knowledge-graph/server/test-mcp-session.ts +127 -0
  41. package/.deepquark/skills/bundled/knowledge-graph/server/test-model-combinations.ts +316 -0
  42. package/.deepquark/skills/bundled/knowledge-graph/server/test-ollama-models.ts +228 -0
  43. package/.deepquark/skills/bundled/knowledge-graph/server/test-openrouter-models.ts +460 -0
  44. package/.deepquark/skills/bundled/knowledge-graph/server/test-real-life-mcp.ts +311 -0
  45. package/.deepquark/skills/bundled/knowledge-graph/server/test-search-debug.ts +199 -0
  46. package/.deepquark/skills/bundled/knowledge-graph/tools/Install.md +104 -0
  47. package/.deepquark/skills/bundled/knowledge-graph/tools/README.md +120 -0
  48. package/.deepquark/skills/bundled/knowledge-graph/tools/knowledge-cli.ts +996 -0
  49. package/.deepquark/skills/bundled/knowledge-graph/tools/server-cli.ts +531 -0
  50. package/.deepquark/skills/bundled/knowledge-graph/workflows/BulkImport.md +514 -0
  51. package/.deepquark/skills/bundled/knowledge-graph/workflows/CaptureEpisode.md +242 -0
  52. package/.deepquark/skills/bundled/knowledge-graph/workflows/ClearGraph.md +392 -0
  53. package/.deepquark/skills/bundled/knowledge-graph/workflows/GetRecent.md +352 -0
  54. package/.deepquark/skills/bundled/knowledge-graph/workflows/GetStatus.md +373 -0
  55. package/.deepquark/skills/bundled/knowledge-graph/workflows/HealthReport.md +212 -0
  56. package/.deepquark/skills/bundled/knowledge-graph/workflows/InvestigateEntity.md +142 -0
  57. package/.deepquark/skills/bundled/knowledge-graph/workflows/OntologyManagement.md +201 -0
  58. package/.deepquark/skills/bundled/knowledge-graph/workflows/RunMaintenance.md +302 -0
  59. package/.deepquark/skills/bundled/knowledge-graph/workflows/SearchByDate.md +255 -0
  60. package/.deepquark/skills/bundled/knowledge-graph/workflows/SearchFacts.md +382 -0
  61. package/.deepquark/skills/bundled/knowledge-graph/workflows/SearchKnowledge.md +374 -0
  62. package/.deepquark/skills/bundled/knowledge-graph/workflows/StixImport.md +212 -0
  63. package/bin/deepquark +0 -0
  64. package/package.json +1 -1
  65. package/.deepquark/skills/bundled/ge-payroll/SKILL.md +0 -153
  66. package/.deepquark/skills/bundled/ge-payroll/evals/evals.json +0 -23
  67. package/.deepquark/skills/bundled/ge-payroll/references/pain-points-improvements.md +0 -106
  68. package/.deepquark/skills/bundled/ge-payroll/references/process-detail.md +0 -217
  69. package/.deepquark/skills/bundled/ge-payroll/references/raci-stakeholders.md +0 -85
  70. package/.deepquark/skills/bundled/ge-payroll/references/timeline-mandays.md +0 -64
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * MCP Knowledge System Test with proper session handling
4
+ */
5
+
6
+ const MCP_URL = 'http://localhost:8000/mcp';
7
+
8
+ async function initSession(): Promise<string | null> {
9
+ const res = await fetch(MCP_URL, {
10
+ method: 'POST',
11
+ headers: { 'Content-Type': 'application/json' },
12
+ body: JSON.stringify({
13
+ jsonrpc: '2.0',
14
+ id: 1,
15
+ method: 'initialize',
16
+ params: {
17
+ protocolVersion: '2024-11-05',
18
+ capabilities: {},
19
+ clientInfo: { name: 'bun-test', version: '1.0' },
20
+ },
21
+ }),
22
+ });
23
+
24
+ // Get session ID from header or response
25
+ const sessionId = res.headers.get('mcp-session-id') || res.headers.get('x-session-id');
26
+ const text = await res.text();
27
+ console.log('Init response:', text.slice(0, 200));
28
+ console.log('Headers:', Object.fromEntries(res.headers.entries()));
29
+ return sessionId;
30
+ }
31
+
32
+ async function callTool(sessionId: string | null, tool: string, args: any) {
33
+ const headers: Record<string, string> = { 'Content-Type': 'application/json' };
34
+ if (sessionId) {
35
+ headers['mcp-session-id'] = sessionId;
36
+ headers['x-session-id'] = sessionId;
37
+ }
38
+
39
+ const start = Date.now();
40
+ const res = await fetch(MCP_URL, {
41
+ method: 'POST',
42
+ headers,
43
+ body: JSON.stringify({
44
+ jsonrpc: '2.0',
45
+ id: Date.now(),
46
+ method: 'tools/call',
47
+ params: { name: tool, arguments: args },
48
+ }),
49
+ });
50
+
51
+ const text = await res.text();
52
+ const duration = Date.now() - start;
53
+
54
+ // Parse result
55
+ let result: any = null;
56
+ for (const line of text.split('\n')) {
57
+ if (line.startsWith('data: ')) {
58
+ try {
59
+ const data = JSON.parse(line.slice(6));
60
+ if (data.result) result = data.result;
61
+ } catch {}
62
+ }
63
+ }
64
+
65
+ // Also try direct JSON
66
+ if (!result) {
67
+ try {
68
+ const json = JSON.parse(text);
69
+ result = json.result || json;
70
+ } catch {}
71
+ }
72
+
73
+ return { result, duration, raw: text, headers: Object.fromEntries(res.headers.entries()) };
74
+ }
75
+
76
+ async function main() {
77
+ console.log('═'.repeat(60));
78
+ console.log('🧪 MCP Session Test');
79
+ console.log('═'.repeat(60));
80
+
81
+ // Step 1: Initialize session
82
+ console.log('\n📋 Step 1: Initialize session');
83
+ const sessionId = await initSession();
84
+ console.log(`Session ID: ${sessionId || 'none'}`);
85
+
86
+ // Step 2: Call tools/list to see available tools
87
+ console.log('\n📋 Step 2: List available tools');
88
+ const listRes = await fetch(MCP_URL, {
89
+ method: 'POST',
90
+ headers: {
91
+ 'Content-Type': 'application/json',
92
+ ...(sessionId ? { 'mcp-session-id': sessionId } : {}),
93
+ },
94
+ body: JSON.stringify({
95
+ jsonrpc: '2.0',
96
+ id: 2,
97
+ method: 'tools/list',
98
+ params: {},
99
+ }),
100
+ });
101
+ const listText = await listRes.text();
102
+ console.log('Tools response:', listText.slice(0, 500));
103
+
104
+ // Step 3: Try add_memory with session
105
+ console.log('\n📋 Step 3: Test add_memory');
106
+ const addRes = await callTool(sessionId, 'add_memory', {
107
+ name: 'Test Episode',
108
+ episode_body: 'This is a test episode for MCP testing. John works at Acme Corp.',
109
+ source: 'text',
110
+ group_id: 'mcp-test',
111
+ });
112
+ console.log(`Duration: ${addRes.duration}ms`);
113
+ console.log('Response:', addRes.raw.slice(0, 300));
114
+ console.log('Response headers:', addRes.headers);
115
+
116
+ // Step 4: Search nodes
117
+ console.log('\n📋 Step 4: Test search_nodes');
118
+ await Bun.sleep(2000); // Wait for processing
119
+ const searchRes = await callTool(sessionId, 'search_nodes', {
120
+ query: 'Acme Corp',
121
+ max_nodes: 5,
122
+ });
123
+ console.log(`Duration: ${searchRes.duration}ms`);
124
+ console.log('Response:', searchRes.raw.slice(0, 500));
125
+ }
126
+
127
+ main();
@@ -0,0 +1,316 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Test multiple LLM model combinations via MCP
4
+ * Restarts container with different configs for each model
5
+ */
6
+
7
+ import { $ } from 'bun';
8
+
9
+ const MCP_URL = 'http://localhost:8000/mcp';
10
+ const ACCEPT = 'application/json, text/event-stream';
11
+
12
+ // Models to test (all 100% pass rate from benchmarks)
13
+ const LLM_MODELS = [
14
+ { name: 'Llama 3.1 8B', model: 'meta-llama/llama-3.1-8b-instruct', cost: 0.0145 },
15
+ { name: 'DeepSeek V3', model: 'deepseek/deepseek-chat', cost: 0.0585 },
16
+ { name: 'GPT-4o Mini', model: 'openai/gpt-4o-mini', cost: 0.129 },
17
+ { name: 'Gemini 2.0 Flash', model: 'google/gemini-2.0-flash-001', cost: 0.125 },
18
+ ];
19
+
20
+ // Get API keys from environment
21
+ const OPENROUTER_API_KEY = process.env.OPENROUTER_API_KEY;
22
+ if (!OPENROUTER_API_KEY) {
23
+ console.error('❌ OPENROUTER_API_KEY not set');
24
+ console.log('\nUsage: OPENROUTER_API_KEY=sk-or-v1-... bun test-model-combinations.ts');
25
+ process.exit(1);
26
+ }
27
+
28
+ const TEST_EPISODES = [
29
+ {
30
+ name: 'Tech Stack',
31
+ body: 'Team uses TypeScript with Bun. Sarah chose Hono for HTTP framework.',
32
+ group: 'model-test',
33
+ },
34
+ {
35
+ name: 'Partnership',
36
+ body: 'John Smith from Acme Corp met CTO Alice Chen about payment API integration.',
37
+ group: 'model-test',
38
+ },
39
+ ];
40
+
41
+ let sessionId: string | null = null;
42
+
43
+ async function mcpRequest(method: string, params: any = {}): Promise<any> {
44
+ const headers: Record<string, string> = {
45
+ 'Content-Type': 'application/json',
46
+ Accept: ACCEPT,
47
+ };
48
+ if (sessionId) headers['mcp-session-id'] = sessionId;
49
+
50
+ const res = await fetch(MCP_URL, {
51
+ method: 'POST',
52
+ headers,
53
+ body: JSON.stringify({ jsonrpc: '2.0', id: Date.now(), method, params }),
54
+ });
55
+
56
+ const newSessionId = res.headers.get('mcp-session-id');
57
+ if (newSessionId) sessionId = newSessionId;
58
+
59
+ const text = await res.text();
60
+ let result: any = null;
61
+ let error: any = null;
62
+
63
+ for (const line of text.split('\n')) {
64
+ if (line.startsWith('data: ')) {
65
+ try {
66
+ const data = JSON.parse(line.slice(6));
67
+ if (data.result) result = data.result;
68
+ if (data.error) error = data.error;
69
+ } catch {}
70
+ }
71
+ }
72
+
73
+ if (!result && !error) {
74
+ try {
75
+ const json = JSON.parse(text);
76
+ result = json.result;
77
+ error = json.error;
78
+ } catch {}
79
+ }
80
+
81
+ return { result, error, raw: text };
82
+ }
83
+
84
+ async function callTool(
85
+ name: string,
86
+ args: any
87
+ ): Promise<{ success: boolean; data: any; duration: number }> {
88
+ const start = Date.now();
89
+ const { result, error } = await mcpRequest('tools/call', { name, arguments: args });
90
+ const duration = Date.now() - start;
91
+
92
+ if (error) return { success: false, data: error, duration };
93
+
94
+ let data: any = result;
95
+ try {
96
+ if (result?.content?.[0]?.text) {
97
+ data = JSON.parse(result.content[0].text);
98
+ }
99
+ } catch {}
100
+
101
+ return { success: true, data, duration };
102
+ }
103
+
104
+ async function waitForHealth(maxWait = 60000): Promise<boolean> {
105
+ const start = Date.now();
106
+ while (Date.now() - start < maxWait) {
107
+ try {
108
+ const res = await fetch('http://localhost:8000/health');
109
+ if (res.ok) return true;
110
+ } catch {}
111
+ await Bun.sleep(1000);
112
+ }
113
+ return false;
114
+ }
115
+
116
+ async function restartWithModel(modelConfig: (typeof LLM_MODELS)[0]): Promise<boolean> {
117
+ console.log(`\n🔄 Restarting container with ${modelConfig.name}...`);
118
+
119
+ // Update .env file for OpenRouter
120
+ const envContent = `# Test configuration for ${modelConfig.name}
121
+ OPENAI_API_KEY=${OPENROUTER_API_KEY}
122
+ OPENAI_BASE_URL=https://openrouter.ai/api/v1
123
+ MODEL_NAME=${modelConfig.model}
124
+ LLM_PROVIDER=openai
125
+
126
+ # Embedder (keep Ollama mxbai)
127
+ EMBEDDER_BASE_URL=http://10.0.0.150:11434/v1
128
+ EMBEDDER_MODEL=mxbai-embed-large
129
+ EMBEDDER_DIMENSIONS=1024
130
+ EMBEDDER_PROVIDER=openai
131
+
132
+ # Neo4j
133
+ NEO4J_URI=bolt://neo4j:7687
134
+ NEO4J_USER=neo4j
135
+ NEO4J_PASSWORD=madeinozknowledge
136
+ `;
137
+
138
+ await Bun.write('../../config/.env.test', envContent);
139
+
140
+ // Restart container with test env
141
+ try {
142
+ await $`docker-compose -f docker-compose-neo4j.yml down graphiti-mcp`.quiet();
143
+ await Bun.sleep(2000);
144
+
145
+ // Start with test env file
146
+ await $`docker-compose -f docker-compose-neo4j.yml up -d graphiti-mcp`
147
+ .env({
148
+ ...process.env,
149
+ COMPOSE_ENV_FILES: '../../config/.env.test',
150
+ })
151
+ .quiet();
152
+
153
+ // Wait for health
154
+ const healthy = await waitForHealth();
155
+ if (!healthy) {
156
+ console.log(' ❌ Container failed to start');
157
+ return false;
158
+ }
159
+ console.log(' ✅ Container ready');
160
+ return true;
161
+ } catch (e: any) {
162
+ console.log(` ❌ Error: ${e.message}`);
163
+ return false;
164
+ }
165
+ }
166
+
167
+ async function testModel(modelConfig: (typeof LLM_MODELS)[0]): Promise<any> {
168
+ sessionId = null; // Reset session
169
+
170
+ // Initialize MCP session
171
+ await mcpRequest('initialize', {
172
+ protocolVersion: '2024-11-05',
173
+ capabilities: {},
174
+ clientInfo: { name: 'model-test', version: '1.0' },
175
+ });
176
+ await mcpRequest('notifications/initialized', {});
177
+
178
+ const results = {
179
+ model: modelConfig.name,
180
+ cost: modelConfig.cost,
181
+ add_memory: { success: 0, total: 0, avgMs: 0 },
182
+ search_nodes: { success: 0, total: 0, avgMs: 0, avgResults: 0 },
183
+ search_facts: { success: 0, total: 0, avgMs: 0, avgResults: 0 },
184
+ };
185
+
186
+ // Test add_memory
187
+ console.log(' 📥 Testing add_memory...');
188
+ let totalAddMs = 0;
189
+ for (const ep of TEST_EPISODES) {
190
+ const res = await callTool('add_memory', {
191
+ name: ep.name,
192
+ episode_body: ep.body,
193
+ source: 'text',
194
+ group_id: ep.group,
195
+ });
196
+ results.add_memory.total++;
197
+ if (res.success) results.add_memory.success++;
198
+ totalAddMs += res.duration;
199
+ await Bun.sleep(3000); // Wait for processing
200
+ }
201
+ results.add_memory.avgMs = Math.round(totalAddMs / TEST_EPISODES.length);
202
+
203
+ // Test search_nodes
204
+ console.log(' 🔍 Testing search_nodes...');
205
+ const nodeQueries = ['TypeScript', 'Acme Corp'];
206
+ let totalSearchMs = 0;
207
+ let totalNodes = 0;
208
+ for (const q of nodeQueries) {
209
+ const res = await callTool('search_nodes', {
210
+ query: q,
211
+ group_ids: ['model-test'],
212
+ max_nodes: 10,
213
+ });
214
+ results.search_nodes.total++;
215
+ const nodes = res.data?.nodes || [];
216
+ if (nodes.length > 0) results.search_nodes.success++;
217
+ totalSearchMs += res.duration;
218
+ totalNodes += nodes.length;
219
+ }
220
+ results.search_nodes.avgMs = Math.round(totalSearchMs / nodeQueries.length);
221
+ results.search_nodes.avgResults = Math.round(totalNodes / nodeQueries.length);
222
+
223
+ // Test search_facts
224
+ console.log(' 🔗 Testing search_facts...');
225
+ const factQueries = ['partnership', 'technology'];
226
+ let totalFactMs = 0;
227
+ let totalFacts = 0;
228
+ for (const q of factQueries) {
229
+ const res = await callTool('search_memory_facts', {
230
+ query: q,
231
+ group_ids: ['model-test'],
232
+ max_facts: 10,
233
+ });
234
+ results.search_facts.total++;
235
+ const facts = res.data?.facts || [];
236
+ if (facts.length > 0) results.search_facts.success++;
237
+ totalFactMs += res.duration;
238
+ totalFacts += facts.length;
239
+ }
240
+ results.search_facts.avgMs = Math.round(totalFactMs / factQueries.length);
241
+ results.search_facts.avgResults = Math.round(totalFacts / factQueries.length);
242
+
243
+ return results;
244
+ }
245
+
246
+ async function main() {
247
+ console.log('═'.repeat(60));
248
+ console.log('🧪 Model Combination MCP Test');
249
+ console.log('═'.repeat(60));
250
+ console.log(`\nModels to test: ${LLM_MODELS.map((m) => m.name).join(', ')}`);
251
+
252
+ const allResults: any[] = [];
253
+
254
+ for (const model of LLM_MODELS) {
255
+ console.log(`\n${'─'.repeat(60)}`);
256
+ console.log(`📋 Testing: ${model.name} (${model.model})`);
257
+ console.log(`${'─'.repeat(60)}`);
258
+
259
+ const started = await restartWithModel(model);
260
+ if (!started) {
261
+ allResults.push({ model: model.name, error: 'Failed to start' });
262
+ continue;
263
+ }
264
+
265
+ const results = await testModel(model);
266
+ allResults.push(results);
267
+
268
+ console.log(
269
+ ` ✅ add_memory: ${results.add_memory.success}/${results.add_memory.total} (${results.add_memory.avgMs}ms)`
270
+ );
271
+ console.log(
272
+ ` ✅ search_nodes: ${results.search_nodes.success}/${results.search_nodes.total} (${results.search_nodes.avgMs}ms, avg ${results.search_nodes.avgResults} results)`
273
+ );
274
+ console.log(
275
+ ` ✅ search_facts: ${results.search_facts.success}/${results.search_facts.total} (${results.search_facts.avgMs}ms, avg ${results.search_facts.avgResults} results)`
276
+ );
277
+ }
278
+
279
+ // Summary
280
+ console.log(`\n${'═'.repeat(60)}`);
281
+ console.log('📊 RESULTS SUMMARY');
282
+ console.log(`${'═'.repeat(60)}`);
283
+ console.log('\n| Model | Cost/1K | add_memory | search_nodes | search_facts |');
284
+ console.log('|-------|---------|------------|--------------|--------------|');
285
+ for (const r of allResults) {
286
+ if (r.error) {
287
+ console.log(`| ${r.model} | - | ERROR | ERROR | ERROR |`);
288
+ } else {
289
+ console.log(
290
+ `| ${r.model} | $${r.cost.toFixed(4)} | ${r.add_memory.success}/${r.add_memory.total} | ${r.search_nodes.success}/${r.search_nodes.total} | ${r.search_facts.success}/${r.search_facts.total} |`
291
+ );
292
+ }
293
+ }
294
+
295
+ // Save results
296
+ await Bun.write(
297
+ 'model-combination-results.json',
298
+ JSON.stringify(
299
+ {
300
+ results: allResults,
301
+ timestamp: new Date().toISOString(),
302
+ },
303
+ null,
304
+ 2
305
+ )
306
+ );
307
+
308
+ console.log('\n📁 Results saved to model-combination-results.json');
309
+
310
+ // Restore original config
311
+ console.log('\n🔄 Restoring original configuration...');
312
+ await $`docker-compose -f docker-compose-neo4j.yml down graphiti-mcp`.quiet();
313
+ await $`docker-compose -f docker-compose-neo4j.yml up -d graphiti-mcp`.quiet();
314
+ }
315
+
316
+ main().catch(console.error);
@@ -0,0 +1,228 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Test Ollama models for JSON output compatibility with Graphiti
4
+ * Tests entity extraction capability similar to what Graphiti requires
5
+ */
6
+
7
+ const OLLAMA_HOST = 'http://10.0.0.150:11434';
8
+
9
+ // Test prompt similar to Graphiti's entity extraction
10
+ const TEST_PROMPT = `Extract entities from this text and return ONLY valid JSON, no other text:
11
+
12
+ Text: "John Smith works at Acme Corp in New York. He met Sarah Jones yesterday to discuss the Q4 budget."
13
+
14
+ Return format:
15
+ {"entities": [{"name": "string", "type": "string"}], "relationships": [{"source": "string", "target": "string", "type": "string"}]}`;
16
+
17
+ // LLM models to test (excluding embedding models)
18
+ const MODELS = [
19
+ 'tulu3:latest',
20
+ 'Qwen3:latest',
21
+ 'Qwen3:8b',
22
+ 'qwen3-coder:latest',
23
+ 'phi4:latest',
24
+ 'phi3:medium',
25
+ 'mistral:latest',
26
+ 'mistral:instruct',
27
+ 'Llama3.2:latest',
28
+ 'llama3.1:latest',
29
+ 'gemma2:9b',
30
+ 'dolphin-mistral:7b-v2.6-dpo-laser-q8_0',
31
+ 'Deepseek-r1:latest',
32
+ 'Deepseek-r1:8b',
33
+ 'deepseek-coder-v2:latest',
34
+ 'codestral:latest',
35
+ ];
36
+
37
+ interface TestResult {
38
+ model: string;
39
+ status: 'passed' | 'failed';
40
+ entities?: number;
41
+ relationships?: number;
42
+ duration_ms?: number;
43
+ error?: string;
44
+ raw_output?: string;
45
+ }
46
+
47
+ function extractJSON(text: string): { entities: any[]; relationships: any[] } | null {
48
+ // Remove markdown code blocks
49
+ let clean = text.replace(/```json\n?/g, '').replace(/```\n?/g, '');
50
+
51
+ // Remove thinking tags (DeepSeek-r1 uses these)
52
+ clean = clean.replace(/<think>[\s\S]*?<\/think>/g, '');
53
+
54
+ // Trim whitespace
55
+ clean = clean.trim();
56
+
57
+ // Try direct parse first
58
+ try {
59
+ const obj = JSON.parse(clean);
60
+ if (obj.entities && obj.relationships) {
61
+ return obj;
62
+ }
63
+ } catch {}
64
+
65
+ // Try to find JSON object in response
66
+ const jsonMatch = clean.match(/\{[\s\S]*"entities"[\s\S]*"relationships"[\s\S]*\}/);
67
+ if (jsonMatch) {
68
+ try {
69
+ const obj = JSON.parse(jsonMatch[0]);
70
+ if (obj.entities && obj.relationships) {
71
+ return obj;
72
+ }
73
+ } catch {}
74
+ }
75
+
76
+ // Try line by line to find the JSON
77
+ const lines = clean.split('\n');
78
+ let jsonStr = '';
79
+ let inJson = false;
80
+ let braceCount = 0;
81
+
82
+ for (const line of lines) {
83
+ if (line.includes('{') && !inJson) {
84
+ inJson = true;
85
+ }
86
+ if (inJson) {
87
+ jsonStr += `${line}\n`;
88
+ braceCount += (line.match(/\{/g) || []).length;
89
+ braceCount -= (line.match(/\}/g) || []).length;
90
+ if (braceCount === 0) {
91
+ try {
92
+ const obj = JSON.parse(jsonStr.trim());
93
+ if (obj.entities && obj.relationships) {
94
+ return obj;
95
+ }
96
+ } catch {}
97
+ jsonStr = '';
98
+ inJson = false;
99
+ }
100
+ }
101
+ }
102
+
103
+ return null;
104
+ }
105
+
106
+ async function testModel(model: string): Promise<TestResult> {
107
+ console.log(`\nTesting: ${model}`);
108
+ console.log('---');
109
+
110
+ const startTime = Date.now();
111
+
112
+ try {
113
+ const controller = new AbortController();
114
+ const timeout = setTimeout(() => controller.abort(), 120000); // 2 min timeout
115
+
116
+ const response = await fetch(`${OLLAMA_HOST}/api/generate`, {
117
+ method: 'POST',
118
+ headers: { 'Content-Type': 'application/json' },
119
+ body: JSON.stringify({
120
+ model,
121
+ prompt: TEST_PROMPT,
122
+ stream: false,
123
+ options: { temperature: 0.1 },
124
+ }),
125
+ signal: controller.signal,
126
+ });
127
+
128
+ clearTimeout(timeout);
129
+
130
+ if (!response.ok) {
131
+ const error = await response.text();
132
+ console.log(` ❌ HTTP Error: ${response.status}`);
133
+ return { model, status: 'failed', error: `HTTP ${response.status}: ${error}` };
134
+ }
135
+
136
+ const data = (await response.json()) as { response?: string; error?: string };
137
+ const duration = Date.now() - startTime;
138
+
139
+ if (data.error) {
140
+ console.log(` ❌ API Error: ${data.error}`);
141
+ return { model, status: 'failed', error: data.error };
142
+ }
143
+
144
+ if (!data.response) {
145
+ console.log(' ❌ No response content');
146
+ return { model, status: 'failed', error: 'No response content' };
147
+ }
148
+
149
+ const json = extractJSON(data.response);
150
+
151
+ if (json) {
152
+ console.log(
153
+ ` ✅ VALID JSON - ${json.entities.length} entities, ${json.relationships.length} relationships (${duration}ms)`
154
+ );
155
+ return {
156
+ model,
157
+ status: 'passed',
158
+ entities: json.entities.length,
159
+ relationships: json.relationships.length,
160
+ duration_ms: duration,
161
+ };
162
+ }
163
+ console.log(' ❌ Invalid JSON structure');
164
+ console.log(` Preview: ${data.response.slice(0, 150)}...`);
165
+ return {
166
+ model,
167
+ status: 'failed',
168
+ error: 'Invalid JSON structure',
169
+ raw_output: data.response.slice(0, 300),
170
+ };
171
+ } catch (err: any) {
172
+ if (err.name === 'AbortError') {
173
+ console.log(' ❌ TIMEOUT');
174
+ return { model, status: 'failed', error: 'Timeout (>120s)' };
175
+ }
176
+ console.log(` ❌ Error: ${err.message}`);
177
+ return { model, status: 'failed', error: err.message };
178
+ }
179
+ }
180
+
181
+ async function main() {
182
+ console.log('==============================================');
183
+ console.log('Ollama Model JSON Compatibility Test');
184
+ console.log(`Testing ${MODELS.length} models for entity extraction`);
185
+ console.log('==============================================');
186
+
187
+ const results: TestResult[] = [];
188
+
189
+ for (const model of MODELS) {
190
+ const result = await testModel(model);
191
+ results.push(result);
192
+ }
193
+
194
+ const passed = results.filter((r) => r.status === 'passed');
195
+ const failed = results.filter((r) => r.status === 'failed');
196
+
197
+ console.log('\n==============================================');
198
+ console.log('RESULTS SUMMARY');
199
+ console.log('==============================================\n');
200
+
201
+ console.log(`✅ PASSED (${passed.length} models):`);
202
+ for (const r of passed) {
203
+ console.log(
204
+ ` - ${r.model} (${r.entities} entities, ${r.relationships} rels, ${r.duration_ms}ms)`
205
+ );
206
+ }
207
+
208
+ console.log(`\n❌ FAILED (${failed.length} models):`);
209
+ for (const r of failed) {
210
+ console.log(` - ${r.model} (${r.error})`);
211
+ }
212
+
213
+ // Output JSON for documentation
214
+ console.log('\n==============================================');
215
+ console.log('JSON OUTPUT (for documentation)');
216
+ console.log('==============================================');
217
+ console.log(JSON.stringify({ passed, failed }, null, 2));
218
+
219
+ // Write results to file
220
+ const outputPath = new URL('./test-results.json', import.meta.url).pathname;
221
+ await Bun.write(
222
+ outputPath,
223
+ JSON.stringify({ passed, failed, timestamp: new Date().toISOString() }, null, 2)
224
+ );
225
+ console.log(`\nResults written to: ${outputPath}`);
226
+ }
227
+
228
+ main();