How to implement Dragon Copilot Ambient SDK

This article walks you through the steps required to construct and use Dragon Copilot Ambient SDK on Android:

  1. Prepare an access token provider function
  2. Construct the AmbientClient
  3. Start a session
  4. Start and stop recording
  5. Provide feedback

The Ambient SDK is an async Android library that makes use of suspend functions; you'll generally call these directly in a view model within an async scope.

1. Prepare an access token provider function

First, prepare an AccessTokenProvider callback function to pass your access token to the Ambient SDK. For more information, see Acquire an access token.

import com.microsoft.dragoncopilot.ambient.data.AccessTokenProvider

private val accessTokenProvider = AccessTokenProvider { onSuccess, onError ->
  EntraIdHelper.acquireToken(context = context as Activity,
    onSuccess = { token, expiryTime ->
      onSuccess(token, expiryTime)
    },
    onError = { error ->
      onError(error)
    },
  )
}

2. Construct the AmbientClient

To initialize the Ambient SDK, construct and configure an AmbientClient service by configuring your dependencies. Then initialize the AmbientClient. For more information, see Initialize Dragon Copilot Ambient SDK.

import com.microsoft.dragoncopilot.ambient.AmbientClient
import com.microsoft.dragoncopilot.ambient.data.Provider

@HiltViewModel
class RecordingViewModel
  @ApplicationContext private val appContext: Context
  private lateinit var client: AmbientClient

  /*...*/

  fun initAmbient() {
    client = AmbientClient(
      appContext,
      Provider(
        userId = /* USER_ID */,
        name = /* PROVIDER_NAME */,
        productId = /* PRODUCT_ID */,
        partnerId = /* PARTNER_ID */,
        customerId = /* CUSTOMER_ID */,
        geography = "US",
    ),
    accessTokenProvider,
    this,
  )
}

3. Start the session

Prepare the patient encounter data:

import com.microsoft.dragoncopilot.ambient.data.EHRData
import com.microsoft.dragoncopilot.ambient.data.Patient

private val patient = Patient(
  /* PATIENT_ID */,
  /* MRN */,
  /* FIRST_NAME */,
  /* MIDDLE_NAME */,
  /* LAST_NAME */,
  /* DOB */,
  /* GENDER */,
)

private val ehrData = EHRData(
  /* APPOINTMENT_ID */,
  /* ENCOUNTER_ID */,
  patient,
  /* REASON_FOR_VISIT */,
)

Recordings are generated within a session; use AmbientClient to create a session:

import com.microsoft.dragoncopilot.ambient.AmbientSession

@HiltViewModel
class RecordingViewModel
  private lateinit var session: AmbientSession

  /* ... */

  fun newSession() {
    sessionId = UUID.randomUUID()
    session =
      client.session(
        identifier = sessionId.toString(),
        ehrData = ehrData,
        reportLocale = Locale("en", "US"),
        uxLocale = Locale("en", "US"),
      )
  }

4. Start and stop recording

import com.microsoft.dragoncopilot.ambient.AmbientRecording
@HiltViewModel
class RecordingViewModel
    private var recording: AmbientRecording? = null

    fun startRecording() {
        val listener = this
        viewModelScope.launch {
            viewModelScope.launch(Dispatchers.IO) {
                try {
                    recording =
                        session.startRecording(
                            arrayOf(Locale("en", "US")),
                            autoNoteGeneration = true,
                            listener = listener,
                        )
                }
                catch (e: Throwable) {
                    Log.e("RecordingViewModel", "Failed to start recording")
                }
            }
        }
    }

    fun stopRecording() {
        viewModelScope.launch {
            recording?.stop()
        }
    }

5. Provide feedback

Implement the AmbientClient and AmbientRecording listeners to provide feedback on the ambient session and the recording.

import com.microsoft.dragoncopilot.ambient.AmbientClient
import com.microsoft.dragoncopilot.ambient.AmbientRecording

@HiltViewModel
class RecordingViewModel : AmbientClient.Listener, AmbientRecording.Listener {

  /* ... */

  override fun onUploadComplete(recordingId: String, sessionId: String) {}

  override fun onUploadFailed(recordingId: String, sessionId: String, error: Error, willRetryUpload: Boolean) {}

  override fun onUploadStarted(recordingId: String, sessionId: String) {}

  override fun onSupportedLanguages(recordingLocales: Array<Locale>, reportLocales: Array<Locale>) {}

  /* ... */

  override fun onRecordingStopped(
    recordingId: String,
    sessionId: String,
    duration: Duration
  ) {}

  override fun onRecordingInterrupted(
    recordingId: String,
    sessionId: String,
    reason: InterruptionReason,
    details: String?,
  ) {}

  override fun onWarnDurationReached(
    recordingId: String,
    sessionId: String,
    remaining: Duration,
  ) {}

  override fun onRecordedAudio(
    recordingId: String,
    sessionId: String,
    duration: Duration,
    level : Float,
  )
}

See also