Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c636c67
Add dependencies for HuggingFace Embedding and enhanced Chat support
Profile-exe Nov 15, 2025
858fc1b
Add HuggingFace provider to observability conventions
Profile-exe Nov 15, 2025
ce4bae1
Add unified HuggingFace API client for Chat and Embedding
Profile-exe Nov 15, 2025
d884b2d
Add HuggingFace Chat and Embedding options classes
Profile-exe Nov 15, 2025
1b6e7a6
Add HuggingFace EmbeddingModel implementation
Profile-exe Nov 15, 2025
f7d3e84
Refactor HuggingFace ChatModel to use unified API and options
Profile-exe Nov 15, 2025
ba84235
Add AOT/GraalVM native image support for HuggingFace models
Profile-exe Nov 15, 2025
3db736c
Add HuggingFace auto-configuration foundation with ConnectionDetails …
Profile-exe Nov 15, 2025
2a495ea
Refactor HuggingFace Chat auto-configuration to use ConnectionDetails…
Profile-exe Nov 15, 2025
6ac3a39
Add HuggingFace Embedding auto-configuration
Profile-exe Nov 15, 2025
5506318
Add comprehensive integration and observation tests for HuggingFace m…
Profile-exe Nov 15, 2025
c23e272
Remove obsolete ClientIT integration test
Profile-exe Nov 15, 2025
406a9d4
Add comprehensive Antora documentation for HuggingFace Chat and Embed…
Profile-exe Nov 16, 2025
ebbaf2c
Fix documentation accuracy issues in HuggingFace integration
Profile-exe Nov 16, 2025
0d02e17
Align HuggingFace Embedding implementation with official API specific…
Profile-exe Nov 16, 2025
47ef0d7
Align HuggingFace Chat implementation with official API specification
Profile-exe Nov 16, 2025
9c2b14a
Apply Spring Java Format to BaseHuggingfaceIT
Profile-exe Nov 16, 2025
54a6415
Remove legacy TGI references and improve documentation accuracy
Profile-exe Nov 16, 2025
d0417dc
Add function calling support to HuggingFace ChatModel
Profile-exe Nov 17, 2025
af450a9
Fix Spring Boot 4.0 and Spring Framework 7 compatibility issues
Profile-exe Nov 19, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,37 @@

<!-- Spring AI auto configurations -->

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-tool</artifactId>
<version>${project.parent.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-chat-observation</artifactId>
<version>${project.parent.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-retry</artifactId>
<version>${project.parent.version}</version>
</dependency>

<!-- Boot dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-restclient</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.ai.model.huggingface.autoconfigure;

import org.springframework.ai.huggingface.api.HuggingfaceApi;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.context.annotation.Bean;

/**
* {@link AutoConfiguration Auto-configuration} for HuggingFace API.
*
* @author Myeongdeok Kang
*/
@AutoConfiguration(after = RestClientAutoConfiguration.class)
@ConditionalOnClass(HuggingfaceApi.class)
@EnableConfigurationProperties(HuggingfaceConnectionProperties.class)
public class HuggingfaceApiAutoConfiguration {

@Bean
@ConditionalOnMissingBean(HuggingfaceConnectionDetails.class)
PropertiesHuggingfaceConnectionDetails huggingfaceConnectionDetails(HuggingfaceConnectionProperties properties) {
return new PropertiesHuggingfaceConnectionDetails(properties);
}

// This bean is no longer created here since Chat and Embedding
// need different base URLs. Each AutoConfiguration creates its own API instance.

static class PropertiesHuggingfaceConnectionDetails implements HuggingfaceConnectionDetails {

private final HuggingfaceConnectionProperties properties;

PropertiesHuggingfaceConnectionDetails(HuggingfaceConnectionProperties properties) {
this.properties = properties;
}

@Override
public String getApiKey() {
return this.properties.getApiKey();
}

}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,27 +16,101 @@

package org.springframework.ai.model.huggingface.autoconfigure;

import io.micrometer.observation.ObservationRegistry;

import org.springframework.ai.chat.observation.ChatModelObservationConvention;
import org.springframework.ai.huggingface.HuggingfaceChatModel;
import org.springframework.ai.huggingface.api.HuggingfaceApi;
import org.springframework.ai.model.SpringAIModelProperties;
import org.springframework.ai.model.SpringAIModels;
import org.springframework.ai.model.tool.DefaultToolExecutionEligibilityPredicate;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.model.tool.ToolExecutionEligibilityPredicate;
import org.springframework.ai.model.tool.autoconfigure.ToolCallingAutoConfiguration;
import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;

@AutoConfiguration
/**
* {@link AutoConfiguration Auto-configuration} for HuggingFace Chat Model.
*
* @author Mark Pollack
* @author Josh Long
* @author Soby Chacko
* @author Ilayaperumal Gopinathan
* @author Myeongdeok Kang
*/
@AutoConfiguration(after = { RestClientAutoConfiguration.class, HuggingfaceApiAutoConfiguration.class,
SpringAiRetryAutoConfiguration.class, ToolCallingAutoConfiguration.class })
@ConditionalOnClass(HuggingfaceChatModel.class)
@EnableConfigurationProperties(HuggingfaceChatProperties.class)
@ConditionalOnProperty(name = SpringAIModelProperties.CHAT_MODEL, havingValue = SpringAIModels.HUGGINGFACE,
matchIfMissing = true)
@EnableConfigurationProperties({ HuggingfaceConnectionProperties.class, HuggingfaceChatProperties.class })
public class HuggingfaceChatAutoConfiguration {

@Bean
@ConditionalOnMissingBean(name = "huggingfaceChatApi")
@ConditionalOnProperty(prefix = HuggingfaceChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
public HuggingfaceApi huggingfaceChatApi(HuggingfaceConnectionDetails connectionDetails,
HuggingfaceChatProperties chatProperties, ObjectProvider<RestClient.Builder> restClientBuilderProvider,
ObjectProvider<ResponseErrorHandler> responseErrorHandlerProvider) {

String apiKey = connectionDetails.getApiKey();
if (!StringUtils.hasText(apiKey)) {
throw new IllegalArgumentException(
"HuggingFace API key must be set. Please configure spring.ai.huggingface.api-key");
}

RestClient.Builder restClientBuilder = restClientBuilderProvider.getIfAvailable(RestClient::builder);
ResponseErrorHandler responseErrorHandler = responseErrorHandlerProvider.getIfAvailable(() -> null);

HuggingfaceApi.Builder apiBuilder = HuggingfaceApi.builder()
.baseUrl(chatProperties.getUrl())
.apiKey(apiKey)
.restClientBuilder(restClientBuilder);

if (responseErrorHandler != null) {
apiBuilder.responseErrorHandler(responseErrorHandler);
}

return apiBuilder.build();
}

@Bean
@ConditionalOnMissingBean
public HuggingfaceChatModel huggingfaceChatModel(HuggingfaceChatProperties huggingfaceChatProperties) {
return new HuggingfaceChatModel(huggingfaceChatProperties.getApiKey(), huggingfaceChatProperties.getUrl());
@ConditionalOnProperty(prefix = HuggingfaceChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
public HuggingfaceChatModel huggingfaceChatModel(@Qualifier("huggingfaceChatApi") HuggingfaceApi huggingfaceApi,
HuggingfaceChatProperties chatProperties, ToolCallingManager toolCallingManager,
ObjectProvider<ObservationRegistry> observationRegistry,
ObjectProvider<ChatModelObservationConvention> observationConvention, RetryTemplate retryTemplate,
ObjectProvider<ToolExecutionEligibilityPredicate> huggingfaceToolExecutionEligibilityPredicate) {

var chatModel = HuggingfaceChatModel.builder()
.huggingfaceApi(huggingfaceApi)
.defaultOptions(chatProperties.getOptions())
.toolCallingManager(toolCallingManager)
.toolExecutionEligibilityPredicate(huggingfaceToolExecutionEligibilityPredicate
.getIfUnique(DefaultToolExecutionEligibilityPredicate::new))
.observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
.retryTemplate(retryTemplate)
.build();

observationConvention.ifAvailable(chatModel::setObservationConvention);

return chatModel;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,7 +16,11 @@

package org.springframework.ai.model.huggingface.autoconfigure;

import org.springframework.ai.huggingface.HuggingfaceChatOptions;
import org.springframework.ai.huggingface.api.HuggingfaceApi;
import org.springframework.ai.huggingface.api.common.HuggingfaceApiConstants;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;

/**
* Configuration properties for Hugging Face chat model.
Expand All @@ -25,28 +29,39 @@
* @author Josh Long
* @author Mark Pollack
* @author Thomas Vitale
* @author Myeongdeok Kang
*/
@ConfigurationProperties(HuggingfaceChatProperties.CONFIG_PREFIX)
public class HuggingfaceChatProperties {

public static final String CONFIG_PREFIX = "spring.ai.huggingface.chat";

/**
* API Key to authenticate with the Inference Endpoint.
* Enable HuggingFace chat model autoconfiguration.
*/
private String apiKey;
private boolean enabled = true;

/**
* URL of the Inference Endpoint.
* Base URL for the HuggingFace Chat API (OpenAI-compatible endpoint).
*/
private String url;
private String url = HuggingfaceApiConstants.DEFAULT_CHAT_BASE_URL;

public String getApiKey() {
return this.apiKey;
/**
* Client-level HuggingFace chat options. Use this property to configure the model,
* temperature, max_tokens, and other parameters. Null values are ignored, defaulting
* to the API defaults.
*/
@NestedConfigurationProperty
private final HuggingfaceChatOptions options = HuggingfaceChatOptions.builder()
.model(HuggingfaceApi.DEFAULT_CHAT_MODEL)
.build();

public boolean isEnabled() {
return this.enabled;
}

public void setApiKey(String apiKey) {
this.apiKey = apiKey;
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

public String getUrl() {
Expand All @@ -57,4 +72,16 @@ public void setUrl(String url) {
this.url = url;
}

public String getModel() {
return this.options.getModel();
}

public void setModel(String model) {
this.options.setModel(model);
}

public HuggingfaceChatOptions getOptions() {
return this.options;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.ai.model.huggingface.autoconfigure;

/**
* Details required to establish a connection to HuggingFace Inference API.
*
* @author Myeongdeok Kang
*/
public interface HuggingfaceConnectionDetails {

/**
* The API key for authenticating with HuggingFace.
* @return the API key
*/
String getApiKey();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.ai.model.huggingface.autoconfigure;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
* HuggingFace connection configuration properties. These properties are shared across all
* HuggingFace model types (chat, embedding, etc.).
*
* @author Myeongdeok Kang
*/
@ConfigurationProperties(HuggingfaceConnectionProperties.CONFIG_PREFIX)
public class HuggingfaceConnectionProperties {

public static final String CONFIG_PREFIX = "spring.ai.huggingface";

/**
* API Key to authenticate with HuggingFace Inference API.
*/
private String apiKey;

public String getApiKey() {
return this.apiKey;
}

public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}

}
Loading