ekairos

Ekairos · Librería

Flujo base de Ekairos Agent

Referencia mínima de cómo integrar la librería tal cual se encuentra hoy: API route, configuración del agente y UI de prueba. Cada sección enlaza directamente con el archivo real dentro del monorepo.

packages/web/src/lib/agents/simple-agent.ts

1. Configuración del Agente (createAgent)

El núcleo del flujo es la función fluida `createAgent()` expuesta por `ekairos/agent`. Define el contexto persistente, el prompt del sistema y las herramientas (tipadas con Zod) sin tocar genéricos.

import { createAgent } from "ekairos/agent"
import { z } from "zod"
import { tool } from "ai"
const simpleAgentBuilder = createAgent()
  .context(async (stored) => {
    const previous = stored.content

    return {
      userId: previous?.userId ?? "test-user",
      topic: previous?.topic ?? "general",
    }
  })
  .systemPrompt(async ({ content }) => {
    const { topic } = content
    return `You are a helpful assistant for testing purposes.
    Current topic: ${topic}.`
  })
  .tools(async ({ content }, stream) => {
    void stream
    const { topic } = content

    return {
      setTopic: tool({
        description: `Set the conversation topic (current: ${topic})`,
        inputSchema: z.object({
          topic: z.string().describe("The topic to set for the conversation"),
        }),
        execute: async ({ topic }) => ({ success: true, message: `Topic set to ${topic}` }),
      }),
    }
  })
  .model("gpt-4o-mini")

export const simpleAgentConfig = simpleAgentBuilder.config()
export const simpleAgent = simpleAgentBuilder.build()

packages/web/src/app/api/test-agent/route.ts

2. API Route

La API HTTP recibe los mensajes del componente Agent y los convierte en eventos compatibles con Ekairos. Esta es la pieza que siempre se monta primero.

import { UIMessage, createUIMessageStreamResponse } from "ai"
import { simpleAgent } from "@/lib/agents/simple-agent"

function createUserItemFromUIMessages(messages: UIMessage[]) {
  if (!Array.isArray(messages) || messages.length === 0) {
    throw new Error("Missing messages to create event")
  }

  const lastMessage = messages[messages.length - 1]

  return {
    id: lastMessage.id,
    type: "user.message",
    channel: "web",
    content: {
      parts: lastMessage.parts,
    },
    createdAt: new Date().toISOString(),
  }
}

export async function POST(req: Request) {
  const { messages, contextKey }: { messages: UIMessage[]; contextKey?: string } = await req.json()

  const event = createUserItemFromUIMessages(messages)

  try {
    const result = await simpleAgent.progressStream(event, contextKey ? { key: contextKey } : null)

    return createUIMessageStreamResponse({ stream: result.stream })
  } catch (error) {
    console.error("[api/test-agent] progressStream failed", JSON.stringify(error, null, 2))

    return new Response(
      JSON.stringify({
        error: "Agent failed to respond",
      }),
      {
        status: 500,
        headers: { "Content-Type": "application/json" },
      },
    )
  }
}

packages/web/src/app/test-agent/page.tsx

3. UI del Agente (opcional)

La librería es independiente de la UI. Aquí mostramos cómo el `Agent` de Ekairos consume la API, pero cualquier cliente que emita eventos compatibles funcionará.

"use client"

import { useCallback, Suspense } from "react"
import { useSearchParams } from "next/navigation"
import Agent from "@/components/ekairos/agent/Agent"

function TestAgentContent() {
  const searchParams = useSearchParams()

  const handleContextUpdate = useCallback((contextId: string) => {
    const params = new URLSearchParams(window.location.search)
    if (contextId && contextId.length > 0) {
      params.set("contextId", contextId)
    } else {
      params.delete("contextId")
    }
    const paramsString = params.toString()
    let nextUrl = window.location.pathname
    if (paramsString.length > 0) {
      nextUrl = `${nextUrl}?${paramsString}`
    }
    window.history.replaceState({}, "", nextUrl)

    console.log("[Test Agent] Context updated:", contextId)
  }, [])

  const initialContextId = searchParams.get("contextId") || undefined

  return (
    <div className="min-h-screen h-screen w-full">
      <Agent
        apiUrl="/api/test-agent"
        onContextUpdate={handleContextUpdate}
        toolComponents={{}}
        initialContextId={initialContextId}
      />
    </div>
  )
}

export default function TestAgentPage() {
  return (
    <Suspense
      fallback={
        <div className="min-h-screen h-screen flex items-center justify-center bg-background">
          <div className="text-center">
            <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto mb-4"></div>
            <p className="text-muted-foreground">Loading Ekairos Agent...</p>
          </div>
        </div>
      }
    >
      <TestAgentContent />
    </Suspense>
  )
}

Flujo completo

Resumen operacional

Estos pasos explican el pipeline sin modificar nada: ideal para validar antes de iterar nombres o comportamientos.

  1. 1. Configuración con `createAgent()`. Definimos el contexto, prompt y herramientas mediante la API fluida sin genéricos. El builder produce el `AgentConfig` y la instancia final (`build()`).
  2. 2. La API traduce a eventos. La función `createUserItemFromUIMessages` del route prepara un evento `user.message` compatible con `simpleAgent.progressStream`.
  3. 3. El agente responde en streaming. `simpleAgent.progressStream` (definido en `packages/web/src/lib/agents/simple-agent.ts`) regresa un stream que `createUIMessageStreamResponse` transforma en SSE para el componente `Agent`.

Por qué usar la librería

Conceptos clave de `ekairos/agent`

Contexto tipado

El callback `context()` del builder define y persiste estados arbitrarios (InstantDB, KV, etc.). Su return type alimenta automáticamente al resto de la cadena.

Herramientas basadas en Zod

Cada `tool` usa `inputSchema` para validar la carga útil. Esto genera JSON Schema automáticamente para AI SDK y evita prompts frágiles.

Streams con control de progreso

`progressStream(event, opts?)` retorna `{ stream, metadata }`. El stream es compatible con `ai` (`createUIMessageStreamResponse`) y conserva `contextKey` para reanudar conversaciones.