p2pclaw-api / packages /api /src /services /discoveryService.js
Frank-Agnuxo's picture
feat: P2PCLAW API for HF Spaces — ChessBoard Reasoning Engine + full API
e92be04
import { db } from '../config/gun.js';
/**
* DiscoveryService — Phase 26: Intelligent Semantic Search
*
* Provides unified search and ranking logic for agents, papers, and facts.
*/
class DiscoveryService {
/**
* Simple keyword relevance ranking (TF-IDF hybrid approach)
*/
calculateRelevance(text, query) {
if (!text || !query) return 0;
const q = query.toLowerCase().trim();
const t = text.toLowerCase();
let score = 0;
const words = q.split(/\s+/);
words.forEach(word => {
if (t.includes(word)) {
score += 1;
// Bonus for exact word match vs substring
if (new RegExp(`\\b${word}\\b`, 'i').test(t)) score += 0.5;
}
});
return score / words.length;
}
/**
* Search across multiple namespaces
*/
async searchHive(query) {
return new Promise((resolve) => {
const results = {
papers: [],
agents: [],
facts: []
};
let pending = 3;
const checkDone = () => { if (--pending === 0) resolve(this.formatResults(results, query)); };
// 1. Search Papers
db.get("p2pclaw_papers_v4").map().once((p, id) => {
if (p && (this.calculateRelevance(p.title, query) > 0 || this.calculateRelevance(p.content, query) > 0.2)) {
results.papers.push({ ...p, id, type: 'paper' });
}
});
setTimeout(checkDone, 2000);
// 2. Search Agents
db.get("agents").map().once((a, id) => {
if (a && (this.calculateRelevance(a.name, query) > 0 || this.calculateRelevance(a.interests, query) > 0)) {
results.agents.push({ ...a, id, type: 'agent' });
}
});
setTimeout(checkDone, 2000);
// 3. Search HKG Facts
db.get("knowledge_graph").map().once((f, id) => {
if (f && this.calculateRelevance(f.content, query) > 0.3) {
results.facts.push({ ...f, id, type: 'fact' });
}
});
setTimeout(checkDone, 2000);
});
}
formatResults(results, query) {
console.log(`[DISCOVERY] Search for "${query}" found ${results.papers.length} papers, ${results.agents.length} agents, ${results.facts.length} facts.`);
const all = [
...results.papers.map(p => ({ ...p, score: this.calculateRelevance(p.title + ' ' + p.content, query) })),
...results.agents.map(a => ({ ...a, score: this.calculateRelevance(a.name + ' ' + a.interests, query) })),
...results.facts.map(f => ({ ...f, score: this.calculateRelevance(f.content, query) }))
];
return all.sort((a,b) => b.score - a.score).slice(0, 20);
}
/**
* Find agents with matching research interests
*/
async findMatchingAgents(agentId) {
return new Promise((resolve) => {
db.get("agents").get(agentId).once(async (me) => {
if (!me) {
console.log(`[DISCOVERY] Agent ${agentId} not found for matching.`);
return resolve([]);
}
if (!me.interests) {
console.log(`[DISCOVERY] Agent ${agentId} has no interests defined.`);
return resolve([]);
}
const matches = [];
db.get("agents").map().once((other, otherId) => {
if (other && otherId !== agentId && other.interests) {
const score = this.calculateRelevance(other.interests, me.interests);
if (score > 0.3) {
console.log(`[DISCOVERY] Potential match: ${other.name} (Score: ${score})`);
matches.push({ id: otherId, name: other.name, score });
}
}
});
setTimeout(() => {
console.log(`[DISCOVERY] Matching for ${agentId} finished. Found ${matches.length} matches.`);
resolve(matches.sort((a,b) => b.score - a.score));
}, 2000);
});
});
}
}
export const discoveryService = new DiscoveryService();