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
- JavaScript integration
- React integration
- Required properties reference
- Optional properties reference