Saltar al contenido principal
Esta guía detalla cómo integrar SofIA SDK en aplicaciones Angular, incluyendo la configuración necesaria para el uso de Web Components y la implementación de callbacks.

Configuración inicial

1. Instalación del paquete

npm install @omniloy/sofia-sdk

2. Configuración del módulo

Angular requiere configuración específica para soportar Web Components:
// app.module.ts
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';

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

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

3. Configuración en standalone components (Angular 15+)

Para aplicaciones que usan 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 {
  // Implementación del componente
}

Implementación del componente

Template HTML

<!-- 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="es"
  (handle-report)="onReport($event.detail)"
  (set-is-open)="onSetIsOpen($event.detail)">
</sofia-sdk>
SofIA SDK soporta dos mecanismos de callback: eventos DOM (mostrado arriba con event binding de Angular) y callbacks de propiedad (asignando funciones directamente via ViewChild). Ambos funcionan — use el event binding de Angular por simplicidad.

Componente TypeScript

// 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;

  // Esquema JSON para configuración clínica
  templateJson = JSON.stringify({
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "clinical_notes",
    "type": "object",
    "properties": {
      "diagnosis": {
        "type": "string",
        "description": "Diagnóstico principal"
      }
    },
    "required": ["diagnosis"]
  });

  // Callback cuando se genera un reporte
  onReport(report: any) {
    console.log('Reporte recibido:', report);
    // Aquí puedes procesar el reporte generado
    // Por ejemplo, enviarlo a tu backend o EHR
  }

  // Callback cuando se cambia el estado de apertura
  onSetIsOpen(isOpen: boolean) {
    this.isOpen = isOpen;
    console.log('Estado de apertura:', isOpen);
  }

  // Método para abrir/cerrar programáticamente
  toggleSofIA() {
    this.isOpen = !this.isOpen;
  }
}

Obtener el último reporte

Use el callback set-get-last-report para acceder al último reporte generado para la sesión actual de paciente/usuario:
ngAfterViewInit() {
  const sofiaElement = document.querySelector('sofia-sdk') as any;
  if (sofiaElement) {
    sofiaElement.setGetLastReport = (getLastReportFn: () => Promise<object | undefined>) => {
      this.getLastReport = getLastReportFn;
    };
  }
}

// Después, obtener el último reporte
async fetchLastReport() {
  const report = await this.getLastReport?.();
  if (report) {
    console.log('Último reporte:', report);
  }
}
Añada debug="true" al elemento <sofia-sdk> para habilitar logging detallado en consola. Esto es útil durante el desarrollo para rastrear eventos del ciclo de vida del SDK, estado de conexión y validación de configuración.
<sofia-sdk debug="true" ...></sofia-sdk>

Actualización de datos del paciente

Actualice el contexto del paciente dinámicamente cambiando las propiedades vinculadas:
// Actualizar paciente para nueva consulta
updatePatient(newId: string, data: object) {
  this.patientId = newId;
  this.patientData = JSON.stringify(data);
}
Consulta Datos del paciente para la referencia completa de la estructura de datos.

Configuración avanzada

Interfaces TypeScript

Para mejor tipado, puede definir interfaces para los datos:
// 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>;
}

Servicio Angular

Puede encapsular la lógica de SofIA en un servicio:
// 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);

  // Observables públicos
  report$: Observable<SofiaReport | null> = this.reportSubject.asObservable();
  isOpen$: Observable<boolean> = this.isOpenSubject.asObservable();

  // Métodos para manejar reportes
  handleReport(report: SofiaReport) {
    this.reportSubject.next(report);
    // Aquí puedes añadir lógica adicional como guardar en localStorage
    // o enviar al servidor
  }

  // Métodos para manejar estado
  setIsOpen(isOpen: boolean) {
    this.isOpenSubject.next(isOpen);
  }

  // Método para generar template dinámicamente
  generateTemplate(specialty: string): string {
    const baseSchema = {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "title": `${specialty} Consultation`,
      "type": "object"
    };

    // Personalizar según especialidad
    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" }
          }
        });
    }
  }
}

Uso del servicio en componente

// 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() {
    // Suscribirse a cambios de estado
    this.sofiaService.isOpen$
      .pipe(takeUntil(this.destroy$))
      .subscribe(isOpen => {
        this.isOpen = isOpen;
      });

    // Suscribirse a reportes
    this.sofiaService.report$
      .pipe(takeUntil(this.destroy$))
      .subscribe(report => {
        if (report) {
          console.log('Nuevo reporte:', report);
          // Procesar reporte
        }
      });

    // Configurar template inicial
    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);
  }
}

Consideraciones importantes

Detección de cambios

Angular puede requerir detección manual de cambios en algunos casos:
import { ChangeDetectorRef } from '@angular/core';

constructor(private cdr: ChangeDetectorRef) {}

onReport(report: any) {
  // Procesar reporte
  this.cdr.detectChanges(); // Forzar detección si es necesario
}

Ciclo de vida de componentes

ngAfterViewInit() {
  // El Web Component está disponible después de la inicialización de la vista
  const sofiaElement = document.querySelector('sofia-sdk');
  if (sofiaElement) {
    // Configuración adicional si es necesaria
  }
}

Manejo de errores

onReport(report: any) {
  try {
    // Procesar reporte
    this.processReport(report);
  } catch (error) {
    console.error('Error procesando reporte:', error);
    // Manejar error apropiadamente
  }
}
Para la lista completa de mensajes de error y sus pasos de resolución, consulta la Referencia de errores.
Nunca exponga su apikey en el código del cliente en producción. Use un proxy en el backend para inyectar la API key del lado del servidor. Consulte la guía de instalación para más detalles.

Próximos pasos

  1. Integración con JavaScript
  2. Integración con React
  3. Referencia de propiedades requeridas
  4. Referencia de propiedades opcionales