lint: fix code format
This commit is contained in:
parent
4663794ba6
commit
55aac1cb9b
@ -1,9 +1,9 @@
|
||||
import { getConfigDir } from '@main/utils/file'
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
|
||||
import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'
|
||||
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from '@modelcontextprotocol/sdk/types.js'
|
||||
import { Mutex } from 'async-mutex' // 引入 Mutex
|
||||
import { promises as fs } from 'fs'
|
||||
import path from 'path'
|
||||
import { Mutex } from 'async-mutex' // 引入 Mutex
|
||||
|
||||
// Define memory file path
|
||||
const defaultMemoryPath = path.join(getConfigDir(), 'memory.json')
|
||||
@ -62,7 +62,10 @@ class KnowledgeGraphManager {
|
||||
} catch (error) {
|
||||
console.error('Failed to ensure memory path exists:', error)
|
||||
// Propagate the error or handle it more gracefully depending on requirements
|
||||
throw new McpError(ErrorCode.InternalError, `Failed to ensure memory path: ${error instanceof Error ? error.message : String(error)}`)
|
||||
throw new McpError(
|
||||
ErrorCode.InternalError,
|
||||
`Failed to ensure memory path: ${error instanceof Error ? error.message : String(error)}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,8 +84,8 @@ class KnowledgeGraphManager {
|
||||
const graph: KnowledgeGraph = JSON.parse(data)
|
||||
this.entities.clear()
|
||||
this.relations.clear()
|
||||
graph.entities.forEach(entity => this.entities.set(entity.name, entity))
|
||||
graph.relations.forEach(relation => this.relations.add(this._serializeRelation(relation)))
|
||||
graph.entities.forEach((entity) => this.entities.set(entity.name, entity))
|
||||
graph.relations.forEach((relation) => this.relations.add(this._serializeRelation(relation)))
|
||||
} catch (error) {
|
||||
if (error instanceof Error && 'code' in error && (error as any).code === 'ENOENT') {
|
||||
// File doesn't exist (should have been created by _ensureMemoryPathExists, but handle defensively)
|
||||
@ -97,7 +100,10 @@ class KnowledgeGraphManager {
|
||||
await this._persistGraph()
|
||||
} else {
|
||||
console.error('Failed to load knowledge graph from disk:', error)
|
||||
throw new McpError(ErrorCode.InternalError, `Failed to load graph: ${error instanceof Error ? error.message : String(error)}`)
|
||||
throw new McpError(
|
||||
ErrorCode.InternalError,
|
||||
`Failed to load graph: ${error instanceof Error ? error.message : String(error)}`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -108,13 +114,16 @@ class KnowledgeGraphManager {
|
||||
try {
|
||||
const graphData: KnowledgeGraph = {
|
||||
entities: Array.from(this.entities.values()),
|
||||
relations: Array.from(this.relations).map(rStr => this._deserializeRelation(rStr))
|
||||
relations: Array.from(this.relations).map((rStr) => this._deserializeRelation(rStr))
|
||||
}
|
||||
await fs.writeFile(this.memoryPath, JSON.stringify(graphData, null, 2))
|
||||
} catch (error) {
|
||||
console.error('Failed to save knowledge graph:', error)
|
||||
// Decide how to handle write errors - potentially retry or notify
|
||||
throw new McpError(ErrorCode.InternalError, `Failed to save graph: ${error instanceof Error ? error.message : String(error)}`)
|
||||
throw new McpError(
|
||||
ErrorCode.InternalError,
|
||||
`Failed to save graph: ${error instanceof Error ? error.message : String(error)}`
|
||||
)
|
||||
} finally {
|
||||
release()
|
||||
}
|
||||
@ -133,10 +142,10 @@ class KnowledgeGraphManager {
|
||||
|
||||
async createEntities(entities: Entity[]): Promise<Entity[]> {
|
||||
const newEntities: Entity[] = []
|
||||
entities.forEach(entity => {
|
||||
entities.forEach((entity) => {
|
||||
if (!this.entities.has(entity.name)) {
|
||||
// Ensure observations is always an array
|
||||
const newEntity = { ...entity, observations: Array.isArray(entity.observations) ? entity.observations : [] };
|
||||
const newEntity = { ...entity, observations: Array.isArray(entity.observations) ? entity.observations : [] }
|
||||
this.entities.set(entity.name, newEntity)
|
||||
newEntities.push(newEntity)
|
||||
}
|
||||
@ -149,11 +158,11 @@ class KnowledgeGraphManager {
|
||||
|
||||
async createRelations(relations: Relation[]): Promise<Relation[]> {
|
||||
const newRelations: Relation[] = []
|
||||
relations.forEach(relation => {
|
||||
relations.forEach((relation) => {
|
||||
// Ensure related entities exist before creating a relation
|
||||
if (!this.entities.has(relation.from) || !this.entities.has(relation.to)) {
|
||||
console.warn(`Skipping relation creation: Entity not found for relation ${relation.from} -> ${relation.to}`)
|
||||
return; // Skip this relation
|
||||
return // Skip this relation
|
||||
}
|
||||
const relationStr = this._serializeRelation(relation)
|
||||
if (!this.relations.has(relationStr)) {
|
||||
@ -172,7 +181,7 @@ class KnowledgeGraphManager {
|
||||
): Promise<{ entityName: string; addedObservations: string[] }[]> {
|
||||
const results: { entityName: string; addedObservations: string[] }[] = []
|
||||
let changed = false
|
||||
observations.forEach(o => {
|
||||
observations.forEach((o) => {
|
||||
const entity = this.entities.get(o.entityName)
|
||||
if (!entity) {
|
||||
// Option 1: Throw error
|
||||
@ -183,9 +192,9 @@ class KnowledgeGraphManager {
|
||||
}
|
||||
// Ensure observations array exists
|
||||
if (!Array.isArray(entity.observations)) {
|
||||
entity.observations = [];
|
||||
entity.observations = []
|
||||
}
|
||||
const newObservations = o.contents.filter(content => !entity.observations.includes(content))
|
||||
const newObservations = o.contents.filter((content) => !entity.observations.includes(content))
|
||||
if (newObservations.length > 0) {
|
||||
entity.observations.push(...newObservations)
|
||||
results.push({ entityName: o.entityName, addedObservations: newObservations })
|
||||
@ -206,7 +215,7 @@ class KnowledgeGraphManager {
|
||||
const namesToDelete = new Set(entityNames)
|
||||
|
||||
// Delete entities
|
||||
namesToDelete.forEach(name => {
|
||||
namesToDelete.forEach((name) => {
|
||||
if (this.entities.delete(name)) {
|
||||
changed = true
|
||||
}
|
||||
@ -214,14 +223,14 @@ class KnowledgeGraphManager {
|
||||
|
||||
// Delete relations involving deleted entities
|
||||
const relationsToDelete = new Set<string>()
|
||||
this.relations.forEach(relStr => {
|
||||
this.relations.forEach((relStr) => {
|
||||
const rel = this._deserializeRelation(relStr)
|
||||
if (namesToDelete.has(rel.from) || namesToDelete.has(rel.to)) {
|
||||
relationsToDelete.add(relStr)
|
||||
}
|
||||
})
|
||||
|
||||
relationsToDelete.forEach(relStr => {
|
||||
relationsToDelete.forEach((relStr) => {
|
||||
if (this.relations.delete(relStr)) {
|
||||
changed = true
|
||||
}
|
||||
@ -234,12 +243,12 @@ class KnowledgeGraphManager {
|
||||
|
||||
async deleteObservations(deletions: { entityName: string; observations: string[] }[]): Promise<void> {
|
||||
let changed = false
|
||||
deletions.forEach(d => {
|
||||
deletions.forEach((d) => {
|
||||
const entity = this.entities.get(d.entityName)
|
||||
if (entity && Array.isArray(entity.observations)) {
|
||||
const initialLength = entity.observations.length
|
||||
const observationsToDelete = new Set(d.observations)
|
||||
entity.observations = entity.observations.filter(o => !observationsToDelete.has(o))
|
||||
entity.observations = entity.observations.filter((o) => !observationsToDelete.has(o))
|
||||
if (entity.observations.length !== initialLength) {
|
||||
changed = true
|
||||
}
|
||||
@ -252,7 +261,7 @@ class KnowledgeGraphManager {
|
||||
|
||||
async deleteRelations(relations: Relation[]): Promise<void> {
|
||||
let changed = false
|
||||
relations.forEach(rel => {
|
||||
relations.forEach((rel) => {
|
||||
const relStr = this._serializeRelation(rel)
|
||||
if (this.relations.delete(relStr)) {
|
||||
changed = true
|
||||
@ -266,27 +275,29 @@ class KnowledgeGraphManager {
|
||||
// Read the current state from memory
|
||||
async readGraph(): Promise<KnowledgeGraph> {
|
||||
// Return a deep copy to prevent external modification of the internal state
|
||||
return JSON.parse(JSON.stringify({
|
||||
return JSON.parse(
|
||||
JSON.stringify({
|
||||
entities: Array.from(this.entities.values()),
|
||||
relations: Array.from(this.relations).map(rStr => this._deserializeRelation(rStr))
|
||||
}));
|
||||
relations: Array.from(this.relations).map((rStr) => this._deserializeRelation(rStr))
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// Search operates on the in-memory graph
|
||||
async searchNodes(query: string): Promise<KnowledgeGraph> {
|
||||
const lowerCaseQuery = query.toLowerCase()
|
||||
const filteredEntities = Array.from(this.entities.values()).filter(
|
||||
e =>
|
||||
(e) =>
|
||||
e.name.toLowerCase().includes(lowerCaseQuery) ||
|
||||
e.entityType.toLowerCase().includes(lowerCaseQuery) ||
|
||||
(Array.isArray(e.observations) && e.observations.some(o => o.toLowerCase().includes(lowerCaseQuery)))
|
||||
(Array.isArray(e.observations) && e.observations.some((o) => o.toLowerCase().includes(lowerCaseQuery)))
|
||||
)
|
||||
|
||||
const filteredEntityNames = new Set(filteredEntities.map(e => e.name))
|
||||
const filteredEntityNames = new Set(filteredEntities.map((e) => e.name))
|
||||
|
||||
const filteredRelations = Array.from(this.relations)
|
||||
.map(rStr => this._deserializeRelation(rStr))
|
||||
.filter(r => filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to))
|
||||
.map((rStr) => this._deserializeRelation(rStr))
|
||||
.filter((r) => filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to))
|
||||
|
||||
return {
|
||||
entities: filteredEntities,
|
||||
@ -296,26 +307,26 @@ class KnowledgeGraphManager {
|
||||
|
||||
// Open operates on the in-memory graph
|
||||
async openNodes(names: string[]): Promise<KnowledgeGraph> {
|
||||
const nameSet = new Set(names);
|
||||
const filteredEntities = Array.from(this.entities.values()).filter(e => nameSet.has(e.name));
|
||||
const filteredEntityNames = new Set(filteredEntities.map(e => e.name));
|
||||
const nameSet = new Set(names)
|
||||
const filteredEntities = Array.from(this.entities.values()).filter((e) => nameSet.has(e.name))
|
||||
const filteredEntityNames = new Set(filteredEntities.map((e) => e.name))
|
||||
|
||||
const filteredRelations = Array.from(this.relations)
|
||||
.map(rStr => this._deserializeRelation(rStr))
|
||||
.filter(r => filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to));
|
||||
.map((rStr) => this._deserializeRelation(rStr))
|
||||
.filter((r) => filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to))
|
||||
|
||||
return {
|
||||
entities: filteredEntities,
|
||||
relations: filteredRelations
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MemoryServer {
|
||||
public server: Server
|
||||
// Hold the manager instance, initialized asynchronously
|
||||
private knowledgeGraphManager: KnowledgeGraphManager | null = null;
|
||||
private initializationPromise: Promise<void>; // To track initialization
|
||||
private knowledgeGraphManager: KnowledgeGraphManager | null = null
|
||||
private initializationPromise: Promise<void> // To track initialization
|
||||
|
||||
constructor(envPath: string = '') {
|
||||
const memoryPath = envPath
|
||||
@ -336,33 +347,32 @@ class MemoryServer {
|
||||
}
|
||||
)
|
||||
// Start initialization, but don't block constructor
|
||||
this.initializationPromise = this._initializeManager(memoryPath);
|
||||
this.setupRequestHandlers(); // Setup handlers immediately
|
||||
this.initializationPromise = this._initializeManager(memoryPath)
|
||||
this.setupRequestHandlers() // Setup handlers immediately
|
||||
}
|
||||
|
||||
// Private async method to handle manager initialization
|
||||
private async _initializeManager(memoryPath: string): Promise<void> {
|
||||
try {
|
||||
this.knowledgeGraphManager = await KnowledgeGraphManager.create(memoryPath);
|
||||
console.log("KnowledgeGraphManager initialized successfully.");
|
||||
this.knowledgeGraphManager = await KnowledgeGraphManager.create(memoryPath)
|
||||
console.log('KnowledgeGraphManager initialized successfully.')
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize KnowledgeGraphManager:", error);
|
||||
console.error('Failed to initialize KnowledgeGraphManager:', error)
|
||||
// Server might be unusable, consider how to handle this state
|
||||
// Maybe set a flag and return errors for all tool calls?
|
||||
this.knowledgeGraphManager = null; // Ensure it's null if init fails
|
||||
this.knowledgeGraphManager = null // Ensure it's null if init fails
|
||||
}
|
||||
}
|
||||
|
||||
// Ensures the manager is initialized before handling tool calls
|
||||
private async _getManager(): Promise<KnowledgeGraphManager> {
|
||||
await this.initializationPromise; // Wait for initialization to complete
|
||||
await this.initializationPromise // Wait for initialization to complete
|
||||
if (!this.knowledgeGraphManager) {
|
||||
throw new McpError(ErrorCode.InternalError, "Memory server failed to initialize. Cannot process requests.");
|
||||
throw new McpError(ErrorCode.InternalError, 'Memory server failed to initialize. Cannot process requests.')
|
||||
}
|
||||
return this.knowledgeGraphManager;
|
||||
return this.knowledgeGraphManager
|
||||
}
|
||||
|
||||
|
||||
// Setup handlers (can be called from constructor)
|
||||
setupRequestHandlers() {
|
||||
// ListTools remains largely the same, descriptions might be updated if needed
|
||||
@ -371,11 +381,11 @@ class MemoryServer {
|
||||
// Although ListTools itself doesn't *call* the manager, it implies the
|
||||
// manager is ready to handle calls for those tools.
|
||||
try {
|
||||
await this._getManager(); // Wait for initialization before confirming tools are available
|
||||
await this._getManager() // Wait for initialization before confirming tools are available
|
||||
} catch (error) {
|
||||
// If manager failed to init, maybe return an empty tool list or throw?
|
||||
console.error("Cannot list tools, manager initialization failed:", error);
|
||||
return { tools: [] }; // Return empty list if server is not ready
|
||||
console.error('Cannot list tools, manager initialization failed:', error)
|
||||
return { tools: [] } // Return empty list if server is not ready
|
||||
}
|
||||
|
||||
return {
|
||||
@ -409,7 +419,8 @@ class MemoryServer {
|
||||
},
|
||||
{
|
||||
name: 'create_relations',
|
||||
description: 'Create multiple new relations between EXISTING entities. Skips existing relations or relations with non-existent entities.',
|
||||
description:
|
||||
'Create multiple new relations between EXISTING entities. Skips existing relations or relations with non-existent entities.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@ -560,7 +571,7 @@ class MemoryServer {
|
||||
|
||||
// CallTool handler needs to await the manager and the async methods
|
||||
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
const manager = await this._getManager(); // Ensure manager is ready
|
||||
const manager = await this._getManager() // Ensure manager is ready
|
||||
const { name, arguments: args } = request.params
|
||||
|
||||
if (!args) {
|
||||
@ -573,40 +584,74 @@ class MemoryServer {
|
||||
case 'create_entities':
|
||||
// Validate args structure if necessary, though SDK might do basic validation
|
||||
if (!args.entities || !Array.isArray(args.entities)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'entities' array is required.`);
|
||||
throw new McpError(
|
||||
ErrorCode.InvalidParams,
|
||||
`Invalid arguments for ${name}: 'entities' array is required.`
|
||||
)
|
||||
}
|
||||
return {
|
||||
content: [{ type: 'text', text: JSON.stringify(await manager.createEntities(args.entities as Entity[]), null, 2) }]
|
||||
content: [
|
||||
{ type: 'text', text: JSON.stringify(await manager.createEntities(args.entities as Entity[]), null, 2) }
|
||||
]
|
||||
}
|
||||
case 'create_relations':
|
||||
if (!args.relations || !Array.isArray(args.relations)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'relations' array is required.`);
|
||||
throw new McpError(
|
||||
ErrorCode.InvalidParams,
|
||||
`Invalid arguments for ${name}: 'relations' array is required.`
|
||||
)
|
||||
}
|
||||
return {
|
||||
content: [{ type: 'text', text: JSON.stringify(await manager.createRelations(args.relations as Relation[]), null, 2) }]
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(await manager.createRelations(args.relations as Relation[]), null, 2)
|
||||
}
|
||||
]
|
||||
}
|
||||
case 'add_observations':
|
||||
if (!args.observations || !Array.isArray(args.observations)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'observations' array is required.`);
|
||||
throw new McpError(
|
||||
ErrorCode.InvalidParams,
|
||||
`Invalid arguments for ${name}: 'observations' array is required.`
|
||||
)
|
||||
}
|
||||
return {
|
||||
content: [{ type: 'text', text: JSON.stringify(await manager.addObservations(args.observations as { entityName: string; contents: string[] }[]), null, 2) }]
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(
|
||||
await manager.addObservations(args.observations as { entityName: string; contents: string[] }[]),
|
||||
null,
|
||||
2
|
||||
)
|
||||
}
|
||||
]
|
||||
}
|
||||
case 'delete_entities':
|
||||
if (!args.entityNames || !Array.isArray(args.entityNames)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'entityNames' array is required.`);
|
||||
throw new McpError(
|
||||
ErrorCode.InvalidParams,
|
||||
`Invalid arguments for ${name}: 'entityNames' array is required.`
|
||||
)
|
||||
}
|
||||
await manager.deleteEntities(args.entityNames as string[])
|
||||
return { content: [{ type: 'text', text: 'Entities deleted successfully' }] }
|
||||
case 'delete_observations':
|
||||
if (!args.deletions || !Array.isArray(args.deletions)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'deletions' array is required.`);
|
||||
throw new McpError(
|
||||
ErrorCode.InvalidParams,
|
||||
`Invalid arguments for ${name}: 'deletions' array is required.`
|
||||
)
|
||||
}
|
||||
await manager.deleteObservations(args.deletions as { entityName: string; observations: string[] }[])
|
||||
return { content: [{ type: 'text', text: 'Observations deleted successfully' }] }
|
||||
case 'delete_relations':
|
||||
if (!args.relations || !Array.isArray(args.relations)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'relations' array is required.`);
|
||||
throw new McpError(
|
||||
ErrorCode.InvalidParams,
|
||||
`Invalid arguments for ${name}: 'relations' array is required.`
|
||||
)
|
||||
}
|
||||
await manager.deleteRelations(args.relations as Relation[])
|
||||
return { content: [{ type: 'text', text: 'Relations deleted successfully' }] }
|
||||
@ -617,17 +662,21 @@ class MemoryServer {
|
||||
}
|
||||
case 'search_nodes':
|
||||
if (typeof args.query !== 'string') {
|
||||
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'query' string is required.`);
|
||||
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'query' string is required.`)
|
||||
}
|
||||
return {
|
||||
content: [{ type: 'text', text: JSON.stringify(await manager.searchNodes(args.query as string), null, 2) }]
|
||||
content: [
|
||||
{ type: 'text', text: JSON.stringify(await manager.searchNodes(args.query as string), null, 2) }
|
||||
]
|
||||
}
|
||||
case 'open_nodes':
|
||||
if (!args.names || !Array.isArray(args.names)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'names' array is required.`);
|
||||
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'names' array is required.`)
|
||||
}
|
||||
return {
|
||||
content: [{ type: 'text', text: JSON.stringify(await manager.openNodes(args.names as string[]), null, 2) }]
|
||||
content: [
|
||||
{ type: 'text', text: JSON.stringify(await manager.openNodes(args.names as string[]), null, 2) }
|
||||
]
|
||||
}
|
||||
default:
|
||||
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`)
|
||||
@ -635,11 +684,14 @@ class MemoryServer {
|
||||
} catch (error) {
|
||||
// Catch errors from manager methods (like entity not found) or other issues
|
||||
if (error instanceof McpError) {
|
||||
throw error; // Re-throw McpErrors directly
|
||||
throw error // Re-throw McpErrors directly
|
||||
}
|
||||
console.error(`Error executing tool ${name}:`, error);
|
||||
console.error(`Error executing tool ${name}:`, error)
|
||||
// Throw a generic internal error for unexpected issues
|
||||
throw new McpError(ErrorCode.InternalError, `Error executing tool ${name}: ${error instanceof Error ? error.message : String(error)}`);
|
||||
throw new McpError(
|
||||
ErrorCode.InternalError,
|
||||
`Error executing tool ${name}: ${error instanceof Error ? error.message : String(error)}`
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -324,7 +324,6 @@ const McpSettings: React.FC<Props> = ({ server }) => {
|
||||
}
|
||||
|
||||
const onToggleActive = async (active: boolean) => {
|
||||
|
||||
if (isFormChanged && active) {
|
||||
await onSave()
|
||||
return
|
||||
|
||||
@ -2,8 +2,8 @@ import '@renderer/databases'
|
||||
|
||||
import { useSettings } from '@renderer/hooks/useSettings'
|
||||
import store, { persistor, useAppDispatch } from '@renderer/store'
|
||||
import { message } from 'antd'
|
||||
import { setCustomCss } from '@renderer/store/settings'
|
||||
import { message } from 'antd'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Provider } from 'react-redux'
|
||||
import { PersistGate } from 'redux-persist/integration/react'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user