Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Inference] Move provider-specific logic away from makeRequestOptions (1 provider == 1 module) #1208

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

Wauplin
Copy link
Contributor

@Wauplin Wauplin commented Feb 18, 2025

Goal of this PR is to move away any provider-specific logic from makeRequestOptions.ts. In theory no tests should be updated as we only want to modify the internal logic without modifying input/output behavior.

Feedback around structure, TS convention, naming, etc. is welcome.

How it works ?

Each provider must define a providerConfigconsisting of:

export interface ProviderConfig {
	baseUrl: string;
	makeBody: (params: BodyParams) => unknown;
	makeHeaders: (params: HeaderParams) => Record<string, string>;
	makeUrl: (params: UrlParams) => string;
}

(with HeaderParams, UrlParams, BodyParams the parameters required to build these)

Comment on lines 44 to 49
export const replicateConfig: ProviderConfig = {
baseUrl: REPLICATE_API_BASE_URL,
makeBody,
makeHeaders,
makeUrl,
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if a class with static method would be more idiomatic - wdyt @coyotte508 @julien-c ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As an example, here is how a class would look like (transcribed using hf.co/chat). In Python I usually tend to avoid static methods as it's more boilerplate and usually don't benefit from being a class. But happy to change if it's more idiomatic in JS!

export abstract class ProviderConfig {
	static baseUrl: string;
	static makeBody(params: BodyParams): unknown {
		throw new Error("Not implemented");
	}
	static makeHeaders(params: HeaderParams): Record<string, string> {
		throw new Error("Not implemented");
	}
	static makeUrl(params: UrlParams): string {
		throw new Error("Not implemented");
	}
}

(...)

export class ReplicateConfig extends ProviderConfig {
	static baseUrl = "https://api.replicate.com";

	static makeBody({ args, model }: BodyParams): unknown {
		return {
			input: args,
			version: model.includes(":") ? model.split(":")[1] : undefined,
		};
	}

	static makeHeaders({ accessToken }: HeaderParams): Record<string, string> {
		return {
			Authorization: `Bearer ${accessToken}`,
			"Content-Type": "application/json",
			Prefer: "Wait",
		};
	}

	static makeUrl({ baseUrl, model }: UrlParams): string {
		if (model.includes(":")) {
			return `${baseUrl}/v1/predictions`;
		}
		return `${baseUrl}/v1/models/${model}/predictions`;
	}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that in huggingface_hub we use classes because we decided to go with "1 class == 1 provider<>task pair" and we benefit from inheritance (example).

Copy link
Contributor

@SBrandeis SBrandeis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love it!!
I think using classes instead of interfaces might be slightly more idiomatic - would love opinion from @coyotte508 and @julien-c

@Wauplin Wauplin changed the title [draft] start structure + add replicate + add hyperbolix [Inference] Move provider-specific logic away from makeRequestOptions (1 provider == 1 module) Feb 18, 2025
@Wauplin Wauplin marked this pull request as ready for review February 18, 2025 14:39
@Wauplin Wauplin requested a review from SBrandeis February 18, 2025 14:40
@Wauplin
Copy link
Contributor Author

Wauplin commented Feb 18, 2025

I completed all the providers. Now hf-inference is treated the same way as the other ones.

There is still a topic around the data structure to adopt (interface vs class with static methods - see #1208 (comment)). Happy to adapt once agreed on it.

Copy link
Contributor

@SBrandeis SBrandeis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, I have a few code style suggestions.

Subsequently to this PR, I thinl we should also factor task-specific logic within the provider modules.

Comment on lines +125 to 136
// Make body
const body = binary
? args.data
: JSON.stringify(
providerConfig.makeBody({
args: remainingArgs as Record<string, unknown>,
model,
taskHint,
chatCompletion,
})
);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent from the changes introduced here: I dislike this binary data special case, it's not obvious from an external pov that passing data in arguments will result in the payload being passed as raw bytes in the body.

@Wauplin
Copy link
Contributor Author

Wauplin commented Feb 19, 2025

(addressed the code style comments, thanks for the advice)

Copy link
Contributor

@SBrandeis SBrandeis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!
Would love to have another review from @coyotte508 and/or @julien-c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants