Endpoints compatíveis
| Endpoint | Descrição |
|---|---|
/v1/chat/completions | Solicitações de conclusão de chat |
/v1/embeddings | Geração de embeddings |
/v1/images/generations | Geração de imagens |
Ciclo de vida
Um lote passa por estes status:validating → in_progress → finalizing → completed
↓ ↓
failed cancelling → cancelled
| Status | Descrição |
|---|---|
validating | O arquivo de entrada está sendo verificado quanto a erros de formato e conteúdo. |
failed | A validação falhou — o arquivo de entrada contém erros. Consulte batch.errors para detalhes. |
in_progress | As solicitações estão sendo processadas. |
finalizing | Todas as solicitações foram processadas e os arquivos de saída estão sendo gerados. |
completed | O lote foi concluído. Baixe os resultados de output_file_id. |
cancelling | Um cancelamento foi solicitado. As solicitações em andamento estão sendo concluídas. |
cancelled | O lote foi cancelado. Resultados parciais podem estar disponíveis. |
expired | O lote não foi concluído dentro da janela de 24 horas. |
Etapa 1 — Prepare o arquivo de entrada
Crie um arquivo JSONL no qual cada linha seja uma solicitação. Cada linha tem quatro campos:| Campo | Tipo | Descrição |
|---|---|---|
custom_id | string | Seu identificador para esta solicitação. Usado para associar a entrada à saída. Deve ser único no arquivo. |
method | string | "POST" — o único método compatível. |
url | string | O caminho do endpoint — deve corresponder ao endpoint declarado ao criar o lote. |
body | object | O corpo da solicitação — os mesmos parâmetros que você enviaria ao endpoint síncrono. |
{"custom_id": "request-1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "meetkai:functionary-pt", "messages": [{"role": "user", "content": "Resuma os benefícios do processamento em lote em uma frase."}], "max_tokens": 100}}
{"custom_id": "request-2", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "meetkai:functionary-pt", "messages": [{"role": "user", "content": "Qual é a capital da França?"}], "max_tokens": 100}}
{"custom_id": "request-3", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "meetkai:functionary-pt", "messages": [{"role": "user", "content": "Explique embeddings em um parágrafo."}], "max_tokens": 100}}
Etapa 2 — Envie o arquivo de entrada
Envie o arquivo JSONL usando a API de Files compurpose: "batch".
mka1 llm files upload \
--file ./batch_input.jsonl \
--purpose batch \
-H 'X-On-Behalf-Of: <end-user-id>'
import { SDK } from '@meetkai/mka1';
const mka1 = new SDK({
bearerAuth: `Bearer ${YOUR_API_KEY}`,
});
const file = await mka1.llm.files.upload({
requestBody: {
file: new File([jsonlContent], 'batch_input.jsonl', { type: 'application/jsonl' }),
purpose: 'batch',
},
});
console.log(file.id); // "file_abc123"
console.log(file.status); // "processed"
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: '<mka1-api-key>',
baseURL: 'https://apigw.mka1.com/api/v1/llm/',
defaultHeaders: { 'X-On-Behalf-Of': '<end-user-id>' },
});
const file = await openai.files.create({
file: new File([jsonlContent], 'batch_input.jsonl', { type: 'application/jsonl' }),
purpose: 'batch',
});
console.log(file.id); // "file_abc123"
console.log(file.status); // "processed"
using System.Text;
using MeetKai.MKA1;
using MeetKai.MKA1.Types.Components;
using MeetKai.MKA1.Types.Requests;
var sdk = new SDK(bearerAuth: "Bearer YOUR_API_KEY");
var file = await sdk.Llm.Files.UploadAsync(new UploadFileRequestBody()
{
File = new UploadFileFile()
{
FileName = "batch_input.jsonl",
Content = Encoding.UTF8.GetBytes(jsonlContent),
},
Purpose = UploadFilePurpose.Batch,
});
Console.WriteLine(file.File!.Id); // "file_abc123"
from meetkai_mka1 import SDK
sdk = SDK(bearer_auth="Bearer YOUR_API_KEY")
file = sdk.llm.files.upload(
file={"file_name": "batch_input.jsonl", "content": open("batch_input.jsonl", "rb")},
purpose="batch",
)
print(file.id) # "file_abc123"
print(file.status) # "processed"
curl https://apigw.mka1.com/api/v1/llm/files \
--request POST \
--header 'Authorization: Bearer <mka1-api-key>' \
--header 'X-On-Behalf-Of: <end-user-id>' \
--form 'file=@batch_input.jsonl;type=application/jsonl' \
--form 'purpose=batch'
Etapa 3 — Crie o lote
Informe o ID do arquivo enviado, o endpoint de destino e a janela de conclusão.mka1 llm batches create --body '{
"input_file_id": "file_abc123",
"endpoint": "/v1/chat/completions",
"completion_window": "24h"
}'
const batch = await mka1.llm.batches.create({
createBatchRequest: {
inputFileId: file.id,
endpoint: '/v1/chat/completions',
completionWindow: '24h',
},
});
console.log(batch.id); // "batch_abc123"
console.log(batch.status); // "validating" ou "in_progress"
console.log(batch.requestCounts); // { total: 3, completed: 0, failed: 0 }
const batch = await openai.batches.create({
input_file_id: file.id,
endpoint: '/v1/chat/completions',
completion_window: '24h',
});
console.log(batch.id); // "batch_abc123"
console.log(batch.status); // "validating" ou "in_progress"
console.log(batch.request_counts); // { total: 3, completed: 0, failed: 0 }
using MeetKai.MKA1;
using MeetKai.MKA1.Types.Components;
var sdk = new SDK(bearerAuth: "Bearer YOUR_API_KEY");
var batch = await sdk.Llm.Batches.CreateAsync(new CreateBatchRequest()
{
InputFileId = file.File!.Id,
Endpoint = BatchEndpoint.RootV1ChatCompletions,
});
Console.WriteLine(batch.BatchObject!.Id); // "batch_abc123"
Console.WriteLine(batch.BatchObject!.Status); // "validating" ou "in_progress"
Console.WriteLine(batch.BatchObject!.RequestCounts); // { Total: 3, Completed: 0, Failed: 0 }
batch = sdk.llm.batches.create(
input_file_id=file.id,
endpoint="/v1/chat/completions",
)
print(batch.id) # "batch_abc123"
print(batch.status) # "validating" ou "in_progress"
print(batch.request_counts) # { total: 3, completed: 0, failed: 0 }
curl https://apigw.mka1.com/api/v1/llm/batches \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <mka1-api-key>' \
--header 'X-On-Behalf-Of: <end-user-id>' \
--data '{
"input_file_id": "file_abc123",
"endpoint": "/v1/chat/completions",
"completion_window": "24h"
}'
mka1 llm batches create --body '{
"input_file_id": "file_abc123",
"endpoint": "/v1/chat/completions",
"completion_window": "24h",
"metadata": {
"description": "execução noturna de avaliação",
"run_id": "eval-2026-03-31"
}
}'
const batch = await mka1.llm.batches.create({
createBatchRequest: {
inputFileId: file.id,
endpoint: '/v1/chat/completions',
completionWindow: '24h',
metadata: {
description: 'execução noturna de avaliação',
run_id: 'eval-2026-03-31',
},
},
});
const batch = await openai.batches.create({
input_file_id: file.id,
endpoint: '/v1/chat/completions',
completion_window: '24h',
metadata: {
description: 'execução noturna de avaliação',
run_id: 'eval-2026-03-31',
},
});
using MeetKai.MKA1;
using MeetKai.MKA1.Types.Components;
var sdk = new SDK(bearerAuth: "Bearer YOUR_API_KEY");
var batch = await sdk.Llm.Batches.CreateAsync(new CreateBatchRequest()
{
InputFileId = file.File!.Id,
Endpoint = BatchEndpoint.RootV1ChatCompletions,
Metadata = new Dictionary<string, string>
{
{ "description", "execução noturna de avaliação" },
{ "run_id", "eval-2026-03-31" },
},
});
batch = sdk.llm.batches.create(
input_file_id=file.id,
endpoint="/v1/chat/completions",
metadata={
"description": "execução noturna de avaliação",
"run_id": "eval-2026-03-31",
},
)
Etapa 4 — Verifique o status do lote
Consulte o lote até que ele alcance um status terminal.mka1 llm batches get --batch-id batch_abc123
const batch = await mka1.llm.batches.get({ batchId: 'batch_abc123' });
console.log(batch.status); // "completed"
console.log(batch.requestCounts.completed); // 3
console.log(batch.requestCounts.failed); // 0
console.log(batch.outputFileId); // "file_xyz789"
const batch = await openai.batches.retrieve('batch_abc123');
console.log(batch.status); // "completed"
console.log(batch.request_counts.completed); // 3
console.log(batch.request_counts.failed); // 0
console.log(batch.output_file_id); // "file_xyz789"
using MeetKai.MKA1;
using MeetKai.MKA1.Types.Components;
var sdk = new SDK(bearerAuth: "Bearer YOUR_API_KEY");
var batch = await sdk.Llm.Batches.GetAsync("batch_abc123");
Console.WriteLine(batch.BatchObject!.Status); // "completed"
Console.WriteLine(batch.BatchObject!.RequestCounts); // { Total: 3, Completed: 3, Failed: 0 }
Console.WriteLine(batch.BatchObject!.OutputFileId); // "file_xyz789"
batch = sdk.llm.batches.get(batch_id="batch_abc123")
print(batch.status) # "completed"
print(batch.request_counts.completed) # 3
print(batch.request_counts.failed) # 0
print(batch.output_file_id) # "file_xyz789"
curl https://apigw.mka1.com/api/v1/llm/batches/batch_abc123 \
--header 'Authorization: Bearer <mka1-api-key>' \
--header 'X-On-Behalf-Of: <end-user-id>'
# Consulte um lote até que ele alcance um status terminal usando --jq e um loop de shell.
BATCH_ID=batch_abc123
while :; do
STATUS=$(mka1 llm batches get --batch-id "$BATCH_ID" --jq '.status' --output-format json)
echo "status: $STATUS"
case "$STATUS" in
completed|failed|cancelled|expired) break ;;
esac
sleep 2
done
async function waitForBatch(batchId: string, timeoutMs = 120_000) {
const terminal = ['completed', 'failed', 'cancelled', 'expired'];
const start = Date.now();
while (Date.now() - start < timeoutMs) {
const batch = await mka1.llm.batches.get({ batchId });
if (terminal.includes(batch.status)) return batch;
await new Promise((r) => setTimeout(r, 2000));
}
throw new Error(`O lote ${batchId} não foi concluído dentro de ${timeoutMs}ms`);
}
const completed = await waitForBatch(batch.id);
async function waitForBatch(batchId: string, timeoutMs = 120_000) {
const terminal = ['completed', 'failed', 'cancelled', 'expired'];
const start = Date.now();
while (Date.now() - start < timeoutMs) {
const batch = await openai.batches.retrieve(batchId);
if (terminal.includes(batch.status)) return batch;
await new Promise((r) => setTimeout(r, 2000));
}
throw new Error(`O lote ${batchId} não foi concluído dentro de ${timeoutMs}ms`);
}
const completed = await waitForBatch(batch.id);
using MeetKai.MKA1;
using MeetKai.MKA1.Types.Components;
var sdk = new SDK(bearerAuth: "Bearer YOUR_API_KEY");
async Task<BatchObject> WaitForBatch(SDK sdk, string batchId, int timeoutMs = 300_000)
{
var terminal = new HashSet<BatchObjectStatus>
{
BatchObjectStatus.Completed,
BatchObjectStatus.Failed,
BatchObjectStatus.Cancelled,
BatchObjectStatus.Expired,
};
var start = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
while (DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - start < timeoutMs)
{
var batch = await sdk.Llm.Batches.GetAsync(batchId);
if (terminal.Contains(batch.BatchObject!.Status))
return batch.BatchObject;
await Task.Delay(2000);
}
throw new TimeoutException($"O lote {batchId} não foi concluído dentro de {timeoutMs}ms");
}
var completed = await WaitForBatch(sdk, batch.BatchObject!.Id);
Console.WriteLine(completed.Status); // BatchObjectStatus.Completed
import time
def wait_for_batch(sdk, batch_id, timeout_ms=120_000):
terminal = {"completed", "failed", "cancelled", "expired"}
start = time.time() * 1000
while (time.time() * 1000) - start < timeout_ms:
batch = sdk.llm.batches.get(batch_id=batch_id)
if batch.status in terminal:
return batch
time.sleep(2)
raise TimeoutError(f"O lote {batch_id} não foi concluído dentro de {timeout_ms}ms")
completed = wait_for_batch(sdk, batch.id)
Etapa 5 — Baixe os resultados
Quando o lote estivercompleted, baixe o arquivo de saída. Ele é um arquivo JSONL no qual cada linha contém o custom_id fornecido, a resposta e qualquer erro.
# Baixe o arquivo de saída JSONL
mka1 llm files content \
--file-id file_xyz789 \
--output-file ./batch_output.jsonl
# Inspecione os resultados em linha com jq
mka1 llm files content --file-id file_xyz789 \
--jq '"\(.custom_id): status=\(.response.status_code)"'
const stream = await mka1.llm.files.content({ fileId: completed.outputFileId! });
const reader = stream.getReader();
const chunks: Uint8Array[] = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
}
const text = new TextDecoder().decode(Buffer.concat(chunks));
const results = text
.split('\n')
.filter((line) => line.trim())
.map((line) => JSON.parse(line));
for (const result of results) {
console.log(`${result.custom_id}: status=${result.response.status_code}`);
console.log(` corpo:`, result.response.body);
}
const content = await openai.files.content(completed.output_file_id!);
const text = await content.text();
const results = text
.split('\n')
.filter((line) => line.trim())
.map((line) => JSON.parse(line));
for (const result of results) {
console.log(`${result.custom_id}: status=${result.response.status_code}`);
console.log(` corpo:`, result.response.body);
}
using System.Text;
using MeetKai.MKA1;
var sdk = new SDK(bearerAuth: "Bearer YOUR_API_KEY");
var content = await sdk.Llm.Files.ContentAsync(completed.OutputFileId!);
var bytes = content.TwoHundredTextPlainBytes
?? content.TwoHundredApplicationJsonlBytes
?? content.TwoHundredApplicationJsonBytes;
var text = Encoding.UTF8.GetString(bytes!);
Console.WriteLine(text); // JSONL com uma linha por solicitação
import json
content = sdk.llm.files.content(file_id=completed.output_file_id)
text = content.decode("utf-8")
results = [json.loads(line) for line in text.strip().split("\n") if line.strip()]
for result in results:
print(f"{result['custom_id']}: status={result['response']['status_code']}")
print(f" corpo: {result['response']['body']}")
curl https://apigw.mka1.com/api/v1/llm/files/file_xyz789/content \
--header 'Authorization: Bearer <mka1-api-key>' \
--header 'X-On-Behalf-Of: <end-user-id>'
{
"id": "response_abc123",
"custom_id": "request-1",
"response": {
"status_code": 200,
"request_id": "req_abc123",
"body": { "...": "mesma estrutura da resposta do endpoint síncrono" }
},
"error": null
}
response será null e error conterá os detalhes:
{
"id": "response_def456",
"custom_id": "request-2",
"response": null,
"error": {
"code": "processing_error",
"message": "A solicitação não pôde ser processada."
}
}
error_file_id contendo apenas as entradas que falharam.
Cancele um lote
Cancele um lote que ainda esteja em andamento. As solicitações que já foram concluídas permanecem na saída.mka1 llm batches cancel --batch-id batch_abc123
const cancelled = await mka1.llm.batches.cancel({ batchId: 'batch_abc123' });
console.log(cancelled.status); // "cancelling"
const cancelled = await openai.batches.cancel('batch_abc123');
console.log(cancelled.status); // "cancelling"
using MeetKai.MKA1;
var sdk = new SDK(bearerAuth: "Bearer YOUR_API_KEY");
var cancelled = await sdk.Llm.Batches.CancelAsync("batch_abc123");
Console.WriteLine(cancelled.BatchObject!.Status); // "cancelling"
cancelled = sdk.llm.batches.cancel(batch_id="batch_abc123")
print(cancelled.status) # "cancelling"
curl https://apigw.mka1.com/api/v1/llm/batches/batch_abc123/cancel \
--request POST \
--header 'Authorization: Bearer <mka1-api-key>' \
--header 'X-On-Behalf-Of: <end-user-id>'
cancelling enquanto as solicitações em andamento são concluídas e, então, para cancelled.
Liste os lotes
Recupere todos os lotes da conta atual, começando pelos mais recentes. Compatível com paginação.mka1 llm batches list --limit 20
const page = await mka1.llm.batches.list({ limit: 20 });
for (const batch of page.data) {
console.log(`${batch.id}: ${batch.status} (${batch.requestCounts?.completed}/${batch.requestCounts?.total})`);
}
const page = await openai.batches.list({ limit: 20 });
for (const batch of page.data) {
console.log(`${batch.id}: ${batch.status} (${batch.request_counts?.completed}/${batch.request_counts?.total})`);
}
using MeetKai.MKA1;
var sdk = new SDK(bearerAuth: "Bearer YOUR_API_KEY");
var page = await sdk.Llm.Batches.ListAsync(limit: 20);
foreach (var batch in page.ListBatchesResponseValue!.Data!)
{
Console.WriteLine($"{batch.Id}: {batch.Status} ({batch.RequestCounts?.Completed}/{batch.RequestCounts?.Total})");
}
page = sdk.llm.batches.list(limit=20)
for batch in page.data:
print(f"{batch.id}: {batch.status} ({batch.request_counts.completed}/{batch.request_counts.total})")
curl 'https://apigw.mka1.com/api/v1/llm/batches?limit=20' \
--header 'Authorization: Bearer <mka1-api-key>' \
--header 'X-On-Behalf-Of: <end-user-id>'
after com um ID de lote para paginar pelos resultados.
Exemplo: embeddings em lote
O mesmo fluxo funciona para embeddings. Altere aurl em cada linha JSONL e o endpoint ao criar o lote.
{"custom_id": "embed-1", "method": "POST", "url": "/v1/embeddings", "body": {"model": "meetkai:functionary-pt", "input": "A rápida raposa marrom"}}
{"custom_id": "embed-2", "method": "POST", "url": "/v1/embeddings", "body": {"model": "meetkai:functionary-pt", "input": "salta sobre o cachorro preguiçoso"}}
# Envie a entrada JSONL de embeddings
FILE_ID=$(mka1 llm files upload \
--file ./embed_batch.jsonl \
--purpose batch \
--jq '.id' --output-format json | tr -d '"')
# Crie o lote no endpoint de embeddings
mka1 llm batches create --body "{
\"input_file_id\": \"$FILE_ID\",
\"endpoint\": \"/v1/embeddings\",
\"completion_window\": \"24h\"
}"
# Consulte e, em seguida, baixe os resultados — consulte as Etapas 4 e 5
const file = await mka1.llm.files.upload({
requestBody: {
file: new File([jsonlContent], 'embed_batch.jsonl', { type: 'application/jsonl' }),
purpose: 'batch',
},
});
const batch = await mka1.llm.batches.create({
createBatchRequest: {
inputFileId: file.id,
endpoint: '/v1/embeddings',
completionWindow: '24h',
},
});
const completed = await waitForBatch(batch.id);
const stream = await mka1.llm.files.content({ fileId: completed.outputFileId! });
// ... leia o stream conforme mostrado na Etapa 5
const file = await openai.files.create({
file: new File([jsonlContent], 'embed_batch.jsonl', { type: 'application/jsonl' }),
purpose: 'batch',
});
const batch = await openai.batches.create({
input_file_id: file.id,
endpoint: '/v1/embeddings',
completion_window: '24h',
});
const completed = await waitForBatch(batch.id);
const content = await openai.files.content(completed.output_file_id!);
const results = (await content.text()).split('\n').filter(Boolean).map(JSON.parse);
for (const r of results) {
console.log(`${r.custom_id}: ${r.response.body.data[0].embedding.length} dimensões`);
}
using System.Text;
using MeetKai.MKA1;
using MeetKai.MKA1.Types.Components;
using MeetKai.MKA1.Types.Requests;
var sdk = new SDK(bearerAuth: "Bearer YOUR_API_KEY");
var file = await sdk.Llm.Files.UploadAsync(new UploadFileRequestBody()
{
File = new UploadFileFile()
{
FileName = "embed_batch.jsonl",
Content = Encoding.UTF8.GetBytes(jsonlContent),
},
Purpose = UploadFilePurpose.Batch,
});
var batch = await sdk.Llm.Batches.CreateAsync(new CreateBatchRequest()
{
InputFileId = file.File!.Id,
Endpoint = BatchEndpoint.RootV1Embeddings,
});
var completed = await WaitForBatch(sdk, batch.BatchObject!.Id);
// Baixe e analise os resultados conforme mostrado na Etapa 5
file = sdk.llm.files.upload(
file={"file_name": "embed_batch.jsonl", "content": open("embed_batch.jsonl", "rb")},
purpose="batch",
)
batch = sdk.llm.batches.create(
input_file_id=file.id,
endpoint="/v1/embeddings",
)
completed = wait_for_batch(sdk, batch.id)
# Baixe e analise os resultados conforme mostrado na Etapa 5
Erros de validação
Se o arquivo de entrada tiver problemas de formatação, o lote passará parafailed imediatamente.
Causas comuns:
- JSON inválido — uma linha não é um JSON válido.
- Campos ausentes — uma linha não possui
custom_id,method,urloubody. - Método incorreto —
methoddeve ser"POST". - Incompatibilidade de URL — a
urlem uma linha não corresponde aoendpointdeclarado ao criar o lote. custom_idduplicado — cadacustom_iddeve ser único no arquivo.
batch.errors.data para ver as mensagens de erro e os números de linha específicos.
mka1 llm batches get --batch-id batch_abc123 \
--jq '.errors.data[] | "Linha \(.line): [\(.code)] \(.message)"'
const batch = await mka1.llm.batches.get({ batchId: 'batch_abc123' });
if (batch.status === 'failed' && batch.errors) {
for (const err of batch.errors.data ?? []) {
console.log(`Linha ${err.line}: [${err.code}] ${err.message}`);
}
}
const batch = await openai.batches.retrieve('batch_abc123');
if (batch.status === 'failed' && batch.errors) {
for (const err of batch.errors.data ?? []) {
console.log(`Linha ${err.line}: [${err.code}] ${err.message}`);
}
}
using MeetKai.MKA1;
using MeetKai.MKA1.Types.Components;
var sdk = new SDK(bearerAuth: "Bearer YOUR_API_KEY");
var batch = await sdk.Llm.Batches.GetAsync("batch_abc123");
if (batch.BatchObject!.Status == BatchObjectStatus.Failed && batch.BatchObject.Errors != null)
{
foreach (var err in batch.BatchObject.Errors.Data ?? new List<BatchObjectErrorsData>())
{
Console.WriteLine($"Linha {err.Line}: [{err.Code}] {err.Message}");
}
}
batch = sdk.llm.batches.get(batch_id="batch_abc123")
if batch.status == "failed" and batch.errors:
for err in batch.errors.data or []:
print(f"Linha {err.line}: [{err.code}] {err.message}")
Veja também
- Gere uma resposta para o padrão síncrono de conclusões de chat.
- Files e armazenamentos de vetores para a API de Files usada para enviar entradas em lote.