npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@verydia/react

v0.2.0

Published

React bindings for Verydia agents and workflows

Readme

@verydia/react

React bindings for Verydia agents and workflows. This package is backend-agnostic: you pass in a client that knows how to call your Verydia backend (Next.js API route, FastAPI, etc.).

Installation

Using pnpm:

pnpm add @verydia/react

Using npm:

npm install @verydia/react

Using yarn:

yarn add @verydia/react

Quick Start

1. Define a Client Implementation

Create a client that implements the VerydiaReactClient interface. This client handles HTTP calls to your backend:

// app/verydiaClient.ts
import type { VerydiaReactClient } from "@verydia/react";

export const verydiaClient: VerydiaReactClient = {
  async runAgent({ agentName, prompt, context }) {
    const res = await fetch("/api/verydia/agent", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ agentName, prompt, context }),
    });
    if (!res.ok) throw new Error("Agent request failed");
    return res.json();
  },

  async runWorkflow({ workflowName, payload, context }) {
    const res = await fetch("/api/verydia/workflow", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ workflowName, payload, context }),
    });
    if (!res.ok) throw new Error("Workflow request failed");
    return res.json();
  },
};

2. Wrap Your App with VerydiaProvider

// app/layout.tsx or app/page.tsx
import { VerydiaProvider } from "@verydia/react";
import { verydiaClient } from "./verydiaClient";

export default function App({ children }) {
  return (
    <VerydiaProvider client={verydiaClient}>
      {children}
    </VerydiaProvider>
  );
}

3. Use Hooks in Your Components

useVerydiaAgent

import { useVerydiaAgent } from "@verydia/react";

function MyAgentComponent() {
  const { runAgent, result, isLoading, error } = useVerydiaAgent("my-agent");

  const handleSubmit = async (prompt: string) => {
    await runAgent(prompt);
  };

  return (
    <div>
      {isLoading && <p>Loading...</p>}
      {error && <p>Error: {String(error)}</p>}
      {result?.messages && (
        <div>
          {result.messages.map((msg, idx) => (
            <div key={idx}>
              <strong>{msg.role}:</strong> {msg.content}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

useVerydiaWorkflow

import { useVerydiaWorkflow } from "@verydia/react";

function MyWorkflowComponent() {
  const { runWorkflow, result, isLoading, error } = useVerydiaWorkflow("my-workflow");

  const handleRun = async () => {
    await runWorkflow({ input: "some data" });
  };

  return (
    <div>
      <button onClick={handleRun} disabled={isLoading}>
        Run Workflow
      </button>
      {result && <pre>{JSON.stringify(result, null, 2)}</pre>}
    </div>
  );
}

4. Use the AgentChat Component

For quick prototyping, use the built-in AgentChat component:

import { AgentChat } from "@verydia/react";

function ChatPage() {
  return (
    <div>
      <h1>Chat with Verydia Agent</h1>
      <AgentChat agentName="customer-support" placeholder="How can I help you?" />
    </div>
  );
}

API Reference

VerydiaReactClient

Interface for backend communication:

interface VerydiaReactClient {
  runAgent(input: {
    agentName: string;
    prompt: string;
    context?: unknown;
  }): Promise<VerydiaReactAgentResult>;

  runWorkflow(input: {
    workflowName: string;
    payload: unknown;
    context?: unknown;
  }): Promise<VerydiaReactWorkflowResult>;
}

VerydiaProvider

Props:

  • client: VerydiaReactClient - Your backend client implementation
  • children: React.ReactNode - Your app components

useVerydiaAgent(agentName: string)

Returns:

  • runAgent(prompt: string, context?: unknown) - Function to run the agent
  • result: VerydiaReactAgentResult | null - Agent response
  • status: "idle" | "loading" | "success" | "error" - Current status
  • isLoading: boolean - Loading state
  • isIdle: boolean - Idle state
  • isSuccess: boolean - Success state
  • isError: boolean - Error state
  • error: unknown - Error if any

useVerydiaWorkflow(workflowName: string)

Returns:

  • runWorkflow(payload: unknown, context?: unknown) - Function to run the workflow
  • result: VerydiaReactWorkflowResult | null - Workflow response
  • status: "idle" | "loading" | "success" | "error" - Current status
  • isLoading: boolean - Loading state
  • isIdle: boolean - Idle state
  • isSuccess: boolean - Success state
  • isError: boolean - Error state
  • error: unknown - Error if any

AgentChat

Props:

  • agentName: string - Name of the agent to chat with
  • placeholder?: string - Input placeholder text (default: "Ask me anything...")

Admin Dashboard Starter

Verydia ships a light "admin SDK" for React that provides ready-made components for monitoring safety scores, LLM providers, and system health.

Components

  • SafetyPanel - Combined safety overview + category breakdown
  • ProvidersPanel - Display configured LLM providers and models
  • LlmStatusCard - Health status indicator for primary LLM
  • VerydiaAdminDashboard - Complete admin page composing all panels

Hooks

  • useSafetyDashboard(endpoint) - Fetch safety scorecard data
  • useProviders(endpoint) - Fetch provider configuration
  • useModels(endpoint) - Fetch model list
  • useLlmStatus(endpoint) - Fetch LLM health status
  • useSafetyHistory(endpoint) - Fetch historical safety scores

Quick Start: Admin Dashboard

import { VerydiaAdminDashboard } from "@verydia/react";

export default function AdminPage() {
  return (
    <VerydiaAdminDashboard
      safetyEndpoint="/api/verydia/safety/latest"
      providersEndpoint="/api/verydia/providers"
      llmStatusEndpoint="/api/verydia/llm-status"
    />
  );
}

Backend API Examples

The admin components are backend-agnostic and fetch data from HTTP endpoints you implement.

Safety Endpoint (Next.js)

// pages/api/verydia/safety/latest.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { createStoreFromEnv, computeTrend } from "@verydia/safety-insights";
import type { SafetyDashboardData } from "@verydia/react";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<SafetyDashboardData | { error: string }>
) {
  try {
    const store = createStoreFromEnv();
    const scorecards = await store.listScorecards({ limit: 2 });

    if (scorecards.length === 0) {
      return res.status(404).json({ error: "No scorecards found" });
    }

    const [current, previous] = scorecards;
    const trend = previous ? computeTrend(previous.result, current.result) : null;

    const dashboardData: SafetyDashboardData = {
      environment: (current.metadata?.environment as string) || "unknown",
      suiteName: current.metadata?.suiteName as string | undefined,
      timestamp: current.timestamp.toISOString(),
      totalScore: current.result.totalWeighted,
      classification: current.result.classification,
      categories: current.result.breakdown.map((cat) => ({
        id: cat.categoryId,
        label: cat.label,
        weight: cat.weight,
        weightedScore: cat.weightedScore,
      })),
      trend: trend
        ? {
            direction: trend.direction,
            delta: trend.delta,
            percentChange: trend.percentChange,
          }
        : null,
    };

    res.status(200).json(dashboardData);
  } catch (error) {
    console.error("Safety dashboard API error:", error);
    res.status(500).json({
      error: error instanceof Error ? error.message : "Internal server error",
    });
  }
}

Providers Endpoint (Next.js)

// pages/api/verydia/providers.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { getProviderRegistry } from "@verydia/providers";
import type { VerydiaProviderSummary } from "@verydia/react";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<VerydiaProviderSummary[] | { error: string }>
) {
  try {
    const registry = getProviderRegistry();
    const providers = registry.listProviders();

    const providerSummaries: VerydiaProviderSummary[] = providers.map((provider) => {
      const models = registry.listModels(provider.id);

      return {
        id: provider.id,
        label: provider.title || provider.id,
        kind: provider.kind,
        environment: (provider.metadata?.environment as string) || undefined,
        models: models.map((model) => ({
          id: model.modelId,
          label: model.displayName || model.modelId,
          providerId: model.providerId,
          contextWindowTokens: undefined, // Add if available in your provider config
          inputPricePer1KTokensUsd: model.cost?.inputPer1K,
          outputPricePer1KTokensUsd: model.cost?.outputPer1K,
          default: model.modelId === provider.metadata?.defaultModel,
        })),
      };
    });

    res.status(200).json(providerSummaries);
  } catch (error) {
    console.error("Providers API error:", error);
    res.status(500).json({
      error: error instanceof Error ? error.message : "Internal server error",
    });
  }
}

LLM Status Endpoint (Next.js)

// pages/api/verydia/llm-status.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { getProviderRegistry } from "@verydia/providers";
import type { LlmStatusSnapshot } from "@verydia/react";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<LlmStatusSnapshot | { error: string }>
) {
  try {
    const registry = getProviderRegistry();
    const primaryProvider = registry.listProviders()[0]; // Get first provider

    if (!primaryProvider) {
      return res.status(404).json({ error: "No providers configured" });
    }

    const models = registry.listModels(primaryProvider.id);
    const primaryModel = models[0];

    if (!primaryModel) {
      return res.status(404).json({ error: "No models found" });
    }

    // Perform a lightweight health check (ping)
    const startTime = Date.now();
    let status: "healthy" | "degraded" | "offline" | "unknown" = "unknown";
    let errorMessage: string | undefined;

    try {
      // Simple test request to check if provider is responsive
      await registry.chat(primaryProvider.id, {
        model: primaryModel.modelId,
        messages: [{ role: "user", content: "ping" }],
        maxTokens: 5,
      });
      status = "healthy";
    } catch (error) {
      status = "offline";
      errorMessage = error instanceof Error ? error.message : "Unknown error";
    }

    const latencyMs = Date.now() - startTime;

    const statusSnapshot: LlmStatusSnapshot = {
      providerId: primaryProvider.id,
      modelId: primaryModel.modelId,
      displayName: `${primaryProvider.title || primaryProvider.id} – ${primaryModel.displayName || primaryModel.modelId}`,
      environment: (primaryProvider.metadata?.environment as string) || undefined,
      status,
      latencyMs,
      lastCheckedAt: new Date().toISOString(),
      errorMessage,
    };

    res.status(200).json(statusSnapshot);
  } catch (error) {
    console.error("LLM status API error:", error);
    res.status(500).json({
      error: error instanceof Error ? error.message : "Internal server error",
    });
  }
}

Individual Components

You can also use admin components individually:

import {
  SafetyPanel,
  ProvidersPanel,
  LlmStatusCard,
  useSafetyDashboard,
  useProviders,
  useLlmStatus,
} from "@verydia/react";

function CustomAdminPage() {
  const { data: safety } = useSafetyDashboard("/api/verydia/safety/latest");
  const { data: providers } = useProviders("/api/verydia/providers");
  const { data: status } = useLlmStatus("/api/verydia/llm-status");

  return (
    <div style={{ padding: "24px", display: "flex", flexDirection: "column", gap: "24px" }}>
      {status && <LlmStatusCard status={status} />}
      {safety && <SafetyPanel data={safety} />}
      {providers && <ProvidersPanel providers={providers} />}
    </div>
  );
}

Admin Types

import type {
  VerydiaProviderSummary,
  VerydiaModelSummary,
  LlmStatusSnapshot,
  SafetyDashboardData,
  SafetyHistoryPoint,
} from "@verydia/react";

Backend-Agnostic Design

This package does NOT assume any specific backend. You can use it with:

  • Next.js API routes
  • FastAPI endpoints
  • Express.js servers
  • tRPC procedures
  • Any HTTP API that can run Verydia agents/workflows

The client interface is intentionally minimal to support any backend architecture.

TypeScript Support

Full TypeScript support with exported types:

import type {
  VerydiaReactClient,
  VerydiaReactAgentResult,
  VerydiaReactWorkflowResult,
} from "@verydia/react";

Contract-First Workflow Streaming with useWorkflow

The useWorkflow hook provides a first-class way to call Verydia workflows with full type safety from @verydia/contracts. It replaces the need for hacky useChat glue and works with both local dev server and cloud API.

Basic Usage

import { defineEventContract, z } from "@verydia/contracts";
import { useWorkflow } from "@verydia/react";

// Define your workflow's event contract
const StoryBookContract = defineEventContract({
  "chapter-generation": z.object({
    status: z.string(),
    chapterNumber: z.number(),
  }),
  "chapter-content": z.object({
    status: z.string(),
    content: z.string(),
    chapterNumber: z.number(),
  }),
});

function StoryGenerator() {
  const { run, events, status, lastEvent } = useWorkflow<typeof StoryBookContract>({
    workflowId: "story-book",
    mode: "dev", // or "cloud"
    devBaseUrl: "http://localhost:8787",
    onEvent: (event) => {
      // Fully typed discriminated union!
      if (event.type === "chapter-content") {
        console.log("Chapter:", event.data.content);
      }
    },
  });

  return (
    <div>
      <button onClick={() => run({ prompt: "Write a story about dragons" })}>
        Generate Story
      </button>

      <div>Status: {status}</div>

      {events.map((event, idx) => (
        <div key={idx}>
          {event.type === "chapter-content" && (
            <p>{event.data.content}</p> // Fully typed!
          )}
        </div>
      ))}
    </div>
  );
}

API Reference: useWorkflow

Options

interface UseWorkflowOptions<TContract> {
  workflowId: string;              // Workflow identifier
  initialInput?: unknown;          // Auto-run on mount with this input
  mode?: "cloud" | "dev";          // Execution mode (default: "cloud")
  devBaseUrl?: string;             // Dev server URL (default: "http://localhost:8787")
  cloudBaseUrl?: string;           // Cloud API URL (optional override)
  onEvent?: (event) => void;       // Callback for each event
  onComplete?: (output) => void;   // Callback on completion
  onError?: (error) => void;       // Callback on error
}

Return Value

interface UseWorkflowResult<TContract> {
  status: "idle" | "running" | "completed" | "error";
  error: Error | null;
  events: InferEventMap<TContract>[];  // Fully typed events!
  lastEvent: InferEventMap<TContract> | null;
  output: unknown;
  run: (input: unknown) => Promise<void>;
  cancel: () => void;
  reset: () => void;
}

Cloud Mode

const { run, events } = useWorkflow<typeof MyContract>({
  workflowId: "my-workflow",
  mode: "cloud",
  // Uses VERYDIA_API_KEY and VERYDIA_PROJECT_ID from environment
});

await run({ input: "data" });

Dev Mode

const { run, events } = useWorkflow<typeof MyContract>({
  workflowId: "my-workflow",
  mode: "dev",
  devBaseUrl: "http://localhost:8787",
});

await run({ input: "data" });

Type Safety

When you provide a contract, all events are fully typed:

const StoryContract = defineEventContract({
  progress: z.object({ percent: z.number() }),
  complete: z.object({ result: z.string() }),
});

const { events, lastEvent } = useWorkflow<typeof StoryContract>({
  workflowId: "story",
  mode: "dev",
});

// TypeScript knows the exact shape of events!
events.forEach((event) => {
  if (event.type === "progress") {
    console.log(event.data.percent); // number
  } else if (event.type === "complete") {
    console.log(event.data.result); // string
  }
});

Future: Streaming Support

The hook is designed to support streaming events once the dev-server and cloud API expose streaming endpoints. The API will remain the same - events will just arrive incrementally instead of all at once.

// TODO: Once streaming is implemented, events will arrive in real-time:
// - Dev server: SSE or WebSocket endpoint
// - Cloud API: Streaming response from client-sdk
// - Hook automatically processes events as they arrive
// - No API changes needed!

License

MIT