@kapy.dev/ui
v1.8.0
Published
React hooks and components for [Kapy](https://kapy.dev). Fetch plans, run executions, and watch results update in real time — with built-in caching and automatic polling, similar to TanStack Query but without the dependency.
Downloads
1,142
Readme
@kapy.dev/ui
React hooks and components for Kapy. Fetch plans, run executions, and watch results update in real time — with built-in caching and automatic polling, similar to TanStack Query but without the dependency.
Requirements
- React ≥ 17
- A running bridge endpoint from
@kapy.dev/sdk
Installation
npm install @kapy.dev/ui
# or
pnpm add @kapy.dev/uiSetup
Wrap your app (or the relevant subtree) with KapyProvider, pointing it at your backend bridge endpoint:
import { KapyProvider } from "@kapy.dev/ui";
export default function App() {
return (
<KapyProvider endpoint="/api/krl">
<YourApp />
</KapyProvider>
);
}The endpoint is the URL of the bridge you set up with @kapy.dev/sdk. It handles authentication server-side so your API key is never exposed to the browser.
Hooks
useKapyPlans()
Fetches and caches the list of research plans. Automatically re-fetches after any execute or planAndExecute call.
import { useKapyPlans } from "@kapy.dev/ui";
function PlansList() {
const { plans, loading, error, refetch } = useKapyPlans();
if (loading) return <p>Loading…</p>;
if (error) return <p>Error: {error}</p>;
return (
<ul>
{plans?.map((plan) => (
<li key={plan.id}>{plan.input_question} — {plan.status}</li>
))}
</ul>
);
}Returns:
| Field | Type | Description |
|-------|------|-------------|
| plans | PlanSummary[] \| null | Cached plan list |
| loading | boolean | True while fetching |
| error | string \| null | Last error message |
| refetch | () => void | Force a fresh fetch |
useKapyExecution(planId, executionId)
Fetches an execution and polls every 2 seconds while its status is pending. Stops polling once the status changes to success or error, then caches the result indefinitely.
import { useKapyExecution } from "@kapy.dev/ui";
function ExecutionView({ planId, executionId }: { planId: number; executionId: number }) {
const { execution, loading, error } = useKapyExecution(planId, executionId);
if (loading && !execution) return <p>Loading…</p>;
if (error) return <p>Error: {error}</p>;
return (
<div>
<p>Status: {execution?.status}</p>
<pre>{JSON.stringify(execution?.output, null, 2)}</pre>
</div>
);
}Pass null or undefined for either argument to skip fetching (useful while IDs are not yet available).
Returns:
| Field | Type | Description |
|-------|------|-------------|
| execution | Execution \| null | Cached execution result |
| loading | boolean | True while fetching |
| error | string \| null | Last error message |
useKapyActions()
Returns async action functions. After each call, the plans cache is automatically invalidated so useKapyPlans refetches on the next render.
import { useKapyActions } from "@kapy.dev/ui";
function Controls() {
const { execute, planAndExecute } = useKapyActions();
const handleExecute = async () => {
const { planId, executionId } = await execute(42, { userId: "u1" });
console.log("Started execution", executionId, "on plan", planId);
};
const handleNew = async () => {
const { planId, executionId } = await planAndExecute(
"What drives user retention?",
{ segment: "enterprise" }
);
console.log("New plan", planId, "execution", executionId);
};
return (
<>
<button onClick={handleExecute}>Run existing plan</button>
<button onClick={handleNew}>Create & run</button>
</>
);
}execute(planId, input?)
Runs an existing plan. Returns { planId, executionId }.
planAndExecute(question, input?)
Creates a new plan and immediately reserves an execution slot. Uses kind: "auto" and empty context. Returns { planId, executionId }.
Caching behaviour
| Hook | Cache key | Invalidation |
|------|-----------|--------------|
| useKapyPlans | Global (one per provider) | After every execute or planAndExecute call |
| useKapyExecution | "planId:executionId" | Never (final results are immutable); polling stops once status is no longer pending |
TypeScript
All types are exported:
import type {
PlanSummary,
PlanStatus,
PlanKind,
Execution,
ExecutionStatus,
ExecutionOutput,
RunnerNode,
RunnerEdge,
} from "@kapy.dev/ui";Full example
import { KapyProvider, useKapyPlans, useKapyExecution, useKapyActions } from "@kapy.dev/ui";
import { useState } from "react";
function Dashboard() {
const { plans } = useKapyPlans();
const { planAndExecute } = useKapyActions();
const [ids, setIds] = useState<{ planId: number; executionId: number } | null>(null);
const { execution } = useKapyExecution(ids?.planId, ids?.executionId);
const run = async () => {
const result = await planAndExecute("Top retention drivers", {});
setIds(result);
};
return (
<div>
<button onClick={run}>Run new analysis</button>
<p>Plans: {plans?.length ?? "…"}</p>
{execution && <p>Status: {execution.status}</p>}
</div>
);
}
export default function App() {
return (
<KapyProvider endpoint="/api/krl">
<Dashboard />
</KapyProvider>
);
}License
MIT
