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 15+)

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
  [attr.baseurl]="baseUrl"
  [attr.wssurl]="wssUrl"
  [attr.apikey]="apiKey"
  [attr.userid]="userId"
  [attr.patientid]="patientId"
  [attr.templateid]="templateId"
  [attr.template]="templateJson"
  [attr.isopen]="isOpen ? 'true' : 'false'"
  [attr.showconsentindicator]="showConsentIndicator ? 'true' : 'false'"
  language="en"
  (handle-report)="onReport($event.detail)"
  (set-is-open)="onSetIsOpen($event.detail)">
</sofia-sdk>
SofIA SDK supports two callback mechanisms: DOM events (shown above with Angular event binding) and property callbacks (assigning functions directly via ViewChild). Both work — use Angular’s event binding for simplicity.

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 {
  apiKey = 'sk-your-api-key-here';
  baseUrl = 'https://api.example.com/v1';
  wssUrl = 'wss://ws.example.com';
  userId = 'user_12345';
  patientId = 'patient_67890';
  templateId = 'clinical-notes-v1';
  isOpen = false;
  showConsentIndicator = false;

  // JSON Schema for clinical configuration
  templateJson = JSON.stringify({
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "clinical_notes",
    "type": "object",
    "properties": {
      "diagnosis": {
        "type": "string",
        "description": "Primary diagnosis"
      }
    },
    "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;
  }
}

Retrieving the last report

Use the set-get-last-report callback to access the last generated report for the current patient/user session:
ngAfterViewInit() {
  const sofiaElement = document.querySelector('sofia-sdk') as any;
  if (sofiaElement) {
    sofiaElement.setGetLastReport = (getLastReportFn: () => Promise<object | undefined>) => {
      this.getLastReport = getLastReportFn;
    };
  }
}

// Later, retrieve the last report
async fetchLastReport() {
  const report = await this.getLastReport?.();
  if (report) {
    console.log('Last report:', report);
  }
}
Add debug="true" to the <sofia-sdk> element to enable verbose console logging. This is helpful during development to trace SDK lifecycle events, connection status, and configuration validation.
<sofia-sdk debug="true" ...></sofia-sdk>

Patient data updates

Update patient context dynamically by changing the bound properties:
// Update patient for new consultation
updatePatient(newId: string, data: object) {
  this.patientId = newId;
  this.patientData = JSON.stringify(data);
}
See Patient Data for the complete data structure reference.

Advanced configuration

TypeScript interfaces

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

export interface PatientData {
  fullName?: string;
  birthDate?: string;
  phone?: string;
  address?: string;
  extraData?: Record<string, unknown>;
}

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 template dynamically
  generateTemplate(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 = 'user_12345';
  patientId = 'patient_67890';
  templateId = 'clinical-notes-v1';
  isOpen = false;
  templateJson = '';

  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 template
    this.templateJson = this.sofiaService.generateTemplate('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
  }
}
For a complete list of error messages and their resolution steps, see the Error Reference.
Never expose your apikey in client code in production. Use a backend proxy to inject the API key server-side. See the Installation guide for details.

Next steps

  1. JavaScript integration
  2. React integration
  3. Required properties reference
  4. Optional properties reference