# Jobs e execuções

> Consulte periodicamente, liste, cancele e exclua execuções do Nodaro em TypeScript: client.jobs para gerações avulsas e client.executions para workflows.

Source: https://nodaro.ai/pt-BR/docs/developers/sdk/jobs-and-executions

Um **job** é uma única geração no Nodaro, como uma imagem, uma renderização de vídeo ou uma narração, e uma **execução** é cada vez que um workflow inteiro é executado. **`client.jobs`** lê, lista, cancela e exclui jobs, e **`client.executions`** lê, lista e cancela execuções de workflows. **`client.videoPro`** interrompe ou continua uma execução longa do nó **Gerar vídeo Pro** (Generate Video Pro). Os métodos chamam os mesmos endpoints das APIs REST de [Jobs](https://nodaro.ai/docs/developers/api/jobs) e de [Execuções](https://nodaro.ai/docs/developers/api/executions).

## Métodos
| Método | O que faz |
| --- | --- |
| [`executions.get(id)`](#executionsgetid) | Lê uma execução de workflow, com o estado de cada nó |
| [`executions.listForWorkflow(workflowId, params?)`](#executionslistforworkflowworkflowid-params) | Lista as execuções de um workflow |
| [`executions.cancel(id, params?)`](#executionscancelid-params) | Interrompe uma execução de workflow |
| [`jobs.get(id)`](#jobsgetid) | Lê um job com a entrada e a saída dele |
| [`jobs.list(params?)`](#jobslistparams) | Lista os seus jobs, dos mais recentes para os mais antigos |
| [`jobs.getStatus(id)`](#jobsgetstatusid) | Lê o status de um job, para consulta periódica |
| [`jobs.cancel(id)`](#jobscancelid) | Interrompe um job e reembolsa os créditos reservados |
| [`jobs.delete(id)`](#jobsdeleteid) | Exclui um job e a mídia que ele produziu |
| [`videoPro.stop(jobId)`](#videoprostopjobid) | Interrompe uma execução do Gerar vídeo Pro e mantém o que já está pronto |
| [`videoPro.continueRun(jobId, opts?)`](#videoprocontinuerunjobid-opts) | Continua uma execução do Gerar vídeo Pro como um novo job |

## Status
Um job passa por estes status:

| Status do job | Significado |
| --- | --- |
| `pending`, `queued` | Esperando um worker. |
| `processing` | Em execução. `progress` vai de 0 a 100 quando o modelo informa o progresso. |
| `pending_review` | Retido para revisão humana por uma política de conteúdo desta implantação. Não é um estado final. |
| `completed` | Concluído. `output_data` contém o resultado. |
| `failed` | Falhou. `error_message` explica o motivo, e `error_hint` pode classificá-lo. |
| `cancelled` | Interrompido por um cancelamento. |

Uma execução tem status próprios: `pending`, `running`, `completed`, `failed`, `cancelled`, `stopping`, `timed_out` e `discarded`. Cada nó dentro dela fica em `pending`, `running`, `completed`, `failed` ou `skipped`.

## client.executions
Uma execução é cada vez que um workflow é executado. Ela reúne um job por nó de IA e o estado de cada nó.

### executions.get(id)
Lê uma execução de workflow, incluindo um mapa com o estado de cada nó. Quando o ID pertence a um job de um único nó, e não a uma execução de workflow, o servidor responde no mesmo formato, para esse único nó.

```ts
get(id: string): Promise<{ data: WorkflowExecution }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "O ID da execução que client.workflows.run() retornou." },
}}
/>

```ts
const { data } = await client.executions.get(executionId)
console.log(data.status, `${data.completedNodes}/${data.totalNodes}`, data.totalCreditsUsed)
```

Um `WorkflowExecution` tem `id`, `workflowId`, `status`, `triggerType` (`manual`, `webhook`, `schedule`, `app_run` ou `single-node`), `nodeStates`, `totalNodes`, `completedNodes`, `failedNodes`, `totalCreditsUsed`, `errorMessage`, `startedAt`, `completedAt`, `createdAt` e `updatedAt`.

**Como ler a saída de um nó.** `nodeStates[nodeId].output` está presente quando o nó terminou em `completed`. Também pode estar presente quando o nó terminou em `failed`, mas a execução manteve um resultado utilizável, como fazem os nós de cena 3D. Verifique o campo, não o status, e nunca trate um `output` presente como sucesso:

```ts

const node = data.nodeStates["scene-1"]
if (node.status === "failed") {
console.error(node.error) // the failure stands
if (node.output?.plan) {
// the draft the run kept is still here, and it was billed
}
}
nodeStateMayCarryOutput(node.status) // true for "completed" and "failed"
```

`OUTPUT_BEARING_NODE_STATUSES`, o conjunto desses dois status, também é exportado.

### executions.listForWorkflow(workflowId, params?)
Lista as execuções de um workflow, das mais recentes para as mais antigas, uma página por vez. A lista inclui as execuções de um único nó iniciadas nesse workflow.

```ts
listForWorkflow(workflowId: string, params?: ListExecutionsForWorkflowParams): Promise<{
data: WorkflowExecutionSummary[]
nextCursor?: string
}>
```

<TypeTable
type={{
workflowId: { type: 'string', required: true, description: "O ID do workflow." },
limit: { type: 'number', description: "O tamanho da página." },
cursor: { type: 'string', description: "O nextCursor da página anterior." },
status: { type: 'string', description: "Uma lista de status separados por vírgula, como pending,running." },
source: { type: '"editor" | "all"', description: "editor deixa de fora as execuções iniciadas por apps, webhooks e agendamentos." },
}}
/>

```ts
const { data: runs, nextCursor } = await client.executions.listForWorkflow(workflowId, {
limit: 20,
status: "completed",
})
```

### executions.cancel(id, params?)
Interrompe uma execução de workflow. Há três maneiras de interrompê-la:

- **De imediato** (o padrão): os jobs em andamento são cancelados, os créditos reservados deles são reembolsados e o status passa a `cancelled`.
- **`mode: "after_current"`**: o status passa a `stopping`. Os nós em andamento terminam e aparecem no canvas e na sua biblioteca, e depois a execução para.
- **`mode: "discard"`**: nenhum nó novo começa, mas os jobs em andamento não são cancelados, porque não dá para interromper no meio a chamada a um modelo externo. Eles terminam e são salvos na sua biblioteca, mas os resultados não voltam para o canvas. O status passa a `discarded`, e não há reembolso, porque os jobs foram concluídos.

```ts
cancel(id: string, params?: { mode?: "after_current" | "discard" }): Promise<{ success: true }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "O ID da execução." },
mode: { type: '"after_current" | "discard"', description: "Como interromper. Omita-o para interromper de imediato." },
}}
/>

```ts
await client.executions.cancel(executionId, { mode: "after_current" })
```

## client.jobs
Um job é uma única geração: uma imagem, uma renderização de vídeo, uma narração. Uma execução de workflow cria um job por nó de IA, e toda execução de um único nó cria um job. Os campos do job usam snake_case, como a API os envia.

### jobs.get(id)
Lê um job, incluindo a entrada e a saída dele.

```ts
get(id: string): Promise<{ data: Job }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "O ID do job." },
}}
/>

```ts
const { data: job } = await client.jobs.get(jobId)
if (job.status === "completed") console.log(job.output_data)
```

Um `Job` tem estes campos:

| Campo | Descrição |
| --- | --- |
| `id`, `status`, `progress` | O ID do job, o status dele e o progresso, de 0 a 100. |
| `input_data`, `output_data` | A requisição e o resultado. Os valores exclusivos do servidor são removidos dos dois. |
| `error_message` | Por que o job falhou, ou `null`. |
| `error_hint` | Um motivo estruturado para algumas falhas. Veja abaixo. |
| `credits` | Os créditos reservados para o job, ou `null`. |
| `credit_status` | `reserved`, `committed` ou `refunded`: a situação dos créditos. `null` para um job sem cobrança. |
| `job_type` | O tipo de job. |
| `created_at`, `started_at`, `completed_at` | Marcações de data e hora. |
| `user_id` | O dono. |
| `source`, `source_detail` | De onde o job veio: `sdk`, `cli`, `mcp`, `app`, `web`, `api` e outros, com um detalhe como `sdk/2.17.0`. |
| `recovering` | `true` enquanto a plataforma recupera um job cujo worker parou depois que o modelo entregou o resultado. |

**`error_hint`** tem dois tipos. Verifique `kind` antes de ler o restante:

- **`safety-block`**: o filtro de segurança do próprio modelo recusou a saída. `class` é `copyright`, `likeness` ou `safety`. `retried` diz se o Nodaro já tentou de novo uma vez, e `suggestedProvider`, quando presente, indica um modelo em que você pode repetir a mesma requisição. Veja [Quando um modelo bloqueia o prompt](https://nodaro.ai/docs/nodes/image/generate-image#when-a-model-blocks-the-prompt).
- **`policy-block`**: uma política de conteúdo desta implantação rejeitou a requisição ou o resultado. `reason` foi escrito para os usuários, então mostre-o como está.

### jobs.list(params?)
Lista os seus jobs, dos mais recentes para os mais antigos, uma página por vez (`GET /v1/jobs`).

```ts
list(params?: { type?: string; origin?: string; limit?: number; cursor?: string }): Promise<{
data: Job[]
next: string | null
}>
```

<TypeTable
type={{
type: { type: 'string', description: "Apenas jobs criados por esta rota, como llm-structured ou video-analysis. Correspondência exata." },
origin: { type: 'string', description: "Apenas jobs cuja requisição trazia este valor de origin, o nome do app que a enviou. Correspondência exata." },
limit: { type: 'number', default: '50', description: "O tamanho da página, de 1 a 100." },
cursor: { type: 'string', description: "O valor next da página anterior." },
}}
/>

```ts
let cursor: string | undefined
do {
const page = await client.jobs.list({ type: "llm-structured", origin: "my-app", cursor })
for (const job of page.data) console.log(job.id, job.status)
cursor = page.next ?? undefined
} while (cursor)
```

Uma página pode ter menos linhas que `limit`, até nenhuma, e ainda assim ter um `next`. Pagine com base em `next`, nunca no número de linhas.

### jobs.getStatus(id)
Lê apenas o status de um job: `id`, `status`, `progress`, `output_data`, `error_message`, `error_hint` e `credit_status` (`GET /v1/jobs/:id/status`). Deixa de fora os dados da requisição e os campos de custo, então é bem mais leve que `get()`. Use-o em loops de consulta periódica.

```ts
getStatus(id: string): Promise<{ data: JobStatusResult }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "O ID do job." },
}}
/>

```ts
async function waitForJob(jobId: string) {
for (;;) {
const { data } = await client.jobs.getStatus(jobId)
if (data.status === "completed") return data.output_data
if (data.status === "failed" || data.status === "cancelled") {
throw new Error(data.error_message ?? data.status)
}
await new Promise((resolve) => setTimeout(resolve, 2_000))
}
}
```

[`client.nodes.runAndWait()`](https://nodaro.ai/docs/developers/sdk/nodes#runandwaittype-params-opts) executa esse loop por você, com progresso, cancelamento e erros tipados.

### jobs.cancel(id)
Cancela um job e reembolsa os créditos que ele tinha reservado. Um job retido para revisão também pode ser cancelado.

```ts
cancel(id: string): Promise<{ success: true; cancelled: number }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "O ID do job." },
}}
/>

```ts
const { cancelled } = await client.jobs.cancel(jobId)
```

### jobs.delete(id)
Exclui um job e a mídia privada que ele produziu (`DELETE /v1/jobs/:id`). Só o dono do job pode excluí-lo. Um job em andamento é excluído no estado em que está, então cancele-o antes quando o worker dele precisar parar.

```ts
delete(id: string): Promise<{ success: true }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "O ID do job." },
}}
/>

```ts
await client.jobs.cancel(jobId)
await client.jobs.delete(jobId)
```

## client.videoPro
Controle de execução para o [Gerar vídeo Pro](https://nodaro.ai/docs/nodes/video/generate-video-pro), o nó que renderiza vídeos longos em segmentos. Funciona no Nodaro Cloud. Inicie a execução como qualquer execução de nó e depois use estes métodos no job dela.

### videoPro.stop(jobId)
Interrompe de forma controlada um job do Gerar vídeo Pro em andamento. O segmento em andamento é abandonado e cobrado mesmo assim, e os segmentos restantes são pulados. Os segmentos concluídos são unidos no vídeo final do job, e a reserva não usada é reembolsada. Um job que ainda não começou é cancelado com reembolso total.

```ts
stop(jobId: string): Promise<StopVideoProResult>
```

<TypeTable
type={{
jobId: { type: 'string', required: true, description: "O ID do job do Gerar vídeo Pro." },
}}
/>

```ts
await client.videoPro.stop(jobId)
const { data } = await client.jobs.getStatus(jobId) // becomes completed, with the partial video
```

Continue consultando o job periodicamente. Ele termina com `output_data.pro.stopped` igual a `true`.

### videoPro.continueRun(jobId, opts?)
Continua uma execução interrompida, com falha ou concluída como um **novo job**. Os segmentos anteriores a `fromSegment` são reaproveitados, e todos os segmentos a partir dele são gerados de novo. Você paga apenas pelos segmentos gerados de novo, mais a taxa fixa do Pro.

```ts
continueRun(jobId: string, opts?: { fromSegment?: number }): Promise<ContinueVideoProResult>
```

<TypeTable
type={{
jobId: { type: 'string', required: true, description: "O job a continuar." },
fromSegment: { type: 'number', description: "O primeiro segmento a gerar de novo, contado a partir de 1. Por padrão, o primeiro segmento que não foi entregue." },
}}
/>

```ts
const { jobId: newJobId, fromSegment } = await client.videoPro.continueRun(jobId, { fromSegment: 4 })
```

O resultado traz o novo `jobId` a consultar periodicamente e pode trazer `continuedFromJobId`, `fromSegment`, `segmentCount` e `deduped`.

## Frequently asked questions

### Qual é a diferença entre um job e uma execução?

Um job é uma única geração, como uma imagem ou um vídeo. Uma execução é cada vez que um workflow inteiro é executado: ela reúne um job por nó de IA, mais o estado de cada nó.

### Qual método um loop de consulta periódica deve chamar?

client.jobs.getStatus(jobId). Ele retorna apenas o status, o progresso, a saída e o erro, então é bem mais leve que client.jobs.get.

### Cancelar um job reembolsa os créditos?

Sim. client.jobs.cancel interrompe o job e reembolsa os créditos que ele tinha reservado. Cancelar uma execução de imediato faz o mesmo com os jobs dela em andamento.

### O que significa o status pending_review?

Uma política de conteúdo desta implantação está retendo o resultado para que uma pessoa o revise. Não é um estado final: depois, o job passa a completed, failed ou cancelled. Continue esperando e não execute a requisição de novo.
