Skip to content

Commit

Permalink
feat: new queue url format
Browse files Browse the repository at this point in the history
  • Loading branch information
drochetti committed Jan 13, 2024
1 parent 0735ed5 commit 8521636
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 39 deletions.
2 changes: 1 addition & 1 deletion apps/demo-nextjs-app-router/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export default function Home() {
const start = Date.now();
try {
const result: Result = await fal.subscribe(
'54285744-illusion-diffusion',
'54285744/illusion-diffusion',
{
input: {
prompt,
Expand Down
4 changes: 2 additions & 2 deletions apps/demo-nextjs-page-router/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ export function Index() {
setLoading(true);
const start = Date.now();
try {
const result: Result = await fal.subscribe('110602490-lora', {
const result: Result = await fal.subscribe('110602490/lora', {
input: {
prompt,
model_name: 'stabilityai/stable-diffusion-xl-base-1.0',
image_size: 'square_hd',
},
pollInterval: 5000, // Default is 1000 (every 1s)
pollInterval: 3000, // Default is 1000 (every 1s)
logs: true,
onQueueUpdate(update) {
setElapsedTime(Date.now() - start);
Expand Down
5 changes: 2 additions & 3 deletions libs/client/src/function.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import uuid from 'uuid-random';
import { getConfig } from './config';
import { buildUrl } from './function';

describe('The function test suite', () => {
Expand All @@ -9,10 +8,10 @@ describe('The function test suite', () => {
expect(url).toMatch(`trigger/12345/${id}`);
});

it('should build the URL with a function user-id/app-alias', () => {
it('should build the URL with a function user-id-app-alias', () => {
const alias = '12345-some-alias';
const url = buildUrl(alias);
expect(url).toMatch(`${alias}.gateway.alpha.fal.ai`);
expect(url).toMatch(`fal.run/12345/some-alias`);
});

it('should build the URL with a function username/app-alias', () => {
Expand Down
62 changes: 40 additions & 22 deletions libs/client/src/function.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { getConfig } from './config';
import { dispatchRequest } from './request';
import { storageImpl } from './storage';
import { EnqueueResult, QueueStatus } from './types';
import { isLegacyAppId, isUUIDv4, isValidUrl } from './utils';
import { ensureAppIdFormat, isUUIDv4, isValidUrl } from './utils';

/**
* The function input and other configuration when running
Expand Down Expand Up @@ -36,6 +35,15 @@ type RunOptions<Input> = {
readonly autoUpload?: boolean;
};

type ExtraOptions = {
/**
* If `true`, the function will use the queue to run the function
* asynchronously and return the result in a separate call. This
* influences how the URL is built.
*/
readonly subdomain?: string;
};

/**
* Builds the final url to run the function based on its `id` or alias and
* a the options from `RunOptions<Input>`.
Expand All @@ -47,7 +55,7 @@ type RunOptions<Input> = {
*/
export function buildUrl<Input>(
id: string,
options: RunOptions<Input> = {}
options: RunOptions<Input> & ExtraOptions = {}
): string {
const method = (options.method ?? 'post').toLowerCase();
const path = (options.path ?? '').replace(/^\//, '').replace(/\/{2,}/, '/');
Expand All @@ -65,26 +73,20 @@ export function buildUrl<Input>(
}

// TODO remove this after some time, fal.run should be preferred
const host = 'gateway.alpha.fal.ai';
if (parts.length === 2 && isUUIDv4(parts[1])) {
const host = 'gateway.shark.fal.ai';
return `https://${host}/trigger/${id}/${path}${queryParams}`;
}
if (isLegacyAppId(id)) {
return `https://${id}.${host}/${path}${queryParams}`;
}

return `https://fal.run/${id}/${path}${queryParams}`;
const appId = ensureAppIdFormat(id);
const subdomain = options.subdomain ? `${options.subdomain}.` : '';
const url = `https://${subdomain}matteo.shark.fal.run/${appId}/${path}`;
return `${url.replace(/\/$/, '')}${queryParams}`;
}

/**
* Runs a fal serverless function identified by its `id`.
*
* @param id the registered function revision id or alias.
* @returns the remote function output
*/
export async function run<Input, Output>(
export async function send<Input, Output>(
id: string,
options: RunOptions<Input> = {}
options: RunOptions<Input> & ExtraOptions = {}
): Promise<Output> {
const input =
options.input && options.autoUpload !== false
Expand All @@ -97,6 +99,19 @@ export async function run<Input, Output>(
);
}

/**
* Runs a fal serverless function identified by its `id`.
*
* @param id the registered function revision id or alias.
* @returns the remote function output
*/
export async function run<Input, Output>(
id: string,
options: RunOptions<Input> = {}
): Promise<Output> {
return send(id, options);
}

/**
* Subscribes to updates for a specific request in the queue.
*
Expand Down Expand Up @@ -257,19 +272,21 @@ export const queue: Queue = {
const query = webhookUrl
? '?' + new URLSearchParams({ fal_webhook: webhookUrl }).toString()
: '';
return run(id, {
return send(id, {
...runOptions,
subdomain: 'queue',
method: 'post',
path: '/fal/queue/submit' + path + query,
path: path + query,
});
},
async status(
id: string,
{ requestId, logs = false }: QueueStatusOptions
): Promise<QueueStatus> {
return run(id, {
return send(id, {
subdomain: 'queue',
method: 'get',
path: `/fal/queue/requests/${requestId}/status`,
path: `/requests/${requestId}/status`,
input: {
logs: logs ? '1' : '0',
},
Expand All @@ -279,9 +296,10 @@ export const queue: Queue = {
id: string,
{ requestId }: BaseQueueOptions
): Promise<Output> {
return run(id, {
return send(id, {
subdomain: 'queue',
method: 'get',
path: `/fal/queue/requests/${requestId}/response`,
path: `/requests/${requestId}`,
});
},
subscribe,
Expand Down
12 changes: 5 additions & 7 deletions libs/client/src/realtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import {
transition,
} from 'robot3';
import uuid from 'uuid-random';
import { getConfig, getRestApiUrl } from './config';
import { getRestApiUrl } from './config';
import { dispatchRequest } from './request';
import { ApiError } from './response';
import { isBrowser } from './runtime';
import { isLegacyAppId, isReact, throttle } from './utils';
import { ensureAppIdFormat, isReact, throttle } from './utils';

// Define the context
interface Context {
Expand Down Expand Up @@ -262,10 +262,8 @@ function buildRealtimeUrl(
if (maxBuffering !== undefined) {
queryParams.set('max_buffering', maxBuffering.toFixed(0));
}
if (isLegacyAppId(app)) {
return `wss://${app}.gateway.alpha.fal.ai/ws?${queryParams.toString()}`;
}
return `wss://fal.run/${app}/ws?${queryParams.toString()}`;
const appId = ensureAppIdFormat(app);
return `wss://fal.run/${appId}/ws?${queryParams.toString()}`;
}

const TOKEN_EXPIRATION_SECONDS = 120;
Expand All @@ -287,7 +285,7 @@ async function getToken(app: string): Promise<string> {
const [_, ...appAlias] = app.split('-');

Check warning on line 285 in libs/client/src/realtime.ts

View workflow job for this annotation

GitHub Actions / build

'_' is assigned a value but never used
const token: string | object = await dispatchRequest<any, string>(
'POST',
`https://${getRestApiUrl()}/tokens/`,
`${getRestApiUrl()}/tokens/`,
{
allowed_apps: [appAlias.join('-')],
token_expiration: TOKEN_EXPIRATION_SECONDS,
Expand Down
2 changes: 1 addition & 1 deletion libs/client/src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ async function initiateUpload(file: Blob): Promise<InitiateUploadResult> {
file.name || `${Date.now()}.${getExtensionFromContentType(contentType)}`;
return await dispatchRequest<InitiateUploadData, InitiateUploadResult>(
'POST',
`https://${getRestApiUrl()}/storage/upload/initiate`,
`${getRestApiUrl()}/storage/upload/initiate`,
{
content_type: contentType,
file_name: filename,
Expand Down
17 changes: 16 additions & 1 deletion libs/client/src/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import uuid from 'uuid-random';
import { isUUIDv4 } from './utils';
import { ensureAppIdFormat, isUUIDv4 } from './utils';

describe('The utils test suite', () => {
it('should match a valid v4 uuid', () => {
Expand All @@ -11,4 +11,19 @@ describe('The utils test suite', () => {
const id = 'e726b886-e2c2-11ed-b5ea-0242ac120002';
expect(isUUIDv4(id)).toBe(false);
});

it('shoud match match a legacy appOwner-appId format', () => {
const id = '12345-abcde-fgh';
expect(ensureAppIdFormat(id)).toBe('12345/abcde-fgh');
});

it('shoud match a current appOwner/appId format', () => {
const id = 'fal-ai/fast-sdxl';
expect(ensureAppIdFormat(id)).toBe(id);
});

it('should throw on an invalid app id format', () => {
const id = 'just-an-id';
expect(() => ensureAppIdFormat(id)).toThrowError();
});
});
14 changes: 12 additions & 2 deletions libs/client/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,18 @@ export function isUUIDv4(id: string): boolean {
);
}

export function isLegacyAppId(id: string): boolean {
return /^\d+-[a-zA-Z0-9-]+/.test(id);
export function ensureAppIdFormat(id: string): string {
const parts = id.split('/');
if (parts.length === 2) {
return id;
}
const [, appOwner, appId] = /^([0-9]+)-([a-zA-Z0-9-]+)$/.exec(id);
if (appOwner && appId) {
return `${appOwner}/${appId}`;
}
throw new Error(
`Invalid app id: ${id}. Must be in the format <appOwner>/<appId>`
);
}

export function isValidUrl(url: string) {
Expand Down

0 comments on commit 8521636

Please sign in to comment.