Guía de TypeScript

OpenAI Decisions API en TypeScript

OpenAI no ha publicado un SDK para su Decisions API — no hay ningún client.decisions.create que copiar. Esta página muestra TypeScript funcional y totalmente tipado para el endpoint de este sitio — una alternativa llamable a la OpenAI Decisions API que sirve decisions-1 y sigue el mismo patrón de decisión restringida.

Actualizado

Define el contrato

Tipa la petición una vez y reutilízala en todas partes. model es una unión de literales, así que el id fijado nunca se convierte en un typo. questions es un record indexado por tus ids, cada uno de tipo noul, choice o score.

Tipa las respuestas como una unión discriminada por type — eso es lo que hace seguro el manejo de la respuesta: una respuesta noul tiene noul, una choice tiene choice y probabilities — al estrechar por type obtienes los campos correctos.

TypeScript

interface DecisionQuestion {
  type: 'noul' | 'choice' | 'score'
  instructions: string
  criteria?: Record<string, string> | string[]
}

interface DecisionRequest {
  model: 'decisions-1' | 'decisions-latest'
  state: string | Record<string, unknown> | unknown[]
  questions: Record<string, DecisionQuestion>
}

interface DecisionAnswers {
  [questionId: string]:
    | { type: 'noul'; noul: number }
    | { type: 'choice'; choice: string; probabilities: Record<string, number>; confidence: number }
    | { type: 'score'; score: number; legend: string[]; probabilities: Record<string, number>; confidence: number }
}

Haz la llamada

Un solo POST con una clave Bearer. satisfies DecisionRequest comprueba el cuerpo en tiempo de compilación; AbortSignal.timeout evita que llamadas colgadas atasquen el pipeline. Estrecha answers por el campo type antes de leerlas.

TypeScript

const res = await fetch('https://decisions-api.net/api/v1/decisions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.DECISIONS_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(request satisfies DecisionRequest),
  signal: AbortSignal.timeout(30000),
})
const { answers } = (await res.json()) as { answers: DecisionAnswers }

const team = answers.team
if (team.type === 'choice' && team.confidence >= 0.7) {
  routeTo(team.choice)
}

Timeouts y reintentos

429 y 502 merecen un reintento corto con backoff — las llamadas fallidas no se cobran. 402 significa saldo vacío: recarga, no reintentes. 422 es un error de validación; el mensaje nombra el campo, así que corrige el cuerpo en lugar de reintentar.

TypeScript

async function decide(body: DecisionRequest, attempts = 3): Promise<DecisionAnswers> {
  for (let i = 0; i < attempts; i++) {
    const res = await fetch('https://decisions-api.net/api/v1/decisions', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.DECISIONS_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(body),
      signal: AbortSignal.timeout(30000),
    })
    if (res.status === 429 || res.status === 502) {
      await new Promise(r => setTimeout(r, 2 ** i * 1000))
      continue
    }
    if (res.status === 402) throw new Error('out of credits')
    if (!res.ok) throw new Error(`decision call failed: ${res.status}`)
    return (await res.json() as { answers: DecisionAnswers }).answers
  }
  throw new Error('decision call failed')
}

Mantén el proveedor tras una función

Quien llama debería ver una función tipada que toma texto y devuelve una etiqueta — no detalles HTTP. Cuando OpenAI abra su Decisions API cambias el interior de decide() y todos los puntos de llamada quedan igual. El texto de las preguntas, las opciones y los umbrales se conservan.

TypeScript

// Keep the decision behind one typed function. Swap the HTTP
// layer when OpenAI publishes its schema — callers never change.
export async function routeTicket(text: string): Promise<string> {
  const answers = await decide({
    model: 'decisions-1',
    state: text,
    questions: {
      team: {
        type: 'choice',
        instructions: 'Which team should own this ticket?',
        criteria: {
          payments: 'Checkout or billing.',
          frontend: 'Rendering or browser behavior.',
          account: 'Login or permissions.',
        },
      },
    },
  })
  const team = answers.team
  return team.type === 'choice' && team.confidence >= 0.7 ? team.choice : 'triage'
}

Preguntas frecuentes

¿Hay un ejemplo oficial del SDK de OpenAI para la Decisions API?

No. OpenAI no ha publicado métodos de SDK ni un esquema de petición para su Decisions API — cualquier cosa con client.decisions.create es inventada. Esta página usa HTTP plano, que es lo que envolvería cualquier SDK de proveedor.

¿Necesito un generador de código para los tipos?

No — las interfaces de esta página cubren todo el contrato. Cópialas a tu proyecto; son lo bastante pequeñas para auditar y estables entre proveedores.

¿Cómo envío contexto estructurado?

state acepta un objeto JSON o un array, no solo una cadena — pasa dicts directamente en el cuerpo json y el modelo los lee como contexto.

Ejecuta una decisión desde el navegador

Sin configuración — ejecuta una llamada real en el playground con 2 créditos gratis para visitantes nuevos.