Skip to main content
This guide details how to integrate SofIA SDK in Angular applications, including the necessary configuration for using Web Components and implementing callbacks.

Initial Setup

1. Package Installation

npm install @omniloy/sofia-sdk

2. Module Configuration

Angular requires specific configuration to support Web Components:
// app.module.ts
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';

// Import SofIA SDK
import '@omniloy/sofia-sdk';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  providers: [],
  bootstrap: [AppComponent],
  schemas: [CUSTOM_ELEMENTS_SCHEMA] // Required for Web Components
})
export class AppModule { }

3. Standalone Components Configuration (Angular 14+)

For applications using Standalone Components:
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import '@omniloy/sofia-sdk';

bootstrapApplication(AppComponent, {
  providers: []
});
// app.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  templateUrl: './app.component.html',
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppComponent {
  // Component implementation
}

Component Implementation

HTML Template

<!-- app.component.html -->
<sofia-sdk
  baseurl="https://api.omniloy.com"
  wssurl="wss://wss.omniloy.com"
  apikey="PROD_KEY"
  [attr.userid]="userId"
  [attr.patientid]="patientId"
  [attr.toolsargs]="toolsArgsJson"
  [attr.isopen]="isOpen ? 'true' : 'false'"
  (handle-report)="onReport($event.detail)"
  (set-is-open)="onSetIsOpen($event.detail)">
</sofia-sdk>

TypeScript Component

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  userId = 'angular-user-123';
  patientId = 'patient-456';
  isOpen = false;
  
  // JSON Schema for clinical configuration
  toolsArgsJson = JSON.stringify({
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "Medical Consultation",
    "type": "object",
    "properties": {
      "diagnosis": {
        "type": "string",
        "description": "Primary diagnosis"
      },
      "treatment": {
        "type": "string",
        "description": "Treatment plan"
      }
    },
    "required": ["diagnosis"]
  });

  // Callback when a report is generated
  onReport(report: any) {
    console.log('Report received:', report);
    // Here you can process the generated report
    // For example, send it to your backend or EHR
  }

  // Callback when open state changes
  onSetIsOpen(isOpen: boolean) {
    this.isOpen = isOpen;
    console.log('Open state:', isOpen);
  }

  // Method to open/close programmatically
  toggleSofIA() {
    this.isOpen = !this.isOpen;
  }
}

Advanced Configuration

TypeScript Interfaces

For better typing, you can define interfaces for the data:
// interfaces/sofia.interface.ts
export interface SofiaReport {
  diagnosis: string;
  treatment?: string;
  timestamp: string;
  userId: string;
  patientId: string;
}

export interface PatientData {
  name: string;
  age: number;
  gender: 'M' | 'F';
  history?: string;
}

Angular Service

You can encapsulate SofIA logic in a service:
// services/sofia.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { SofiaReport } from '../interfaces/sofia.interface';

@Injectable({
  providedIn: 'root'
})
export class SofiaService {
  private reportSubject = new BehaviorSubject<SofiaReport | null>(null);
  private isOpenSubject = new BehaviorSubject<boolean>(false);

  // Public observables
  report$: Observable<SofiaReport | null> = this.reportSubject.asObservable();
  isOpen$: Observable<boolean> = this.isOpenSubject.asObservable();

  // Methods to handle reports
  handleReport(report: SofiaReport) {
    this.reportSubject.next(report);
    // Here you can add additional logic like saving to localStorage
    // or sending to server
  }

  // Methods to handle state
  setIsOpen(isOpen: boolean) {
    this.isOpenSubject.next(isOpen);
  }

  // Method to generate toolsArgs dynamically
  generateToolsArgs(specialty: string): string {
    const baseSchema = {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "title": `${specialty} Consultation`,
      "type": "object"
    };

    // Customize by specialty
    switch(specialty) {
      case 'cardiology':
        return JSON.stringify({
          ...baseSchema,
          "properties": {
            "diagnosis": { "type": "string" },
            "heart_rate": { "type": "number" },
            "blood_pressure": { "type": "string" }
          }
        });
      case 'dermatology':
        return JSON.stringify({
          ...baseSchema,
          "properties": {
            "diagnosis": { "type": "string" },
            "lesion_type": { "type": "string" },
            "location": { "type": "string" }
          }
        });
      default:
        return JSON.stringify({
          ...baseSchema,
          "properties": {
            "diagnosis": { "type": "string" },
            "treatment": { "type": "string" }
          }
        });
    }
  }
}

Using the service in component

// app.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { SofiaService } from './services/sofia.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent implements OnInit, OnDestroy {
  private destroy$ = new Subject<void>();
  
  userId = 'angular-user-123';
  patientId = 'patient-456';
  isOpen = false;
  toolsArgsJson = '';

  constructor(private sofiaService: SofiaService) {}

  ngOnInit() {
    // Subscribe to state changes
    this.sofiaService.isOpen$
      .pipe(takeUntil(this.destroy$))
      .subscribe(isOpen => {
        this.isOpen = isOpen;
      });

    // Subscribe to reports
    this.sofiaService.report$
      .pipe(takeUntil(this.destroy$))
      .subscribe(report => {
        if (report) {
          console.log('New report:', report);
          // Process report
        }
      });

    // Configure initial toolsArgs
    this.toolsArgsJson = this.sofiaService.generateToolsArgs('general');
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }

  onReport(report: any) {
    this.sofiaService.handleReport(report);
  }

  onSetIsOpen(isOpen: boolean) {
    this.sofiaService.setIsOpen(isOpen);
  }
}

Important Considerations

Change Detection

Angular may require manual change detection in some cases:
import { ChangeDetectorRef } from '@angular/core';

constructor(private cdr: ChangeDetectorRef) {}

onReport(report: any) {
  // Process report
  this.cdr.detectChanges(); // Force detection if necessary
}

Component Lifecycle

ngAfterViewInit() {
  // Web Component is available after view initialization
  const sofiaElement = document.querySelector('sofia-sdk');
  if (sofiaElement) {
    // Additional configuration if necessary
  }
}

Error Handling

onReport(report: any) {
  try {
    // Process report
    this.processReport(report);
  } catch (error) {
    console.error('Error processing report:', error);
    // Handle error appropriately
  }
}

Integration with Angular Material

If you use Angular Material, you can integrate SofIA with its components:
<mat-card>
  <mat-card-header>
    <mat-card-title>Medical Consultation</mat-card-title>
    <mat-card-subtitle>Patient: {{patientId}}</mat-card-subtitle>
  </mat-card-header>
  
  <mat-card-content>
    <sofia-sdk
      [attr.apikey]="apiKey"
      [attr.userid]="userId"
      [attr.patientid]="patientId"
      [attr.toolsargs]="toolsArgsJson"
      [attr.isopen]="isOpen ? 'true' : 'false'"
      (handle-report)="onReport($event.detail)"
      (set-is-open)="onSetIsOpen($event.detail)">
    </sofia-sdk>
  </mat-card-content>
  
  <mat-card-actions>
    <button mat-button (click)="toggleSofIA()">
      {{isOpen ? 'Close' : 'Open'}} SofIA
    </button>
  </mat-card-actions>
</mat-card>
This implementation provides a solid foundation for integrating SofIA SDK in modern Angular applications, with strong typing and Angular best practices.