Tài liệu

Tài liệu

hoàn thành cuộc trò chuyện

Endpoint /v1/chat/completions của Kunavo dùng giao thức Chat Completions của OpenAI cho các họ Claude và GPT. Streaming, tools và đầu ra JSON hoạt động qua cùng SDK; tác dụng của reasoning_effort phụ thuộc vào họ mô hình và trang này nêu rõ tác dụng đó.

Endpoint: POST /v1/chat/completions. Hình dạng yêu cầu và phản hồi khớp chính xác với API Chat Completions của OpenAI — bao gồm streaming và các trường tùy chọn tool_calls / reasoning_tokens.

Lệnh gọi cơ bản

resp = client.chat.completions.create(
    model="claude-sonnet-5",
    messages=[
        {"role": "system", "content": "You are a senior staff engineer."},
        {"role": "user", "content": "Pros and cons of postgres LISTEN/NOTIFY for a job queue?"},
    ],
    max_tokens=800,
)
print(resp.choices[0].message.content)

Tham số

Mọi tham số OpenAI tiêu chuẩn đều được chấp nhận. Một số tham số chỉ có ý nghĩa với một số nhà cung cấp nhất định — bộ chuyển đổi sẽ chuyển tiếp những tham số mà từng nhà cung cấp thượng nguồn hỗ trợ.

Tham sốLoạiGhi chú
modelstring (bắt buộc)Mọi slug đã bật trong /v1/models.
messagesarray (bắt buộc)Định dạng thông điệp OpenAI tiêu chuẩn.
temperature0..2Nhiệt độ lấy mẫu. Mặc định là 1. Không có tác dụng với các mô hình được liệt kê bên dưới bảng này.
top_p0..1Lấy mẫu theo hạt nhân. Có cùng ngoại lệ như temperature.
max_tokensintGiới hạn đầu ra; max_completion_tokens được ưu tiên nếu gửi cả hai. Trên các mô hình GPT, token suy luận được tính vào giới hạn này. Yêu cầu Claude không gửi giá trị nào trong hai giá trị sẽ dùng 4.096.
streamboolTruyền các phần dữ liệu theo dạng SSE. Xem bên dưới.
toolsarrayĐịnh nghĩa hàm/công cụ. Cả Claude và GPT đều hỗ trợ sử dụng công cụ.
tool_choiceauto|none|namedBuộc sử dụng một công cụ cụ thể hoặc để mô hình tự quyết định.
response_formatobjectĐầu ra JSON. Trên Claude, json_schema được thực thi còn json_object thì không — xem Đầu ra JSON để biết chi tiết cho từng họ.
reasoning_effortstringChỉ áp dụng cho mô hình GPT — được chuyển tiếp dưới dạng reasoning.effort. Không chuyển tiếp cho Claude. Xem Suy luận.
seedintLấy mẫu xác định trong trường hợp được hỗ trợ.
stopstring|arrayCác chuỗi dừng bắt buộc.
Claude Fable 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5, Claude Opus 4.8, Claude Opus 4.7 và Claude Sonnet 5 không chấp nhận temperature, top_p hoặc top_k. Kunavo xóa ba tham số này khỏi yêu cầu gửi đến các mô hình đó thay vì chuyển tiếp lỗi 400 của nhà cung cấp cho bạn, nên chúng không có tác dụng với các mô hình ấy.

Truyền dữ liệu theo luồng

Đặt stream=True. Kunavo phát các sự kiện do máy chủ gửi theo đúng định dạng của OpenAI: mỗi phần dữ liệu là một chat.completion.chunk có choices[0].delta.content. Tải trọng usage cuối cùng được gửi kèm data: [DONE].

stream = client.chat.completions.create(
    model="claude-haiku-4-5",
    messages=[{"role": "user", "content": "Explain B-trees in one paragraph."}],
    stream=True,
)

for chunk in stream:
    delta = chunk.choices[0].delta.content
    if delta:
        print(delta, end="", flush=True)
Streaming hoạt động với mọi mô hình chat. Với Claude (nhà cung cấp thượng nguồn dùng giao thức Anthropic), chúng tôi dịch luồng sự kiện Anthropic thành các delta OpenAI ngay khi dữ liệu đến — SDK của bạn không nhận thấy khác biệt.

Gọi công cụ / hàm

Gọi công cụ hoạt động trên nhiều nhà cung cấp. Định nghĩa công cụ bằng lược đồ JSON; mô hình trả về tool_calls trong thông điệp của nó; bạn thực thi công cụ rồi gửi kết quả trở lại dưới dạng thông điệp role: "tool".

tools = [{
    "type": "function",
    "function": {
        "name": "get_current_weather",
        "description": "Get current weather for a city",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string"},
                "unit": {"type": "string", "enum": ["c", "f"]},
            },
            "required": ["city"],
        },
    },
}]

resp = client.chat.completions.create(
    model="claude-sonnet-5",
    messages=[{"role": "user", "content": "What's the weather in Tokyo?"}],
    tools=tools,
    tool_choice="auto",
)

# Inspect tool calls the model wants to make
for call in resp.choices[0].message.tool_calls or []:
    print(call.function.name, call.function.arguments)

Đầu ra JSON (response_format)

response_format yêu cầu đầu ra JSON. Mức độ bảo đảm phụ thuộc vào họ mô hình vì yêu cầu của từng họ được dịch sang một cơ chế thượng nguồn khác nhau:

Họjson_schemajson_object
ClaudeĐược thực thi. Gửi dưới dạng structured outputs của Anthropic (output_config.format); phản hồi bị giới hạn theo lược đồ của bạn. Đã kiểm tra trên mọi mô hình Claude mà Kunavo cung cấp, 2026-09-24.Không được thực thi. Anthropic không có chế độ JSON không cần lược đồ, nên tham số được chấp nhận nhưng bỏ qua — phản hồi có thể bắt đầu bằng văn xuôi hoặc khối mã.
GPTĐược chuyển tiếp dưới dạng text.format của Responses API, nhưng nhà cung cấp thượng nguồn hiện tại không áp dụng tham số này cho mọi yêu cầu. Hãy xác thực trước khi phân tích cú pháp.Được chuyển tiếp dưới dạng text.format.
import json

resp = client.chat.completions.create(
    model="claude-haiku-4-5",
    messages=[{"role": "user", "content": "Jane Doe <jane@example.com> wants the Pro plan."}],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "signup",
            "schema": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "email": {"type": "string"},
                    "plan": {"type": "string", "enum": ["free", "pro", "enterprise"]},
                },
                "required": ["name", "email", "plan"],
                "additionalProperties": False,
            },
        },
    },
)
signup = json.loads(resp.choices[0].message.content)  # constrained to the schema

Có bốn điều cần biết về Claude:

  • Lược đồ được gửi đến Anthropic gần như nguyên trạng như bạn đã viết. Anthropic yêu cầu additionalProperties: false trên các đối tượng, vì vậy Kunavo thêm trường này vào mọi đối tượng có liệt kê thuộc tính và để trống giá trị — đúng như cách model_json_schema() của Pydantic tạo ra. Những trường hợp Anthropic vẫn từ chối sẽ trả lỗi 400 nêu rõ vấn đề: một đối tượng để mở (additionalProperties được đặt thành bất kỳ giá trị nào khác false, hoặc không liệt kê thuộc tính nào và additionalProperties để trống), cùng với oneOf — hãy dùng anyOf. Trang structured outputs của Anthropic liệt kê các trường hợp còn lại.
  • strict và name bị bỏ qua: Anthropic thực thi mọi lược đồ mà họ chấp nhận, dù ở chế độ strict hay không.
  • Không thể kết hợp lược đồ với thông điệp assistant cuối cùng (prefill); Anthropic trả lỗi 400.
  • Khi max_tokens hết giữa chừng trong một đối tượng, JSON sẽ bị cắt cụt và finish_reason là "length".

Đầu vào vision / đa phương thức

Các mô hình có khả năng vision chấp nhận khối nội dung hình ảnh. Dùng URL HTTPS hoặc URI base64 data:.

# Pass an image URL or a base64 data URI as part of a multimodal message
resp = client.chat.completions.create(
    model="claude-haiku-4-5",
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": "What's in this image?"},
            {"type": "image_url", "image_url": {
                "url": "https://example.com/cat.jpg"
            }},
        ],
    }],
)
print(resp.choices[0].message.content)

Các mô hình có khả năng thị giác trong danh mục bao gồm Claude Opus 5.5 — xem danh sách mô hình để biết các mô hình khác.

Với hình ảnh https, Kunavo tự tải tệp xuống rồi gửi trực tiếp cho mô hình vì một số nhà cung cấp thượng nguồn bỏ qua URL hình ảnh thuần túy. Giới hạn: 16 hình ảnh cho mỗi yêu cầu, 5 MB mỗi hình ảnh, tổng cộng 20 MB, thời gian tải xuống 10 giây. Nếu không thể tải hình ảnh xuống, hệ thống trả về lỗi nêu tên hình ảnh đó thay vì bỏ qua.

Suy luận

Tác dụng của điều khiển suy luận trên endpoint này phụ thuộc vào họ mô hình vì yêu cầu của từng họ được dịch sang API thượng nguồn khác nhau:

Họreasoning_effortToken suy luận trong usage
GPTĐược chuyển tiếp nguyên giá trị dưới dạng reasoning.effort của Responses API trên GPT-6 Astra, GPT-6 Sol, GPT-6 Luna, GPT-5.6 Sol, GPT-5.6 Terra và GPT-5.5. Các giá trị được chấp nhận là giá trị riêng của mô hình; trên GPT-6 Sol và GPT-6 Luna, none tắt suy luận còn max là mức cao nhất.Được tính trong completion_tokens và tách riêng thành completion_tokens_details.reasoning_tokens, tính phí theo mức giá đầu ra.
ClaudeKhông được chuyển tiếp. Yêu cầu Anthropic được tạo từ một danh sách trường cố định — model, max_tokens, messages, stream, temperature, top_p, top_k, stop, tools, tool_choice và response_format — vì vậy reasoning_effort, thinking và mọi trường khác nằm ngoài danh sách này đều bị loại bỏ tại đây.Không có phần phân tách. Anthropic tính mọi nội dung suy nghĩ bên trong output_tokens, đây là giá trị mà completion_tokens báo cáo.
# GPT models: reasoning_effort is forwarded as the Responses API's
# reasoning.effort. Reasoning tokens are part of completion_tokens.
resp = client.chat.completions.create(
    model="gpt-6-sol",
    messages=[{"role": "user", "content": "Plan a 3-week MLOps migration."}],
    reasoning_effort="high",
)
details = resp.usage.completion_tokens_details   # absent when no reasoning ran
print(details.reasoning_tokens if details else 0)
Extended thinking trên Claude sử dụng Messages API gốc, API này chuyển tiếp thinking đến Anthropic mà không dịch.

bộ nhớ đệm prompt

Một system prompt dài, tài liệu tham chiếu, một khối few-shot — bất kỳ tiền tố ổn định nào cũng có thể được lưu vào bộ nhớ đệm ở upstream và phát lại trong các lần gọi sau với chi phí đầu vào chỉ bằng một phần nhỏ. Cache hit được hiển thị trong đối tượng usage dưới dạng prompt_tokens_details.cached_tokens.

Các mô hình GPT tự động lưu bộ nhớ đệm — không cần thay đổi request. cached_tokens là tập con của prompt_tokens và được tính phí theo mức đọc từ bộ nhớ đệm giảm giá.

Claude chỉ lưu bộ nhớ đệm đến một điểm ngắt cache_control, và với prompt dài, endpoint này đặt các điểm ngắt thay cho bạn: trên system prompt, trên các định nghĩa công cụ và trên tin nhắn cuối cùng khi cuộc trò chuyện đã có lượt trả lời của trợ lý. Để tự chọn tiền tố, hãy gắn cache_control vào một content block như bên dưới; Kunavo giữ nguyên các điểm ngắt của bạn và chỉ thêm điểm ngắt của mình vào những vị trí bạn để trống:

# Claude caches prefixes you mark with cache_control. Attach it to a content
# block; later calls reusing that prefix read it at ~10% of the input price.
resp = client.chat.completions.create(
    model="claude-sonnet-5",
    messages=[{
        "role": "user",
        "content": [
            {
                "type": "text",
                "text": LONG_DOCUMENT,            # the stable, reused prefix
                "cache_control": {"type": "ephemeral"},
            },
            {"type": "text", "text": "Summarize the document above."},
        ],
    }],
)
print(resp.usage.prompt_tokens_details.cached_tokens)
Để kiểm soát toàn diện việc lưu bộ nhớ đệm prompt của Claude — lưu system prompt và định nghĩa công cụ, dùng nhiều điểm ngắt, và các trường usage gốc cache_creation_input_tokens / cache_read_input_tokens — hãy dùng Messages API gốc. Endpoint này cũng lưu system prompt và các định nghĩa công cụ vào bộ nhớ đệm, nhận nhiều điểm ngắt trong một yêu cầu, và báo cáo phần ghi vào bộ nhớ đệm dưới dạng cache_creation_input_tokens, phần đọc từ bộ nhớ đệm dưới dạng prompt_tokens_details.cached_tokens; điều chỉ endpoint gốc làm là chuyển tiếp phần thân yêu cầu của bạn mà không thêm điểm ngắt nào.

Đối tượng usage

Mỗi phản hồi (và chunk cuối cùng của luồng) đều có đối tượng usage:

TrườngÝ nghĩa
prompt_tokensSố token đầu vào được tính phí — bao gồm cả token được lưu trong bộ nhớ đệm.
prompt_tokens_details.cached_tokensĐầu vào được lưu trong bộ nhớ đệm — một tập con của prompt_tokens, được tính phí theo mức đọc từ bộ nhớ đệm giảm giá.
completion_tokensSố token đầu ra đã tính phí. Trên các mô hình GPT, số này bao gồm token suy luận; trên Claude, đây là output_tokens của Anthropic.
completion_tokens_details.reasoning_tokensCác mô hình GPT: phần token suy luận trong completion_tokens. Không có trường này khi giá trị bằng 0 và không được báo cáo cho Claude.
total_tokensprompt_tokens + completion_tokens.

Khả năng tương thích OpenAI của Ollama — /v1/chat/completions

Ollama cung cấp các mô hình chạy cục bộ thông qua API tương thích với OpenAI tại http://localhost:11434/v1, triển khai cùng hợp đồng /chat/completions được mô tả trên trang này. Nhờ đó, hai API có thể thay thế cho nhau ở phía client: mọi thứ được viết cho API v1 của Ollama đều chạy được với API của Kunavo chỉ bằng cách đổi URL cơ sở và mô hình.

import os

from openai import OpenAI

# Before — Ollama's OpenAI-compatible API, local models
client = OpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama",                       # ignored locally
)
resp = client.chat.completions.create(
    model="llama3.2",
    messages=[{"role": "user", "content": "Summarize this changelog."}],
)

# After — the same call against hosted frontier models.
# Two lines changed. Everything below them is untouched.
client = OpenAI(
    base_url="https://api.kunavo.com/v1",   # <- was localhost:11434/v1
    api_key=os.environ["KUNAVO_API_KEY"],
)
resp = client.chat.completions.create(
    model="claude-sonnet-5",  # <- was llama3.2
    messages=[{"role": "user", "content": "Summarize this changelog."}],
)

Những phần thường gặp lỗi khi chuyển đổi nhà cung cấp vẫn hoạt động ở đây, vì định dạng truyền tải là như nhau:

Tính năngOllama v1Kunavo v1
Đường dẫn endpoint/v1/chat/completions/v1/chat/completions
Truyền dữ liệu theo luồngSSE, chat.completion.chunkGiống hệt — cùng cấu trúc delta
Gọi công cụtools / tool_callsGiống hệt trên Claude và GPT
Đầu vào hình ảnhCác khối image_url trên những mô hình cục bộ đa phương thứcCác khối giống nhau; URL hoặc URI dữ liệu base64
api_keyGiá trị giữ chỗ, bị bỏ quaMột khóa Kunavo thật
modelMột tag đã tải về (llama3.2)Một slug lưu trữ (claude-sonnet-5)
usage bổ sungChỉ số lượng tokenBổ sung cached_tokens và reasoning_tokens
Ngoài chatcompletions, embeddingsCộng thêm các endpoint hình ảnh, video và âm thanh
Dùng cả hai? Đọc base_url, model và khóa từ các biến môi trường rồi chuyển đổi theo môi trường — mô hình cục bộ để lặp lại khi ngoại tuyến, mô hình lưu trữ khi cần chất lượng, tất cả trên cùng một luồng mã. Hướng dẫn đầy đủ, bao gồm những gì giao diện OpenAI của Ollama có và không triển khai, có trong hướng dẫn API tương thích với OpenAI của Ollama.

Tài liệu chính thức về endpoint /v1/chat/completions tương thích OpenAI của Ollama ở đâu?

Ollama lưu tài liệu về lớp tương thích OpenAI trong kho mã nguồn riêng, tại docs/openai.md; đây là tài liệu tham khảo có thẩm quyền về các trường OpenAI mà máy chủ cục bộ hỗ trợ. Trang này là tài liệu tương đương cho endpoint /v1/chat/completions được lưu trữ trên Kunavo: cùng hợp đồng Chat Completions của OpenAI, được mô tả từng trường ở trên, để bạn có thể đối chiếu trực tiếp hai giao diện. Tóm lại, hình dạng yêu cầu và phản hồi là giống nhau — Ollama cung cấp endpoint tại http://localhost:11434/v1 cho các mô hình được tải xuống cục bộ, còn Kunavo cung cấp endpoint tại https://api.kunavo.com/v1 cho các mô hình Claude và GPT được lưu trữ.

Endpoint /v1/chat/completions của Kunavo có tương thích với API OpenAI của Ollama không?

Có — cả hai đều triển khai cùng hợp đồng Chat Completions của OpenAI, nên ứng dụng viết cho Ollama tại http://localhost:11434/v1 sẽ hoạt động nguyên trạng với https://api.kunavo.com/v1. Các trường yêu cầu (messages, temperature, max_tokens, stream, tools, tool_choice, response_format, stop, seed) và trường phản hồi (choices[].message, choices[].delta trong luồng stream, usage) đều trùng khớp. Khác biệt đúng như dự kiến đối với các mô hình được lưu trữ: api_key là khóa thực thay vì giá trị giữ chỗ, model nhận slug được lưu trữ như claude-sonnet-5 thay vì thẻ mô hình đã tải như llama3.2, và usage có cached_tokens cùng reasoning_tokens mà các mô hình cục bộ không báo cáo.

Làm cách nào để chuyển ứng dụng Ollama sang Claude hoặc GPT được lưu trữ?

Thay đổi hai dòng: đặt base_url từ http://localhost:11434/v1 thành https://api.kunavo.com/v1, rồi đặt model từ thẻ cục bộ thành slug được lưu trữ. Dùng khóa Kunavo thực thay cho giá trị giữ chỗ mà Ollama bỏ qua. Không cần thay đổi mã xử lý streaming, gọi công cụ hay vision vì cấu trúc SSE và tool_calls giống hệt nhau.

Tôi có thể tiếp tục dùng Ollama để phát triển và dùng mô hình được lưu trữ trong môi trường production không?

Có, đây là cách thiết lập phổ biến — đọc URL cơ sở, mô hình và khóa từ các biến môi trường rồi chuyển đổi theo từng môi trường. Vì cả hai đầu đều dùng cùng giao thức OpenAI nên bạn không cần duy trì nhánh mã thứ hai: dùng Llama hoặc Qwen cục bộ để phát triển ngoại tuyến, rồi dùng Claude hoặc GPT được lưu trữ khi cần chất lượng cao. Có thể dùng nguyên trạng chung định nghĩa công cụ và trình xử lý streaming.

Kunavo bổ sung những endpoint OpenAI nào ngoài giao diện tương thích của Ollama?

Giao diện tương thích OpenAI của Ollama hỗ trợ /chat/completions, /completions, /embeddings và /models. Kunavo bổ sung các endpoint đa phương tiện dùng cùng một khóa: /v1/images/generations, /v1/images/edits, /v1/video/generations, /v1/videos và /v1/audio/music, cùng Claude trên endpoint /v1/messages gốc của Anthropic. Có hai điểm cần nêu rõ theo chiều ngược lại: /v1/audio/speech, /v1/audio/transcriptions và /v1/embeddings được triển khai theo định dạng truyền tải, nhưng hiện chưa có mô hình nào được bật phía sau chúng nên hiện tại sẽ không gọi thành công — và khác với Ollama, Kunavo không chạy mô hình cục bộ. GET /v1/models luôn là nguồn có thẩm quyền để biết những gì có thể gọi.

Tiếp theo nên xem gì