AI Orchestrator v2.3

Standard Port: 2022 • Async Queue & Webhooks
Unknown

System Overview

Hệ thống AI Orchestrator cung cấp API để xử lý các tác vụ AI nặng (FaceSwap, Video Gen, Upscale, Editing) thông qua cơ chế Async Queue.

Hỗ trợ: Faceswap (Single/Multi) Faceswap Video AHV Image/Video GG Image CX Image Upscale & Beauty Remove Obj Scan Obj Voice TTS Voice STT Music Gen

Open Dashboard →

Recommended Workflow

  • 1. Client gửi POST /v1/jobs kèm webhook_url.
  • 2. Server trả về job_id và trạng thái queued.
  • 3. Client tiếp tục làm việc khác.
  • 4. Khi hoàn thành, Server gọi POST webhook_url trả kết quả.

Authentication

Header bắt buộc cho mọi request:
X-API-Key: your_secret_key

1. Async Jobs API

Endpoint: POST /v1/jobs. Hỗ trợ retry, queue management và webhook notification.

Create FaceSwap Job (Image)

Mode 1: Single Face

curl -X POST "{{API_BASE}}/v1/jobs" \
  -H "X-API-Key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
    "task": "faceswap",
    "webhook_url": "https://your-site.com/webhook",
    "payload": {
      "source_url": "https://example.com/face.jpg",
      "target_url": "https://example.com/body.jpg",
      "upscale": 1,
      "max_side": 1280
    }
  }'

Mode 2: Multi-Face

curl -X POST "{{API_BASE}}/v1/jobs" \
  -H "X-API-Key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
    "task": "faceswap_multi",
    "webhook_url": "https://your-site.com/webhook",
    "payload": {
      "source_urls": [
        "https://ex.com/face1.jpg", 
        "https://ex.com/face2.jpg"
      ],
      "target_url": "https://example.com/group.jpg",
      "swap_faces_index": [0, 1],
      "target_faces_index": [2, 0],
      "sharpen": 0.5
    }
  }'
ParamTypeDesc
target_urlstringRequired. Ảnh đích.
source_urlstringẢnh chứa mặt gốc (1 mặt).
source_urlsarrayMảng ảnh chứa mặt (Nhiều mặt).
swap_faces_indexarrayIndex mặt trong nguồn cần lấy.
target_faces_indexarrayIndex mặt trong đích cần thay.
max_sideintResize cạnh lớn nhất (Def: 1080).
upscale0/1Bật Upscale sau khi swap (Def: 1).
sharpenfloatĐộ sắc nét (e.g. 0.12).
max_facesintGiới hạn số mặt xử lý (Def: 0 - All).
POST /v1/jobs task: nsfw_check

Kiểm tra ảnh có chứa nội dung 18+/NSFW hay không. Trả về điểm NSFW score và nhãn safe/nsfw.

curl -X POST "{{API_BASE}}/v1/jobs" \
  -H "X-API-Key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
    "task": "nsfw_check",
    "webhook_url": "https://your-site.com/webhook",
    "payload": {
      "image_url": "https://example.com/photo.jpg",
      "strict": "medium"
    }
  }'
ParamTypeDesc
image_urlstring *URL ảnh cần kiểm tra (bắt buộc).
strictstringMức độ lọc: low (0.75), medium (0.55), high (0.35). Def: low.
thresholdfloatNgưỡng tùy chỉnh 0.0-1.0. Nếu set sẽ ghi đè strict.

Response result:

{
  "nsfw_score": 0.0001,
  "is_nsfw": false,
  "threshold": 0.75,
  "strict": "low",
  "label": "safe",
  "elapsed": 0.098
}
Strict LevelThresholdMô tả
low0.75Mặc định, chỉ block rõ ràng
medium0.55Lọc vừa phải
high0.35Lọc gắt, nhạy cảm cao
POST /v1/nsfw-check Upload file trực tiếp

Endpoint hỗ trợ upload file ảnh trực tiếp (multipart/form-data) hoặc gửi URL. Tự động upload → tạo job → trả job_id.

Upload file:

curl -X POST "{{API_BASE}}/v1/nsfw-check" \
  -H "X-API-Key: {{API_KEY}}" \
  -F "file=@/path/to/image.jpg" \
  -F "strict=medium"

Gửi URL:

curl -X POST "{{API_BASE}}/v1/nsfw-check" \
  -H "X-API-Key: {{API_KEY}}" \
  -F "image_url=https://example.com/photo.jpg" \
  -F "strict=low"
ParamTypeDesc
filefileUpload ảnh trực tiếp (JPG/PNG/WEBP/GIF, max 10MB). Ưu tiên nếu có.
image_urlstringURL ảnh (dùng nếu không upload file).
strictstringMức độ lọc: low, medium, high. Def: low.
thresholdfloatNgưỡng tùy chỉnh 0.0-1.0.
webhook_urlstringURL nhận kết quả qua webhook.

Response:

{
  "ok": true,
  "job_id": "abc123...",
  "status": "queued",
  "image_url": "https://...uploaded-file.jpg",
  "predict_time": 5
}

💡 Sau đó dùng GET /v1/jobs/{job_id} để lấy kết quả, hoặc nhận qua webhook.

2. Webhook & Job Status

Webhook Payload: succeeded

{
  "event": "job.finished",
  "event_id": "f03675fc03334822be22b98ee9215c1d",
  "ts": "2026-02-04T18:04:19.463754+00:00",
  "job_id": "d1142818265748d9914053dafc20dd98",
  "task": "upscale",
  "status": "succeeded",
  "progress": 100,
  "ok": true,
  "created_at": "2026-02-04T18:04:13.773524+00:00",
  "created_at_ms": 1770228253773,
  "queued_at": "2026-02-04T18:04:13.773524+00:00",
  "queued_at_ms": 1770228253773,
  "started_at": "2026-02-04T18:04:14.264808+00:00",
  "started_at_ms": 1770228254264,
  "finished_at": "2026-02-04T18:04:19.462901+00:00",
  "finished_at_ms": 1770228259462,
  "duration": 5.198093,
  "predict_time": 3,
  "eta_utc": "2026-02-04T18:04:16.773638+00:00",
  "result": {
    "ok": true,
    "status": "succeeded",
    "url": "http://15.235.200.66:2022/files/3c3da5ddac6a4c5081d46d829d168cb3.jpg",
    "filename": "3c3da5ddac6a4c5081d46d829d168cb3.jpg",
    "meta": {
      "width": 507,
      "height": 678,
      "backend": "UPVN1-1"
    }
  },
  "error": null,
  "predictTime": 3
}

Job Lifecycle Status

Click vào badge để xem mẫu Webhook JSON tương ứng.

queued Đang chờ trong hàng đợi.
running Backend đang xử lý.
succeeded Hoàn thành, có result.url.
failed Lỗi kỹ thuật hoặc timeout.
rejected Từ chối (NSFW blocked).

3. API Reference

Management Endpoints

GET /stats Lấy danh sách các jobs gần nhất.
GET /v1/jobs/{id} Polling trạng thái Job (nếu không dùng Webhook).
DELETE /tasks/{id} Xóa job khỏi hệ thống.
GET /diag/backends Xem trạng thái tải (slots/concurrency) của các node GPU.

Direct Blocking Endpoints

Gọi trực tiếp, giữ kết nối cho đến khi xong. Payload giống hệt payload của Async Job.

POST /faceswapXử lý FaceSwap Ảnh
POST /faceswap/videoXử lý FaceSwap Video
POST /upscaleUpscale & Restore
POST /ahv/imageGen Ảnh (Text2Img, Img2Img)
POST /ahv/videoVideo Gen (MiniMax H3 — có tiếng)
POST /gg/imageGG Image Gen (Imagen 4)
POST /cx/imageCX Image Gen (GPT-5.4 Image)
POST /remove-objectXóa vật thể (Mask/Prompt)
POST /scan-objectsDetect vật thể (JSON)
POST /remove-backgroundTách nền

4. Voice AI — TTS & STT

Giọng nói tiếng Việt qua cùng một API job của hệ thống (POST /v1/jobs) — có hàng đợi, retry, webhook và quản lý backend trên Orchestrator, giống mọi tác vụ khác. Chỉ cần đổi task.

task: tts task: stt 26 ngôn ngữ Tùy chỉnh giọng Nhân bản giọng

Text → Speech  task: "tts"

Cơ bản

curl -X POST "{{API_BASE}}/v1/jobs" \
  -H "X-API-Key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
    "task": "tts",
    "webhook_url": "https://your-site.com/webhook",
    "payload": {
      "text": "Xin chào anh, đây là giọng đọc thử.",
      "language": "vi"
    }
  }'

Nâng cao — tùy chỉnh giọng + nhân bản

curl -X POST "{{API_BASE}}/v1/jobs" \
  -H "X-API-Key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
    "task": "tts",
    "webhook_url": "https://your-site.com/webhook",
    "payload": {
      "text": "Chào mừng quý khách.",
      "language": "vi",
      "instruct": "female, young adult, high pitch",
      "speed": 1.1,
      "ref_audio_url": "https://example.com/mau_giong.wav",
      "ref_text": "Nội dung chính xác của file mẫu."
    }
  }'

Kết quả (qua webhook / GET /v1/jobs/{id})

{
  "job_id": "2d71af26-...",
  "task": "tts",
  "status": "succeeded",
  "result": {
    "url": "http://.../files/tts_xxxx.wav",
    "duration": 3.63,
    "gen_time": 1.3,
    "mode": "tts",
    "meta": { "backend": "tts-183" }
  }
}
payload.*TypeDesc
textstringRequired. Văn bản cần đọc.
languagestringMã ngôn ngữ hoặc auto.
instructstringMô tả giọng bằng thẻ tiếng Anh, ngăn cách bởi dấu phẩy.
speedfloatTốc độ đọc 0.52.0 (mặc định 1.0).
durationfloat(Optional) Ép độ dài audio (giây).
ref_audio_urlstring(Optional) Link audio mẫu để nhân bản giọng.
ref_textstringKèm ref_audio_url: transcript của mẫu.
num_step, guidance_scaleint/float(Optional) Tinh chỉnh chất lượng diffusion.

Thẻ instruct

NhómGiá trị
Giới tínhfemale, male
Độ tuổichild, teenager, young adult, middle-aged, elderly
Cao độvery low pitchvery high pitch
Phong cáchwhisper

VD "female, young adult, high pitch". Ngôn ngữ: vi, en, zh, ja, ko, th, id, ms, fr, de, es, it, pt, ru, ar, hi, km, lo, my, nl, tr, pl, uk, fa, ta + auto.

Speech → Text  task: "stt"

Request

curl -X POST "{{API_BASE}}/v1/jobs" \
  -H "X-API-Key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
    "task": "stt",
    "webhook_url": "https://your-site.com/webhook",
    "payload": {
      "audio_url": "https://example.com/ghi_am.wav",
      "language": "vi"
    }
  }'

Kết quả

{
  "task": "stt",
  "status": "succeeded",
  "result": {
    "text": "Xin chào anh, đây là giọng đọc thử.",
    "lang": "vi",
    "gen_time": 0.26,
    "mode": "stt",
    "meta": { "backend": "stt-183" }
  }
}
payload.*TypeDesc
audio_urlstringRequired. Link file audio (wav/mp3/m4a...).
languagestring(Optional) Ép ngôn ngữ. Bỏ trống = tự nhận diện.
Voice dùng chung luồng job với các task khác: gửi POST /v1/jobs nhận job_id, kết quả trả qua webhook_url hoặc GET /v1/jobs/{job_id}. Backend giọng nói được quản lý trên Orchestrator (bật/tắt/định tuyến), không gọi thẳng GPU server.

5. Music Generation — ACE-Step v1.5

Tạo nhạc có lời từ mô tả, qua cùng một API job (POST /v1/jobs, task: "music_gen"). Hai chế độ: AI tự viết lời hoặc tự đưa lời.

task: music_gen AI tự viết lời Chọn giọng nam/nữ 30–240 giây Hoà tấu

Chế độ 1 — AI tự viết lời lyrics: "[auto:vi]"

curl -X POST "{{API_BASE}}/v1/jobs" \
  -H "X-API-Key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
    "task": "music_gen",
    "webhook_url": "https://your-site.com/webhook",
    "payload": {
      "prompt": "female vocals, nhạc pop ballad Việt Nam, có piano, về tình bạn tuổi học trò",
      "lyrics": "[auto:vi]",
      "duration": 90,
      "format": "mp3"
    }
  }'

Hệ thống gọi LLM viết lời bám sát mô tả (giữ tên riêng, câu chuyện) rồi mới dựng nhạc.

Chế độ 2 — Tự đưa lời (hát đúng từng chữ)

curl -X POST "{{API_BASE}}/v1/jobs" \
  -H "X-API-Key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
    "task": "music_gen",
    "payload": {
      "prompt": "male vocals, acoustic ballad, guitar mộc, ấm áp",
      "lyrics": "[Verse]\nNắng sớm lên rồi, anh gọi em dậy\nTiếng cười giòn tan theo gió bay xa\n\n[Chorus]\nDẫu mai lớn khôn ta vẫn là gia đình\nNắm chặt tay nhau đi hết con đường",
      "duration": 80,
      "seed": 42
    }
  }'

Chế độ 3 — Sáng tác từ ẢNH image_url 🆕

curl -X POST "{{API_BASE}}/v1/jobs" \
  -H "X-API-Key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
    "task": "music_gen",
    "payload": {
      "image_url": "https://example.com/anh-ky-niem.jpg",
      "language": "vi",
      "duration": 90,
      "prompt": "giọng nữ, nhẹ nhàng"
    }
  }'

AI nhìn ảnh (chủ thể, bối cảnh, ánh sáng, tâm trạng) rồi tự quyết phong cách nhạc + viết lời hợp cảnh. prompt lúc này là gợi ý thêm (không bắt buộc). Kết quả trả thêm meta.scene (mô tả ảnh) và lyrics (lời đã hát).

Hoà tấu (không lời)

"lyrics": "[instrumental]"

Kết quả (qua webhook / GET /v1/jobs/{id})

{
  "job_id": "7796e62b-...",
  "task": "music_gen",
  "status": "succeeded",
  "result": {
    "url": "http://.../files/7724747c.mp3",
    "duration": 90,
    "mode": "music_gen",
    "meta": { "backend": "music-239" }
  }
}
payload.*TypeDesc
promptstringBắt buộc nếu không có image_url. Mô tả phong cách/nội dung, tối đa 1000 ký tự.
image_urlstring🆕 Link ảnh — AI đọc ảnh rồi sáng tác bài hát hợp bối cảnh & tâm trạng. Có ảnh thì prompt/lyrics thành gợi ý.
languagestringNgôn ngữ lời khi dùng image_url (mặc định vi).
lyricsstringLời bài hát, hoặc [auto:xx] để AI viết, hoặc [instrumental]. Tối đa 4000 ký tự. Bỏ trống = hoà tấu.
durationintĐộ dài giây, 30–240 (mặc định 75). Ngoài khoảng sẽ bị kẹp về biên.
formatstringmp3 (mặc định) hoặc wav.
seedint(Optional) Cố định seed để tái tạo bài giống nhau.

[auto:xx] — ngôn ngữ lời (18 ngôn ngữ)

Mức
Mạnh nhất en Anh · zh Trung
Tốt ja Nhật · ko Hàn · es Tây Ban Nha · ru Nga · de Đức · fr Pháp · pt Bồ Đào Nha · it Ý
Có hỗ trợ pl Ba Lan · tr Thổ Nhĩ Kỳ · cs Séc · nl Hà Lan · ar Ả Rập · hu Hungary · hi Hindi
Ngoài vùng huấn luyện vi Việt — vẫn hát được, nhưng model chưa được huấn luyện với nhạc Việt nên chất lượng thấp hơn các nhóm trên
[auto] không kèm mã = mặc định vi · [instrumental] = không lời (hoà tấu) · Mã lạ ngoài bảng cũng rơi về vi.
17 mã đầu là danh sách ngôn ngữ model ACE-Step hỗ trợ chính thức. Trường language của music_cover dùng chung bảng mã này.

Chọn giọng hát

Giọng điều khiển qua prompt bằng thẻ tiếng Anh đặt ở đầu: female vocals, ... hoặc male vocals, .... Ghi "giọng nữ" bằng tiếng Việt không đảm bảo — model chỉ hiểu thẻ tiếng Anh.

Cấu trúc lời (chế độ 2)

Dùng nhãn tiếng Anh trên dòng riêng: [Intro], [Verse], [Pre-Chorus], [Chorus], [Bridge], [Outro]. Mỗi câu hát một dòng.
Thời gian xử lý: ~10–30 giây/bài (chế độ tự viết lời cộng thêm ~10s cho bước soạn lời). Job chạy tuần tự trên 1 slot — gửi nhiều bài sẽ xếp hàng, theo dõi bằng GET /v1/jobs/{job_id}.

6. Music Cover — Phối nhạc từ audio có sẵn

Gửi một file âm thanh + mô tả phong cách → hệ thống phối lại thành bài mới. Hai kịch bản dùng chung task: "music_cover", khác nhau ở mode:

mode: vocal — bạn tự hát → phối nhạc mode: remix — đổi phong cách, giữ lời Tự giữ lời bằng STT

A. Tự hát → phối nhạc mode: "vocal"

curl -X POST "{{API_BASE}}/v1/jobs" \
  -H "X-API-Key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
    "task": "music_cover",
    "webhook_url": "https://your-site.com/webhook",
    "payload": {
      "audio_url": "https://example.com/toi-tu-hat.m4a",
      "prompt": "warm acoustic ballad, gentle guitar, soft piano, emotional",
      "mode": "vocal",
      "language": "vi"
    }
  }'

B. Phối lại bài cũ, giữ nguyên lời mode: "remix"

curl -X POST "{{API_BASE}}/v1/jobs" \
  -H "X-API-Key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
    "task": "music_cover",
    "payload": {
      "audio_url": "https://example.com/bai-goc.mp3",
      "prompt": "upbeat EDM dance remix, energetic synths, festival vibe",
      "mode": "remix",
      "keep_lyrics": true,
      "language": "vi"
    }
  }'

Kết quả

{
  "task": "music_cover",
  "status": "succeeded",
  "result": {
    "url": "http://.../files/51b843ab.mp3",
    "duration": 30,
    "src_duration": 30.0,
    "lyrics": "Nắng sớm lên rồi, em bước qua sân nhà...",
    "mode": "music_cover",
    "meta": { "backend": "cover-239", "mode": "remix", "lyrics_from": "stt" }
  }
}
payload.*TypeDesc
audio_urlstringRequired. Link audio nguồn (mp3/wav/m4a/flac/ogg).
promptstringRequired. Phong cách nhạc muốn ra (nên viết tiếng Anh cho chuẩn: thể loại, nhạc cụ, tempo, mood).
modestringvocal: GIỮ NGUYÊN giọng bạn thu, hệ sinh nhạc nền theo prompt rồi hoà âm (có ducking — nhạc nhường khi có giọng). remix (mặc định): tách giọng hát khỏi bản gốc (AI separation), sinh nhạc nền phong cách mới khớp BPM & tông của bài gốc, rồi hoà âm lại → giữ nguyên 100% giọng hát và lời, chỉ thay nhạc. Lời trả về được nhận dạng trên track giọng đã tách nên chính xác hơn nhiều.
keep_lyricsboolMặc định true: nếu không gửi lyrics, hệ thống tự nghe audio nguồn (STT) để giữ nguyên lời.
lyricsstring(Optional) Lời tự nhập, ưu tiên hơn STT. Tối đa 4000 ký tự.
languagestringNgôn ngữ lời (mặc định vi).
durationintGiây. Bỏ trống/0 = theo độ dài audio nguồn (kẹp 20–240s).
keepfloat0–1: mức bám bản gốc. Càng cao càng ít nhạc cụ (chỉ giữ giọng); càng thấp thì hệ dựng nhạc càng nhiều. Mặc định 0.25 (vocal) / 0.35 (remix). Muốn nhiều nhạc: 0.15–0.25.
strengthfloat0–1: mức áp phong cách mới (nhỏ = đổi mạnh). Mặc định 0.15 (vocal) / 0.2 (remix).
seedint(Optional) Cố định để tái tạo.
Mẹo chất lượng: bản thu càng rõ tiếng, ít ồn thì phối càng chuẩn. Muốn đổi phong cách mạnh → giảm keep (0.15–0.25); muốn giữ giai điệu gốc → tăng keep (0.5–0.7).
Thời gian xử lý: ~5–30 giây tuỳ độ dài (cộng ~1s nếu hệ thống tự lấy lời bằng STT). Chạy tuần tự 1 slot như Music Gen.

7. Chat LLM — API chuẩn OpenAI

Endpoint chat tương thích hoàn toàn với OpenAI. Đang dùng thư viện openai (Python/Node) hay bất kỳ công cụ nào nói chuẩn OpenAI, bạn chỉ cần đổi base_url — không sửa một dòng code nào khác. Khoá của nhà cung cấp nằm ở phía máy chủ, không bao giờ lộ ra trình duyệt.

Model mặc định là ahv-qwen38model của nhà, chạy trên GPU riêng, biết suy luận và gọi công cụ. Không truyền model thì hệ thống tự dùng nó. Nếu máy chủ Qwen chưa sẵn sàng, hệ thống tự lui về nhà cung cấp bên ngoài để câu hỏi của bạn không bị lỗi — bạn không phải xử lý gì thêm.

POST /v1/chat/completions GET /v1/models Streaming SSE Gọi công cụ Chế độ suy luận Đọc ảnh Nhớ ngữ cảnh nhiều lượt Ngữ cảnh 256K · đầu ra 64K 1012 model

Đổi base_url là chạy  POST /v1/chat/completions

Python — thư viện openai

from openai import OpenAI

# api_key chưa bắt buộc, chỉ để cho hợp SDK
cl = OpenAI(
    base_url="http://15.235.200.66:2022/v1",
    api_key="anhyeuem",
)

# bỏ model đi cũng được, mặc định đã là ahv-qwen38
r = cl.chat.completions.create(
    model="ahv-qwen38",
    messages=[
        {"role": "user", "content": "Viết 3 câu quảng cáo"}
    ],
)
print(r.choices[0].message.content)

Streaming — chữ hiện dần

s = cl.chat.completions.create(
    model="ahv-qwen38",
    messages=[
        {"role": "user", "content": "Kể một câu chuyện"}
    ],
    stream=True,
)
for chunk in s:
    manh = chunk.choices[0].delta.content
    if manh:
        print(manh, end="", flush=True)

Gọi thẳng bằng curl

curl -s http://15.235.200.66:2022/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "ahv-qwen38",
    "messages": [
      {"role": "system", "content": "Bạn là trợ lý của AHV, trả lời bằng tiếng Việt."},
      {"role": "user",   "content": "Giới thiệu hệ thống AHV trong 2 câu"}
    ],
    "temperature": 0.6,
    "max_tokens": 500
  }'

Tham số

TrườngKiểuMặc địnhMô tả
modelstringahv-qwen38 Tên model. Bỏ trống thì dùng ahv-qwen38 (model của nhà). Lấy danh sách đầy đủ ở GET /v1/models.
messagesarraybắt buộc Mảng {role, content}. role nhận system / user / assistant. Gửi cả lịch sử để model nhớ ngữ cảnh.
streamboolfalse true để nhận SSE từng mảnh (choices[0].delta.content), kết thúc bằng data: [DONE].
temperaturefloat0.6 Càng cao càng sáng tạo, càng thấp càng bám sát. Sinh code nên đặt 0.1–0.3.
max_tokensint65536 Số token tối đa cho câu trả lời. Cửa sổ ngữ cảnh 262.144 token (256K) — đủ cho các CLI coding nạp cả kho mã. Bỏ trống thì hệ thống tự lấy 65536, và tự co lại nếu câu hỏi quá dài để không tràn ngữ cảnh. Lưu ý: câu trả lời cực dài mất nhiều thời gian (tốc độ ~18 token/giây, 65.536 token ≈ 58 phút) — nên dùng stream: true để nhận dần.
toolsarray Khai báo công cụ để model gọi — xem phần Gọi công cụ.
reasoning_effortstringlow Mức suy luận: minimal / low / medium / high, hoặc none để tắt hẳn — xem phần Chế độ suy luận.
reasoningobject {effort, enabled, exclude} — cách viết của OpenRouter, cũng nhận.
thinkingobject {type, budget_tokens} — cách viết của Anthropic, cũng nhận.

Giới hạn & lỗi

Ý nghĩaXử lý
400Thiếu messages hoặc body không phải JSONKiểm lại body.
429Quá 120 lượt/phút trên một IPChờ một chút rồi gọi lại.
502Cả model của nhà lẫn nhà cung cấp dự phòng đều không trả nội dung (thường do đang giới hạn tần suất) Hiếm gặp, vì hệ thống đã tự lui về dự phòng. Thử lại sau ít phút hoặc đổi model khác.
503Dịch vụ LLM đang tắt trong cấu hìnhBật lại ở trang Admin.

Lỗi trả về đúng khuôn OpenAI — {"error": {"message": "...", "type": "..."}} — nên SDK bắt được như với API gốc. Mỗi lượt tối đa 400 tin nhắn; hội thoại dài hơn thì phần cũ nhất bị lược bớt nhưng mọi tin system luôn được giữ nguyên. Nội dung không bao giờ bị cắt âm thầm: vượt cửa sổ ngữ cảnh thì trả lỗi 400 context_length_exceeded kèm số token ước tính.

Danh sách model  GET /v1/models

curl -s http://15.235.200.66:2022/v1/models
{
  "object": "list",
  "data": [
    {"id": "ahv-qwen38",        "object": "model", "created": 1787330074, "owned_by": "ahv"},
    {"id": "AHV-Holding-TroLy", "object": "model", "created": 1787330074, "owned_by": "ahv"},
    {"id": "Grok-SuperHeavy",   "object": "model", "created": 1787330074, "owned_by": "ahv"}
  ]
}

Chế độ suy luận — reasoning_effort

ahv-qwen38 là model biết suy nghĩ trước khi trả lời. Mặc định hệ thống đặt mức low — chúng tôi đã đo trên bộ bài code khó và low cho kết quả đúng ngang mức cao nhất nhưng nhanh hơn khoảng 32%. Bạn chỉnh bằng reasoning_effortcấp cao nhất của body, đúng như cách gọi OpenAI:

curl -X POST http://15.235.200.66:2022/v1/chat/completions \
  -H 'Content-Type: application/json' -d '{
  "messages": [{"role": "user", "content": "1 + 1 bằng mấy?"}],
  "reasoning_effort": "low"
}'
Giá trịModel làm gìDùng khi nào
low / minimal mặc định Nghĩ ngắn gọn rồi trả lời ngay Hầu hết mọi việc, kể cả code — đo được 10/10 bài
mediumNghĩ vừa phảiCâu hỏi thường ngày
high Nghĩ kỹ, tự kiểm giả định, cân nhắc phương án khác Bài thật sự hóc búa — chậm hơn ~50%, không cho thêm điểm ở bài thường
noneTắt hẳn phần suy nghĩ, trả lời thẳng Chat nhanh, dịch, viết lại câu

Viết theo chuẩn nào cũng chạy

Mỗi hãng đặt tên tham số một kiểu, nên các CLI và SDK cũng gửi mỗi bên một kiểu. Máy chủ nhận cả bốn, bạn không phải sửa công cụ đang dùng:

"reasoning_effort": "high"                          // OpenAI
"reasoning": {"effort": "high", "exclude": false}   // OpenRouter
"thinking": {"type": "enabled", "budget_tokens": 8000}   // Anthropic
"chat_template_kwargs": {"reasoning_effort": "xhigh"}    // vLLM

Đặt "reasoning": {"exclude": true} nếu muốn model vẫn nghĩ nhưng không trả phần nghĩ về cho bạn (tiết kiệm băng thông khi bạn không hiển thị nó). Gửi giá trị lạ thì máy chủ trả 400 kèm danh sách giá trị hợp lệ, chứ không im lặng bỏ qua.

Đọc phần suy nghĩ ở đâu

content trả về luôn sạch — không bao giờ lẫn khối <think>. Phần suy nghĩ nằm riêng, và trả cùng lúc ở cả hai tên (reasoning theo OpenRouter, reasoning_content theo DeepSeek) để công cụ nào cũng đọc được:

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "2",
      "reasoning": "Người dùng hỏi 1+1...",
      "reasoning_content": "Người dùng hỏi 1+1..."
    }
  }]
}

Khi stream: true, phần nghĩ về trước theo từng mảnh ở delta.reasoningdelta.reasoning_content, xong mới tới delta.content — nên bạn hiện được khối “đang suy nghĩ” theo thời gian thực.

Lưu ý về max_tokens: phần suy nghĩ cũng tính vào max_tokens. Đặt quá thấp (dưới ~800) thì model có thể dùng hết token vào việc nghĩ và trả content rỗng — phần nghĩ vẫn nằm trong reasoning. Mặc định 65536 đã đủ rộng cho hầu hết nhu cầu; muốn ngắn và nhanh thì hạ reasoning_effort xuống low / none.

Gọi công cụ — tools / tool_calls

Hỗ trợ function calling chuẩn OpenAI. Bạn khai báo tools như với OpenAI, model trả về tool_calls đã được cấu trúc hoá sẵn (kèm idarguments dạng chuỗi JSON) — không phải XML thô, không cần bạn tự bóc tách. Khi model quyết định gọi công cụ, finish_reasontool_calls. Chế độ streaming cũng hỗ trợ: tên hàm về trước, arguments nhả dần theo từng mảnh.

curl -X POST http://15.235.200.66:2022/v1/chat/completions \
  -H 'Content-Type: application/json' -d '{
  "messages": [{"role": "user", "content": "Tra cứu thời tiết Hà Nội giúp tôi."}],
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "Lấy thời tiết một thành phố",
      "parameters": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
      }
    }
  }]
}'

Model trả về:

{
  "choices": [{
    "finish_reason": "tool_calls",
    "message": {
      "role": "assistant",
      "content": "",
      "tool_calls": [{
        "id": "chatcmpl-tool-9ef9a5d721211647",
        "type": "function",
        "function": {"name": "get_weather", "arguments": "{\"city\": \"Ha Noi\"}"}
      }]
    }
  }]
}

Sau khi chạy hàm, gửi kết quả trở lại bằng message role: "tool" kèm tool_call_id đúng bằng id ở trên, model sẽ dùng nó để viết câu trả lời cuối:

"messages": [
  {"role": "user", "content": "Tra cứu thời tiết Hà Nội giúp tôi."},
  {"role": "assistant", "tool_calls": [ ...như trên... ]},
  {"role": "tool", "tool_call_id": "chatcmpl-tool-9ef9a5d721211647",
   "content": "{\"temp_c\": 31, \"desc\": \"nắng\"}"}
]
Dùng được luôn với thư viện openai. Vì đúng chuẩn nên client.chat.completions.create(..., tools=[...]) chạy y hệt như khi trỏ vào OpenAI, kể cả tool_choiceparallel_tool_calls.

8. Giọng nói — API chuẩn OpenAI

Hai endpoint cho trò chuyện bằng giọng nói, dùng thẳng TTS/STT chạy trên GPU nhà. Gọi trực tiếp (không qua hàng đợi /v1/jobs) vì hội thoại là thời gian thực.

POST /v1/audio/transcriptions POST /v1/audio/speech Nghe: ~0,4s Đọc: ~0,9s Tiếng Việt có dấu

Nghe → chữ  POST /v1/audio/transcriptions

Nhận một trong hai cách: gửi link âm thanh, hoặc tải thẳng file lên (multipart). Đo thật: gửi link 0,79s, gửi file 1,77s (đã gồm thời gian lưu file).

Cách 1 — gửi link

curl -s http://15.235.200.66:2022/v1/audio/transcriptions \
  -H 'Content-Type: application/json' \
  -d '{"audio_url": "https://.../ghi_am.wav"}'

Cách 2 — tải file lên

curl -s http://15.235.200.66:2022/v1/audio/transcriptions \
  -F "file=@ghi_am.webm"
{
  "text": "Xin chào anh Hiếu, đây là giọng đọc thử.",
  "language": "vi",
  "duration": 0.38
}

Định dạng nhận được: webm, wav, mp3, m4a, ogg, opus, flac. Tối đa 25MB.

Chữ → tiếng  POST /v1/audio/speech

curl -s http://15.235.200.66:2022/v1/audio/speech \
  -H 'Content-Type: application/json' \
  -d '{
    "input": "Xin chào, tôi là trợ lý AHV. Bạn cần giúp gì ạ?",
    "language": "vi"
  }'
{
  "url": "http://178.63.96.183:2037/files/tts_714812a2.wav",
  "duration": 3.3,
  "xu_ly": 1.24
}

Trả về link file thay vì luồng nhị phân, để trình duyệt phát ngay bằng thẻ <audio> mà không phải chờ tải hết.

Tham số

EndpointTrườngKiểuMặc địnhMô tả
transcriptions audio_urlstring Link file âm thanh. Bắt buộc nếu không gửi file.
filemultipart File âm thanh tải thẳng lên. Bắt buộc nếu không gửi audio_url.
speech inputstringbắt buộc Văn bản cần đọc. Tối đa 1500 ký tự mỗi lượt.
languagestringvi Mã ngôn ngữ.
voicestring(mặc định) female hoặc male.

Mã lỗi

Ý nghĩaXử lý
400Thiếu trường bắt buộc, định dạng âm thanh không hỗ trợ, hoặc file quá 25MBKiểm lại body.
422Nghe được file nhưng không ra chữ nào (tiếng quá nhỏ hoặc chỉ có tạp âm)Nhắc người dùng nói to và rõ hơn.
429Quá 120 lượt/phút trên một IPChờ một chút rồi gọi lại.
502Dịch vụ TTS/STT lỗi hoặc không trả kết quảThử lại sau ít phút.

Lỗi trả về đúng khuôn OpenAI — {"error": {"message": "...", "type": "..."}}.

Mẹo làm hội thoại phản hồi nhanh. Đừng chờ mô hình viết xong cả đoạn rồi mới đọc. Hãy cắt câu theo dấu . ! ? ngay trong lúc nhận luồng chữ, rồi gọi /v1/audio/speech cho từng câu và phát nối tiếp. Trang demo Chat AI làm đúng như vậy — người dùng nghe thấy tiếng sau khoảng 3–5 giây thay vì phải đợi hết câu trả lời.
© AHV AI Orchestrator • Documentation