Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

**IMPORTANT:** This is the last release before `1.0.0`, which will introduce breaking changes. The upcoming major version will add a component registry (via ShadCN) and significantly change the library developer experience. Most primitive components will be removed in `1.0.0`, so please plan your upgrade path accordingly. A migration guide will be provided to assist upgrading.

[0.5.1]
- `PipecatAppBase` updated to use `startAndConnect` pattern of recent clients.
- Added: `handleConnect` now accepts optional `startBot` params in `PipecatAppBase` so callers can supply connection details at call time without recreating the client.

[0.5.0]

- Added: `initDevicesOnMount` prop to `PipecatAppBase`, giving developers full control over when device access permissions are requested.
Expand Down
10 changes: 5 additions & 5 deletions package/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pipecat-ai/voice-ui-kit",
"version": "0.5.0",
"version": "0.5.1-rc.5",
"type": "module",
"license": "BSD-2-Clause",
"repository": {
Expand Down Expand Up @@ -49,7 +49,7 @@
"@pipecat-ai/client-js": ">=1.4.0",
"@pipecat-ai/client-react": ">=1.1.0",
"@pipecat-ai/daily-transport": ">=1.2.1",
"@pipecat-ai/small-webrtc-transport": ">=1.3.0",
"@pipecat-ai/small-webrtc-transport": ">=1.7.0",
"react": ">=16.8.0 || >=17.0.0 || >=18.0.0",
"react-dom": ">=16.8.0 || >=17.0.0 || >=18.0.0",
"three": ">=0.140.0"
Expand All @@ -73,7 +73,7 @@
},
"@pipecat-ai/small-webrtc-transport": {
"optional": true,
"version": "^1.3.0"
"version": "^1.7.0"
},
"three": {
"optional": true
Expand Down Expand Up @@ -113,7 +113,7 @@
"@pipecat-ai/client-js": "^1.4.0",
"@pipecat-ai/client-react": "^1.1.0",
"@pipecat-ai/daily-transport": "^1.4.0",
"@pipecat-ai/small-webrtc-transport": "^1.5.0",
"@pipecat-ai/small-webrtc-transport": "^1.7.0",
"@tailwindcss/vite": "^4.1.13",
"@types/node": "^22.18.8",
"@types/react": "^19.1.16",
Expand All @@ -140,4 +140,4 @@
"node": ">=18"
},
"packageManager": "[email protected]"
}
}
76 changes: 25 additions & 51 deletions package/src/components/PipecatAppBase.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"use client";

import { ConversationProvider } from "@/components/ConversationProvider";
import {
ThemeProvider,
type ThemeProviderProps,
} from "@/components/ThemeProvider";
import { ConversationProvider } from "@/components/ConversationProvider";
import { createTransport } from "@/lib/transports";
import {
APIRequest,
Expand All @@ -17,10 +17,7 @@ import {
PipecatClientProvider,
} from "@pipecat-ai/client-react";
import type { DailyTransportConstructorOptions } from "@pipecat-ai/daily-transport";
import type {
SmallWebRTCTransport,
SmallWebRTCTransportConstructorOptions,
} from "@pipecat-ai/small-webrtc-transport";
import type { SmallWebRTCTransportConstructorOptions } from "@pipecat-ai/small-webrtc-transport";
import React, { useCallback, useEffect, useState } from "react";

/**
Expand Down Expand Up @@ -73,8 +70,11 @@ export interface PipecatBaseProps {
export interface PipecatBaseChildProps {
/** Pipecat client instance */
client: PipecatClient | null;
/** Function to initiate a connection to the session. Can be sync or async. */
handleConnect?: () => void | Promise<void>;
/**
* Function to initiate a connection to the session. Can be sync or async.
* Optional params allow supplying startBot data at call time.
*/
handleConnect?: (params?: APIRequest) => void | Promise<void>;
/** Function to disconnect from the current session. Can be sync or async. */
handleDisconnect?: () => void | Promise<void>;
/** Error message if connection fails */
Expand All @@ -85,10 +85,6 @@ export interface PipecatBaseChildProps {
transformedStartBotResponse?: TransportConnectionParams | unknown;
}

const defaultStartBotResponseTransformer = (
response: TransportConnectionParams,
) => response;

/**
* PipecatAppBase component that provides a configured Pipecat client with audio capabilities.
*
Expand All @@ -100,6 +96,7 @@ const defaultStartBotResponseTransformer = (
* - Automatically disconnects the client when unmounting
* - Optionally disables theme provider based on noThemeProvider prop
* - Optionally auto-connects to the session on mount based on connectOnMount prop
* - Allows providing startBot params either as a prop or when calling handleConnect
*
* @param props - Configuration for the audio client including connection params, transport type, and auto-connect behavior
* @returns A provider component that wraps children with client context and handlers
Expand Down Expand Up @@ -146,68 +143,47 @@ const defaultStartBotResponseTransformer = (
* >
* <YourComponent />
* </PipecatAppBase>
*
* // Passing startBot params at call time (when they aren't available at mount)
* <PipecatAppBase transportType="daily">
* {({ handleConnect }) => (
* <button onClick={() => handleConnect?.(lateParams)}>Connect</button>
* )}
* </PipecatAppBase>
* ```
*/
export const PipecatAppBase: React.FC<PipecatBaseProps> = ({
clientOptions,
connectOnMount = false,
connectParams,
initDevicesOnMount = false,
noAudioOutput = false,
noThemeProvider = false,
startBotParams,
startBotResponseTransformer = defaultStartBotResponseTransformer,
transportOptions,
transportType,
themeProps,
children,
}) => {
const [client, setClient] = useState<PipecatClient | null>(null);
const [error, setError] = useState<string | null>(null);
const [rawStartBotResponse, setRawStartBotResponse] = useState<
TransportConnectionParams | unknown
>(null);
const [transformedStartBotResponse, setTransformedStartBotResponse] =
useState<TransportConnectionParams | unknown>(null);

const startAndConnect = useCallback(
async (client: PipecatClient) => {
async (client: PipecatClient, overrideParams?: APIRequest) => {
const params = overrideParams ?? startBotParams;
if (!params) {
return;
}
try {
if (startBotParams) {
const response = await client.startBot({
requestData: {},
...startBotParams,
});
setRawStartBotResponse(response);
if (transportType === "smallwebrtc") {
// Check if response has ICEServers
if (
typeof response === "object" &&
response !== null &&
"iceConfig" in response
) {
const iceConfig = response.iceConfig as {
iceServers: RTCIceServer[];
};
(client.transport as SmallWebRTCTransport).iceServers =
iceConfig.iceServers;
}
}
const transformedResponse =
await startBotResponseTransformer(response);
await client.connect(transformedResponse);
setTransformedStartBotResponse(transformedResponse);
} else {
await client.connect(connectParams ?? {});
}
console.debug("Connecting to session with params:", params);
await client.startBotAndConnect(params);
} catch (err) {
console.error("Connection error:", err);
setError(
`Failed to start session: ${err instanceof Error ? err.message : String(err)}`,
);
}
},
[connectParams, startBotParams, startBotResponseTransformer, transportType],
[startBotParams],
);

/**
Expand Down Expand Up @@ -264,7 +240,7 @@ export const PipecatAppBase: React.FC<PipecatBaseProps> = ({
* Only allows connection from specific states (initialized, disconnected, error).
* Clears any previous errors and handles connection failures.
*/
const handleConnect = async () => {
const handleConnect = async (params?: APIRequest) => {
if (
!client ||
!["initialized", "disconnected", "error"].includes(client.state)
Expand All @@ -273,7 +249,7 @@ export const PipecatAppBase: React.FC<PipecatBaseProps> = ({
}
setError(null);

await startAndConnect(client);
await startAndConnect(client, params);
};

/**
Expand All @@ -300,8 +276,6 @@ export const PipecatAppBase: React.FC<PipecatBaseProps> = ({
handleConnect,
handleDisconnect,
error,
rawStartBotResponse,
transformedStartBotResponse,
};

// Only create PipecatClientProvider when client is fully initialized
Expand Down
40 changes: 35 additions & 5 deletions package/src/components/elements/SessionInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import CopyText from "@/components/elements/CopyText";
import DataList from "@/components/elements/DataList";
import { TextDashBlankslate } from "@/index";
import Daily from "@daily-co/daily-js";
import { usePipecatClient } from "@pipecat-ai/client-react";
import { useEffect, useState } from "react";

interface Props {
noTransportType?: boolean;
Expand All @@ -22,14 +22,44 @@ export const SessionInfo: React.FC<Props> = ({
participantId,
}) => {
const client = usePipecatClient();
const [dailyVersion, setDailyVersion] = useState<string | null>(null);
const transport = client?.transport;
const isDailyTransport = !!transport && "dailyCallClient" in transport;

useEffect(() => {
let isMounted = true;

const loadDailyVersion = async () => {
try {
const { default: Daily } = await import("@daily-co/daily-js");
if (!isMounted) {
return;
}
setDailyVersion(Daily.version());
} catch {
if (isMounted) {
setDailyVersion(null);
}
}
};

if (isDailyTransport) {
loadDailyVersion();
} else {
setDailyVersion(null);
}

return () => {
isMounted = false;
};
}, [isDailyTransport]);

let transportTypeName = "Unknown";
if (client && "dailyCallClient" in client.transport) {
transportTypeName = `Daily (v${Daily.version()})`;
if (isDailyTransport) {
transportTypeName = dailyVersion ? `Daily (v${dailyVersion})` : "Daily";
} else if (
// @ts-expect-error - __proto__ not typed
client?.transport.__proto__.constructor.SERVICE_NAME ===
"small-webrtc-transport"
transport?.__proto__.constructor.SERVICE_NAME === "small-webrtc-transport"
) {
transportTypeName = "Small WebRTC";
}
Expand Down
3 changes: 2 additions & 1 deletion package/src/components/metrics/Metrics.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useState } from "react";

import { RTVIEvent } from "@pipecat-ai/client-js";
import {
usePipecatClientTransportState,
Expand All @@ -14,7 +16,6 @@ import {
Title,
Tooltip,
} from "chart.js";
import { useState } from "react";
import { Line } from "react-chartjs-2";

import { cn } from "@/lib/utils";
Expand Down
18 changes: 15 additions & 3 deletions package/src/components/panels/ConversationPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import type { ConversationProps } from "@/components/elements/Conversation";
import Conversation from "@/components/elements/Conversation";
import { Metrics } from "@/components/metrics";
import { Button } from "@/components/ui/button";
import { Panel, PanelContent, PanelHeader } from "@/components/ui/panel";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { TextMode } from "@/types/conversation";
import { LineChartIcon, MessagesSquareIcon } from "lucide-react";
import { memo, useState } from "react";
import { Suspense, lazy, memo, useState } from "react";

const LazyMetrics = lazy(async () => {
const { Metrics } = await import("@/components/metrics");
return { default: Metrics };
});

interface ConversationPanelProps {
/**
Expand Down Expand Up @@ -90,7 +94,15 @@ export const ConversationPanel: React.FC<ConversationPanelProps> = memo(
)}
{!noMetrics && (
<TabsContent value="metrics" className="h-full">
<Metrics />
<Suspense
fallback={
<div className="flex h-full items-center justify-center text-sm text-muted-foreground">
Loading metrics...
</div>
}
>
<LazyMetrics />
</Suspense>
</TabsContent>
)}
</PanelContent>
Expand Down
2 changes: 1 addition & 1 deletion package/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default defineConfig({
},
format: ["esm", "cjs"],
dts: true,
splitting: false,
splitting: true,
clean: true,
external: [
"react",
Expand Down
16 changes: 14 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.