Skip to Content
Mélodium 0.10.1 is now available!
DocsExamplesMeeting Summary Service

Meeting Summary Service

Source: 10_meeting_summary_service

An HTTP server that accepts raw audio uploads, transcribes them with the ElevenLabs Scribe API, generates a structured meeting summary with Claude Sonnet, and streams the summary back as the HTTP response. Each request is handled in its own track; the server processes multiple simultaneous uploads without any explicit thread management.

Running

melodium run 10_meeting_summary_service/Compo.toml \ --anthropic_key sk-ant-... \ --elevenlabs_key el-...
Note

anthropic_key is an Anthropic API key. elevenlabs_key is an ElevenLabs API key.

$ curl -X POST http://127.0.0.1:8080/summarise \ --data-binary @meeting.wav \ -H "Content-Type: audio/wav" ## Meeting Summary **Overview:** **Key decisions:** - **Action items:** -

How it works

Two models wrap the remote STT and LLM backends:

model Stt(const elevenlabs_key: string) : RemoteStt { backend = "elevenlabs" api_key = |wrap<string>(elevenlabs_key) base_url = "" model = "scribe_v1" } model Llm(const anthropic_key: string) : RemoteLlm { backend = "anthropic" api_key = |wrap<string>(anthropic_key) base_url = "" model = "claude-sonnet-4-6" system = "You are an expert meeting assistant. Given a raw transcript, produce a concise, structured summary with: key decisions, action items, and a one-paragraph overview." max_tokens = 512 temperature = 0.4 top_p = _ timeout = _ }

Stt uses the ElevenLabs scribe_v1 model. Llm uses Claude Sonnet with a structured summarisation system prompt and a lower temperature (0.4) to produce focused, consistent output. All three models, including the HttpServer, are instantiated at startup:

treatment main( const anthropic_key: string, const elevenlabs_key: string, const port: u16 = 8080 ) model server: HttpServer(host=|from_ipv4(|localhost_ipv4()), port=port) model stt: Stt(elevenlabs_key=elevenlabs_key) model llm: Llm(anthropic_key=anthropic_key) { startup() start[http_server=server]() logReady: logInfoMessage(label="service", message="meeting summary service ready") startup.trigger -> start.trigger startup.trigger -> logReady.trigger connection[http_server=server](method=|post(), route="/summarise") status: emit<HttpStatus>(value=|ok()) headers: emit<StringMap>(value=|map([])) bodyTrigger: trigger<byte>() connection.data -> bodyTrigger.stream,start --> status.trigger,emit -> connection.status bodyTrigger.start --------> headers.trigger,emit -> connection.headers summariseRequest[stt=stt, llm=llm]() connection.data -> summariseRequest.audio,response -> connection.data }

main treatment diagram See in Compositeur Studio

Per-request pipeline

The summariseRequest sub-treatment handles everything for one request:

treatment summariseRequest[stt: RemoteStt, llm: RemoteLlm]() input audio: Stream<byte> output response: Stream<byte> { transcribe[stt=stt]() transcriptStream: stream<string>() sttFailed: logErrorMessage(label="stt", message="transcription failed") sttError: logError(label="stt") Self.audio -> transcribe.audio transcribe.transcript -> transcriptStream.block,stream -> buildSummaryPrompt.transcript transcribe.failed -> sttFailed.trigger transcribe.error -> sttError.message buildSummaryPrompt() chat[llm=llm]() encode() llmErrors: logErrors(label="llm") buildSummaryPrompt.prompt -> chat.prompt,response -> encode.text,data -> Self.response chat.error -> llmErrors.messages }

transcribe returns a Block<string>. The stream<string>() adapter converts it to a Stream<string> so it can flow into buildSummaryPrompt, which wraps the raw transcript in a prompt template using entry and format:

treatment buildSummaryPrompt() input transcript: Stream<string> output prompt: Stream<string> { wrapEntry: entry(key="t") fmt: format(format="Here is the meeting transcript:\n\n{t}\n\nPlease produce a structured meeting summary.") Self.transcript -> wrapEntry.value,map -> fmt.entries,formatted -> Self.prompt }

chat from RemoteLlm returns a Stream<string> of response tokens, which are encoded and forwarded directly into connection.data. Summary tokens appear in the HTTP response as they are generated.

summariseRequest treatment diagram See in Compositeur Studio

Video Explanation

Dependencies

[dependencies] std = "0.10.1" # core flows, logging, data structures http = "0.10.1" # HTTP server and client net = "0.10.1" # IP address helpers encoding = "0.10.1" # UTF-8 encode / decode ml = "0.10.1" # LLM, STT, TTS and local model inference