ahmedallem.
Engineering · 4 min read

Getting Started with Allem SDK Agents: Agentic AI with Tool Calling for React

Learn how to build AI agents with @allem-sdk/agents — multi-step tool calling, status tracking, and typed tool definitions built on the Vercel AI SDK.

Ahmed Allem

Ahmed Allem

Founder & CTO · Aviation, AI & Startups

ShareShare
Getting Started with Allem SDK Agents: Agentic AI with Tool Calling for React

@allem-sdk/agents brings agentic AI to React. Your AI can call tools, execute multi-step workflows, and report its status back to the UI — all with typed hooks built on the Vercel AI SDK.

Installation

npm install @allem-sdk/agents @allem-sdk/ai ai @ai-sdk/react

Install your preferred AI provider:

npm install @ai-sdk/google
# or @ai-sdk/anthropic, @ai-sdk/openai

For defining tool schemas:

npm install zod

How It Works

  1. You define tools with descriptions and typed parameters
  2. The AI model decides when to call a tool based on the conversation
  3. The tool executes and returns results to the model
  4. The model uses the results to form its response
  5. Your UI tracks every step through the useAllemAgent hook

Step 1: Define Tools

Use createAllemTool with a Zod schema for type-safe parameters:

// lib/tools.ts
import { createAllemTool } from "@allem-sdk/agents";
import { z } from "zod";

export const weatherTool = createAllemTool({
  description: "Get the current weather for a city",
  parameters: z.object({
    city: z.string().describe("The city name"),
  }),
  execute: async ({ city }) => {
    const res = await fetch(`https://api.weather.com/v1?city=${city}`);
    return res.json();
  },
});

export const searchTool = createAllemTool({
  description: "Search the web for information",
  parameters: z.object({
    query: z.string().describe("The search query"),
    limit: z.number().optional().describe("Max results to return"),
  }),
  execute: async ({ query, limit = 5 }) => {
    const results = await searchAPI(query, limit);
    return results;
  },
});

Step 2: Create the API Route

// app/api/agent/route.ts
import { createAllemAgentHandler } from "@allem-sdk/agents";
import { google } from "@ai-sdk/google";
import { weatherTool, searchTool } from "@/lib/tools";

export const POST = createAllemAgentHandler({
  providers: {
    google: (model) => google(model ?? "gemini-2.0-flash"),
  },
  defaultProvider: "google",
  systemPrompt: "You are a helpful assistant with access to weather and search tools.",
  tools: {
    weather: weatherTool,
    search: searchTool,
  },
});

Step 3: Build the UI

"use client";
import { AllemAIProvider } from "@allem-sdk/ai";
import { useAllemAgent } from "@allem-sdk/agents";

function AgentChat() {
  const {
    messages,
    sendMessage,
    agentStatus,
    steps,
    currentToolCalls,
    isAgentRunning,
  } = useAllemAgent();

  return (
    <div>
      {/* Status indicator */}
      {agentStatus === "thinking" && <p>Thinking...</p>}
      {agentStatus === "calling-tool" && (
        <p>Using tool: {currentToolCalls[0]?.toolName}...</p>
      )}

      {/* Messages */}
      {messages.map((m) => (
        <div key={m.id}>
          <strong>{m.role === "user" ? "You" : "Agent"}:</strong>
          {m.parts
            ?.filter((p) => p.type === "text")
            .map((p) => p.text)
            .join("")}
        </div>
      ))}

      {/* Step count */}
      {steps.length > 0 && (
        <p className="text-sm text-gray-500">{steps.length} steps taken</p>
      )}

      {/* Input */}
      <form
        onSubmit={(e) => {
          e.preventDefault();
          const input = e.currentTarget.elements.namedItem("msg") as HTMLInputElement;
          sendMessage({ text: input.value });
          input.value = "";
        }}
      >
        <input name="msg" placeholder="Ask the agent..." disabled={isAgentRunning} />
        <button type="submit" disabled={isAgentRunning}>Send</button>
      </form>
    </div>
  );
}

export default function AgentPage() {
  return (
    <AllemAIProvider api="/api/agent" provider="google">
      <AgentChat />
    </AllemAIProvider>
  );
}

Agent Status States

The agentStatus property tracks what the agent is doing:

Status When What to Show
idle No conversation yet, or agent finished Ready state, input field
thinking AI is generating a response Loading spinner, "Thinking..."
calling-tool AI decided to call a tool Tool name, "Looking up weather..."
done Agent finished its response Final answer, step summary

AgentProvider for Tool Metadata

Register tool metadata so your UI can display what tools are available:

import { AgentProvider, useAgentTools } from "@allem-sdk/agents";

const tools = [
  { name: "weather", description: "Get current weather for a city" },
  { name: "search", description: "Search the web for information" },
];

function ToolList() {
  const tools = useAgentTools();

  return (
    <div className="text-sm text-gray-500">
      <p>Available tools:</p>
      <ul>
        {tools.map((t) => (
          <li key={t.name}>{t.name} — {t.description}</li>
        ))}
      </ul>
    </div>
  );
}

export default function App() {
  return (
    <AllemAIProvider api="/api/agent" provider="google">
      <AgentProvider tools={tools}>
        <ToolList />
        <AgentChat />
      </AgentProvider>
    </AllemAIProvider>
  );
}

useAllemAgent Returns

All properties from useAllemChat, plus:

Property Type Description
agentStatus AgentStatus "idle" | "thinking" | "calling-tool" | "done"
steps AgentStep[] Ordered list of steps the agent has taken
currentToolCalls AgentToolCall[] Tool calls from the most recent step
isAgentRunning boolean Whether the agent is actively processing

Complete Agent with Status UI

"use client";
import { AllemAIProvider } from "@allem-sdk/ai";
import { useAllemAgent } from "@allem-sdk/agents";
import { Button, Card, CardBody, Spinner, Badge } from "@allem-ui/react";
import { ChatContainer, ChatList, ChatBubble, ChatInput } from "@allem-ui/chat";

function StatusBadge({ status }: { status: string }) {
  const colors = {
    idle: "neutral",
    thinking: "warning",
    "calling-tool": "primary",
    done: "success",
  };

  return (
    <Badge color={colors[status] ?? "neutral"} variant="outline">
      {status === "calling-tool" ? "Using tool" : status}
    </Badge>
  );
}

function Agent() {
  const {
    messages,
    sendMessage,
    agentStatus,
    steps,
    currentToolCalls,
    isAgentRunning,
  } = useAllemAgent();

  return (
    <Card>
      <CardBody>
        <div className="flex items-center justify-between mb-4">
          <StatusBadge status={agentStatus} />
          {steps.length > 0 && (
            <span className="text-sm text-gray-500">{steps.length} steps</span>
          )}
        </div>

        <ChatContainer>
          <ChatList>
            {messages.map((m) => (
              <ChatBubble
                key={m.id}
                variant={m.role === "user" ? "sent" : "received"}
                avatar={m.role === "user" ? "You" : "AI"}
              >
                {m.parts
                  ?.filter((p) => p.type === "text")
                  .map((p) => p.text)
                  .join("")}
              </ChatBubble>
            ))}
            {agentStatus === "calling-tool" && (
              <ChatBubble variant="received" avatar="AI">
                <div className="flex items-center gap-2">
                  <Spinner size="sm" />
                  Using {currentToolCalls[0]?.toolName}...
                </div>
              </ChatBubble>
            )}
          </ChatList>
          <ChatInput
            onSend={(text) => sendMessage({ text })}
            isLoading={isAgentRunning}
            placeholder="Ask the agent..."
          />
        </ChatContainer>
      </CardBody>
    </Card>
  );
}

export default function AgentPage() {
  return (
    <AllemAIProvider api="/api/agent" provider="google">
      <Agent />
    </AllemAIProvider>
  );
}

What's Next