File size: 2,090 Bytes
4553650
9f13621
4553650
 
9f13621
 
a7feaeb
9f13621
60bcf8d
a7feaeb
60bcf8d
96c379c
 
 
 
9f13621
 
 
 
 
 
bdffcf8
99149d9
60bcf8d
99149d9
 
 
60bcf8d
99149d9
96c379c
 
 
60bcf8d
 
 
 
 
 
 
bdffcf8
dd55ec1
9f13621
a227b91
9f13621
 
dd55ec1
bae3e66
60bcf8d
bae3e66
 
9f13621
 
bae3e66
60bcf8d
bae3e66
 
 
60bcf8d
 
9f13621
 
96d4a31
bdffcf8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
defmodule MedicalTranscription.Transcription do
  @moduledoc """
  Takes a path to an audio file and transcribes it to text. As each chunk is available, it passes it to the `Coding`
  context to look for possible matching codes.
  """

  alias MedicalTranscription.Coding

  defp get_tags_and_send_result(chunk, index, live_view_pid) do
    tags = Coding.process_chunk(chunk.text)
    result = build_result(index, chunk, tags)

    send(live_view_pid, {:transcription_row, result})
  end

  # Ideas for future exploration:
  # - Instead of storing the long description vectors in a binary file on disk, we could store them within a vector DB
  #   (such as pgvector or Pinecone.io)
  # - A potential improvement would be to not code each chunk of transcribed audio separately, but to instead gather
  #   complete sentences based on punctuation. We may want to suggest codes for the entire audio as a single piece as
  #   well
  def stream_transcription_and_search(live_view_pid, audio_file_path) do
    # audio transcription + semantic search
    summary_text =
      audio_file_path
      |> stream_transcription()
      |> Enum.reduce("", fn {chunk, index}, acc ->
        get_tags_and_send_result(chunk, index, live_view_pid)

        acc <> chunk.text
      end)

    summary_chunk = %{
      text: summary_text,
      start_timestamp_seconds: nil,
      end_timestamp_seconds: nil
    }

    get_tags_and_send_result(summary_chunk, 0, live_view_pid)
  end

  defp stream_transcription(audio_file_path) do
    MedicalTranscription.TranscriptionServing
    |> Nx.Serving.batched_run({:file, audio_file_path})
    |> Stream.with_index()
  end

  defp build_result(index, chunk, tags) do
    %{
      id: index,
      start_mark: format_timestamp(chunk.start_timestamp_seconds),
      end_mark: format_timestamp(chunk.end_timestamp_seconds),
      text: chunk.text,
      tags: tags
    }
  end

  defp format_timestamp(seconds) when is_nil(seconds), do: nil

  defp format_timestamp(seconds) do
    seconds |> round() |> Time.from_seconds_after_midnight() |> Time.to_string()
  end
end