@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.
- package/.deepquark/skills/bundled/knowledge-graph/SKILL.md +385 -0
- package/.deepquark/skills/bundled/knowledge-graph/STANDARDS.md +461 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/cli.ts +588 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/config.ts +630 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/connection-profile.ts +629 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/container.ts +756 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/mcp-client.ts +1310 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/output-formatter.ts +997 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/token-metrics.ts +335 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/transformation-log.ts +137 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/wrapper-config.ts +113 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/.env.example +129 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/compare-embeddings.ts +175 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/config-falkordb.yaml +108 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/config-neo4j.yaml +111 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/diagnose.ts +483 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-falkordb-dev.yml +146 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-falkordb.yml +151 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-neo4j-dev-local.yml +161 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-neo4j-dev.yml +161 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-neo4j.yml +169 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-production.yml +128 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-test.yml +10 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose.yml +84 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/entrypoint.sh +40 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/install.ts +2054 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/podman-compose-falkordb.yml +78 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/podman-compose-neo4j.yml +88 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/podman-compose.yml +83 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-all-llms-mcp.ts +387 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-embedding-models.ts +201 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-embedding-providers.ts +641 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-graphiti-model.ts +217 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-grok-correct.ts +141 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-grok-llms-mcp.ts +386 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-grok-models.ts +173 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-llama-extraction.ts +188 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-mcp-final.ts +240 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-mcp-live.ts +187 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-mcp-session.ts +127 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-model-combinations.ts +316 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-ollama-models.ts +228 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-openrouter-models.ts +460 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-real-life-mcp.ts +311 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-search-debug.ts +199 -0
- package/.deepquark/skills/bundled/knowledge-graph/tools/Install.md +104 -0
- package/.deepquark/skills/bundled/knowledge-graph/tools/README.md +120 -0
- package/.deepquark/skills/bundled/knowledge-graph/tools/knowledge-cli.ts +996 -0
- package/.deepquark/skills/bundled/knowledge-graph/tools/server-cli.ts +531 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/BulkImport.md +514 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/CaptureEpisode.md +242 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/ClearGraph.md +392 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/GetRecent.md +352 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/GetStatus.md +373 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/HealthReport.md +212 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/InvestigateEntity.md +142 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/OntologyManagement.md +201 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/RunMaintenance.md +302 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/SearchByDate.md +255 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/SearchFacts.md +382 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/SearchKnowledge.md +374 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/StixImport.md +212 -0
- package/bin/deepquark +0 -0
- package/package.json +1 -1
- package/.deepquark/skills/bundled/ge-payroll/SKILL.md +0 -153
- package/.deepquark/skills/bundled/ge-payroll/evals/evals.json +0 -23
- package/.deepquark/skills/bundled/ge-payroll/references/pain-points-improvements.md +0 -106
- package/.deepquark/skills/bundled/ge-payroll/references/process-detail.md +0 -217
- package/.deepquark/skills/bundled/ge-payroll/references/raci-stakeholders.md +0 -85
- 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();
|