Customization and Extension

This document provides detailed guidance on customizing and extending Qwello to meet specific organizational needs, covering custom entity types, relationship types, external system integration, API usage, and plugin development.

Custom Entity Types

Qwello's knowledge graph system supports the creation of custom entity types to represent domain-specific concepts and objects.

Entity Type Definition

Custom entity types are defined through configuration files that specify their properties and behaviors:

// custom-entity-types.json
{
  "entityTypes": [
    {
      "id": "product",
      "name": "Product",
      "description": "A commercial product or offering",
      "color": "#00BCD4",
      "icon": "package",
      "defaultAttributes": [
        {
          "id": "productId",
          "name": "Product ID",
          "type": "string",
          "required": true
        },
        {
          "id": "category",
          "name": "Category",
          "type": "string",
          "required": false
        },
        {
          "id": "price",
          "name": "Price",
          "type": "number",
          "required": false
        },
        {
          "id": "launchDate",
          "name": "Launch Date",
          "type": "date",
          "required": false
        }
      ],
      "searchable": true,
      "displayTemplate": "{{name}} ({{attributes.productId}})"
    },
    {
      "id": "regulation",
      "name": "Regulation",
      "description": "A legal or regulatory requirement",
      "color": "#9C27B0",
      "icon": "gavel",
      "defaultAttributes": [
        {
          "id": "regulationId",
          "name": "Regulation ID",
          "type": "string",
          "required": true
        },
        {
          "id": "jurisdiction",
          "name": "Jurisdiction",
          "type": "string",
          "required": false
        },
        {
          "id": "effectiveDate",
          "name": "Effective Date",
          "type": "date",
          "required": false
        },
        {
          "id": "complianceLevel",
          "name": "Compliance Level",
          "type": "enum",
          "options": ["Mandatory", "Recommended", "Optional"],
          "required": false
        }
      ],
      "searchable": true,
      "displayTemplate": "{{name}} ({{attributes.jurisdiction}})"
    }
  ]
}

Entity Type Registration

Custom entity types must be registered with the system:

// entity-type-registry.ts
import { EntityTypeRegistry } from '@qwello/core';
import customEntityTypes from './custom-entity-types.json';

export function registerCustomEntityTypes() {
  const registry = EntityTypeRegistry.getInstance();
  
  for (const entityType of customEntityTypes.entityTypes) {
    registry.register(entityType);
  }
  
  console.log(`Registered ${customEntityTypes.entityTypes.length} custom entity types`);
}

Entity Type Usage

Custom entity types can be used throughout the system:

// Example of creating an entity with a custom type
const productEntity = {
  id: generateId(),
  type: 'product',
  name: 'Qwello Enterprise Edition',
  attributes: {
    productId: 'QWL-ENT-2025',
    category: 'Software',
    price: 1999.99,
    launchDate: '2025-01-15'
  }
};

// Example of querying entities by custom type
const products = await knowledgeGraphService.findEntities({
  type: 'product',
  attributes: {
    category: 'Software'
  }
});

Entity Type Visualization

Custom entity types can have specific visualization settings:

// visualization-config.ts
export const entityTypeVisualization = {
  product: {
    shape: 'box',
    color: {
      background: '#00BCD4',
      border: '#0097A7',
      highlight: {
        background: '#4DD0E1',
        border: '#00BCD4'
      }
    },
    font: {
      color: '#FFFFFF'
    },
    icon: {
      code: '\uf466', // Font Awesome package icon
      color: '#FFFFFF'
    }
  },
  regulation: {
    shape: 'diamond',
    color: {
      background: '#9C27B0',
      border: '#7B1FA2',
      highlight: {
        background: '#BA68C8',
        border: '#9C27B0'
      }
    },
    font: {
      color: '#FFFFFF'
    },
    icon: {
      code: '\uf52b', // Font Awesome gavel icon
      color: '#FFFFFF'
    }
  }
};

Custom Relationship Types

Qwello allows the definition of custom relationship types to represent domain-specific connections between entities.

Relationship Type Definition

Custom relationship types are defined through configuration files:

// custom-relationship-types.json
{
  "relationshipTypes": [
    {
      "id": "manufactures",
      "name": "Manufactures",
      "description": "Indicates that an organization manufactures a product",
      "color": "#FF9800",
      "validSourceTypes": ["organization"],
      "validTargetTypes": ["product"],
      "bidirectional": false,
      "inverseName": "Manufactured by",
      "defaultAttributes": [
        {
          "id": "since",
          "name": "Since",
          "type": "date",
          "required": false
        },
        {
          "id": "facility",
          "name": "Manufacturing Facility",
          "type": "string",
          "required": false
        }
      ]
    },
    {
      "id": "regulates",
      "name": "Regulates",
      "description": "Indicates that a regulation applies to an entity",
      "color": "#673AB7",
      "validSourceTypes": ["regulation"],
      "validTargetTypes": ["organization", "product", "method"],
      "bidirectional": false,
      "inverseName": "Regulated by",
      "defaultAttributes": [
        {
          "id": "complianceStatus",
          "name": "Compliance Status",
          "type": "enum",
          "options": ["Compliant", "Non-compliant", "In Progress", "Not Applicable"],
          "required": false
        },
        {
          "id": "reviewDate",
          "name": "Last Review Date",
          "type": "date",
          "required": false
        }
      ]
    }
  ]
}

Relationship Type Registration

Custom relationship types must be registered with the system:

// relationship-type-registry.ts
import { RelationshipTypeRegistry } from '@qwello/core';
import customRelationshipTypes from './custom-relationship-types.json';

export function registerCustomRelationshipTypes() {
  const registry = RelationshipTypeRegistry.getInstance();
  
  for (const relType of customRelationshipTypes.relationshipTypes) {
    registry.register(relType);
  }
  
  console.log(`Registered ${customRelationshipTypes.relationshipTypes.length} custom relationship types`);
}

Relationship Type Usage

Custom relationship types can be used to connect entities:

// Example of creating a relationship with a custom type
const manufacturingRelationship = {
  source: 'org123', // ID of an organization entity
  target: 'prod456', // ID of a product entity
  type: 'manufactures',
  attributes: {
    since: '2020-03-15',
    facility: 'Frankfurt Plant'
  }
};

// Example of querying relationships by custom type
const manufacturingRelationships = await knowledgeGraphService.findRelationships({
  type: 'manufactures',
  attributes: {
    facility: 'Frankfurt Plant'
  }
});

Integration with External Systems

Qwello can be integrated with external systems to exchange data and enhance functionality.

Integration Architecture

The integration architecture supports various integration patterns:

graph TD
    A[Qwello] --> B[Integration Layer]
    B --> C[REST API Connectors]
    B --> D[Webhook Handlers]
    B --> E[Message Queue Connectors]
    B --> F[File-Based Integrations]
    C --> G[External REST APIs]
    D --> H[Webhook Consumers]
    E --> I[Message Queues]
    F --> J[File Systems/Storage]

REST API Integration

Integrate with external REST APIs:

// external-api-connector.ts
import axios from 'axios';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class ExternalApiConnector {
  constructor(private configService: ConfigService) {}
  
  async fetchData(endpoint: string, params: any = {}): Promise<any> {
    const baseUrl = this.configService.get<string>('EXTERNAL_API_BASE_URL');
    const apiKey = this.configService.get<string>('EXTERNAL_API_KEY');
    
    try {
      const response = await axios.get(`${baseUrl}${endpoint}`, {
        params,
        headers: {
          'Authorization': `Bearer ${apiKey}`,
          'Content-Type': 'application/json'
        }
      });
      
      return response.data;
    } catch (error) {
      this.handleApiError(error);
    }
  }
  
  async postData(endpoint: string, data: any): Promise<any> {
    const baseUrl = this.configService.get<string>('EXTERNAL_API_BASE_URL');
    const apiKey = this.configService.get<string>('EXTERNAL_API_KEY');
    
    try {
      const response = await axios.post(`${baseUrl}${endpoint}`, data, {
        headers: {
          'Authorization': `Bearer ${apiKey}`,
          'Content-Type': 'application/json'
        }
      });
      
      return response.data;
    } catch (error) {
      this.handleApiError(error);
    }
  }
  
  private handleApiError(error: any): never {
    // Error handling logic
    throw new Error(`External API error: ${error.message}`);
  }
}

Webhook Integration

Set up webhook handlers to receive data from external systems:

// webhook.controller.ts
import { Controller, Post, Body, Headers, UnauthorizedException } from '@nestjs/common';
import { WebhookService } from './webhook.service';
import { ConfigService } from '@nestjs/config';
import * as crypto from 'crypto';

@Controller('webhooks')
export class WebhookController {
  constructor(
    private webhookService: WebhookService,
    private configService: ConfigService
  ) {}
  
  @Post('document-update')
  async handleDocumentUpdate(
    @Body() payload: any,
    @Headers('x-webhook-signature') signature: string
  ) {
    // Verify webhook signature
    this.verifySignature(payload, signature);
    
    // Process the webhook payload
    await this.webhookService.processDocumentUpdate(payload);
    
    return { success: true };
  }
  
  private verifySignature(payload: any, signature: string): void {
    const secret = this.configService.get<string>('WEBHOOK_SECRET');
    const hmac = crypto.createHmac('sha256', secret);
    const digest = hmac.update(JSON.stringify(payload)).digest('hex');
    
    if (signature !== digest) {
      throw new UnauthorizedException('Invalid webhook signature');
    }
  }
}

Message Queue Integration

Integrate with message queues for asynchronous communication:

// message-queue.service.ts
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as amqp from 'amqplib';

@Injectable()
export class MessageQueueService {
  private connection: amqp.Connection;
  private channel: amqp.Channel;
  
  constructor(private configService: ConfigService) {}
  
  async initialize() {
    const url = this.configService.get<string>('RABBITMQ_URL');
    this.connection = await amqp.connect(url);
    this.channel = await this.connection.createChannel();
    
    // Declare queues
    await this.channel.assertQueue('document-updates', { durable: true });
    await this.channel.assertQueue('knowledge-graph-updates', { durable: true });
    
    // Set up consumers
    this.channel.consume('document-updates', this.handleDocumentUpdate.bind(this), { noAck: false });
  }
  
  async publishMessage(queue: string, message: any) {
    await this.channel.sendToQueue(
      queue,
      Buffer.from(JSON.stringify(message)),
      { persistent: true }
    );
  }
  
  private async handleDocumentUpdate(msg: amqp.ConsumeMessage | null) {
    if (!msg) return;
    
    try {
      const content = JSON.parse(msg.content.toString());
      // Process the message
      
      // Acknowledge the message
      this.channel.ack(msg);
    } catch (error) {
      // Handle error
      this.channel.nack(msg, false, true); // Requeue the message
    }
  }
}

API Usage

Qwello provides a comprehensive API for programmatic interaction with the system.

Authentication

Authenticate with the API:

// api-client.ts
import axios from 'axios';

export class QwelloApiClient {
  private baseUrl: string;
  private apiKey: string;
  private token: string | null = null;
  
  constructor(baseUrl: string, apiKey: string) {
    this.baseUrl = baseUrl;
    this.apiKey = apiKey;
  }
  
  async authenticate() {
    const response = await axios.post(`${this.baseUrl}/api/v1/auth/token`, {
      apiKey: this.apiKey
    });
    
    this.token = response.data.token;
    return this.token;
  }
  
  async request(method: string, endpoint: string, data?: any) {
    if (!this.token) {
      await this.authenticate();
    }
    
    try {
      const response = await axios({
        method,
        url: `${this.baseUrl}/api/v1/${endpoint}`,
        data,
        headers: {
          'Authorization': `Bearer ${this.token}`,
          'Content-Type': 'application/json'
        }
      });
      
      return response.data;
    } catch (error) {
      if (error.response?.status === 401) {
        // Token expired, re-authenticate and retry
        await this.authenticate();
        return this.request(method, endpoint, data);
      }
      
      throw error;
    }
  }
  
  async get(endpoint: string) {
    return this.request('GET', endpoint);
  }
  
  async post(endpoint: string, data: any) {
    return this.request('POST', endpoint, data);
  }
  
  async put(endpoint: string, data: any) {
    return this.request('PUT', endpoint, data);
  }
  
  async delete(endpoint: string) {
    return this.request('DELETE', endpoint);
  }
}

Document Management API

Manage documents through the API:

// Example of document management API usage
const apiClient = new QwelloApiClient('https://api.qwello.example.com', 'your-api-key');

// Upload a document
const uploadResponse = await apiClient.post('documents/upload', {
  file: fileBuffer, // Base64 encoded file content
  filename: 'strategic-plan-2025.pdf',
  metadata: {
    title: 'Strategic Plan 2025',
    author: 'Executive Team',
    department: 'Strategy',
    tags: ['strategy', 'planning', '2025']
  }
});

const documentId = uploadResponse.id;

// Get document status
const documentStatus = await apiClient.get(`documents/${documentId}/status`);

// Get document knowledge graph
const knowledgeGraph = await apiClient.get(`documents/${documentId}/knowledge-graph`);

Knowledge Graph API

Interact with knowledge graphs through the API:

// Example of knowledge graph API usage
const apiClient = new QwelloApiClient('https://api.qwello.example.com', 'your-api-key');

// Get all entities of a specific type
const products = await apiClient.get('knowledge-graph/entities?type=product');

// Get entity by ID
const entity = await apiClient.get(`knowledge-graph/entities/${entityId}`);

// Create a new entity
const newEntity = await apiClient.post('knowledge-graph/entities', {
  type: 'product',
  name: 'New Product Line',
  attributes: {
    productId: 'NPL-2025',
    category: 'Hardware',
    price: 299.99
  }
});

// Create a relationship
const newRelationship = await apiClient.post('knowledge-graph/relationships', {
  source: organizationId,
  target: newEntity.id,
  type: 'manufactures',
  attributes: {
    since: '2025-01-01',
    facility: 'Main Plant'
  }
});

Plugin Development

Qwello supports plugins to extend functionality and integrate with external systems.

Plugin Architecture

The plugin architecture follows a modular design:

graph TD
    A[Qwello Core] --> B[Plugin Manager]
    B --> C[Plugin Registry]
    C --> D[Plugin 1]
    C --> E[Plugin 2]
    C --> F[Plugin 3]
    D --> G[Plugin API]
    E --> G
    F --> G
    G --> H[Qwello Services]

Plugin Structure

Plugins follow a standard structure:

my-qwello-plugin/
├── package.json
├── README.md
├── src/
│   ├── index.ts
│   ├── plugin.ts
│   ├── components/
│   │   └── CustomComponent.tsx
│   ├── services/
│   │   └── CustomService.ts
│   └── hooks/
│       └── useCustomHook.ts
└── dist/
    └── (compiled plugin)

Plugin Manifest

Each plugin includes a manifest file:

// package.json
{
  "name": "my-qwello-plugin",
  "version": "1.0.0",
  "description": "Custom plugin for Qwello",
  "main": "dist/index.js",
  "qwello": {
    "id": "my-qwello-plugin",
    "displayName": "My Custom Plugin",
    "description": "Adds custom functionality to Qwello",
    "version": "1.0.0",
    "apiVersion": "1.0",
    "entryPoint": "dist/index.js",
    "permissions": [
      "read:documents",
      "write:knowledge-graph",
      "read:knowledge-graph"
    ],
    "hooks": [
      "document:processed",
      "entity:created",
      "relationship:created"
    ],
    "components": [
      {
        "id": "custom-entity-card",
        "type": "entity-card",
        "entityTypes": ["product", "regulation"]
      },
      {
        "id": "custom-dashboard-widget",
        "type": "dashboard-widget",
        "name": "Custom Analytics"
      }
    ]
  },
  "dependencies": {
    "@qwello/plugin-sdk": "^1.0.0"
  }
}

Plugin Implementation

Implement the plugin functionality:

// src/index.ts
import { QwelloPlugin, PluginContext } from '@qwello/plugin-sdk';
import { CustomService } from './services/CustomService';
import { CustomComponent } from './components/CustomComponent';
import { useCustomHook } from './hooks/useCustomHook';

class MyQwelloPlugin implements QwelloPlugin {
  private context: PluginContext;
  private service: CustomService;
  
  async initialize(context: PluginContext): Promise<void> {
    this.context = context;
    this.service = new CustomService(context);
    
    // Register event handlers
    context.events.on('document:processed', this.handleDocumentProcessed.bind(this));
    context.events.on('entity:created', this.handleEntityCreated.bind(this));
    
    // Register components
    context.registerComponent('custom-entity-card', CustomComponent);
    context.registerComponent('custom-dashboard-widget', CustomDashboardWidget);
    
    // Register hooks
    context.registerHook('useCustomHook', useCustomHook);
    
    this.context.logger.info('MyQwelloPlugin initialized successfully');
  }
  
  private async handleDocumentProcessed(event: any): Promise<void> {
    const { documentId } = event;
    this.context.logger.info(`Document processed: ${documentId}`);
    
    // Custom logic for processed documents
    await this.service.processDocument(documentId);
  }
  
  private async handleEntityCreated(event: any): Promise<void> {
    const { entity } = event;
    
    if (entity.type === 'product') {
      // Custom logic for new product entities
      await this.service.handleNewProduct(entity);
    }
  }
  
  async shutdown(): Promise<void> {
    // Cleanup resources
    await this.service.shutdown();
    this.context.logger.info('MyQwelloPlugin shutdown successfully');
  }
}

// Export the plugin instance
export default new MyQwelloPlugin();

Custom Components

Create custom UI components:

// src/components/CustomComponent.tsx
import React, { useState, useEffect } from 'react';
import { usePluginContext, EntityCard, Button } from '@qwello/plugin-sdk';

export const CustomComponent: React.FC<{ entity: any }> = ({ entity }) => {
  const { services } = usePluginContext();
  const [additionalData, setAdditionalData] = useState<any>(null);
  
  useEffect(() => {
    const fetchData = async () => {
      if (entity.type === 'product') {
        const data = await services.customService.getProductDetails(entity.attributes.productId);
        setAdditionalData(data);
      }
    };
    
    fetchData();
  }, [entity]);
  
  const handleAction = async () => {
    await services.customService.performAction(entity.id);
  };
  
  return (
    <EntityCard entity={entity}>
      {additionalData && (
        <div className="additional-info">
          <h4>Additional Information</h4>
          <p>Sales Volume: {additionalData.salesVolume}</p>
          <p>Customer Rating: {additionalData.rating}/5</p>
          <Button onClick={handleAction}>Perform Custom Action</Button>
        </div>
      )}
    </EntityCard>
  );
};

Best Practices for Customization

When customizing and extending Qwello, follow these best practices to ensure maintainability, performance, and compatibility:

Entity Type Design

  1. Consistent Naming: Use clear, consistent naming conventions for entity types and attributes

  2. Attribute Planning: Define attributes carefully, considering search and filtering needs

  3. Color Coding: Choose visually distinct colors for different entity types

  4. Icon Selection: Select meaningful icons that represent the entity concept

  5. Documentation: Document the purpose and usage of custom entity types

Integration Development

  1. Error Handling: Implement robust error handling for all external integrations

  2. Retry Logic: Add retry mechanisms for transient failures

  3. Circuit Breaking: Implement circuit breakers to prevent cascading failures

  4. Logging: Include comprehensive logging for troubleshooting

  5. Security: Follow security best practices for authentication and data protection

Plugin Development

  1. Isolation: Ensure plugins operate in isolation without interfering with each other

  2. Performance: Optimize plugin performance to minimize impact on the core system

  3. Versioning: Maintain compatibility with the Qwello API version

  4. Testing: Thoroughly test plugins across different scenarios

  5. Documentation: Provide clear documentation for plugin installation and usage

By following these guidelines and leveraging Qwello's extensibility features, organizations can customize the platform to meet their specific needs while maintaining a robust and maintainable implementation.