@@ -14,7 +14,12 @@ import { makeRequestOptionsFromResolvedModel } from "../lib/makeRequestOptions.j
1414import type { InferenceProviderOrPolicy , InferenceTask , RequestArgs } from "../types.js" ;
1515import { templates } from "./templates.exported.js" ;
1616
17- export type InferenceSnippetOptions = { streaming ?: boolean ; billTo ?: string } & Record < string , unknown > ;
17+ export type InferenceSnippetOptions = {
18+ streaming ?: boolean ;
19+ billTo ?: string ;
20+ accessToken ?: string ;
21+ directRequest ?: boolean ;
22+ } & Record < string , unknown > ;
1823
1924const PYTHON_CLIENTS = [ "huggingface_hub" , "fal_client" , "requests" , "openai" ] as const ;
2025const JS_CLIENTS = [ "fetch" , "huggingface.js" , "openai" ] as const ;
@@ -121,11 +126,15 @@ const HF_JS_METHODS: Partial<Record<WidgetType, string>> = {
121126 translation : "translation" ,
122127} ;
123128
129+ // Placeholders to replace with env variable in snippets
130+ // little hack to support both direct requests and routing => routed requests should start with "hf_"
131+ const ACCESS_TOKEN_ROUTING_PLACEHOLDER = "hf_token_placeholder" ;
132+ const ACCESS_TOKEN_DIRECT_REQUEST_PLACEHOLDER = "not_hf_token_placeholder" ;
133+
124134// Snippet generators
125135const snippetGenerator = ( templateName : string , inputPreparationFn ?: InputPreparationFn ) => {
126136 return (
127137 model : ModelDataMinimal ,
128- accessToken : string ,
129138 provider : InferenceProviderOrPolicy ,
130139 inferenceProviderMapping ?: InferenceProviderModelMapping ,
131140 opts ?: InferenceSnippetOptions
@@ -149,13 +158,19 @@ const snippetGenerator = (templateName: string, inputPreparationFn?: InputPrepar
149158 console . error ( `Failed to get provider helper for ${ provider } (${ task } )` , e ) ;
150159 return [ ] ;
151160 }
161+
162+ const placeholder = opts ?. directRequest
163+ ? ACCESS_TOKEN_DIRECT_REQUEST_PLACEHOLDER
164+ : ACCESS_TOKEN_ROUTING_PLACEHOLDER ;
165+ const accessTokenOrPlaceholder = opts ?. accessToken ?? placeholder ;
166+
152167 /// Prepare inputs + make request
153168 const inputs = inputPreparationFn ? inputPreparationFn ( model , opts ) : { inputs : getModelInputSnippet ( model ) } ;
154169 const request = makeRequestOptionsFromResolvedModel (
155170 providerModelId ,
156171 providerHelper ,
157172 {
158- accessToken,
173+ accessToken : accessTokenOrPlaceholder ,
159174 provider,
160175 ...inputs ,
161176 } as RequestArgs ,
@@ -180,7 +195,7 @@ const snippetGenerator = (templateName: string, inputPreparationFn?: InputPrepar
180195
181196 /// Prepare template injection data
182197 const params : TemplateParams = {
183- accessToken,
198+ accessToken : accessTokenOrPlaceholder ,
184199 authorizationHeader : ( request . info . headers as Record < string , string > ) ?. Authorization ,
185200 baseUrl : removeSuffix ( request . url , "/chat/completions" ) ,
186201 fullUrl : request . url ,
@@ -248,6 +263,11 @@ const snippetGenerator = (templateName: string, inputPreparationFn?: InputPrepar
248263 snippet = `${ importSection } \n\n${ snippet } ` ;
249264 }
250265
266+ /// Replace access token placeholder
267+ if ( snippet . includes ( placeholder ) ) {
268+ snippet = replaceAccessTokenPlaceholder ( opts ?. directRequest , placeholder , snippet , language , provider ) ;
269+ }
270+
251271 /// Snippet is ready!
252272 return { language, client : client as string , content : snippet } ;
253273 } )
@@ -299,7 +319,6 @@ const snippets: Partial<
299319 PipelineType ,
300320 (
301321 model : ModelDataMinimal ,
302- accessToken : string ,
303322 provider : InferenceProviderOrPolicy ,
304323 inferenceProviderMapping ?: InferenceProviderModelMapping ,
305324 opts ?: InferenceSnippetOptions
@@ -339,13 +358,12 @@ const snippets: Partial<
339358
340359export function getInferenceSnippets (
341360 model : ModelDataMinimal ,
342- accessToken : string ,
343361 provider : InferenceProviderOrPolicy ,
344362 inferenceProviderMapping ?: InferenceProviderModelMapping ,
345363 opts ?: Record < string , unknown >
346364) : InferenceSnippet [ ] {
347365 return model . pipeline_tag && model . pipeline_tag in snippets
348- ? snippets [ model . pipeline_tag ] ?.( model , accessToken , provider , inferenceProviderMapping , opts ) ?? [ ]
366+ ? snippets [ model . pipeline_tag ] ?.( model , provider , inferenceProviderMapping , opts ) ?? [ ]
349367 : [ ] ;
350368}
351369
@@ -420,3 +438,69 @@ function indentString(str: string): string {
420438function removeSuffix ( str : string , suffix : string ) {
421439 return str . endsWith ( suffix ) ? str . slice ( 0 , - suffix . length ) : str ;
422440}
441+
442+ function replaceAccessTokenPlaceholder (
443+ directRequest : boolean | undefined ,
444+ placeholder : string ,
445+ snippet : string ,
446+ language : InferenceSnippetLanguage ,
447+ provider : InferenceProviderOrPolicy
448+ ) : string {
449+ // If "opts.accessToken" is not set, the snippets are generated with a placeholder.
450+ // Once snippets are rendered, we replace the placeholder with code to fetch the access token from an environment variable.
451+
452+ // Determine if HF_TOKEN or specific provider token should be used
453+ const useHfToken =
454+ provider == "hf-inference" || // hf-inference provider => use $HF_TOKEN
455+ ( ! directRequest && // if explicit directRequest => use provider-specific token
456+ ( ! snippet . includes ( "https://" ) || // no URL provided => using a client => use $HF_TOKEN
457+ snippet . includes ( "https://router.huggingface.co" ) ) ) ; // explicit routed request => use $HF_TOKEN
458+
459+ const accessTokenEnvVar = useHfToken
460+ ? "HF_TOKEN" // e.g. routed request or hf-inference
461+ : provider . toUpperCase ( ) . replace ( "-" , "_" ) + "_API_KEY" ; // e.g. "REPLICATE_API_KEY"
462+
463+ // Replace the placeholder with the env variable
464+ if ( language === "sh" ) {
465+ snippet = snippet . replace (
466+ `'Authorization: Bearer ${ placeholder } '` ,
467+ `"Authorization: Bearer $${ accessTokenEnvVar } "` // e.g. "Authorization: Bearer $HF_TOKEN"
468+ ) ;
469+ } else if ( language === "python" ) {
470+ snippet = "import os\n" + snippet ;
471+ snippet = snippet . replace (
472+ `"${ placeholder } "` ,
473+ `os.environ["${ accessTokenEnvVar } "]` // e.g. os.environ["HF_TOKEN")
474+ ) ;
475+ snippet = snippet . replace (
476+ `"Bearer ${ placeholder } "` ,
477+ `f"Bearer {os.environ['${ accessTokenEnvVar } ']}"` // e.g. f"Bearer {os.environ['HF_TOKEN']}"
478+ ) ;
479+ snippet = snippet . replace (
480+ `"Key ${ placeholder } "` ,
481+ `f"Key {os.environ['${ accessTokenEnvVar } ']}"` // e.g. f"Key {os.environ['FAL_AI_API_KEY']}"
482+ ) ;
483+ snippet = snippet . replace (
484+ `"X-Key ${ placeholder } "` ,
485+ `f"X-Key {os.environ['${ accessTokenEnvVar } ']}"` // e.g. f"X-Key {os.environ['BLACK_FOREST_LABS_API_KEY']}"
486+ ) ;
487+ } else if ( language === "js" ) {
488+ snippet = snippet . replace (
489+ `"${ placeholder } "` ,
490+ `process.env.${ accessTokenEnvVar } ` // e.g. process.env.HF_TOKEN
491+ ) ;
492+ snippet = snippet . replace (
493+ `Authorization: "Bearer ${ placeholder } ",` ,
494+ `Authorization: \`Bearer $\{process.env.${ accessTokenEnvVar } }\`,` // e.g. Authorization: `Bearer ${process.env.HF_TOKEN}`,
495+ ) ;
496+ snippet = snippet . replace (
497+ `Authorization: "Key ${ placeholder } ",` ,
498+ `Authorization: \`Key $\{process.env.${ accessTokenEnvVar } }\`,` // e.g. Authorization: `Key ${process.env.FAL_AI_API_KEY}`,
499+ ) ;
500+ snippet = snippet . replace (
501+ `Authorization: "X-Key ${ placeholder } ",` ,
502+ `Authorization: \`X-Key $\{process.env.${ accessTokenEnvVar } }\`,` // e.g. Authorization: `X-Key ${process.env.BLACK_FOREST_LABS_AI_API_KEY}`,
503+ ) ;
504+ }
505+ return snippet ;
506+ }
0 commit comments