تبدیل گفتار به متن — Speech-to-text
آشا رونویسی گفتار به متن (STT) را از طریق endpoint اختصاصیِ /v1/audio/transcriptions پشتیبانی میکند. صوتی base64 بفرستید و پاسخ JSON با متن رونویسیشده و آمار استفاده بگیرید.
کشف مدلها
مدلهای STT را از چند راه میتوانید پیدا کنید:
از طریق API
همهٔ مدلها را با endpoint سازگار با OpenAI یعنی
GET /v1/models فهرست کنید. مدلهای رونویسی
صوتی (مثل خانوادهٔ Whisper) با حالتِ audio->text
در architecture.modality قابل شناساییاند:
curl "https://app.asha-ai.ir/v1/models"
در صفحهٔ مدلها
در فهرست مدلها با فیلتر «صوتی» (Audio) مدلهای صوتی — از جمله رونویسی — را ببینید. این فیلتر سمتِ کلاینت و بر اساس نوع مدل در داشبورد اعمال میشود.
استفاده از API
درخواست POST به
/v1/audio/transcriptions با بدنهٔ JSON شامل صوتی
base64 بفرستید. پاسخ JSON است با متن رونویسیشده و آمار استفادهٔ اختیاری.
مثال ساده
import base64
import json
import os
import requests
with open("audio.wav", "rb") as f:
base64_audio = base64.b64encode(f.read()).decode("utf-8")
response = requests.post(
url="https://app.asha-ai.ir/v1/audio/transcriptions",
headers={
"Authorization": f"Bearer {os.environ['ASHA_API_KEY']}",
"Content-Type": "application/json",
},
data=json.dumps({
"model": "~openai/whisper-1",
"input_audio": {
"data": base64_audio,
"format": "wav",
},
}),
)
result = response.json()
print(result["text"])
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
func main() {
raw, err := os.ReadFile("audio.wav")
if err != nil {
panic(err)
}
payload := map[string]any{
"model": "~openai/whisper-1",
"input_audio": map[string]any{
"data": base64.StdEncoding.EncodeToString(raw),
"format": "wav",
},
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://app.asha-ai.ir/v1/audio/transcriptions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ASHA_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
var result map[string]any
_ = json.Unmarshal(out, &result)
fmt.Println(result["text"])
}
import fs from "fs";
const audioBuffer = await fs.promises.readFile("audio.wav");
const base64Audio = audioBuffer.toString("base64");
const response = await fetch("https://app.asha-ai.ir/v1/audio/transcriptions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ASHA_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "~openai/whisper-1",
input_audio: {
data: base64Audio,
format: "wav",
},
}),
});
const result = await response.json();
console.log(result.text);
import fs from "fs/promises";
const audioBuffer = await fs.readFile("audio.wav");
const base64Audio = audioBuffer.toString("base64");
const response = await fetch("https://app.asha-ai.ir/v1/audio/transcriptions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ASHA_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "~openai/whisper-1",
input_audio: {
data: base64Audio,
format: "wav",
},
}),
});
const result = await response.json();
console.log(result.text);
# Base64-encode your audio file
AUDIO_BASE64=$(base64 < audio.wav | tr -d 'n')
curl https://app.asha-ai.ir/v1/audio/transcriptions
-H "Content-Type: application/json"
-H "Authorization: Bearer $ASHA_API_KEY"
-d '{
"model": "~openai/whisper-1",
"input_audio": {
"data": "'"$AUDIO_BASE64"'",
"format": "wav"
}
}'
پارامترهای درخواست
| پارامتر | نوع | الزامی | شرح |
|---|---|---|---|
model | string | بله | مدل STT (مثل ~openai/whisper-1) |
input_audio | object | بله | دادهٔ صوتی برای رونویسی |
input_audio.data | string | بله | دادهٔ صوتی با کدگذاری base64 (بایت خام، نه Data URI) |
input_audio.format | string | بله | فرمت صوتی (مثل wav، mp3، flac، m4a، ogg، webm، aac) |
language | string | خیر | کد زبان ISO-639-1 (مثل "en"، "ja")؛ اگر حذف شود خودکار تشخیص داده میشود |
temperature | number | خیر | دمای نمونهگیری بین ۰ تا ۱؛ مقادیر پایینتر نتیجهٔ قطعیتری میدهند |
provider | object | خیر | پیکربندی عبور پارامترهای مختص ارائهدهنده |
درخواستهای Multipart سازگار با OpenAI
endpoint همچنین درخواستهای multipart/form-data را به
سبک OpenAI میپذیرد؛ پس کلاینتهایی که برای
/v1/audio/transcriptions OpenAI ساخته شدهاند (از جمله
SDKهای رسمی) فقط با عوضکردن Base URL به
https://app.asha-ai.ir/v1 کار میکنند:
import os
from openai import OpenAI
client = OpenAI(
base_url="https://app.asha-ai.ir/v1",
api_key=os.environ["ASHA_API_KEY"],
)
with open("audio.wav", "rb") as f:
result = client.audio.transcriptions.create(
model="~openai/whisper-large-v3",
file=f,
)
print(result.text)
curl https://app.asha-ai.ir/v1/audio/transcriptions
-H "Authorization: Bearer $ASHA_API_KEY"
-F file="@audio.wav"
-F model="~openai/whisper-large-v3"
فیلدهای file، model،
language، temperature،
response_format و
timestamp_granularities پشتیبانی میشوند.
prompt پذیرفته میشود اما نادیده گرفته میشود.
response_format میتواند json
(پیشفرض) یا verbose_json باشد؛ دومی
task، language،
duration و برچسبهای زمانی سطح قطعه را اضافه میکند؛
verbose_json فقط روی ارائهدهندههای سازگار با OpenAI
در دسترس است و بقیه آن را با 400 رد میکنند. text،
srt و vtt با
خطای 400 رد میشوند. با verbose_json،
timestamp_granularities[]=word را هم بدهید تا برچسبهای
زمانی سطح کلمه در آرایهٔ words بیایند
(segment پیشفرض ارائهدهنده است). همین
response_format و
timestamp_granularities در مسیر JSON با base64 هم کار میکنند.
آپلود multipart به ۲۵ MB محدود است، همان سقفی که OpenAI اعمال میکند. برای فرمتهای فشرده
این یعنی حدود ۲۶ دقیقه MP3 با ۱۲۸ kbps، ۵۲ دقیقه با ۶۴ kbps یا بیش از ۲ ساعت ویسِ Opus با
۲۴ kbps. WAV فشردهنشده خیلی زودتر سقف را پر میکند (حدود ۱۳ دقیقه مونو با ۱۶ کیلوهرتز)؛ برای
ضبطهای طولانی mp3 یا opus
را ترجیح بدهید. فایلهای بزرگتر را با base64 و JSON از طریق
input_audio بفرستید که offload جریانی را پشتیبانی
میکند. ضبطهایی که به بیش از حدود یک دقیقه پردازش نیاز دارند را بههرحال بشکنید، چون
ارائهدهندههای پاییندست بعد از ۶۰ ثانیه در هر درخواست timeout میشوند.
گزینههای مختص ارائهدهنده
با پارامتر provider گزینههای مختص ارائهدهنده را
بفرستید؛ با نام ارائهدهنده کلید میخورند و فقط گزینههای ارائهدهندهٔ جورشده منتقل میشوند:
{
"model": "~openai/whisper-large-v3",
"input_audio": {
"data": "UklGRiQA...",
"format": "wav"
},
"provider": {
"options": {
"groq": {
"prompt": "Expected vocabulary: OpenRouter, API, transcription"
}
}
}
}
قالب پاسخ
endpoint STT پاسخ JSON با متن رونویسیشده برمیگرداند:
{
"text": "Hello, this is a test of speech-to-text transcription.",
"usage": {
"seconds": 9.2,
"total_tokens": 113,
"input_tokens": 83,
"output_tokens": 30,
"cost": 8
}
}
فیلدهای پاسخ
| فیلد | نوع | شرح |
|---|---|---|
text | string | متن رونویسیشده |
usage.seconds | number | مدت صوتِ ورودی بر حسب ثانیه |
usage.total_tokens | number | مجموع توکنهای استفادهشده (ورودی + خروجی) |
usage.input_tokens | number | تعداد توکنهای ورودیِ صورتحسابشده |
usage.output_tokens | number | تعداد توکنهای خروجی تولیدشده |
usage.cost | number | هزینهٔ کل درخواست به تومان |
فرمتهای صوتی پشتیبانیشده
فرمتهای صوتی پشتیبانیشده بسته به ارائهدهنده متفاوتاند. فرمتهای رایج:
| فرمت | MIME Type | شرح |
|---|---|---|
wav | audio/wav | صوت بدون فشردهسازی، بالاترین کیفیت |
mp3 | audio/mpeg | صوت فشرده، سازگاری گسترده |
flac | audio/flac | صوت فشردهٔ بدون اتلاف |
m4a | audio/mp4 | صوت MPEG-4 |
ogg | audio/ogg | صوت Ogg Vorbis |
webm | audio/webm | صوت WebM؛ رایج در ضبطهای مرورگر |
aac | audio/aac | کدگذاری پیشرفتهٔ صوتی |
قیمتگذاری
مدلهای STT بسته به ارائهدهنده راهبرد قیمتی متفاوتی دارند:
- بر اساس مدت (مثل OpenAI Whisper): بهازای هر ثانیه صوت ورودی
- بر اساس توکن (مثل مدلهای جدیدتر OpenAI): بهازای هر توکن ورودی/خروجی، شبیه مدلهای متنی
هزینهٔ هر مدل را در فهرست مدلها یا از طریق
Models API ببینید. فیلد usage.cost
در پاسخ، هزینهٔ واقعی هر درخواست را نشان میدهد.
تفاوت با ورودی صوتی
آشا دو راه برای پردازش صوت دارد:
-
تبدیل گفتار به متن (همین صفحه): endpoint اختصاصیِ
/v1/audio/transcriptionsبهینهشده برای رونویسی؛ JSON ساختیافته با متن و دادهٔ مصرف برمیگرداند. برای تبدیل صوت به متن مناسب است. -
ورودی صوتی از طریق Chat Completions (مستندات Audio):
صوت را بهصورت بخشی از درخواست
/v1/chat/completionsبا نوع محتوایinput_audioبفرستید. مدل صوت را کنار متن پردازش و بهصورت گفتگو پاسخ میدهد؛ برای تحلیل صوت، پرسوجو دربارهٔ محتوای صوتی یا ترکیب صوت با دیگر حالتها مناسب است.
بهترین روشها
- فرمت را مشخص کنید: همیشه
input_audio.formatرا بدهید؛ تشخیص خودکار همیشه دقیق نیست. - مدل را آگاهانه انتخاب کنید: برای دقت بالا
~openai/whisper-large-v3و برای نیاز به سرعتِ بیشتر~openai/whisper-1؛ پایداری را با آزمایش روی دادههای خودتان بسنجید. - زبان را در صورت معلومبودن بفرستید: کوتاهتر، دقیقتر و کمخطاتر است.
- خودرو را قطع کنید: درخواستهای بزرگ را بعد از ۶۰ ثانیه timeout کنید؛ ارائهدهندههای پاییندست به همین ترتیب رفتار میکنند.
- دادهٔ base64 را تمیز بفرستید: فقط بایتهای خام کدگذاریشده، بدون پیشوند Data URI.
- برای خروجی ساختیافته از
response_format=verbose_jsonوtimestamp_granularities[]=wordاستفاده کنید.
عیبیابی
| پیام خطا | شرح و راهحل |
|---|---|
400 Bad Request |
بدنهٔ نامعتبر؛ فرمت فیلدها و ویژگیهای اختیاریِ پشتیبانینشده را بررسی کنید. |
413 Content Too Large |
فایل از ۲۵ MB رد شده؛ از فرمت کمحجمتر مثل mp3 استفاده کنید یا
صوت را تکهتکه کنید. |
429 Rate Limit |
محدودیت نرخ؛ با backoff و retry مواجه کنید. |
400 Unsupported response_format |
فرمت پاسخ خواستهشده توسط ارائهدهندهٔ جورشده پشتیبانی نمیشود. |
سؤالات متداول
چرا خطای 413 میگیرم؟
برای multipart سقف ۲۵ MB اعمال میشود. از فرمت کمحجمتر استفاده کنید یا صوت را کوتاهتر
کنید؛ برای ضبطهای طولانیتر مسیر base64 با input_audio را امتحان کنید.
منبع قیمتگذاری چیست؟
قیمت یا بر اساس مدت صوت (بهازای ثانیه) یا بر اساس توکنهاست و در Models API درج میشود.
هزینهٔ هر درخواست دقیقاً در usage.cost به تومان ثبت میشود.
آیا prompt پشتیبانی میشود؟
در multipart پذیرفته میشود اما نادیده گرفته میشود. اگر ارائهدهندهٔ شما گزینهٔ واژگان
جداگانهای دارد، از طریق provider آن را بفرستید.
چرا کل درخواست یک دقیقه طول میکشد؟
میتواند به ۶۰ ثانیه timeout در پردازش ارائهدهنده برسد. ضبطهای طولانی را به قطعات کوچکتر بشکنید و از timeout خودِ کلاینت مطمئن شوید.
کدام مدلها به json خروجی میدهند؟
فقط مدلهایی که json یا verbose_json را در
response_format خود اعلام کردهاند؛ برای بررسی، صفحهٔ مدل یا Models API
را ببینید.
آیا خروجی مثل پاسخ Chat است؟
خیر. این endpoint فقط رونویسی انجام میدهد؛ برای گفتگو یا تحلیل صوت از Audio در Chat Completions استفاده کنید.