Meta 모델 API

Meta 모델 API (Model API)

Meta의 Model API를 LiteLLM에서 사용하는 방법을 알아봐요. Muse Spark 추론 모델과 Muse Voice 전사 기능에 접근할 수 있어요.

출처: 문서

본문

속성 내용
설명 Meta의 Model API는 Muse Spark 추론 모델과 Muse Voice 전사 기능에 대한 접근을 제공해요
LiteLLM 라우트 meta/
지원 엔드포인트 /chat/completions, /responses, /v1/messages, /v1/realtime
개발자 포털 Meta Model API ↗
Speech-to-Text 레퍼런스 Muse Voice transcription ↗

필수 변수

os.environ["META_API_KEY"] = ""  # your Meta Model API key

Chat, Responses, Messages 요청은 기본적으로 https://api.meta.ai/v1로 가며, META_API_BASE로 해당 base를 덮어쓸 수 있어요. Muse Voice realtime은 동일한 META_API_KEY를 사용하고 wss://api.meta.ai/v1/asr/realtime에 연결돼요.

지원 모델

info: LiteLLM은 모델 목록·가격·토큰 윈도우 등을 여기서 적극 유지·관리하고 있어요.

모델 ID 입력 컨텍스트 길이 입력 모달리티 출력 모달리티
muse-spark-1.1 1M Text, Image, Video, PDF Text
muse-voice-transcribe-1.0 N/A Audio Text

muse-spark-1.1은 함수 호출, 병렬 함수 호출, 구조화 출력, 프롬프트 캐싱, 웹 검색 근거 부여, 그리고 reasoning_effort("minimal"부터 "xhigh"까지)를 통한 추론을 지원해요.

muse-voice-transcribe-1.0은 proxy의 /v1/realtime WebSocket을 통해 제공되는 실시간 speech-to-text 모델이며, 오디오 1초당 과금돼요. 실시간 전용이라 /v1/audio/transcriptions 및 기타 배치·파일 전사 엔드포인트는 지원되지 않아요.

또한 이 API는 기본적으로 Anthropic Messages 포맷도 노출하므로, LiteLLM은 /v1/messages 요청을 변환 없이 https://api.meta.ai/v1/messages로 전달하며 thinking 블록 같은 Anthropic 전용 기능을 그대로 보존해요.

Muse Voice 실시간 전사

LiteLLM은 Muse Voice를 proxy의 OpenAI 호환 /v1/realtime 엔드포인트로 제공해요. 클라이언트는 OpenAI Realtime 전사 프로토콜로 말하고, LiteLLM은 PCM16 오디오를 바이너리 프레임으로 wss://api.meta.ai/v1/asr/realtime에 스트리밍하며 Muse의 전사 프레임을 OpenAI 이벤트로 매핑해요. 아래 Python 예시는 완전한 push to talk 세션 예시예요.

1. config에 모델 추가

config.yaml:

model_list:
  - model_name: muse-voice-transcribe
    litellm_params:
      model: meta/muse-voice-transcribe-1.0
      api_key: os.environ/META_API_KEY

api_keyMETA_API_KEY로 폴백돼요. api_base는 선택 사항이며 절대 wss:// 또는 https:// URL이어야 해요. LiteLLM은 호스트와 포트를 유지하고 경로만 /v1/asr/realtime으로 교체해요. http://, ws://, 인라인 자격 증명, URL 프래그먼트는 거부되며, realtime 세션에서는 META_API_BASE가 무시돼요.

LiteLLM Proxy 시작:

litellm --config config.yaml

# RUNNING on http://0.0.0.0:4000

2. 연결

ws://localhost:4000/v1/realtime?model=muse-voice-transcribe&intent=transcription

Authorization: Bearer ***로 인증해요. model은 config의 model_name이며 필수이고, intent=transcription만 있으면 proxy의 기본 OpenAI 전사 모델로 라우팅돼요. intent=transcription은 세션을 전사 전용으로 만들고 session.update에 있는 어떤 모델이든 인증받은 모델로 고정해요.

LiteLLM은 Meta에 연결된 후, Muse 핸드셰이크 전에 object: realtime.transcription_sessionsession.created를 내보내요.

3. 세션 구성

session.update(또는 transcription_session.update)를 하나 보내요. Muse의 승인은 session.updated로 돌아오며, 이후 업데이트는 무시돼요. session.updated 전에 보낸 오디오는 버퍼링됐다가 재생되므로 바로 스트리밍을 시작할 수 있어요.

{
  "type": "session.update",
  "session": {
    "type": "transcription",
    "audio": {
      "input": {
        "format": {"type": "audio/pcm", "rate": 24000, "channels": 1},
        "transcription": {"model": "meta/muse-voice-transcribe-1.0", "language": "en"},
        "turn_detection": null
      }
    }
  }
}

필드 규칙:

필드 규칙
format typeaudio/pcm, rate16000 또는 24000, channels1이어야 함. 생략 시 모노 24kHz
transcription.model 선택. intent=transcription이면 proxy가 muse-voice-transcribe-1.0으로 교체. 없으면 muse-voice-transcribe-1.0 또는 meta/muse-voice-transcribe-1.0만 허용
transcription.language 선택. 이름 또는 ISO 639 코드로 한 언어에 편향. 리전 접미사는 무시되므로 en-US, zh-Hans, pt-BR 모두 동작
turn_detection 모드를 선택(아래 표). server_vad 외의 type은 거부. type이 없는 객체는 server VAD를 의미
그 외 proxy 로그에 경고와 함께 삭제

베타 레이아웃: input_audio_format: "pcm16" + input_audio_transcription는 24kHz에서 허용돼요. 한 업데이트에서 두 레이아웃을 섞지 마세요.

지원 언어: 아랍어, 벵골어, 네덜란드어, 영어, 프랑스어, 독일어, 히브리어, 힌디어, 인도네시아어, 이탈리아어, 일본어, 칸나다어, 한국어, 마라티어, 말레이어, 중국어(만다린), 폴란드어, 포르투갈어, 스페인어, 타갈로그어, 타밀어, 텔루구어, 태국어, 터키어, 베트남어.

Push to talk Server VAD
turn_detection null 생략, 또는 {"type": "server_vad"}
턴 경계 세션당 한 턴. 언제 끝날지는 사용자가 결정 Muse가 발화를 감지. 각 발화는 고유 item_id를 가짐
턴 종료 input_audio_buffer.commit이 버퍼링된 오디오를 플러시하고 Muse 스트림을 종료 input_audio_buffer.commit은 플러시만 수행. 끝날 때 input_audio_buffer.end 전송

잘못된 session.update(잘못된 rate, 채널 수, 턴 감지 타입, 언어)는 코드 1006으로 연결을 닫고 error 이벤트는 없어요. 이유는 디버그 레벨로 Error in client ack messages: ...에 기록돼요. 설정 후 프로토콜 위반(4초 초과 append, 잘못된 base64, 홀수 PCM 바이트 등)도 같은 방식으로 닫혀요.

4. 오디오 스트리밍

input_audio_buffer.append는 설정된 rate의 base64 모노 PCM16을 이벤트당 최대 4초까지 운반해요. LiteLLM은 오디오를 80ms 바이너리 프레임으로 재패키징해 실시간으로 Muse에 전달해요. input_audio_buffer.clear는 LiteLLM이 아직 보유 중인 부분 프레임을 버려요. 이미 Muse에 보낸 오디오는 되돌릴 수 없어요. response.create를 포함한 다른 모든 클라이언트 이벤트는 삭제돼요.

Server VAD는 오디오가 실시간으로 계속 도착해야 해요. 휴지 중엔 침묵을 스트리밍하고, 끝나면 (OpenAI 프로토콜의 일부가 아닌 LiteLLM 이벤트인) input_audio_buffer.end를 보내요. 클라이언트가 단순히 보내기를 멈추면 Meta가 코드 1008(Ingress below real-time)로 닫으며, proxy는 이를 error 이벤트 후 1008 닫기로 중계해요.

5. 전사 읽기

이벤트 필드 시점
session.created session.object: realtime.transcription_session 연결 시, Muse 핸드셰이크 전
session.updated 정규화된 세션: channels 없음, modelmuse-voice-transcribe-1.0, language는 전체 이름 (enEnglish) Muse가 session.update 승인 후
input_audio_buffer.speech_started item_id Muse가 턴 시작 감지
conversation.item.input_audio_transcription.delta item_id, content_index: 0, delta 부분 전사 텍스트
input_audio_buffer.speech_stopped item_id Muse가 턴 끝 감지
conversation.item.input_audio_transcription.completed item_id, content_index: 0, transcript; usage: {"type": "duration", "seconds": 1.36} (이전 completed 이후 새로운 오디오 처리 시) 턴의 최종 전사
error error.type: server_error; error.messageMeta Muse realtime transcription failed, 또는 비-1000 닫기 전 upstream websocket closed with code : Muse 오류 보고 또는 비정상 종료

턴은 겹칠 수 있고 item_id로 상관관계가 매겨져요. Muse에 더 이상 세그먼트가 없으면 코드 1000(보통 이유 No more transcript segments)으로 닫고 proxy가 그 닫기를 중계해요.

가격 및 사용량

Muse Voice는 입력 오디오 1초당 과금돼요(모델 비용 맵의 input_cost_per_second). usage.seconds는 마지막 과금 지점 이후 Muse가 처리한 오디오(침묵 포함)이므로 발화 길이를 초과할 수 있어요. 마지막 completed 이후의 오디오(예: 꼬리 침묵)는 세션 종료 시 과금돼요.

전사본은 메시지 로깅이 꺼져 있지 않으면 스펜드 로그의 messages에 저장돼요. proxy 로그는 가드레일로 차단된 전사의 첫 80자(경고 레벨)를 제외하고 전사 텍스트를 담지 않아요.

가드레일

mode: realtime_input_transcription 가드레일이 각 완성된 전사에서 실행돼요. completed 이벤트는 가드레일 전에 클라이언트에 도달하며, 델타는 검사되지 않아요. 차단 시 type: guardrail_violation, code: content_policy_violationerror 이벤트가 전송되며, on_violation: end_session은 코드 1000으로 소켓도 닫아요.

연결마다 guardrails=name1,name2 쿼리 파라미터로 가드레일을 선택할 수 있어요.

warning: realtime_input_transcription 가드레일을 구성하면 LiteLLM이 Muse가 보기 전에 클라이언트의 turn_detection: null{"create_response": false}로 다시 작성해요. 따라서 push to talk 클라이언트는 server VAD 모드에서 실행되며, input_audio_buffer.commit이 더 이상 스트림을 종료하지 않고, 스트리밍을 멈춘 클라이언트는 코드 1008로 연결이 끊겨요. Server VAD 클라이언트는 영향을 받지 않아요.

예시 Python 클라이언트

Push to talk: 100ms 청크의 모노 PCM16 WAV 파일을 구성·스트리밍하고, commit한 뒤 proxy가 소켓을 닫을 때까지 이벤트를 읽어요.

import asyncio
import base64
import json
import wave

import websockets

URL = "ws://localhost:4000/v1/realtime?model=muse-voice-transcribe&intent=transcription"
HEADERS = {"Authorization": "Bearer sk-1234"}  # your proxy API key

def pcm_chunks(path, chunk_ms=100):
    with wave.open(path) as w:  # mono PCM16 at 16 kHz or 24 kHz
        rate, frames = w.getframerate(), w.readframes(w.getnframes())
    step = rate * 2 * chunk_ms // 1000
    return rate, [frames[i : i + step] for i in range(0, len(frames), step)]

async def main():
    rate, chunks = pcm_chunks("question.wav")
    async with websockets.connect(URL, additional_headers=HEADERS) as ws:
        print(json.loads(await ws.recv())["type"])  # session.created
        await ws.send(json.dumps({
            "type": "session.update",
            "session": {
                "type": "transcription",
                "audio": {
                    "input": {
                        "format": {"type": "audio/pcm", "rate": rate},
                        "transcription": {"model": "meta/muse-voice-transcribe-1.0"},
                        "turn_detection": None,
                    }
                },
            },
        }))
        print(json.loads(await ws.recv())["type"])  # session.updated
        for chunk in chunks:
            await ws.send(json.dumps({"type": "input_audio_buffer.append", "audio": base64.b64encode(chunk).decode()}))
            await asyncio.sleep(0.1)
        await ws.send(json.dumps({"type": "input_audio_buffer.commit"}))
        try:
            while True:
                event = json.loads(await ws.recv())
                if event["type"] == "conversation.item.input_audio_transcription.delta":
                    print(event["delta"], end="", flush=True)
                elif event["type"] == "conversation.item.input_audio_transcription.completed":
                    seconds = event.get("usage", {}).get("seconds")  # absent when Muse reported no new audio
                    print(f"\n{event['transcript']} ({seconds} s)")
        except websockets.exceptions.ConnectionClosedOK:
            pass  # close code 1000: no more transcript segments

asyncio.run(main())
session.created
session.updated
What is the weather in Paris?
What is the weather in Paris? (1.36 s)

Server VAD의 경우 None 대신 "turn_detection": {"type": "server_vad"}를 보내고, 마이크가 열려 있는 동안 (침묵 포함) 스트리밍을 계속하며, {"type": "input_audio_buffer.end"}로 마무리해요. 감지된 각 발화는 고유한 item_id로 도착해요.

LiteLLM Python SDK 사용법

비스트리밍

import os
import litellm
from litellm import completion

os.environ["META_API_KEY"] = ""  # your Meta Model API key

messages = [{"content": "Hello, how are you?", "role": "user"}]

response = completion(model="meta/muse-spark-1.1", messages=messages)

스트리밍

import os
import litellm
from litellm import completion

os.environ["META_API_KEY"] = ""  # your Meta Model API key

messages = [{"content": "Hello, how are you?", "role": "user"}]

response = completion(
    model="meta/muse-spark-1.1",
    messages=messages,
    stream=True
)

for chunk in response:
    print(chunk)

추론 노력 (Reasoning Effort)

muse-spark-1.1reasoning_effort 값을 "minimal", "low", "medium", "high", "xhigh"로 받아요.

import os
import litellm
from litellm import completion

os.environ["META_API_KEY"] = ""  # your Meta Model API key

messages = [{"content": "What is 15% of 2840?", "role": "user"}]

response = completion(
    model="meta/muse-spark-1.1",
    messages=messages,
    reasoning_effort="xhigh"
)

print(response.choices[0].message.content)
print(response.usage.completion_tokens_details.reasoning_tokens)

함수 호출

import os
import litellm
from litellm import completion

os.environ["META_API_KEY"] = ""  # your Meta Model API key

messages = [{"content": "What's the weather like in San Francisco?", "role": "user"}]

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get the current weather in a given location",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "The city and state, e.g. San Francisco, CA"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"]
                    }
                },
                "required": ["location"]
            }
        }
    }
]

response = completion(
    model="meta/muse-spark-1.1",
    messages=messages,
    tools=tools,
    tool_choice="auto"
)

print(response.choices[0].message.tool_calls)

LiteLLM Proxy 사용법

LiteLLM Proxy 설정 파일에 다음을 추가해요.

config.yaml:

model_list:
  - model_name: muse-spark-1.1
    litellm_params:
      model: meta/muse-spark-1.1
      api_key: os.environ/META_API_KEY

Proxy 서버 시작:

litellm --config config.yaml

# RUNNING on http://0.0.0.0:4000

Proxy를 통한 Meta Model API - 비스트리밍 (OpenAI SDK):

from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:4000",  # Your proxy URL
    api_key="your-proxy-api-key"       # Your proxy API key
)

response = client.chat.completions.create(
    model="muse-spark-1.1",
    messages=[{"role": "user", "content": "Write a short poem about AI."}],
    reasoning_effort="minimal"
)

print(response.choices[0].message.content)

스트리밍:

from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:4000",  # Your proxy URL
    api_key="your-proxy-api-key"       # Your proxy API key
)

response = client.chat.completions.create(
    model="muse-spark-1.1",
    messages=[{"role": "user", "content": "Write a short poem about AI."}],
    stream=True
)

for chunk in response:
    if chunk.choices[0].delta.content is not None:
        print(chunk.choices[0].delta.content, end="")

LiteLLM SDK:

import litellm

response = litellm.completion(
    model="litellm_proxy/muse-spark-1.1",
    messages=[{"role": "user", "content": "Write a short poem about AI."}],
    api_base="http://localhost:4000",
    api_key="your-proxy-api-key"
)

print(response.choices[0].message.content)

cURL:

curl http://localhost:4000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your-p...-key" \
  -d '{
    "model": "muse-spark-1.1",
    "messages": [{"role": "user", "content": "Write a short poem about AI."}],
    "reasoning_effort": "minimal"
  }'

Anthropic Messages API

proxy의 /v1/messages 라우트는 meta/ 모델의 요청을 변환 없이 Meta의 네이티브 Anthropic 호환 엔드포인트로 전달해요.

curl http://localhost:4000/v1/messages \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your-p...-key" \
  -d '{
    "model": "muse-spark-1.1",
    "max_tokens": 2048,
    "messages": [{"role": "user", "content": "Write a short poem about AI."}]
  }'

더 알아보기 (Learn more)

  • Meta Model API 개발자 포털
  • Muse Voice 실시간 전사
  • LiteLLM 리얼타임 가드레일