Skip to content

server: Enable transcriptions API for LFM2-Audio#22000

Merged
ngxson merged 1 commit into
ggml-org:masterfrom
tdakhran:tarek/feat/transcriptions-lfm2
Apr 23, 2026
Merged

server: Enable transcriptions API for LFM2-Audio#22000
ngxson merged 1 commit into
ggml-org:masterfrom
tdakhran:tarek/feat/transcriptions-lfm2

Conversation

@tdakhran

Copy link
Copy Markdown
Contributor

Overview

#21863 added support for /v1/audio/transcriptions API with the assumption that models expect [{"role":"user","content":"Transcribe audio to text<__media__>"}] format.

This doesn't hold, e.g., for LiquidAI/LFM2.5-Audio-1.5B-GGUF.

In PR, I try to relax this assumption by defining the model-specific strategy and using it in the server.

Additional information

With this change, transcription works correctly for LiquidAI/LFM2.5-Audio-1.5B-GGUF and adds a mechanism for defining a strategy for future models.

To test, start server with

llama-server -hf LiquidAI/LFM2.5-Audio-1.5B-GGUF

Use the code below to inference

# /// script
# requires-python = ">=3.9"
# dependencies = [
#     "openai",
# ]
# ///
import io
import urllib.request

from openai import OpenAI

client = OpenAI(api_key="your-api-key", base_url="http://localhost:8080/v1")

url = "https://github.com/ggerganov/whisper.cpp/raw/master/samples/jfk.wav"
audio_file = io.BytesIO(urllib.request.urlopen(url).read())
audio_file.name = "jfk.wav"
streaming = True

if streaming:
    transcription = client.audio.transcriptions.create(
        model="whisper-1",
        file=audio_file,
        stream=True
    )

    for chunk in transcription:
        try:
            print(chunk.delta, end="", flush=True)
        except Exception as e:
            print("\n", chunk)

else:
    transcription = client.audio.transcriptions.create(
        model="whisper-1",
        file=audio_file
    )
    print(transcription.text)

Output

❯ uv run transcript.py
And so, my fellow Americans, ask not what your country can do for you, ask what you can do for your country.
 TranscriptionTextDoneEvent(text='', type='transcript.text.done', logprobs=None, usage=Usage(input_tokens=157, output_tokens=27, total_tokens=184, type='tokens', input_token_details=None, input_tokens_details={'cached_tokens': 0}))

Requirements

@tdakhran
tdakhran requested review from a team as code owners April 16, 2026 13:59

@ngxson ngxson left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO the current proposal is a bit complicated. Instead, any prompt-related should be confined to the common/chat.cpp|h system

For example, adding a common_chat_get_asr_prompt() that return the prompt specific to LFM2. See common_chat_params_init_lfm2 for now model-specific chat template is handled.

This way, you don't have to make any changes to libmtmd

@tdakhran

tdakhran commented Apr 17, 2026

Copy link
Copy Markdown
Contributor Author

IMO the current proposal is a bit complicated. Instead, any prompt-related should be confined to the common/chat.cpp|h system

For example, adding a common_chat_get_asr_prompt() that return the prompt specific to LFM2. See common_chat_params_init_lfm2 for now model-specific chat template is handled.

This way, you don't have to make any changes to libmtmd

@ngxson, instructions e.g. for ASR can vary from model to model. chat.h/chat.cpp don't have information about mm projector and about the task.

    if (src.find("<|tool_list_start|>") != std::string::npos &&
        src.find("<|tool_list_end|>") != std::string::npos) {
        LOG_DBG("Using specialized template: LFM2\n");
        return common_chat_params_init_lfm2(tmpl, params);
    }

convert_transcriptions_to_chatcmpl knows we do transcription, but that knowledge is lost immediately after the function exits.
Knowledge of which mm projector is used is available only in MTMD.

I'm struggling to see how these two can be detected for the ASR use case in chat.h/chat.cpp.

UPD: it actually worked, thank you @ngxson !

@tdakhran
tdakhran requested a review from a team as a code owner April 17, 2026 09:07
@tdakhran
tdakhran requested a review from ngxson April 17, 2026 09:13
@tdakhran
tdakhran force-pushed the tarek/feat/transcriptions-lfm2 branch from 6c1b038 to 11b292d Compare April 17, 2026 09:17

@ngxson ngxson left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good, just a small comment. CC @pwilkin if you have any ideas on how to structure the newly added common_chat_get_asr_prompt (can be a follow-up PR to improve it)

std::vector<raw_buffer> files;
json body = convert_transcriptions_to_chatcmpl(
json::parse(req.body),
meta->chat_params.tmpls.get(),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably better to pass the whole chat_params here instead of just tmpls, that will make this function to be more aligned with oaicompat_chat_params_parse

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, gonna think about it, probably might be a good idea to unify it and to just pass the entire JSON messages "preset" array.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pwilkin for now I think it should be good to merge, right? If so, can you give an approval? (This can be improved in a follow-up PR if you have any ideas.) Thanks!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, let's merge it for now.

@ngxson

ngxson commented Apr 22, 2026

Copy link
Copy Markdown
Collaborator

@tdakhran can you resolve the conflict?

@tdakhran
tdakhran force-pushed the tarek/feat/transcriptions-lfm2 branch from 11b292d to f4a9fc3 Compare April 22, 2026 11:05
@tdakhran

Copy link
Copy Markdown
Contributor Author

@tdakhran can you resolve the conflict?

@ngxson had to manually re apply patch because functions were moved across files, please take one more look before merging.

@tdakhran
tdakhran requested a review from ngxson April 22, 2026 11:07

@ngxson ngxson left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, let's merge when the CI passes

@ngxson
ngxson merged commit 550d684 into ggml-org:master Apr 23, 2026
48 of 49 checks passed
IntelNav pushed a commit to IntelNav/llama.cpp that referenced this pull request Apr 29, 2026
IntelNav pushed a commit to IntelNav/llama.cpp that referenced this pull request Apr 29, 2026
samuraieng pushed a commit to samuraieng/llama.cpp that referenced this pull request May 6, 2026
ljubomirj pushed a commit to ljubomirj/llama.cpp that referenced this pull request May 6, 2026
meh pushed a commit to meh/llama.cpp that referenced this pull request May 10, 2026
my-other-github-account pushed a commit to my-other-github-account/llama.cpp that referenced this pull request May 15, 2026
my-other-github-account pushed a commit to my-other-github-account/llama.cpp that referenced this pull request May 15, 2026
baramofme pushed a commit to baramofme/llama-cpp-turboquant that referenced this pull request May 23, 2026
fewtarius pushed a commit to fewtarius/CachyLLama that referenced this pull request May 30, 2026
MrLordCat referenced this pull request in MrLordCat/llama.cpp-with-GUI Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants