feat(client): app id with path + realtime url (#51)

* feat: app id + path format support

* chore: bump client to 0.8.4
This commit is contained in:
Daniel Rochetti 2024-02-20 18:59:37 -08:00 committed by GitHub
parent 66d817b2a6
commit e0b7627f7a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 48 additions and 28 deletions

View File

@ -137,7 +137,7 @@ export default function WebcamPage() {
const previewRef = useRef<HTMLCanvasElement | null>(null); const previewRef = useRef<HTMLCanvasElement | null>(null);
const { send } = fal.realtime.connect<LCMInput, LCMOutput>( const { send } = fal.realtime.connect<LCMInput, LCMOutput>(
'fal-ai/sd-turbo-real-time-high-fps-msgpack', 'fal-ai/sd-turbo-real-time-high-fps-msgpack-a10g',
{ {
connectionKey: 'camera-turbo-demo', connectionKey: 'camera-turbo-demo',
// not throttling the client, handling throttling of the camera itself // not throttling the client, handling throttling of the camera itself

View File

@ -79,27 +79,24 @@ export default function Home() {
setLoading(true); setLoading(true);
const start = Date.now(); const start = Date.now();
try { try {
const result: Result = await fal.subscribe( const result: Result = await fal.subscribe('fal-ai/illusion-diffusion', {
'54285744/illusion-diffusion', input: {
{ prompt,
input: { image_url: imageFile,
prompt, image_size: 'square_hd',
image_url: imageFile, },
image_size: 'square_hd', pollInterval: 3000, // Default is 1000 (every 1s)
}, logs: true,
pollInterval: 5000, // Default is 1000 (every 1s) onQueueUpdate(update) {
logs: true, setElapsedTime(Date.now() - start);
onQueueUpdate(update) { if (
setElapsedTime(Date.now() - start); update.status === 'IN_PROGRESS' ||
if ( update.status === 'COMPLETED'
update.status === 'IN_PROGRESS' || ) {
update.status === 'COMPLETED' setLogs((update.logs || []).map((log) => log.message));
) { }
setLogs((update.logs || []).map((log) => log.message)); },
} });
},
}
);
setResult(result); setResult(result);
} catch (error: any) { } catch (error: any) {
setError(error); setError(error);

View File

@ -1,7 +1,7 @@
{ {
"name": "@fal-ai/serverless-client", "name": "@fal-ai/serverless-client",
"description": "The fal serverless JS/TS client", "description": "The fal serverless JS/TS client",
"version": "0.8.3", "version": "0.8.4",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -10,6 +10,7 @@ import { ensureAppIdFormat, isUUIDv4, isValidUrl } from './utils';
type RunOptions<Input> = { type RunOptions<Input> = {
/** /**
* The path to the function, if any. Defaults to ``. * The path to the function, if any. Defaults to ``.
* @deprecated Pass the path as part of the app id itself, e.g. `fal-ai/sdxl/image-to-image`
*/ */
readonly path?: string; readonly path?: string;
@ -268,11 +269,12 @@ export const queue: Queue = {
id: string, id: string,
options: SubmitOptions<Input> options: SubmitOptions<Input>
): Promise<EnqueueResult> { ): Promise<EnqueueResult> {
const [appOwner, appAlias] = ensureAppIdFormat(id).split('/');
const { webhookUrl, path = '', ...runOptions } = options; const { webhookUrl, path = '', ...runOptions } = options;
const query = webhookUrl const query = webhookUrl
? '?' + new URLSearchParams({ fal_webhook: webhookUrl }).toString() ? '?' + new URLSearchParams({ fal_webhook: webhookUrl }).toString()
: ''; : '';
return send(id, { return send(`${appOwner}/${appAlias}`, {
...runOptions, ...runOptions,
subdomain: 'queue', subdomain: 'queue',
method: 'post', method: 'post',
@ -283,7 +285,8 @@ export const queue: Queue = {
id: string, id: string,
{ requestId, logs = false }: QueueStatusOptions { requestId, logs = false }: QueueStatusOptions
): Promise<QueueStatus> { ): Promise<QueueStatus> {
return send(id, { const [appOwner, appAlias] = ensureAppIdFormat(id).split('/');
return send(`${appOwner}/${appAlias}`, {
subdomain: 'queue', subdomain: 'queue',
method: 'get', method: 'get',
path: `/requests/${requestId}/status`, path: `/requests/${requestId}/status`,
@ -296,7 +299,8 @@ export const queue: Queue = {
id: string, id: string,
{ requestId }: BaseQueueOptions { requestId }: BaseQueueOptions
): Promise<Output> { ): Promise<Output> {
return send(id, { const [appOwner, appAlias] = ensureAppIdFormat(id).split('/');
return send(`${appOwner}/${appAlias}`, {
subdomain: 'queue', subdomain: 'queue',
method: 'get', method: 'get',
path: `/requests/${requestId}`, path: `/requests/${requestId}`,

View File

@ -249,6 +249,17 @@ type RealtimeUrlParams = {
maxBuffering?: number; maxBuffering?: number;
}; };
// This is a list of apps deployed before formal realtime support. Their URLs follow
// a different pattern and will be kept here until we fully sunset them.
const LEGACY_APPS = [
'lcm-sd15-i2i',
'lcm',
'sdxl-turbo-realtime',
'sd-turbo-real-time-high-fps-msgpack-a10g',
'lcm-plexed-sd15-i2i',
'sd-turbo-real-time-high-fps-msgpack',
];
function buildRealtimeUrl( function buildRealtimeUrl(
app: string, app: string,
{ token, maxBuffering }: RealtimeUrlParams { token, maxBuffering }: RealtimeUrlParams
@ -263,7 +274,10 @@ function buildRealtimeUrl(
queryParams.set('max_buffering', maxBuffering.toFixed(0)); queryParams.set('max_buffering', maxBuffering.toFixed(0));
} }
const appId = ensureAppIdFormat(app); const appId = ensureAppIdFormat(app);
return `wss://fal.run/${appId}/ws?${queryParams.toString()}`; const [, appAlias] = ensureAppIdFormat(app).split('/');
const suffix =
LEGACY_APPS.includes(appAlias) || !app.includes('/') ? 'ws' : 'realtime';
return `wss://fal.run/${appId}/${suffix}?${queryParams.toString()}`;
} }
const TOKEN_EXPIRATION_SECONDS = 120; const TOKEN_EXPIRATION_SECONDS = 120;

View File

@ -22,6 +22,11 @@ describe('The utils test suite', () => {
expect(ensureAppIdFormat(id)).toBe(id); expect(ensureAppIdFormat(id)).toBe(id);
}); });
it('shoud match a current appOwner/appId/path format', () => {
const id = 'fal-ai/fast-sdxl/image-to-image';
expect(ensureAppIdFormat(id)).toBe(id);
});
it('should throw on an invalid app id format', () => { it('should throw on an invalid app id format', () => {
const id = 'just-an-id'; const id = 'just-an-id';
expect(() => ensureAppIdFormat(id)).toThrowError(); expect(() => ensureAppIdFormat(id)).toThrowError();

View File

@ -9,7 +9,7 @@ export function isUUIDv4(id: string): boolean {
export function ensureAppIdFormat(id: string): string { export function ensureAppIdFormat(id: string): string {
const parts = id.split('/'); const parts = id.split('/');
if (parts.length === 2) { if (parts.length > 1) {
return id; return id;
} }
const [, appOwner, appId] = /^([0-9]+)-([a-zA-Z0-9-]+)$/.exec(id) || []; const [, appOwner, appId] = /^([0-9]+)-([a-zA-Z0-9-]+)$/.exec(id) || [];