jtomchak
So I’m getting back a streaming response from a whisper api I set up to use fly.io GPU’s. So far I can call it, get back chunks as they come in using req’s into: parameter and put the text and timestamp on a Phoenix LiveView. What I’m struggling with is how to capture all the incoming chunks to a list so I can save the text segments to Postgres and/or to object storage so I don’t have to transcript that particular audio file again.
# calling transcribe_audio with the url
def async_transcribe(%Episode{} = episode) do
Task.Supervisor.start_child(NormanAi.TaskSupervisor, fn ->
result =
NormanAi.Audio.transcribe_audio(episode.enclosure_url, fn ss, text ->
segment = %Episode.Transcript.Segment{ss: ss, text: text}
broadcast!(episode.id, {segment, episode.id})
IO.inspect("BROADCASTING")
segment
end)
IO.inspect(result)
# Repo.update_all(from(e in Episode, where: e.id == ^episode.id),
# set: [transcript: %Episode.Transcript{segments: segments}]
# )
end)
end
Then the work is done here
def transcribe_audio(url, callback) do
# internal fly.io url for making api requests to
req =
Req.new(
url: "http://faster-whisper-server-patient-voice-8559.flycast/v1/audio/transcriptions",
connect_options: [transport_opts: [inet6: true]],
receive_timeout: 120_000
)
# hard coded url with short sample audio
file_path =
file_path_from_url(
"https://s3-us-west-2.amazonaws.com/staging-moth-social/media_attachments/files/113/041/865/730/367/819/original/9fd57c861d8f3fc7.mp3"
)
multipart =
Multipart.new()
|> Multipart.add_part(Multipart.Part.text_field("true", "stream"))
|> Multipart.add_part(Multipart.Part.file_field(file_path, "file"))
content_length = Multipart.content_length(multipart)
content_type = Multipart.content_type(multipart, "multipart/form-data")
headers = [
{"Content-Type", content_type},
{"Content-Length", to_string(content_length)}
]
Req.post(
req,
headers: headers,
body: Multipart.body_stream(multipart),
into: fn {:data, data}, context ->
# fn {:ok, {ss, %{chunks: [%{text: text}]}}} -> func.(ss, text) end
results = parse(data)
IO.inspect(results)
IO.inspect(context, label: "CONTEXT")
segment = Enum.at(results.segments, 0)
callback.(trunc(segment.start), segment.text)
{:cont, context}
end
)
end
How could I gather up the incoming chunks in a list or collection so I have all the data to save to persistent?
Each returning data chunk looks like this:
%{
text: " At Apple Park.",
words: [],
task: "transcribe",
language: "en",
duration: 0.8200000000000003,
segments: [
%{
id: 14,
start: 54.64,
seek: 5736,
tokens: [51695, 1711, 6373, 4964, 13, 51736],
text: " At Apple Park.",
end: 55.46,
words: nil,
temperature: 0.0,
avg_logprob: -0.19924645728253304,
compression_ratio: 1.7433962264150944,
no_speech_prob: 1.9550323486328125e-5
}
]
}
Trending in Questions
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
Hello!
Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app.
I creat...
New
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
So my question is quite simple and i have found no conclusive answer on forum, google or AI.
Should we use :erlang.float for Integer to ...
New
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
apply_graft/2 doesn’t rewrite an add_many sub-workflow’s deps on an add step. Grafted jobs cancel with “upstream job was deleted”
Version...
New
Other Trending Topics
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hobbes is a low-level distributed database for the Elixir programming language.
Hobbes provides a simple, safe, and scalable storage lay...
New
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve.
They are GUI (Emerge) and State management (S...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #blog-post
- #ai
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 7- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
garrison
I have never actually done this but my impression reading the docs is that you’re meant to use the response in the
{req, resp}tuple (yourcontext) as an accumulator. You could place the chunks in the:privatefield ofrespunder your own key. See:privateunder “Fields” at Req.Response.Or you could use the response
:body- not sure if it would get clobbered by anything else.It would be nice if the docs for
:intoactually spelled this out as I was looking into this exact thing a couple weeks ago and had to infer that this was the intended approach. Seems like it would be a very common use case.jtomchak
Yeah I saw the
:privatefield but was scared off with the detailBut it’s worth a shot
wojtekmach
Yeah, sorry about that, it’s on my roadmap to improve documentation around this. req.private and resp.private exist solely so when writing steps and
into: funwe can store intermediate state. The only restriction isprivate.req_*key names are reserved for Req for forward-compatibility.jtomchak
ok. so I should be able to use
resp.privateto append the chunks as they come back into a list. sweet. I’ll try it out and post the results. I tried with an Agent and managed to get the results back correctly, probably not idiomatic Elixir.jtomchak
I don’t seem to be able to append/update
:privatefrom within theinto: funthe
put_privateorupdate_privatefrom theinto: funare not on the returnresponse, but the finalput_privateis addedwojtekmach
put_private, update_private, etc return updated response, you need to reassign the
respvariable.Btw you can nowadays drop Multipart dependency since we have this capability built in, see encode_body/1 step.
jtomchak