Add initial support for Hugging Face Inference Endpoints
- Add maven config to attach swagger generate code to vscode java classpath. - Add license headers. - Streamline the test.
This commit is contained in:
committed by
Christian Tzolov
parent
a97976e434
commit
6d956a6f37
5
pom.xml
5
pom.xml
@@ -17,6 +17,7 @@
|
||||
<module>spring-ai-openai</module>
|
||||
<module>spring-ai-azure-openai</module>
|
||||
<module>spring-ai-ollama</module>
|
||||
<module>spring-ai-huggingface</module>
|
||||
<module>spring-ai-spring-boot-autoconfigure</module>
|
||||
<module>spring-ai-spring-boot-starters/spring-ai-starter-openai</module>
|
||||
<module>spring-ai-spring-boot-starters/spring-ai-starter-azure-openai</module>
|
||||
@@ -71,8 +72,8 @@
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>17</java.version>
|
||||
|
||||
<!-- production dependencies -->
|
||||
<spring-boot.version>3.1.2</spring-boot.version>
|
||||
<!-- prodution dependencies -->
|
||||
<spring-boot.version>3.1.3</spring-boot.version>
|
||||
<stringtemplate.version>4.0.2</stringtemplate.version>
|
||||
<open-ai-client.version>0.16.0</open-ai-client.version>
|
||||
<azure-open-ai-client.version>1.0.0-beta.3</azure-open-ai-client.version>
|
||||
|
||||
@@ -191,7 +191,7 @@ One approach involves presenting both the user's request and the AI model's resp
|
||||
|
||||
Furthermore, leveraging the information stored in the Vector Database as supplementary data can enhance the evaluation process, aiding in the determination of response relevance.
|
||||
|
||||
The Spring AI project currenlty provides some very basic examples of how you can evaluate the responses in the form of prompts to include in a JUnit test.
|
||||
The Spring AI project currently provides some very basic examples of how you can evaluate the responses in the form of prompts to include in a JUnit test.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
= HuggingFace
|
||||
|
||||
== Introduction
|
||||
One of the easiest ways you can get access to many Machine Learning and Artificial Intelligence models is by using the https://en.wikipedia.org/wiki/Hugging_Face[HuggingFace's] https://huggingface.co/inference-endpoints[Inference Endpoints].
|
||||
|
||||
Hugging Face Hub is a platform providing a collaborative environment for creating and sharing tens of thousands of Open Source ML/AI models, data sets, and demo applications.
|
||||
|
||||
Inference Endpoints let you deploy AI Models on dedicated infrastructure with a pay as you go billing model.
|
||||
You can use infrastructure provided by Amazon Web Services, Microsoft Azure and Google Cloud Platform.
|
||||
Hugging Face lets you run the models on your own machine, but it is quite common to not have enough CPU/GPU resources to run the larger, more AI focused models.
|
||||
|
||||
It provides access to Meta's recent (August 2023) Llama 2 and CodeLlama 2 models as well as providing the https://huggingface.co/spaces/HuggingFaceH4/open_llm_leaderboard[Open LLM Leaderboard] where you can quickly discover high quality models.
|
||||
|
||||
While Hugging Face has a free hosting tier, which is very useful for quickly evaluating if a specific ML/AI Model fits your needs, they do not let you access many of those models on the free tier using the https://huggingface.co/docs/text-generation-inference/main/en/index[Text Generation Interface API], so since you want to end up on production anyway, with a stable API, pony up a few cents to try out a reliable solution.
|
||||
Prices are as low as $0.06 per CPU core/hr and $0.6 per GPU/hr.
|
||||
56
spring-ai-huggingface/README.md
Normal file
56
spring-ai-huggingface/README.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# HuggingFace Inference Endpoints with Spring AI
|
||||
|
||||
HuggingFace Inference Endpoints allow you to deploy and serve machine learning models in the cloud, making them accessible via an API. Further details on HuggingFace Inference Endpoints can be found [here](https://huggingface.co/docs/inference-endpoints/index).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You should get your HuggingFace API key and set it as an environment variable
|
||||
|
||||
```shell
|
||||
export HUGGINGFACE_API_KEY=your_api_key_here
|
||||
```
|
||||
|
||||
Note, there is not yet a Spring Boot Starter for this client implementation.
|
||||
|
||||
Obtain the endpoint URL of the Inference Endpoint.
|
||||
You can find this on the Inference Endpoint's UI [here](https://ui.endpoints.huggingface.co/).
|
||||
|
||||
|
||||
## Making a call to the model
|
||||
|
||||
```java
|
||||
HuggingfaceAiClient client = new HuggingfaceAiClient(apiKey, basePath);
|
||||
Prompt prompt = new Prompt("Your text here...");
|
||||
AiResponse response = client.generate(prompt);
|
||||
System.out.println(response.getGeneration().getText());
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
Using the example found [here](https://www.promptingguide.ai/models/mistral-7b)
|
||||
|
||||
```java
|
||||
String mistral7bInstruct = """
|
||||
[INST] You are a helpful code assistant. Your task is to generate a valid JSON object based on the given information:
|
||||
name: John
|
||||
lastname: Smith
|
||||
address: #1 Samuel St.
|
||||
Just generate the JSON object without explanations:
|
||||
[/INST]""";
|
||||
Prompt prompt = new Prompt(mistral7bInstruct);
|
||||
AiResponse aiResponse = huggingfaceAiClient.generate(prompt);
|
||||
System.out.println(response.getGeneration().getText());
|
||||
```
|
||||
|
||||
Will produce the output
|
||||
|
||||
````
|
||||
```json
|
||||
{
|
||||
"name": "John",
|
||||
"lastname": "Smith",
|
||||
"address": "#1 Samuel St."
|
||||
}
|
||||
```
|
||||
````
|
||||
Note the response itself is in Markdown format.
|
||||
118
spring-ai-huggingface/pom.xml
Normal file
118
spring-ai-huggingface/pom.xml
Normal file
@@ -0,0 +1,118 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>spring-ai</artifactId>
|
||||
<version>0.7.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>spring-ai-huggingface</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring AI HuggingFace</name>
|
||||
<description>HuggingFace support</description>
|
||||
<url>https://github.com/spring-projects-experimental/spring-ai</url>
|
||||
|
||||
<scm>
|
||||
<url>https://github.com/spring-projects-experimental/spring-ai</url>
|
||||
<connection>git://github.com/spring-projects-experimental/spring-ai.git</connection>
|
||||
<developerConnection>git@github.com:spring-projects-experimental/spring-ai.git</developerConnection>
|
||||
</scm>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- production dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>spring-ai-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/io.swagger.core.v3/swagger-annotations -->
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>2.2.15</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Spring Framework -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- test dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.swagger.codegen.v3</groupId>
|
||||
<artifactId>swagger-codegen-maven-plugin</artifactId>
|
||||
<version>3.0.46</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>generate</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<inputSpec>${project.basedir}/src/main/resources/openapi.json</inputSpec>
|
||||
<language>java</language>
|
||||
<library>resttemplate</library>
|
||||
<apiPackage>org.springframework.ai.huggingface.api</apiPackage>
|
||||
<modelPackage>org.springframework.ai.huggingface.model</modelPackage>
|
||||
<invokerPackage>org.springframework.ai.huggingface.invoker</invokerPackage>
|
||||
<generateApiTests>false</generateApiTests>
|
||||
<generateModelTests>false</generateModelTests>
|
||||
<configOptions>
|
||||
<sourceFolder>src/main/java</sourceFolder>
|
||||
<dateLibrary>java8</dateLibrary>
|
||||
<!-- jackson secret sauce!! -->
|
||||
<notNullJacksonAnnotation>true</notNullJacksonAnnotation>
|
||||
</configOptions>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<!-- a hint for IDE's to add the java sources to the classpath -->
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>3.4.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>01-add-test-sources</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>add-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>${project.build.directory}/generated-sources/swagger/src/main/java</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2023-2023 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.huggingface.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.ai.client.AiClient;
|
||||
import org.springframework.ai.client.AiResponse;
|
||||
import org.springframework.ai.client.Generation;
|
||||
import org.springframework.ai.huggingface.api.TextGenerationInferenceApi;
|
||||
import org.springframework.ai.huggingface.invoker.ApiClient;
|
||||
import org.springframework.ai.huggingface.model.AllOfGenerateResponseDetails;
|
||||
import org.springframework.ai.huggingface.model.GenerateParameters;
|
||||
import org.springframework.ai.huggingface.model.GenerateRequest;
|
||||
import org.springframework.ai.huggingface.model.GenerateResponse;
|
||||
import org.springframework.ai.prompt.Prompt;
|
||||
|
||||
/**
|
||||
* An implementation of {@link AiClient} that interfaces with HuggingFace Inference
|
||||
* Endpoints for text generation.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
public class HuggingfaceAiClient implements AiClient {
|
||||
|
||||
/**
|
||||
* Token required for authenticating with the HuggingFace Inference API.
|
||||
*/
|
||||
private final String apiToken;
|
||||
|
||||
/**
|
||||
* Client for making API calls.
|
||||
*/
|
||||
private ApiClient apiClient = new ApiClient();
|
||||
|
||||
/**
|
||||
* Mapper for converting between Java objects and JSON.
|
||||
*/
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* API for text generation inferences.
|
||||
*/
|
||||
private TextGenerationInferenceApi textGenApi = new TextGenerationInferenceApi();
|
||||
|
||||
/**
|
||||
* The maximum number of new tokens to be generated. Note: The total token size for
|
||||
* the Mistral7b instruct model should be less than 1500.
|
||||
*/
|
||||
private int maxNewTokens = 1000;
|
||||
|
||||
/**
|
||||
* Constructs a new HuggingfaceAiClient with the specified API token and base path.
|
||||
* @param apiToken The API token for HuggingFace.
|
||||
* @param basePath The base path for API requests.
|
||||
*/
|
||||
public HuggingfaceAiClient(final String apiToken, String basePath) {
|
||||
this.apiToken = apiToken;
|
||||
this.apiClient.setBasePath(basePath);
|
||||
this.apiClient.addDefaultHeader("Authorization", "Bearer " + this.apiToken);
|
||||
this.textGenApi.setApiClient(this.apiClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate text based on the provided prompt.
|
||||
* @param prompt The input prompt based on which text is to be generated.
|
||||
* @return AiResponse containing the generated text and other related details.
|
||||
*/
|
||||
@Override
|
||||
public AiResponse generate(Prompt prompt) {
|
||||
GenerateRequest generateRequest = new GenerateRequest();
|
||||
generateRequest.setInputs(prompt.getContents());
|
||||
GenerateParameters generateParameters = new GenerateParameters();
|
||||
// TODO - need to expose API to set parameters per call.
|
||||
generateParameters.setMaxNewTokens(maxNewTokens);
|
||||
generateRequest.setParameters(generateParameters);
|
||||
GenerateResponse generateResponse = this.textGenApi.generate(generateRequest);
|
||||
String generatedText = generateResponse.getGeneratedText();
|
||||
List<Generation> generations = new ArrayList<>();
|
||||
AllOfGenerateResponseDetails allOfGenerateResponseDetails = generateResponse.getDetails();
|
||||
Map<String, Object> detailsMap = objectMapper.convertValue(allOfGenerateResponseDetails,
|
||||
new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
Generation generation = new Generation(generatedText, detailsMap);
|
||||
generations.add(generation);
|
||||
return new AiResponse(generations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum number of new tokens to be generated.
|
||||
* @return The maximum number of new tokens.
|
||||
*/
|
||||
public int getMaxNewTokens() {
|
||||
return maxNewTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum number of new tokens to be generated.
|
||||
* @param maxNewTokens The maximum number of new tokens.
|
||||
*/
|
||||
public void setMaxNewTokens(int maxNewTokens) {
|
||||
this.maxNewTokens = maxNewTokens;
|
||||
}
|
||||
|
||||
}
|
||||
849
spring-ai-huggingface/src/main/resources/openapi.json
Normal file
849
spring-ai-huggingface/src/main/resources/openapi.json
Normal file
@@ -0,0 +1,849 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Text Generation Inference",
|
||||
"description": "Text Generation Webserver",
|
||||
"contact": {
|
||||
"name": "Olivier Dehaene"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0",
|
||||
"url": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"paths": {
|
||||
"/": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Text Generation Inference"
|
||||
],
|
||||
"summary": "Generate tokens if `stream == false` or a stream of token if `stream == true`",
|
||||
"description": "Generate tokens if `stream == false` or a stream of token if `stream == true`",
|
||||
"operationId": "compat_generate",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CompatGenerateRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Generated Text",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/GenerateResponse"
|
||||
}
|
||||
},
|
||||
"text/event-stream": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/StreamResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Input validation error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
},
|
||||
"example": {
|
||||
"error": "Input validation error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"424": {
|
||||
"description": "Generation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
},
|
||||
"example": {
|
||||
"error": "Request failed during generation"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"429": {
|
||||
"description": "Model is overloaded",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
},
|
||||
"example": {
|
||||
"error": "Model is overloaded"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Incomplete generation",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
},
|
||||
"example": {
|
||||
"error": "Incomplete generation"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/generate": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Text Generation Inference"
|
||||
],
|
||||
"summary": "Generate tokens",
|
||||
"description": "Generate tokens",
|
||||
"operationId": "generate",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/GenerateRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Generated Text",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/GenerateResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Input validation error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
},
|
||||
"example": {
|
||||
"error": "Input validation error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"424": {
|
||||
"description": "Generation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
},
|
||||
"example": {
|
||||
"error": "Request failed during generation"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"429": {
|
||||
"description": "Model is overloaded",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
},
|
||||
"example": {
|
||||
"error": "Model is overloaded"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Incomplete generation",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
},
|
||||
"example": {
|
||||
"error": "Incomplete generation"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/generate_stream": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Text Generation Inference"
|
||||
],
|
||||
"summary": "Generate a stream of token using Server-Sent Events",
|
||||
"description": "Generate a stream of token using Server-Sent Events",
|
||||
"operationId": "generate_stream",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/GenerateRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Generated Text",
|
||||
"content": {
|
||||
"text/event-stream": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/StreamResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Input validation error",
|
||||
"content": {
|
||||
"text/event-stream": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
},
|
||||
"example": {
|
||||
"error": "Input validation error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"424": {
|
||||
"description": "Generation Error",
|
||||
"content": {
|
||||
"text/event-stream": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
},
|
||||
"example": {
|
||||
"error": "Request failed during generation"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"429": {
|
||||
"description": "Model is overloaded",
|
||||
"content": {
|
||||
"text/event-stream": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
},
|
||||
"example": {
|
||||
"error": "Model is overloaded"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Incomplete generation",
|
||||
"content": {
|
||||
"text/event-stream": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
},
|
||||
"example": {
|
||||
"error": "Incomplete generation"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/health": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Text Generation Inference"
|
||||
],
|
||||
"summary": "Health check method",
|
||||
"description": "Health check method",
|
||||
"operationId": "health",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Everything is working fine"
|
||||
},
|
||||
"503": {
|
||||
"description": "Text generation inference is down",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
},
|
||||
"example": {
|
||||
"error": "unhealthy",
|
||||
"error_type": "healthcheck"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/info": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Text Generation Inference"
|
||||
],
|
||||
"summary": "Text Generation Inference endpoint info",
|
||||
"description": "Text Generation Inference endpoint info",
|
||||
"operationId": "get_model_info",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Served model info",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Info"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/metrics": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Text Generation Inference"
|
||||
],
|
||||
"summary": "Prometheus metrics scrape endpoint",
|
||||
"description": "Prometheus metrics scrape endpoint",
|
||||
"operationId": "metrics",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Prometheus Metrics",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"BestOfSequence": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"generated_text",
|
||||
"finish_reason",
|
||||
"generated_tokens",
|
||||
"prefill",
|
||||
"tokens"
|
||||
],
|
||||
"properties": {
|
||||
"finish_reason": {
|
||||
"$ref": "#/components/schemas/FinishReason"
|
||||
},
|
||||
"generated_text": {
|
||||
"type": "string",
|
||||
"example": "test"
|
||||
},
|
||||
"generated_tokens": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"example": 1,
|
||||
"minimum": 0.0
|
||||
},
|
||||
"prefill": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PrefillToken"
|
||||
}
|
||||
},
|
||||
"seed": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"example": 42,
|
||||
"nullable": true,
|
||||
"minimum": 0.0
|
||||
},
|
||||
"tokens": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Token"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CompatGenerateRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"inputs"
|
||||
],
|
||||
"properties": {
|
||||
"inputs": {
|
||||
"type": "string",
|
||||
"example": "My name is Olivier and I"
|
||||
},
|
||||
"parameters": {
|
||||
"$ref": "#/components/schemas/GenerateParameters"
|
||||
},
|
||||
"stream": {
|
||||
"type": "boolean",
|
||||
"default": "false"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Details": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"finish_reason",
|
||||
"generated_tokens",
|
||||
"prefill",
|
||||
"tokens"
|
||||
],
|
||||
"properties": {
|
||||
"best_of_sequences": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/BestOfSequence"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"finish_reason": {
|
||||
"$ref": "#/components/schemas/FinishReason"
|
||||
},
|
||||
"generated_tokens": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"example": 1,
|
||||
"minimum": 0.0
|
||||
},
|
||||
"prefill": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PrefillToken"
|
||||
}
|
||||
},
|
||||
"seed": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"example": 42,
|
||||
"nullable": true,
|
||||
"minimum": 0.0
|
||||
},
|
||||
"tokens": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Token"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ErrorResponse": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"error",
|
||||
"error_type"
|
||||
],
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"error_type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"FinishReason": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"length",
|
||||
"eos_token",
|
||||
"stop_sequence"
|
||||
]
|
||||
},
|
||||
"GenerateParameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"best_of": {
|
||||
"type": "integer",
|
||||
"default": "null",
|
||||
"example": 1,
|
||||
"nullable": true,
|
||||
"minimum": 0.0,
|
||||
"exclusiveMinimum": 0.0
|
||||
},
|
||||
"decoder_input_details": {
|
||||
"type": "boolean",
|
||||
"default": "true"
|
||||
},
|
||||
"details": {
|
||||
"type": "boolean",
|
||||
"default": "true"
|
||||
},
|
||||
"do_sample": {
|
||||
"type": "boolean",
|
||||
"default": "false",
|
||||
"example": true
|
||||
},
|
||||
"max_new_tokens": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": "20",
|
||||
"minimum": 0.0,
|
||||
"exclusiveMaximum": 512.0,
|
||||
"exclusiveMinimum": 0.0
|
||||
},
|
||||
"repetition_penalty": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"default": "null",
|
||||
"example": 1.03,
|
||||
"nullable": true,
|
||||
"exclusiveMinimum": 0.0
|
||||
},
|
||||
"return_full_text": {
|
||||
"type": "boolean",
|
||||
"default": "null",
|
||||
"example": false,
|
||||
"nullable": true
|
||||
},
|
||||
"seed": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"default": "null",
|
||||
"example": "null",
|
||||
"nullable": true,
|
||||
"minimum": 0.0,
|
||||
"exclusiveMinimum": 0.0
|
||||
},
|
||||
"stop": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": [
|
||||
"photographer"
|
||||
],
|
||||
"maxItems": 4
|
||||
},
|
||||
"temperature": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"default": "null",
|
||||
"example": 0.5,
|
||||
"nullable": true,
|
||||
"exclusiveMinimum": 0.0
|
||||
},
|
||||
"top_k": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": "null",
|
||||
"example": 10,
|
||||
"nullable": true,
|
||||
"exclusiveMinimum": 0.0
|
||||
},
|
||||
"top_p": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"default": "null",
|
||||
"example": 0.95,
|
||||
"nullable": true,
|
||||
"maximum": 1.0,
|
||||
"exclusiveMinimum": 0.0
|
||||
},
|
||||
"truncate": {
|
||||
"type": "integer",
|
||||
"default": "null",
|
||||
"example": "null",
|
||||
"nullable": true,
|
||||
"minimum": 0.0
|
||||
},
|
||||
"typical_p": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"default": "null",
|
||||
"example": 0.95,
|
||||
"nullable": true,
|
||||
"maximum": 1.0,
|
||||
"exclusiveMinimum": 0.0
|
||||
},
|
||||
"watermark": {
|
||||
"type": "boolean",
|
||||
"default": "false",
|
||||
"example": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"GenerateRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"inputs"
|
||||
],
|
||||
"properties": {
|
||||
"inputs": {
|
||||
"type": "string",
|
||||
"example": "My name is Olivier and I"
|
||||
},
|
||||
"parameters": {
|
||||
"$ref": "#/components/schemas/GenerateParameters"
|
||||
}
|
||||
}
|
||||
},
|
||||
"GenerateResponse": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"generated_text"
|
||||
],
|
||||
"properties": {
|
||||
"details": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Details"
|
||||
}
|
||||
],
|
||||
"nullable": true
|
||||
},
|
||||
"generated_text": {
|
||||
"type": "string",
|
||||
"example": "test"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Info": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"model_id",
|
||||
"model_dtype",
|
||||
"model_device_type",
|
||||
"max_concurrent_requests",
|
||||
"max_best_of",
|
||||
"max_stop_sequences",
|
||||
"max_input_length",
|
||||
"max_total_tokens",
|
||||
"waiting_served_ratio",
|
||||
"max_batch_total_tokens",
|
||||
"max_waiting_tokens",
|
||||
"validation_workers",
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"docker_label": {
|
||||
"type": "string",
|
||||
"example": "null",
|
||||
"nullable": true
|
||||
},
|
||||
"max_batch_total_tokens": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"example": "32000",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"max_best_of": {
|
||||
"type": "integer",
|
||||
"example": "2",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"max_concurrent_requests": {
|
||||
"type": "integer",
|
||||
"description": "Router Parameters",
|
||||
"example": "128",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"max_input_length": {
|
||||
"type": "integer",
|
||||
"example": "1024",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"max_stop_sequences": {
|
||||
"type": "integer",
|
||||
"example": "4",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"max_total_tokens": {
|
||||
"type": "integer",
|
||||
"example": "2048",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"max_waiting_tokens": {
|
||||
"type": "integer",
|
||||
"example": "20",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"model_device_type": {
|
||||
"type": "string",
|
||||
"example": "cuda"
|
||||
},
|
||||
"model_dtype": {
|
||||
"type": "string",
|
||||
"example": "torch.float16"
|
||||
},
|
||||
"model_id": {
|
||||
"type": "string",
|
||||
"description": "Model info",
|
||||
"example": "bigscience/blomm-560m"
|
||||
},
|
||||
"model_pipeline_tag": {
|
||||
"type": "string",
|
||||
"example": "text-generation",
|
||||
"nullable": true
|
||||
},
|
||||
"model_sha": {
|
||||
"type": "string",
|
||||
"example": "e985a63cdc139290c5f700ff1929f0b5942cced2",
|
||||
"nullable": true
|
||||
},
|
||||
"sha": {
|
||||
"type": "string",
|
||||
"example": "null",
|
||||
"nullable": true
|
||||
},
|
||||
"validation_workers": {
|
||||
"type": "integer",
|
||||
"example": "2",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Router Info",
|
||||
"example": "0.5.0"
|
||||
},
|
||||
"waiting_served_ratio": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"example": "1.2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PrefillToken": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"text",
|
||||
"logprob"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"example": 0,
|
||||
"minimum": 0.0
|
||||
},
|
||||
"logprob": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"example": -0.34,
|
||||
"nullable": true
|
||||
},
|
||||
"text": {
|
||||
"type": "string",
|
||||
"example": "test"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StreamDetails": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"finish_reason",
|
||||
"generated_tokens"
|
||||
],
|
||||
"properties": {
|
||||
"finish_reason": {
|
||||
"$ref": "#/components/schemas/FinishReason"
|
||||
},
|
||||
"generated_tokens": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"example": 1,
|
||||
"minimum": 0.0
|
||||
},
|
||||
"seed": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"example": 42,
|
||||
"nullable": true,
|
||||
"minimum": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
"StreamResponse": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"token"
|
||||
],
|
||||
"properties": {
|
||||
"details": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/StreamDetails"
|
||||
}
|
||||
],
|
||||
"nullable": true
|
||||
},
|
||||
"generated_text": {
|
||||
"type": "string",
|
||||
"default": "null",
|
||||
"example": "test",
|
||||
"nullable": true
|
||||
},
|
||||
"token": {
|
||||
"$ref": "#/components/schemas/Token"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Token": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"text",
|
||||
"logprob",
|
||||
"special"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"example": 0,
|
||||
"minimum": 0.0
|
||||
},
|
||||
"logprob": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"example": -0.34,
|
||||
"nullable": true
|
||||
},
|
||||
"special": {
|
||||
"type": "boolean",
|
||||
"example": "false"
|
||||
},
|
||||
"text": {
|
||||
"type": "string",
|
||||
"example": "test"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "Text Generation Inference",
|
||||
"description": "Hugging Face Text Generation Inference API"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2023-2023 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.huggingface;
|
||||
|
||||
import org.springframework.ai.huggingface.client.HuggingfaceAiClient;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@SpringBootConfiguration
|
||||
public class HuggingfaceTestConfiguration {
|
||||
|
||||
@Bean
|
||||
public HuggingfaceAiClient huggingfaceAiClient() {
|
||||
String apiKey = System.getenv("HUGGINGFACE_API_KEY");
|
||||
if (!StringUtils.hasText(apiKey)) {
|
||||
throw new IllegalArgumentException(
|
||||
"You must provide an API key. Put it in an environment variable under the name HUGGINGFACE_API_KEY");
|
||||
}
|
||||
// Created aws-mistral-7b-instruct-v0-1-805 via
|
||||
// https://ui.endpoints.huggingface.co/
|
||||
HuggingfaceAiClient huggingfaceAiClient = new HuggingfaceAiClient(apiKey,
|
||||
"https://f6hg7b3cvlmntp5i.us-east-1.aws.endpoints.huggingface.cloud");
|
||||
return huggingfaceAiClient;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2023-2023 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.huggingface.client;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
|
||||
|
||||
import org.springframework.ai.client.AiResponse;
|
||||
import org.springframework.ai.prompt.Prompt;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest
|
||||
@EnabledIfEnvironmentVariable(named = "HUGGINGFACE_API_KEY", matches = ".+")
|
||||
public class ClientIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
protected HuggingfaceAiClient huggingfaceAiClient;
|
||||
|
||||
@Test
|
||||
void helloWorldCompletion() {
|
||||
String mistral7bInstruct = """
|
||||
[INST] You are a helpful code assistant. Your task is to generate a valid JSON object based on the given information:
|
||||
name: John
|
||||
lastname: Smith
|
||||
address: #1 Samuel St.
|
||||
Just generate the JSON object without explanations:
|
||||
[/INST]
|
||||
""";
|
||||
Prompt prompt = new Prompt(mistral7bInstruct);
|
||||
AiResponse aiResponse = huggingfaceAiClient.generate(prompt);
|
||||
assertThat(aiResponse.getGeneration().getText()).isNotEmpty();
|
||||
String expectedResponse = """
|
||||
```json
|
||||
{
|
||||
"name": "John",
|
||||
"lastname": "Smith",
|
||||
"address": "#1 Samuel St."
|
||||
}
|
||||
```""";
|
||||
assertThat(aiResponse.getGeneration().getText()).isEqualTo(expectedResponse);
|
||||
assertThat(aiResponse.getGeneration().getInfo()).containsKey("generated_tokens");
|
||||
assertThat(aiResponse.getGeneration().getInfo()).containsEntry("generated_tokens", 39);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,14 +8,13 @@ import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
|
||||
@SpringBootConfiguration
|
||||
public class OpenAiTestConfiguration {
|
||||
|
||||
@Bean
|
||||
public OpenAiService theoOpenAiService() throws IOException {
|
||||
public OpenAiService theoOpenAiService() {
|
||||
String apiKey = System.getenv("OPENAI_API_KEY");
|
||||
if (!StringUtils.hasText(apiKey)) {
|
||||
throw new IllegalArgumentException(
|
||||
|
||||
@@ -41,6 +41,13 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>spring-ai-huggingface</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2023 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.autoconfigure.huggingface;
|
||||
|
||||
import org.springframework.ai.huggingface.client.HuggingfaceAiClient;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass(HuggingfaceAiClient.class)
|
||||
@EnableConfigurationProperties(HuggingfaceProperties.class)
|
||||
public class HuggingfaceAutoConfiguration {
|
||||
|
||||
private final HuggingfaceProperties huggingfaceProperties;
|
||||
|
||||
public HuggingfaceAutoConfiguration(HuggingfaceProperties huggingfaceProperties) {
|
||||
this.huggingfaceProperties = huggingfaceProperties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HuggingfaceAiClient huggingfaceAiClient(HuggingfaceProperties huggingfaceProperties) {
|
||||
return new HuggingfaceAiClient(huggingfaceProperties.getApiKey(), huggingfaceProperties.getUrl());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.springframework.ai.autoconfigure.huggingface;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import static org.springframework.ai.autoconfigure.openai.OpenAiProperties.CONFIG_PREFIX;
|
||||
|
||||
@ConfigurationProperties(CONFIG_PREFIX)
|
||||
public class HuggingfaceProperties {
|
||||
|
||||
public static final String CONFIG_PREFIX = "spring.ai.huggingface";
|
||||
|
||||
private String apiKey;
|
||||
|
||||
private String url;
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
public void setApiKey(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user