typescript

Branches

Branches.ts
/**
 * Branches: leaving a step early, by the AI or by code.
 *
 * Teaches: `branches[]` with `when` (AI) and `if` (code), `then` as a step
 * id, `'end'` or `{ flow }`, `onEnd: 'stay'`, a handoff flow with `say` + `do`.
 * Read next: docs/concepts/pipeline.md
 *
 * Run: GEMINI_API_KEY=... bun run examples/05-branches.ts
 */

import { falai, GeminiProvider } from "@falai/agent";

interface Ctx {
  atendentesOnline: number;
}

const f = falai<Ctx>().fields({
  pedido: { type: "string", ask: "Pergunte o número do pedido." },
  problema: { type: "string", ask: "Pergunte o que aconteceu com o pedido." },
});

const agent = f.agent({
  name: "Léo",
  provider: new GeminiProvider({ apiKey: process.env.GEMINI_API_KEY ?? "", model: "gemini-2.5-flash" }),
  actions: {
    transferir: f.action({
      parameters: { fila: { type: "string" } },
      run: (params, ctx) => {
        console.log(`transferindo ${ctx.run.id} para a fila ${params.fila}`);
        return { ok: true, detail: `enviado para ${params.fila}` };
      },
    }),
  },
  flows: [
    f.flow({
      id: "suporte",
      name: "Suporte a pedidos",
      on: [{ message: ["problema com um pedido", "pedido atrasado ou errado"] }],
      steps: [
        {
          id: "dados",
          collect: ["pedido", "problema"],
          branches: [
            // The AI judges this while the step is asking.
            { when: "a pessoa pede para falar com um humano", then: { flow: "humano" } },
            // Code judges this; it costs nothing.
            { if: ({ context }) => context.atendentesOnline === 0, then: "sem-humanos" },
          ],
        },
        { id: "resolve", prompt: "Explique o próximo passo para o pedido {{data.pedido}}.", then: "end" },
        {
          id: "sem-humanos",
          say: "Nossa equipe está fora agora. Deixe os dados que retornamos assim que alguém entrar.",
          then: "dados",
        },
      ],
      // After the last step the run goes back to the last talk step it took, so follow-up questions land there.
      onEnd: "stay",
    }),
    f.flow({
      id: "humano",
      name: "Passar para um humano",
      // No `on`: only reached by `then: { flow: 'humano' }` or `turn({ start })`.
      steps: [
        { id: "aviso", say: "Claro, vou chamar alguém da equipe. Um minuto." },
        { id: "fila", do: "transferir", with: { fila: "suporte" } },
      ],
    }),
  ],
});

const r = await agent.turn({
  sessionId: "demo",
  context: { atendentesOnline: 2 },
  message: "meu pedido 4411 veio errado, quero falar com uma pessoa",
});
for (const m of r.messages) console.log(`[${m.kind}] ${m.text}`);
console.log(r.ended.map((run) => `${run.flowId} → ${run.reason}`)); // [ 'suporte → flow' ]