From 247db0fb53dba877f5558d548141aa3e0065624c Mon Sep 17 00:00:00 2001 From: Dmytro Struk Date: Thu, 30 Mar 2023 13:01:55 +0100 Subject: [PATCH 01/12] Added local inference server --- .gitignore | 9 ++ .../apps/local_inference_server/Dockerfile | 17 +++ samples/apps/local_inference_server/README.md | 37 +++++ .../local_inference_server/inference_app.py | 126 ++++++++++++++++++ .../local_inference_server/requirements.txt | Bin 0 -> 1306 bytes .../static/css/styles.css | 9 ++ .../templates/completions.html | 22 +++ .../templates/documentation.html | 25 ++++ .../templates/embeddings.html | 19 +++ .../templates/home.html | 12 ++ .../templates/images.html | 19 +++ .../utils/CompletionGenerator.py | 32 +++++ .../utils/EmbeddingGenerator.py | 30 +++++ .../utils/ImageGenerator.py | 33 +++++ .../utils/InferenceGenerator.py | 13 ++ .../utils/create_responses.py | 41 ++++++ 16 files changed, 444 insertions(+) create mode 100644 samples/apps/local_inference_server/Dockerfile create mode 100644 samples/apps/local_inference_server/README.md create mode 100644 samples/apps/local_inference_server/inference_app.py create mode 100644 samples/apps/local_inference_server/requirements.txt create mode 100644 samples/apps/local_inference_server/static/css/styles.css create mode 100644 samples/apps/local_inference_server/templates/completions.html create mode 100644 samples/apps/local_inference_server/templates/documentation.html create mode 100644 samples/apps/local_inference_server/templates/embeddings.html create mode 100644 samples/apps/local_inference_server/templates/home.html create mode 100644 samples/apps/local_inference_server/templates/images.html create mode 100644 samples/apps/local_inference_server/utils/CompletionGenerator.py create mode 100644 samples/apps/local_inference_server/utils/EmbeddingGenerator.py create mode 100644 samples/apps/local_inference_server/utils/ImageGenerator.py create mode 100644 samples/apps/local_inference_server/utils/InferenceGenerator.py create mode 100644 samples/apps/local_inference_server/utils/create_responses.py diff --git a/.gitignore b/.gitignore index 663e4f27f037..43c769a2c0f3 100644 --- a/.gitignore +++ b/.gitignore @@ -446,3 +446,12 @@ _site # Yarn .yarn .yarnrc.yml + +# Python Environments +.env +.venv +.myenv +env/ +venv/ +myvenv/ +ENV/ diff --git a/samples/apps/local_inference_server/Dockerfile b/samples/apps/local_inference_server/Dockerfile new file mode 100644 index 000000000000..5518deebe246 --- /dev/null +++ b/samples/apps/local_inference_server/Dockerfile @@ -0,0 +1,17 @@ +FROM huggingface/transformers-pytorch-gpu + +COPY ./requirements.txt /app/requirements.txt + +# switch working directory +WORKDIR /app + +# install the dependencies and packages in the requirements file +RUN pip install -r requirements.txt + +# copy every content from the local file to the image +COPY . /app + +# configure the container to run in an executed manner +ENTRYPOINT [ "python3" ] + +CMD ["inference_app.py" ] \ No newline at end of file diff --git a/samples/apps/local_inference_server/README.md b/samples/apps/local_inference_server/README.md new file mode 100644 index 000000000000..b1ef03ab1326 --- /dev/null +++ b/samples/apps/local_inference_server/README.md @@ -0,0 +1,37 @@ +# Local Hugging Face Model Inteference Server + +> [!IMPORTANT] +> This learning sample is for educational purposes only and should not be used in any production +> use case. It is intended to make Semantic Kernel features more accessible for scenarios that +> do not require an OpenAI or Azure OpenAI endpoint. + +This application provides an API service for interacting with models available through [Hugging Face](https://huggingface.co/). The request bodies and responses are modeled after OpenAI and Azure OpenAI for smooth transition to more capable LLMs. + +## Building the Sample Container +`docker image build -t hf_model_server .` + +## Running the Sample Container +`docker run -p 5000:5000 -d hf_model_server` + +This will run the service at **`http://localhost:5000`**. Navigating to **`http://localhost:5000`** in a browser window will provide instruction on how to construct requests to the service. + +> [!IMPORTANT] +> If the model has not been cached (ex: first time calling it) the response can take some time +> due to the model being downloaded. +> Using this service to generate images can also take a very long time - a factor that scales with your hardware. + +## Alternative: Bare-Metal +Alternatively, the service can be started on bare-metal. To do this, you will need to have Python 3.9 installed. + +Before proceeding, it is highly recommended that you create a Python 3.9 virtual environment. + +Example: `python -m venv myvenv` or `python3 -m venv myvenv`. + +Make sure your environment is activated: + +For Windows, run in PowerShell: `./myvenv/Scripts/Activate`. +For Linux/macOS, run: `source myvenv/bin/activate`. + +Then, run `pip install -r requirements.txt`. + +Once all the required dependencies have been installed, you can run the service using `python inference_app.py`. Navigating to **`http://localhost:5000`** in a browser window will provide instruction on how to construct requests to the service. \ No newline at end of file diff --git a/samples/apps/local_inference_server/inference_app.py b/samples/apps/local_inference_server/inference_app.py new file mode 100644 index 000000000000..26598d24158d --- /dev/null +++ b/samples/apps/local_inference_server/inference_app.py @@ -0,0 +1,126 @@ +# Importing flask module in the project is mandatory +# An object of Flask class is our WSGI application. +from flask import Flask, request, json, redirect, url_for, render_template, jsonify +from utils import create_responses, CompletionGenerator, EmbeddingGenerator, ImageGenerator +import argparse + +# Flask constructor takes the name of +# current module (__name__) as argument. +app = Flask(__name__) + +@app.route('/') +def home(): + return render_template('home.html') + +@app.route('/docs') +def docs(): + return render_template('documentation.html') + +@app.route('/docs/completions') +def completions_docs(): + return render_template('completions.html') + +@app.route('/docs/embeddings') +def embeddings_docs(): + return render_template('embeddings.html') + +@app.route('/docs/images') +def images_docs(): + return render_template('images.html') + +@app.route('/completions/', methods=['POST']) +def receive_completion_by_model(model): + return process_completion_request(request, model) + +@app.route('/completions//', methods=['POST']) +def receive_completion_by_organization_model(organization, model): + return process_completion_request(request, f'{organization}/{model}') + +@app.route('/embeddings/', methods=['POST']) +def receive_embedding_by_model(model): + return process_embedding_request(request, model) + +@app.route('/embeddings//', methods=['POST']) +def receive_embedding_by_organization_model(organization, model): + return process_embedding_request(request, f'{organization}/{model}') + +@app.route('/images/generations/', methods=['POST']) +def receive_image_generation_by_model(model): + return process_image_generation_request(request, model) + +@app.route('/images/generations//', methods=['POST']) +def receive_image_generation_by_organization_model(organization, model): + return process_image_generation_request(request, f'{organization}/{model}') + +def process_completion_request(request, model): + request_data = request.data + json_data = json.loads(request_data) + try: + prompt = json_data["inputs"] + if "context" in json_data: + context = json_data["context"] + else: + context = "" + + if "max_tokens" in json_data: + max_tokens = json_data["max_tokens"] + else: + max_tokens = 32 + + inference_generator = CompletionGenerator.CompletionGenerator(model) + result, num_prompt_tokens, num_result_tokens = inference_generator.perform_inference(prompt, context, max_tokens) + return jsonify(create_responses.create_completion_response( + result, + model, + num_prompt_tokens, + num_result_tokens)) + except Exception as e: + print(e) + return ("Sorry, unable to perform sentence completion with model {}".format(model)) + +def process_embedding_request(request, model): + request_data = request.data + json_data = json.loads(request_data) + try: + sentences = json_data["inputs"] + inference_generator = EmbeddingGenerator.EmbeddingGenerator(model) + embeddings, num_prompt_tokens = inference_generator.perform_inference(sentences) + return jsonify(create_responses.create_embedding_response( + embeddings, + num_prompt_tokens)) + except Exception as e: + print(e) + return ("Sorry, unable to generate embeddings with model {}".format(model)) + +def process_image_generation_request(request, model): + request_data = request.data + json_data = json.loads(request_data) + num_images = json_data["n"] + prompt = json_data["inputs"] + image_size = json_data["size"] + try: + image_generator = ImageGenerator.ImageGenerator(model) + image_data = image_generator.perform_inference(prompt, num_images, image_size) + return jsonify(create_responses.create_image_gen_response(image_data)) + except Exception as e: + print(e) + return ("Sorry, unable to generate images with model {}".format(model)) + +# main driver function +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('-i', '--ip', + default='0.0.0.0', + help='ip address for flask server endpoint' + ) + parser.add_argument('-p', '--port', + default=5000, + help='port for flask server endpoint', + type=int, + ) + args = parser.parse_args() + + host_ip = args.ip + port = args.port + + app.run(host=host_ip, debug=True, port=port) diff --git a/samples/apps/local_inference_server/requirements.txt b/samples/apps/local_inference_server/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..ac5b62201ab92a9558f9c6e9f0eb794406e890fa GIT binary patch literal 1306 zcmZ9MOHboa428W$nx8^NQ(A@v3pOl9gM>5-#0rI!wxN%7(jXo`9_D=Zy~%*0q-}hB ze0=QN&yUI;t+mc-Tj;*fRoIE{!bUds=b8PmBdZk8h_T0}{*2hlO0gWo)!DJ_tkJKn z-fhJB?iGT0B<|piJmQtDz3xv%h@Pf;jk4h>or(O2j%=C`t-aY^6&qVRZf`5S0Xebr z#8x(-l2kr3%p#nN*l~ zY$ficu38Q)RS7+h@{9KmeYI21fvdK;kfleWO1#~wYn0BDnh>tUW=1flRPU;rU!G`8D1$OMadE%aR?rS)DFWjp-M;rPZ|I8Ia0dBIh%#!_| i^y&&}fII#}ZoTH(JLKeLFT-zG-j%=CMT9vH4*dg%qrDIS literal 0 HcmV?d00001 diff --git a/samples/apps/local_inference_server/static/css/styles.css b/samples/apps/local_inference_server/static/css/styles.css new file mode 100644 index 000000000000..ec0717e79b91 --- /dev/null +++ b/samples/apps/local_inference_server/static/css/styles.css @@ -0,0 +1,9 @@ +.code { + background-color: black; + height: auto; + width: auto; + border-style: solid; + font-family:'Courier New', Courier, monospace; + color: white; + font-size: 14px; +} diff --git a/samples/apps/local_inference_server/templates/completions.html b/samples/apps/local_inference_server/templates/completions.html new file mode 100644 index 000000000000..d0a4d15981aa --- /dev/null +++ b/samples/apps/local_inference_server/templates/completions.html @@ -0,0 +1,22 @@ +{% extends 'documentation.html' %} + +{% block body %} + +

Completions

+ +

Example Request

+
+

curl http://localhost:5000/completions/{model} \

+

-X POST \

+

-H "Content-Type: application/json" \

+

-d '{"inputs": "this is a test"}'

+
+ +

+ HF Text Generation Models +

+

+ HF Text Summarization Models +

+ +{% endblock %} diff --git a/samples/apps/local_inference_server/templates/documentation.html b/samples/apps/local_inference_server/templates/documentation.html new file mode 100644 index 000000000000..8f32cd1657ca --- /dev/null +++ b/samples/apps/local_inference_server/templates/documentation.html @@ -0,0 +1,25 @@ + + + + + + + Flask Docker + + +

API Documentation

+ Home + Completions + Embeddings + Images + + {% block body %} + + +

Documentation

+ + + {% endblock %} + + + \ No newline at end of file diff --git a/samples/apps/local_inference_server/templates/embeddings.html b/samples/apps/local_inference_server/templates/embeddings.html new file mode 100644 index 000000000000..ecd38f22b93f --- /dev/null +++ b/samples/apps/local_inference_server/templates/embeddings.html @@ -0,0 +1,19 @@ +{% extends 'documentation.html' %} + +{% block body %} + +

Embeddings

+ +

Example Request

+
+

curl http://localhost:5000/embeddings/{model} \

+

-X POST \

+

-H "Content-Type: application/json" \

+

-d '{"inputs": ["test string 1", "test string 2", ...]}'

+
+ +

+ HF Text Embedding Models +

+ +{% endblock %} diff --git a/samples/apps/local_inference_server/templates/home.html b/samples/apps/local_inference_server/templates/home.html new file mode 100644 index 000000000000..716f912d25c8 --- /dev/null +++ b/samples/apps/local_inference_server/templates/home.html @@ -0,0 +1,12 @@ + + + + + + Flask Docker + + +

Your Hugging Face model server is running

+ Documentation + + \ No newline at end of file diff --git a/samples/apps/local_inference_server/templates/images.html b/samples/apps/local_inference_server/templates/images.html new file mode 100644 index 000000000000..dff5ccf03164 --- /dev/null +++ b/samples/apps/local_inference_server/templates/images.html @@ -0,0 +1,19 @@ +{% extends 'documentation.html' %} + +{% block body %} + +

Images

+ +

Example Request

+
+

curl http://localhost:5000/images/generations/{model} \

+

-X POST \

+

-H "Content-Type: application/json" \

+

-d '{"inputs": "a test image", "n": 1, "size": "256x256"}'

+
+ +

+ HF Text-to-Image Models +

+ +{% endblock %} diff --git a/samples/apps/local_inference_server/utils/CompletionGenerator.py b/samples/apps/local_inference_server/utils/CompletionGenerator.py new file mode 100644 index 000000000000..fee59d256c7b --- /dev/null +++ b/samples/apps/local_inference_server/utils/CompletionGenerator.py @@ -0,0 +1,32 @@ + +from . import InferenceGenerator +from transformers import AutoTokenizer, AutoModelForCausalLM + +# The model used to get the tokenizer can be a little arbitrary +# since the tokenizers are common within the same model type + +class CompletionGenerator(InferenceGenerator.InferenceGenerator): + def __init__(self, model_name): + super().__init__(model_name) + self.tokenizer = AutoTokenizer.from_pretrained(self.model_name) + self.tokenizer.pad_token = self.tokenizer.eos_token + + def perform_inference(self, prompt, context, max_tokens): + model = AutoModelForCausalLM.from_pretrained(self.model_name, is_decoder=True) + model.to(self.device) + + encodings = self.tokenizer.encode_plus( + text = prompt, + text_pair = context, + truncation = True, + return_tensors= 'pt') + + generated_ids = model.generate( + encodings.input_ids, + max_length = max_tokens, + # num_beams = 5, + # temperature = 0.8, + no_repeat_ngram_size=4, + early_stopping=True) + + return self.tokenizer.decode(generated_ids[0]), encodings.input_ids.numel(), len(generated_ids[0]) diff --git a/samples/apps/local_inference_server/utils/EmbeddingGenerator.py b/samples/apps/local_inference_server/utils/EmbeddingGenerator.py new file mode 100644 index 000000000000..7dcc7030730d --- /dev/null +++ b/samples/apps/local_inference_server/utils/EmbeddingGenerator.py @@ -0,0 +1,30 @@ +import torch +from . import InferenceGenerator +from transformers import AutoModel, AutoTokenizer + +class EmbeddingGenerator(InferenceGenerator.InferenceGenerator): + def __init__(self, model_name): + super().__init__(model_name) + self.tokenizer = AutoTokenizer.from_pretrained(self.model_name) + self.tokenizer.pad_token = self.tokenizer.eos_token + + def _mean_pooling(self, model_output, attention_mask): + token_embeddings = model_output[0] #First element of model_output contains all token embeddings + input_mask_expanded = attention_mask.unsqueeze(-1).float() + x = torch.sum(token_embeddings * input_mask_expanded, 1) + y = torch.clamp(input_mask_expanded.sum(1), min=1e-9) + return (x / y) + + def perform_inference(self, sentences): + model = AutoModel.from_pretrained(self.model_name) + model.to(self.device) + + encodings = self.tokenizer( + sentences, + padding = True, + truncation = True, + return_tensors= 'pt') + + model_output = model(**encodings) + embeddings = self._mean_pooling(model_output, encodings['attention_mask']) + return embeddings, encodings.input_ids.numel() diff --git a/samples/apps/local_inference_server/utils/ImageGenerator.py b/samples/apps/local_inference_server/utils/ImageGenerator.py new file mode 100644 index 000000000000..b67bedca1549 --- /dev/null +++ b/samples/apps/local_inference_server/utils/ImageGenerator.py @@ -0,0 +1,33 @@ +from diffusers import DiffusionPipeline +import base64 +from . import InferenceGenerator +from io import BytesIO +# The model used to get the tokenizer can be a little arbitrary +# since the tokenizers are common within the same model type + +class ImageGenerator(InferenceGenerator.InferenceGenerator): + def __init__(self, model_name): + super().__init__(model_name) + self.default_size = 512 + + def perform_inference(self, prompt, num_images, size): + generator = DiffusionPipeline.from_pretrained(self.model_name) + generator.to(self.device) + + height = self.default_size + width = self.default_size + + if size is not None: + tmp = size.split("x") + height = int(tmp[0]) + width = int(tmp[1]) + + images = generator([prompt] * num_images, height=height, width=width).images + + b64_images = [] + for image in images: + buffered = BytesIO() + image.save(buffered, format="PNG") + base64_image = base64.b64encode(buffered.getvalue()) + b64_images.append({"b64_json": base64_image.decode()}) + return b64_images diff --git a/samples/apps/local_inference_server/utils/InferenceGenerator.py b/samples/apps/local_inference_server/utils/InferenceGenerator.py new file mode 100644 index 000000000000..48fda4e5fea0 --- /dev/null +++ b/samples/apps/local_inference_server/utils/InferenceGenerator.py @@ -0,0 +1,13 @@ + +import os +import torch +from transformers import AutoTokenizer, AutoModelForCausalLM + +# The model used to get the tokenizer can be a little arbitrary +# since the tokenizers are common within the same model type + +class InferenceGenerator(): + def __init__(self, model_name): + os.environ['TOKENIZERS_PARALLELISM'] = "false" + self.model_name = model_name + self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') diff --git a/samples/apps/local_inference_server/utils/create_responses.py b/samples/apps/local_inference_server/utils/create_responses.py new file mode 100644 index 000000000000..e1e58c32972e --- /dev/null +++ b/samples/apps/local_inference_server/utils/create_responses.py @@ -0,0 +1,41 @@ +from datetime import datetime + +# These responses are modeled after the OpenAI REST API + +def create_completion_response(completion_text, model, num_prompt_tokens, num_completion_tokens): + data = [{ + "generated_text": completion_text + }] + return data + +def create_embedding_indices(embeddings): + index = 0 + data_entries = [] + for embedding in embeddings: + data_entries.append({ + "object": "embedding", + "index": index, + "embedding": embedding.tolist() + }) + index = index + 1 + return data_entries + +def create_embedding_response(embeddings, num_prompt_tokens): + data_entries = create_embedding_indices(embeddings) + data = { + "object": "list", + "data": data_entries, + "usage": { + "prompt_tokens": num_prompt_tokens, + "total_tokens": num_prompt_tokens + } + } + return data + + +def create_image_gen_response(image_data): + data = { + "created": datetime.now(), + "data": image_data + } + return data From c32e86c26fdd5d056b168db4772eb4980b58d6d7 Mon Sep 17 00:00:00 2001 From: Dmytro Struk Date: Thu, 30 Mar 2023 13:43:23 +0100 Subject: [PATCH 02/12] Added HuggingFace implementation and tests --- dotnet/SK-dotnet.sln | 21 +++ ...ectors.HuggingFace.IntegrationTests.csproj | 27 +++ .../HuggingFaceTextCompletionTests.cs | 44 +++++ .../Connectors.HuggingFace.UnitTests.csproj | 41 +++++ .../HuggingFaceTestHelper.cs | 44 +++++ .../TestData/completion_test_response.json | 5 + .../TestData/embeddings_test_response.json | 23 +++ .../HuggingFaceTextCompletionTests.cs | 73 ++++++++ .../HuggingFaceEmbeddingGenerationTests.cs | 76 ++++++++ .../Connectors.HuggingFace.csproj | 13 ++ .../TextCompletion/CompletionRequest.cs | 19 ++ .../TextCompletion/CompletionResponse.cs | 17 ++ .../HuggingFaceTextCompletion.cs | 162 ++++++++++++++++++ .../TextEmbedding/EmbeddingRequest.cs | 20 +++ .../TextEmbedding/EmbeddingResponse.cs | 27 +++ .../HuggingFaceEmbeddingGeneration.cs | 119 +++++++++++++ .../src/SemanticKernel/SemanticKernel.csproj | 1 + 17 files changed, 732 insertions(+) create mode 100644 dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/Connectors.HuggingFace.IntegrationTests.csproj create mode 100644 dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/TextCompletion/HuggingFaceTextCompletionTests.cs create mode 100644 dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/Connectors.HuggingFace.UnitTests.csproj create mode 100644 dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/HuggingFaceTestHelper.cs create mode 100644 dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TestData/completion_test_response.json create mode 100644 dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TestData/embeddings_test_response.json create mode 100644 dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TextCompletion/HuggingFaceTextCompletionTests.cs create mode 100644 dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs create mode 100644 dotnet/src/Connectors/Connectors.HuggingFace/Connectors.HuggingFace.csproj create mode 100644 dotnet/src/Connectors/Connectors.HuggingFace/TextCompletion/CompletionRequest.cs create mode 100644 dotnet/src/Connectors/Connectors.HuggingFace/TextCompletion/CompletionResponse.cs create mode 100644 dotnet/src/Connectors/Connectors.HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs create mode 100644 dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/EmbeddingRequest.cs create mode 100644 dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/EmbeddingResponse.cs create mode 100644 dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/HuggingFaceEmbeddingGeneration.cs diff --git a/dotnet/SK-dotnet.sln b/dotnet/SK-dotnet.sln index f81251b11f17..80d3ed1ae72b 100644 --- a/dotnet/SK-dotnet.sln +++ b/dotnet/SK-dotnet.sln @@ -60,6 +60,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "connectors", "connectors", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Connectors.UnitTests", "src\Connectors\Connectors.UnitTests\Connectors.UnitTests.csproj", "{EB3FC57F-E591-4C88-BCD5-B6A1BC635168}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Connectors.HuggingFace", "src\Connectors\Connectors.HuggingFace\Connectors.HuggingFace.csproj", "{AADA39E0-1406-41F2-856A-3AA69B69EFE1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Connectors.HuggingFace.UnitTests", "src\Connectors\Connectors.HuggingFace.UnitTests\Connectors.HuggingFace.UnitTests.csproj", "{86446A30-41A0-47C2-BE48-B410FE1E0BFD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Connectors.HuggingFace.IntegrationTests", "src\Connectors\Connectors.HuggingFace.IntegrationTests\Connectors.HuggingFace.IntegrationTests.csproj", "{CB201CC6-CB17-464F-BBE4-07347EFFB6EF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -130,6 +136,18 @@ Global {EB3FC57F-E591-4C88-BCD5-B6A1BC635168}.Debug|Any CPU.Build.0 = Debug|Any CPU {EB3FC57F-E591-4C88-BCD5-B6A1BC635168}.Release|Any CPU.ActiveCfg = Release|Any CPU {EB3FC57F-E591-4C88-BCD5-B6A1BC635168}.Release|Any CPU.Build.0 = Release|Any CPU + {AADA39E0-1406-41F2-856A-3AA69B69EFE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AADA39E0-1406-41F2-856A-3AA69B69EFE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AADA39E0-1406-41F2-856A-3AA69B69EFE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AADA39E0-1406-41F2-856A-3AA69B69EFE1}.Release|Any CPU.Build.0 = Release|Any CPU + {86446A30-41A0-47C2-BE48-B410FE1E0BFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86446A30-41A0-47C2-BE48-B410FE1E0BFD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86446A30-41A0-47C2-BE48-B410FE1E0BFD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86446A30-41A0-47C2-BE48-B410FE1E0BFD}.Release|Any CPU.Build.0 = Release|Any CPU + {CB201CC6-CB17-464F-BBE4-07347EFFB6EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB201CC6-CB17-464F-BBE4-07347EFFB6EF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB201CC6-CB17-464F-BBE4-07347EFFB6EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB201CC6-CB17-464F-BBE4-07347EFFB6EF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -154,6 +172,9 @@ Global {39E5F0F6-8B36-4ECA-A5F6-FC7522DC2ECF} = {FA3720F1-C99A-49B2-9577-A940257098BF} {0247C2C9-86C3-45BA-8873-28B0948EDC0C} = {831DDCA2-7D2C-4C31-80DB-6BDB3E1F7AE0} {EB3FC57F-E591-4C88-BCD5-B6A1BC635168} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C} + {AADA39E0-1406-41F2-856A-3AA69B69EFE1} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C} + {86446A30-41A0-47C2-BE48-B410FE1E0BFD} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C} + {CB201CC6-CB17-464F-BBE4-07347EFFB6EF} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FBDC56A3-86AD-4323-AA0F-201E59123B83} diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/Connectors.HuggingFace.IntegrationTests.csproj b/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/Connectors.HuggingFace.IntegrationTests.csproj new file mode 100644 index 000000000000..39d29dcfc579 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/Connectors.HuggingFace.IntegrationTests.csproj @@ -0,0 +1,27 @@ + + + + SemanticKernel.Connectors.HuggingFace.IntegrationTests + SemanticKernel.Connectors.HuggingFace.IntegrationTests + net6.0 + 10 + enable + disable + false + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/TextCompletion/HuggingFaceTextCompletionTests.cs b/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/TextCompletion/HuggingFaceTextCompletionTests.cs new file mode 100644 index 000000000000..d38ce8f9656e --- /dev/null +++ b/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/TextCompletion/HuggingFaceTextCompletionTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Threading.Tasks; +using Microsoft.SemanticKernel.AI.TextCompletion; +using Microsoft.SemanticKernel.Connectors.HuggingFace.TextCompletion; +using Xunit; + +namespace SemanticKernel.Connectors.HuggingFace.IntegrationTests; + +/// +/// Integration tests for . +/// +public sealed class HuggingFaceTextCompletionTests +{ + private const string BaseUri = "http://localhost:5000"; + private const string Model = "gpt2"; + + [Fact(Skip = "This test is for manual verification.")] + public async Task HuggingFaceLocalAndRemoteTextCompletionAsync() + { + // Arrange + const string input = "This is test"; + + using var huggingFaceLocal = new HuggingFaceTextCompletion(new Uri(BaseUri), Model); + using var huggingFaceRemote = new HuggingFaceTextCompletion(this.GetApiKey(), Model); + + // Act + var localResponse = await huggingFaceLocal.CompleteAsync(input, new CompleteRequestSettings()).ConfigureAwait(false); + var remoteResponse = await huggingFaceRemote.CompleteAsync(input, new CompleteRequestSettings()).ConfigureAwait(false); + + // Assert + Assert.NotNull(localResponse); + Assert.NotNull(remoteResponse); + + Assert.StartsWith(input, localResponse, StringComparison.InvariantCulture); + Assert.StartsWith(input, remoteResponse, StringComparison.InvariantCulture); + } + + private string GetApiKey() + { + return Environment.GetEnvironmentVariable("HF_API_KEY")!; + } +} diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/Connectors.HuggingFace.UnitTests.csproj b/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/Connectors.HuggingFace.UnitTests.csproj new file mode 100644 index 000000000000..6611dc21bdfe --- /dev/null +++ b/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/Connectors.HuggingFace.UnitTests.csproj @@ -0,0 +1,41 @@ + + + + SemanticKernel.Connectors.HuggingFace.UnitTests + SemanticKernel.Connectors.HuggingFace.UnitTests + net6.0 + 10 + enable + disable + false + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + Always + + + Always + + + + + + + + + diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/HuggingFaceTestHelper.cs b/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/HuggingFaceTestHelper.cs new file mode 100644 index 000000000000..61a52ddd04c6 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/HuggingFaceTestHelper.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.IO; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Moq; +using Moq.Protected; + +namespace SemanticKernel.Connectors.HuggingFace.UnitTests; + +/// +/// Helper for HuggingFace test purposes. +/// +internal static class HuggingFaceTestHelper +{ + /// + /// Reads test response from file for mocking purposes. + /// + /// Name of the file with test response. + internal static string GetTestResponse(string fileName) + { + return File.ReadAllText($"./TestData/{fileName}"); + } + + /// + /// Returns mocked instance of . + /// + /// Message to return for mocked . + internal static HttpClientHandler GetHttpClientHandlerMock(HttpResponseMessage httpResponseMessage) + { + var httpClientHandler = new Mock(); + + httpClientHandler + .Protected() + .Setup>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny()) + .ReturnsAsync(httpResponseMessage); + + return httpClientHandler.Object; + } +} diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TestData/completion_test_response.json b/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TestData/completion_test_response.json new file mode 100644 index 000000000000..e6c7a94a93a3 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TestData/completion_test_response.json @@ -0,0 +1,5 @@ +[ + { + "generated_text": "This is test completion response" + } +] \ No newline at end of file diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TestData/embeddings_test_response.json b/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TestData/embeddings_test_response.json new file mode 100644 index 000000000000..3ccb846673b3 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TestData/embeddings_test_response.json @@ -0,0 +1,23 @@ +{ + "data": [ + { + "embedding": [ + -0.08541165292263031, + 0.08639130741357803, + -0.12805694341659546, + -0.2877824902534485, + 0.2114177942276001, + -0.29374566674232483, + -0.10496602207422256, + 0.009402364492416382 + ], + "index": 0, + "object": "embedding" + } + ], + "object": "list", + "usage": { + "prompt_tokens": 15, + "total_tokens": 15 + } +} \ No newline at end of file diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TextCompletion/HuggingFaceTextCompletionTests.cs b/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TextCompletion/HuggingFaceTextCompletionTests.cs new file mode 100644 index 000000000000..ea25aecb2cc3 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TextCompletion/HuggingFaceTextCompletionTests.cs @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.SemanticKernel.AI.TextCompletion; +using Microsoft.SemanticKernel.Connectors.HuggingFace.TextCompletion; +using Xunit; + +namespace SemanticKernel.Connectors.HuggingFace.UnitTests.TextCompletion; + +/// +/// Unit tests for class. +/// +public class HuggingFaceTextCompletionTests : IDisposable +{ + private const string BaseUri = "http://localhost:5000"; + private const string Model = "gpt2"; + + private readonly HttpResponseMessage _response = new() + { + StatusCode = HttpStatusCode.OK, + }; + + /// + /// Verifies that + /// returns expected completed text without errors. + /// + [Fact] + public async Task ItReturnsCompletionCorrectlyAsync() + { + // Arrange + const string prompt = "This is test"; + CompleteRequestSettings requestSettings = new(); + + using var service = this.CreateService(HuggingFaceTestHelper.GetTestResponse("completion_test_response.json")); + + // Act + var completion = await service.CompleteAsync(prompt, requestSettings); + + // Assert + Assert.Equal("This is test completion response", completion); + } + + /// + /// Initializes with mocked . + /// + /// Test response for to return. + private HuggingFaceTextCompletion CreateService(string testResponse) + { + this._response.Content = new StringContent(testResponse); + + var httpClientHandler = HuggingFaceTestHelper.GetHttpClientHandlerMock(this._response); + + return new HuggingFaceTextCompletion(new Uri(BaseUri), Model, httpClientHandler); + } + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + this._response.Dispose(); + } + } +} diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs b/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs new file mode 100644 index 000000000000..2e95033929d8 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.SemanticKernel.Connectors.HuggingFace.TextEmbedding; +using Xunit; + +namespace SemanticKernel.Connectors.HuggingFace.UnitTests.TextEmbedding; + +/// +/// Unit tests for class. +/// +public class HuggingFaceEmbeddingGenerationTests : IDisposable +{ + private const string BaseUri = "http://localhost:5000"; + private const string Model = "gpt2"; + + private readonly HttpResponseMessage _response = new() + { + StatusCode = HttpStatusCode.OK, + }; + + /// + /// Verifies that + /// returns expected list of generated embeddings without errors. + /// + [Fact] + public async Task ItReturnsEmbeddingsCorrectlyAsync() + { + // Arrange + const int expectedEmbeddingCount = 1; + const int expectedVectorCount = 8; + List data = new() { "test_string_1", "test_string_2", "test_string_3" }; + + using var service = this.CreateService(HuggingFaceTestHelper.GetTestResponse("embeddings_test_response.json")); + + // Act + var embeddings = await service.GenerateEmbeddingsAsync(data); + + // Assert + Assert.NotNull(embeddings); + Assert.Equal(expectedEmbeddingCount, embeddings.Count); + Assert.Equal(expectedVectorCount, embeddings.First().Count); + } + + /// + /// Initializes with mocked . + /// + /// Test response for to return. + private HuggingFaceEmbeddingGeneration CreateService(string testResponse) + { + this._response.Content = new StringContent(testResponse); + + var httpClientHandler = HuggingFaceTestHelper.GetHttpClientHandlerMock(this._response); + + return new HuggingFaceEmbeddingGeneration(new Uri(BaseUri), Model, httpClientHandler); + } + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + this._response.Dispose(); + } + } +} diff --git a/dotnet/src/Connectors/Connectors.HuggingFace/Connectors.HuggingFace.csproj b/dotnet/src/Connectors/Connectors.HuggingFace/Connectors.HuggingFace.csproj new file mode 100644 index 000000000000..7e0edd572f51 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.HuggingFace/Connectors.HuggingFace.csproj @@ -0,0 +1,13 @@ + + + + Microsoft.SemanticKernel.Connectors.HuggingFace + Microsoft.SemanticKernel.Connectors.HuggingFace + netstandard2.1 + + + + + + + diff --git a/dotnet/src/Connectors/Connectors.HuggingFace/TextCompletion/CompletionRequest.cs b/dotnet/src/Connectors/Connectors.HuggingFace/TextCompletion/CompletionRequest.cs new file mode 100644 index 000000000000..a17c4dcf5d9c --- /dev/null +++ b/dotnet/src/Connectors/Connectors.HuggingFace/TextCompletion/CompletionRequest.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Text.Json.Serialization; + +namespace Microsoft.SemanticKernel.Connectors.HuggingFace.TextCompletion; + +/// +/// HTTP schema to perform completion request. +/// +[Serializable] +public sealed class CompletionRequest +{ + /// + /// Prompt to complete. + /// + [JsonPropertyName("inputs")] + public string Input { get; set; } = string.Empty; +} diff --git a/dotnet/src/Connectors/Connectors.HuggingFace/TextCompletion/CompletionResponse.cs b/dotnet/src/Connectors/Connectors.HuggingFace/TextCompletion/CompletionResponse.cs new file mode 100644 index 000000000000..1a267ba2b260 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.HuggingFace/TextCompletion/CompletionResponse.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Text.Json.Serialization; + +namespace Microsoft.SemanticKernel.Connectors.HuggingFace.TextCompletion; + +/// +/// HTTP Schema for completion response. +/// +public sealed class CompletionResponse +{ + /// + /// Completed text. + /// + [JsonPropertyName("generated_text")] + public string? Text { get; set; } +} diff --git a/dotnet/src/Connectors/Connectors.HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs b/dotnet/src/Connectors/Connectors.HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs new file mode 100644 index 000000000000..0132bd65cce4 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs @@ -0,0 +1,162 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.SemanticKernel.AI; +using Microsoft.SemanticKernel.AI.TextCompletion; +using Microsoft.SemanticKernel.Diagnostics; + +namespace Microsoft.SemanticKernel.Connectors.HuggingFace.TextCompletion; + +/// +/// HuggingFace text completion service. +/// +public sealed class HuggingFaceTextCompletion : ITextCompletion, IDisposable +{ + private const string HttpUserAgent = "Microsoft Semantic Kernel"; + private const string HuggingFaceApiBaseUri = "https://api-inference.huggingface.co"; + private const string CompletionRemoteApiEndpoint = "/models"; + private const string CompletionLocalApiEndpoint = "/completions"; + + private readonly string _completionEndpoint; + private readonly string _model; + private readonly HttpClient _httpClient; + private readonly HttpClientHandler? _httpClientHandler; + + /// + /// Initializes a new instance of the class. + /// + /// Base URI for service API call. + /// Model to use for service API call. + /// Instance of to setup specific scenarios. + public HuggingFaceTextCompletion(Uri baseUri, string model, HttpClientHandler httpClientHandler) + { + Verify.NotNull(baseUri, "Base URI cannot be null."); + Verify.NotEmpty(model, "Model cannot be empty."); + + this._model = model; + + this._httpClient = new(httpClientHandler); + + this._httpClient.BaseAddress = baseUri; + this._httpClient.DefaultRequestHeaders.Add("User-Agent", HttpUserAgent); + + this._completionEndpoint = CompletionLocalApiEndpoint; + } + + /// + /// Initializes a new instance of the class. + /// Using default implementation. + /// + /// Base URI for service API call. + /// Model to use for service API call. + public HuggingFaceTextCompletion(Uri baseUri, string model) + { + Verify.NotNull(baseUri, "Base URI cannot be null."); + Verify.NotEmpty(model, "Model cannot be empty."); + + this._model = model; + + this._httpClientHandler = new() { CheckCertificateRevocationList = true }; + this._httpClient = new(this._httpClientHandler); + + this._httpClient.BaseAddress = baseUri; + this._httpClient.DefaultRequestHeaders.Add("User-Agent", HttpUserAgent); + + this._completionEndpoint = CompletionLocalApiEndpoint; + } + + /// + /// Initializes a new instance of the class. + /// Using HuggingFace API for service call, see https://huggingface.co/docs/api-inference/index. + /// + /// HuggingFace API key, see https://huggingface.co/docs/api-inference/quicktour#running-inference-with-api-requests. + /// Model to use for service API call. + /// Instance of to setup specific scenarios. + public HuggingFaceTextCompletion(string apiKey, string model, HttpClientHandler httpClientHandler) + : this(new Uri(HuggingFaceApiBaseUri), model, httpClientHandler) + { + Verify.NotEmpty(apiKey, "HuggingFace API key cannot be empty."); + + this._httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}"); + + this._completionEndpoint = CompletionRemoteApiEndpoint; + } + + /// + /// Initializes a new instance of the class. + /// Using HuggingFace API for service call, see https://huggingface.co/docs/api-inference/index. + /// Using default implementation. + /// + /// HuggingFace API key, see https://huggingface.co/docs/api-inference/quicktour#running-inference-with-api-requests. + /// Model to use for service API call. + public HuggingFaceTextCompletion(string apiKey, string model) + : this(new Uri(HuggingFaceApiBaseUri), model) + { + Verify.NotEmpty(apiKey, "HuggingFace API key cannot be empty."); + + this._httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}"); + + this._completionEndpoint = CompletionRemoteApiEndpoint; + } + + /// + public async Task CompleteAsync(string text, CompleteRequestSettings requestSettings, CancellationToken cancellationToken = default) + { + return await this.ExecuteCompleteRequestAsync(text, cancellationToken); + } + + /// + public void Dispose() + { + this._httpClient.Dispose(); + this._httpClientHandler?.Dispose(); + } + + #region private ================================================================================ + + /// + /// Performs HTTP request to given base URI for text completion. + /// + /// Text to complete. + /// Cancellation token. + /// Completed text. + /// Exception when backend didn't respond with completed text. + private async Task ExecuteCompleteRequestAsync(string text, CancellationToken cancellationToken = default) + { + try + { + var completionRequest = new CompletionRequest + { + Input = text + }; + + using var httpRequestMessage = new HttpRequestMessage() + { + Method = HttpMethod.Post, + RequestUri = new Uri($"{this._completionEndpoint}/{this._model}", UriKind.Relative), + Content = new StringContent(JsonSerializer.Serialize(completionRequest)) + }; + + var response = await this._httpClient.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false); + var body = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + + var completionResponse = JsonSerializer.Deserialize>(body); + + return completionResponse.First().Text!; + } + catch (Exception e) when (e is not AIException) + { + throw new AIException( + AIException.ErrorCodes.UnknownError, + $"Something went wrong: {e.Message}", e); + } + } + + #endregion +} diff --git a/dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/EmbeddingRequest.cs b/dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/EmbeddingRequest.cs new file mode 100644 index 000000000000..fa127a545504 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/EmbeddingRequest.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Microsoft.SemanticKernel.Connectors.HuggingFace.TextEmbedding; + +/// +/// HTTP schema to perform embedding request. +/// +[Serializable] +public sealed class EmbeddingRequest +{ + /// + /// Data to embed. + /// + [JsonPropertyName("inputs")] + public IList Input { get; set; } = new List(); +} diff --git a/dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/EmbeddingResponse.cs b/dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/EmbeddingResponse.cs new file mode 100644 index 000000000000..a5c8467402b2 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/EmbeddingResponse.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Microsoft.SemanticKernel.Connectors.HuggingFace.TextEmbedding; + +/// +/// HTTP Schema for embedding response. +/// +public sealed class EmbeddingResponse +{ + /// + /// Model containing embedding. + /// + public sealed class EmbeddingVector + { + [JsonPropertyName("embedding")] + public IList? Embedding { get; set; } + } + + /// + /// List of embeddings. + /// + [JsonPropertyName("data")] + public IList? Embeddings { get; set; } +} diff --git a/dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/HuggingFaceEmbeddingGeneration.cs b/dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/HuggingFaceEmbeddingGeneration.cs new file mode 100644 index 000000000000..53d1d4acb33f --- /dev/null +++ b/dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/HuggingFaceEmbeddingGeneration.cs @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.SemanticKernel.AI; +using Microsoft.SemanticKernel.AI.Embeddings; +using Microsoft.SemanticKernel.Diagnostics; + +namespace Microsoft.SemanticKernel.Connectors.HuggingFace.TextEmbedding; + +/// +/// HuggingFace embedding generation service. +/// +public sealed class HuggingFaceEmbeddingGeneration : IEmbeddingGenerator, IDisposable +{ + private const string HttpUserAgent = "Microsoft Semantic Kernel"; + private const string EmbeddingEndpoint = "/embeddings"; + + private readonly string _model; + private readonly HttpClient _httpClient; + private readonly HttpClientHandler? _httpClientHandler; + + /// + /// Initializes a new instance of the class. + /// + /// Base URI for service API call. + /// Model to use for service API call. + /// Instance of to setup specific scenarios. + public HuggingFaceEmbeddingGeneration(Uri baseUri, string model, HttpClientHandler httpClientHandler) + { + Verify.NotNull(baseUri, "Base URI cannot be null."); + Verify.NotEmpty(model, "Model cannot be empty."); + + this._model = model; + + this._httpClient = new(httpClientHandler); + + this._httpClient.BaseAddress = baseUri; + this._httpClient.DefaultRequestHeaders.Add("User-Agent", HttpUserAgent); + } + + /// + /// Initializes a new instance of the class. + /// Using default implementation. + /// + /// Base URI for service API call. + /// Model to use for service API call. + public HuggingFaceEmbeddingGeneration(Uri baseUri, string model) + { + Verify.NotNull(baseUri, "Base URI cannot be null."); + Verify.NotEmpty(model, "Model cannot be empty."); + + this._model = model; + + this._httpClientHandler = new() { CheckCertificateRevocationList = true }; + this._httpClient = new(this._httpClientHandler); + + this._httpClient.BaseAddress = baseUri; + this._httpClient.DefaultRequestHeaders.Add("User-Agent", HttpUserAgent); + } + + /// + public async Task>> GenerateEmbeddingsAsync(IList data) + { + return await this.ExecuteEmbeddingRequestAsync(data); + } + + /// + public void Dispose() + { + this._httpClient.Dispose(); + this._httpClientHandler?.Dispose(); + } + + #region private ================================================================================ + + /// + /// Performs HTTP request to given base URI for embedding generation. + /// + /// Data to embed. + /// List of generated embeddings. + /// Exception when backend didn't respond with generated embeddings. + private async Task>> ExecuteEmbeddingRequestAsync(IList data) + { + try + { + var embeddingRequest = new EmbeddingRequest + { + Input = data + }; + + using var httpRequestMessage = new HttpRequestMessage() + { + Method = HttpMethod.Post, + RequestUri = new Uri($"{EmbeddingEndpoint}/{this._model}", UriKind.Relative), + Content = new StringContent(JsonSerializer.Serialize(embeddingRequest)), + }; + + var response = await this._httpClient.SendAsync(httpRequestMessage).ConfigureAwait(false); + var body = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + + var embeddingResponse = JsonSerializer.Deserialize(body); + + return embeddingResponse?.Embeddings?.Select(l => new Embedding(l.Embedding.ToArray())).ToList()!; + } + catch (Exception e) when (e is not AIException) + { + throw new AIException( + AIException.ErrorCodes.UnknownError, + $"Something went wrong: {e.Message}", e); + } + } + + #endregion +} diff --git a/dotnet/src/SemanticKernel/SemanticKernel.csproj b/dotnet/src/SemanticKernel/SemanticKernel.csproj index c784a28f5bcf..f6a5b7f212ae 100644 --- a/dotnet/src/SemanticKernel/SemanticKernel.csproj +++ b/dotnet/src/SemanticKernel/SemanticKernel.csproj @@ -41,6 +41,7 @@ <_Parameter1>Microsoft.SemanticKernel.Connectors.OpenAI + <_Parameter1>Microsoft.SemanticKernel.Connectors.HuggingFace <_Parameter1>DynamicProxyGenAssembly2 From 501e0de4670d162d0ac1692ca3482e6943d95812 Mon Sep 17 00:00:00 2001 From: Dmytro Struk Date: Thu, 30 Mar 2023 14:03:06 +0100 Subject: [PATCH 03/12] Updated config in HuggingFace integration settings --- ...onnectors.HuggingFace.IntegrationTests.csproj | 16 +++++++++++++++- .../HuggingFaceTextCompletionTests.cs | 14 +++++++++++++- .../testsettings.json | 5 +++++ 3 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/testsettings.json diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/Connectors.HuggingFace.IntegrationTests.csproj b/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/Connectors.HuggingFace.IntegrationTests.csproj index 39d29dcfc579..584dfe22c675 100644 --- a/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/Connectors.HuggingFace.IntegrationTests.csproj +++ b/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/Connectors.HuggingFace.IntegrationTests.csproj @@ -11,17 +11,31 @@ + + + + + - runtime; build; native; contentfiles; analyzers; buildtransitive all + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + Always + + + diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/TextCompletion/HuggingFaceTextCompletionTests.cs b/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/TextCompletion/HuggingFaceTextCompletionTests.cs index d38ce8f9656e..11aa5916793a 100644 --- a/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/TextCompletion/HuggingFaceTextCompletionTests.cs +++ b/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/TextCompletion/HuggingFaceTextCompletionTests.cs @@ -2,6 +2,7 @@ using System; using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; using Microsoft.SemanticKernel.AI.TextCompletion; using Microsoft.SemanticKernel.Connectors.HuggingFace.TextCompletion; using Xunit; @@ -16,6 +17,17 @@ public sealed class HuggingFaceTextCompletionTests private const string BaseUri = "http://localhost:5000"; private const string Model = "gpt2"; + private readonly IConfigurationRoot _configuration; + + public HuggingFaceTextCompletionTests() + { + // Load configuration + this._configuration = new ConfigurationBuilder() + .AddJsonFile(path: "testsettings.json", optional: false, reloadOnChange: true) + .AddEnvironmentVariables() + .Build(); + } + [Fact(Skip = "This test is for manual verification.")] public async Task HuggingFaceLocalAndRemoteTextCompletionAsync() { @@ -39,6 +51,6 @@ public async Task HuggingFaceLocalAndRemoteTextCompletionAsync() private string GetApiKey() { - return Environment.GetEnvironmentVariable("HF_API_KEY")!; + return this._configuration.GetSection("HuggingFace:ApiKey").Get()!; } } diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/testsettings.json b/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/testsettings.json new file mode 100644 index 000000000000..4ba9af107fba --- /dev/null +++ b/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/testsettings.json @@ -0,0 +1,5 @@ +{ + "HuggingFace": { + "ApiKey": "" + } +} \ No newline at end of file From 9f072981063d60e06ed8ab1cc72ddf9a39432863 Mon Sep 17 00:00:00 2001 From: Dmytro Struk Date: Thu, 30 Mar 2023 14:24:23 +0100 Subject: [PATCH 04/12] Added kernel syntax example with HuggingFace usage --- dotnet/SK-dotnet.sln | 21 ---------- ...ectors.HuggingFace.IntegrationTests.csproj | 41 ------------------- .../testsettings.json | 5 --- .../Connectors.HuggingFace.UnitTests.csproj | 41 ------------------- .../Connectors.HuggingFace.csproj | 13 ------ .../Connectors.UnitTests.csproj | 9 ++++ .../HuggingFace}/HuggingFaceTestHelper.cs | 4 +- .../TestData/completion_test_response.json | 0 .../TestData/embeddings_test_response.json | 0 .../HuggingFaceTextCompletionTests.cs | 2 +- .../HuggingFaceEmbeddingGenerationTests.cs | 2 +- .../HuggingFaceTextCompletionTests.cs | 3 +- .../SemanticKernel.IntegrationTests/README.md | 8 +++- .../SemanticKernel.IntegrationTests.csproj | 4 ++ .../testsettings.json | 3 ++ .../TextCompletion/CompletionRequest.cs | 0 .../TextCompletion/CompletionResponse.cs | 0 .../HuggingFaceTextCompletion.cs | 0 .../TextEmbedding/EmbeddingRequest.cs | 0 .../TextEmbedding/EmbeddingResponse.cs | 0 .../HuggingFaceEmbeddingGeneration.cs | 2 +- .../src/SemanticKernel/SemanticKernel.csproj | 4 ++ .../Example20_HuggingFace.cs | 33 +++++++++++++++ .../dotnet/kernel-syntax-examples/Program.cs | 3 ++ 24 files changed, 70 insertions(+), 128 deletions(-) delete mode 100644 dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/Connectors.HuggingFace.IntegrationTests.csproj delete mode 100644 dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/testsettings.json delete mode 100644 dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/Connectors.HuggingFace.UnitTests.csproj delete mode 100644 dotnet/src/Connectors/Connectors.HuggingFace/Connectors.HuggingFace.csproj rename dotnet/src/Connectors/{Connectors.HuggingFace.UnitTests => Connectors.UnitTests/HuggingFace}/HuggingFaceTestHelper.cs (90%) rename dotnet/src/Connectors/{Connectors.HuggingFace.UnitTests => Connectors.UnitTests/HuggingFace}/TestData/completion_test_response.json (100%) rename dotnet/src/Connectors/{Connectors.HuggingFace.UnitTests => Connectors.UnitTests/HuggingFace}/TestData/embeddings_test_response.json (100%) rename dotnet/src/Connectors/{Connectors.HuggingFace.UnitTests => Connectors.UnitTests/HuggingFace}/TextCompletion/HuggingFaceTextCompletionTests.cs (97%) rename dotnet/src/Connectors/{Connectors.HuggingFace.UnitTests => Connectors.UnitTests/HuggingFace}/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs (97%) rename dotnet/src/{Connectors/Connectors.HuggingFace.IntegrationTests => SemanticKernel.IntegrationTests/Connectors/HuggingFace}/TextCompletion/HuggingFaceTextCompletionTests.cs (91%) rename dotnet/src/{Connectors/Connectors.HuggingFace => SemanticKernel/Connectors/HuggingFace}/TextCompletion/CompletionRequest.cs (100%) rename dotnet/src/{Connectors/Connectors.HuggingFace => SemanticKernel/Connectors/HuggingFace}/TextCompletion/CompletionResponse.cs (100%) rename dotnet/src/{Connectors/Connectors.HuggingFace => SemanticKernel/Connectors/HuggingFace}/TextCompletion/HuggingFaceTextCompletion.cs (100%) rename dotnet/src/{Connectors/Connectors.HuggingFace => SemanticKernel/Connectors/HuggingFace}/TextEmbedding/EmbeddingRequest.cs (100%) rename dotnet/src/{Connectors/Connectors.HuggingFace => SemanticKernel/Connectors/HuggingFace}/TextEmbedding/EmbeddingResponse.cs (100%) rename dotnet/src/{Connectors/Connectors.HuggingFace => SemanticKernel/Connectors/HuggingFace}/TextEmbedding/HuggingFaceEmbeddingGeneration.cs (99%) create mode 100644 samples/dotnet/kernel-syntax-examples/Example20_HuggingFace.cs diff --git a/dotnet/SK-dotnet.sln b/dotnet/SK-dotnet.sln index 80d3ed1ae72b..f81251b11f17 100644 --- a/dotnet/SK-dotnet.sln +++ b/dotnet/SK-dotnet.sln @@ -60,12 +60,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "connectors", "connectors", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Connectors.UnitTests", "src\Connectors\Connectors.UnitTests\Connectors.UnitTests.csproj", "{EB3FC57F-E591-4C88-BCD5-B6A1BC635168}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Connectors.HuggingFace", "src\Connectors\Connectors.HuggingFace\Connectors.HuggingFace.csproj", "{AADA39E0-1406-41F2-856A-3AA69B69EFE1}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Connectors.HuggingFace.UnitTests", "src\Connectors\Connectors.HuggingFace.UnitTests\Connectors.HuggingFace.UnitTests.csproj", "{86446A30-41A0-47C2-BE48-B410FE1E0BFD}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Connectors.HuggingFace.IntegrationTests", "src\Connectors\Connectors.HuggingFace.IntegrationTests\Connectors.HuggingFace.IntegrationTests.csproj", "{CB201CC6-CB17-464F-BBE4-07347EFFB6EF}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -136,18 +130,6 @@ Global {EB3FC57F-E591-4C88-BCD5-B6A1BC635168}.Debug|Any CPU.Build.0 = Debug|Any CPU {EB3FC57F-E591-4C88-BCD5-B6A1BC635168}.Release|Any CPU.ActiveCfg = Release|Any CPU {EB3FC57F-E591-4C88-BCD5-B6A1BC635168}.Release|Any CPU.Build.0 = Release|Any CPU - {AADA39E0-1406-41F2-856A-3AA69B69EFE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AADA39E0-1406-41F2-856A-3AA69B69EFE1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AADA39E0-1406-41F2-856A-3AA69B69EFE1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AADA39E0-1406-41F2-856A-3AA69B69EFE1}.Release|Any CPU.Build.0 = Release|Any CPU - {86446A30-41A0-47C2-BE48-B410FE1E0BFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {86446A30-41A0-47C2-BE48-B410FE1E0BFD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {86446A30-41A0-47C2-BE48-B410FE1E0BFD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {86446A30-41A0-47C2-BE48-B410FE1E0BFD}.Release|Any CPU.Build.0 = Release|Any CPU - {CB201CC6-CB17-464F-BBE4-07347EFFB6EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CB201CC6-CB17-464F-BBE4-07347EFFB6EF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CB201CC6-CB17-464F-BBE4-07347EFFB6EF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CB201CC6-CB17-464F-BBE4-07347EFFB6EF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -172,9 +154,6 @@ Global {39E5F0F6-8B36-4ECA-A5F6-FC7522DC2ECF} = {FA3720F1-C99A-49B2-9577-A940257098BF} {0247C2C9-86C3-45BA-8873-28B0948EDC0C} = {831DDCA2-7D2C-4C31-80DB-6BDB3E1F7AE0} {EB3FC57F-E591-4C88-BCD5-B6A1BC635168} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C} - {AADA39E0-1406-41F2-856A-3AA69B69EFE1} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C} - {86446A30-41A0-47C2-BE48-B410FE1E0BFD} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C} - {CB201CC6-CB17-464F-BBE4-07347EFFB6EF} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FBDC56A3-86AD-4323-AA0F-201E59123B83} diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/Connectors.HuggingFace.IntegrationTests.csproj b/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/Connectors.HuggingFace.IntegrationTests.csproj deleted file mode 100644 index 584dfe22c675..000000000000 --- a/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/Connectors.HuggingFace.IntegrationTests.csproj +++ /dev/null @@ -1,41 +0,0 @@ - - - - SemanticKernel.Connectors.HuggingFace.IntegrationTests - SemanticKernel.Connectors.HuggingFace.IntegrationTests - net6.0 - 10 - enable - disable - false - - - - - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - - - Always - - - - diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/testsettings.json b/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/testsettings.json deleted file mode 100644 index 4ba9af107fba..000000000000 --- a/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/testsettings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "HuggingFace": { - "ApiKey": "" - } -} \ No newline at end of file diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/Connectors.HuggingFace.UnitTests.csproj b/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/Connectors.HuggingFace.UnitTests.csproj deleted file mode 100644 index 6611dc21bdfe..000000000000 --- a/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/Connectors.HuggingFace.UnitTests.csproj +++ /dev/null @@ -1,41 +0,0 @@ - - - - SemanticKernel.Connectors.HuggingFace.UnitTests - SemanticKernel.Connectors.HuggingFace.UnitTests - net6.0 - 10 - enable - disable - false - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - - - Always - - - Always - - - - - - - - - diff --git a/dotnet/src/Connectors/Connectors.HuggingFace/Connectors.HuggingFace.csproj b/dotnet/src/Connectors/Connectors.HuggingFace/Connectors.HuggingFace.csproj deleted file mode 100644 index 7e0edd572f51..000000000000 --- a/dotnet/src/Connectors/Connectors.HuggingFace/Connectors.HuggingFace.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - Microsoft.SemanticKernel.Connectors.HuggingFace - Microsoft.SemanticKernel.Connectors.HuggingFace - netstandard2.1 - - - - - - - diff --git a/dotnet/src/Connectors/Connectors.UnitTests/Connectors.UnitTests.csproj b/dotnet/src/Connectors/Connectors.UnitTests/Connectors.UnitTests.csproj index 408c2da4a266..ad5180c8bbd9 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/Connectors.UnitTests.csproj +++ b/dotnet/src/Connectors/Connectors.UnitTests/Connectors.UnitTests.csproj @@ -24,4 +24,13 @@ + + + Always + + + Always + + + diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/HuggingFaceTestHelper.cs b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/HuggingFaceTestHelper.cs similarity index 90% rename from dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/HuggingFaceTestHelper.cs rename to dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/HuggingFaceTestHelper.cs index 61a52ddd04c6..70d494b78f01 100644 --- a/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/HuggingFaceTestHelper.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/HuggingFaceTestHelper.cs @@ -7,7 +7,7 @@ using Moq; using Moq.Protected; -namespace SemanticKernel.Connectors.HuggingFace.UnitTests; +namespace SemanticKernel.Connectors.UnitTests.HuggingFace; /// /// Helper for HuggingFace test purposes. @@ -20,7 +20,7 @@ internal static class HuggingFaceTestHelper /// Name of the file with test response. internal static string GetTestResponse(string fileName) { - return File.ReadAllText($"./TestData/{fileName}"); + return File.ReadAllText($"./HuggingFace/TestData/{fileName}"); } /// diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TestData/completion_test_response.json b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TestData/completion_test_response.json similarity index 100% rename from dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TestData/completion_test_response.json rename to dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TestData/completion_test_response.json diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TestData/embeddings_test_response.json b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TestData/embeddings_test_response.json similarity index 100% rename from dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TestData/embeddings_test_response.json rename to dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TestData/embeddings_test_response.json diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TextCompletion/HuggingFaceTextCompletionTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs similarity index 97% rename from dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TextCompletion/HuggingFaceTextCompletionTests.cs rename to dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs index ea25aecb2cc3..9f6b1c4062cf 100644 --- a/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TextCompletion/HuggingFaceTextCompletionTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs @@ -9,7 +9,7 @@ using Microsoft.SemanticKernel.Connectors.HuggingFace.TextCompletion; using Xunit; -namespace SemanticKernel.Connectors.HuggingFace.UnitTests.TextCompletion; +namespace SemanticKernel.Connectors.UnitTests.HuggingFace.TextCompletion; /// /// Unit tests for class. diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs similarity index 97% rename from dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs rename to dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs index 2e95033929d8..a87242f31625 100644 --- a/dotnet/src/Connectors/Connectors.HuggingFace.UnitTests/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs @@ -9,7 +9,7 @@ using Microsoft.SemanticKernel.Connectors.HuggingFace.TextEmbedding; using Xunit; -namespace SemanticKernel.Connectors.HuggingFace.UnitTests.TextEmbedding; +namespace SemanticKernel.Connectors.UnitTests.HuggingFace.TextEmbedding; /// /// Unit tests for class. diff --git a/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/TextCompletion/HuggingFaceTextCompletionTests.cs b/dotnet/src/SemanticKernel.IntegrationTests/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs similarity index 91% rename from dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/TextCompletion/HuggingFaceTextCompletionTests.cs rename to dotnet/src/SemanticKernel.IntegrationTests/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs index 11aa5916793a..b82ad442278e 100644 --- a/dotnet/src/Connectors/Connectors.HuggingFace.IntegrationTests/TextCompletion/HuggingFaceTextCompletionTests.cs +++ b/dotnet/src/SemanticKernel.IntegrationTests/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs @@ -7,7 +7,7 @@ using Microsoft.SemanticKernel.Connectors.HuggingFace.TextCompletion; using Xunit; -namespace SemanticKernel.Connectors.HuggingFace.IntegrationTests; +namespace SemanticKernel.IntegrationTests.Connectors.HuggingFace.TextCompletion; /// /// Integration tests for . @@ -24,6 +24,7 @@ public HuggingFaceTextCompletionTests() // Load configuration this._configuration = new ConfigurationBuilder() .AddJsonFile(path: "testsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile(path: "testsettings.development.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables() .Build(); } diff --git a/dotnet/src/SemanticKernel.IntegrationTests/README.md b/dotnet/src/SemanticKernel.IntegrationTests/README.md index ba4b0ed94ef3..2b404118e507 100644 --- a/dotnet/src/SemanticKernel.IntegrationTests/README.md +++ b/dotnet/src/SemanticKernel.IntegrationTests/README.md @@ -5,7 +5,8 @@ 1. **Azure OpenAI**: go to the [Azure OpenAI Quickstart](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/quickstart) and deploy an instance of Azure OpenAI, deploy a model like "text-davinci-003" find your Endpoint and API key. 2. **OpenAI**: go to [OpenAI](https://openai.com/api/) to register and procure your API key. -3. **Azure Bing Web Search API**: go to [Bing Web Seach API](https://www.microsoft.com/en-us/bing/apis/bing-web-search-api) +3. **HuggingFace API key**: see https://huggingface.co/docs/huggingface_hub/guides/inference for details. +4. **Azure Bing Web Search API**: go to [Bing Web Seach API](https://www.microsoft.com/en-us/bing/apis/bing-web-search-api) and select `Try Now` to get started. ## Setup @@ -42,6 +43,9 @@ For example: "Endpoint": "https://contoso.openai.azure.com/", "ApiKey": "...." }, + "HuggingFace": { + "ApiKey": "" + }, "Bing": { "ApiKey": "...." } @@ -58,6 +62,7 @@ For example: export AzureOpenAI__DeploymentName="azure-text-davinci-003" export AzureOpenAIEmbeddings__DeploymentName="azure-text-embedding-ada-002" export AzureOpenAI__Endpoint="https://contoso.openai.azure.com/" + export HuggingFace__ApiKey="...." export Bing__ApiKey="...." ``` @@ -69,5 +74,6 @@ For example: $env:AzureOpenAI__DeploymentName = "azure-text-davinci-003" $env:AzureOpenAIEmbeddings__DeploymentName = "azure-text-embedding-ada-002" $env:AzureOpenAI__Endpoint = "https://contoso.openai.azure.com/" + $env:HuggingFace__ApiKey = "...." $env:Bing__ApiKey = "...." ``` diff --git a/dotnet/src/SemanticKernel.IntegrationTests/SemanticKernel.IntegrationTests.csproj b/dotnet/src/SemanticKernel.IntegrationTests/SemanticKernel.IntegrationTests.csproj index 3ab7d55d937e..189f0be52f4f 100644 --- a/dotnet/src/SemanticKernel.IntegrationTests/SemanticKernel.IntegrationTests.csproj +++ b/dotnet/src/SemanticKernel.IntegrationTests/SemanticKernel.IntegrationTests.csproj @@ -42,4 +42,8 @@ + + + + diff --git a/dotnet/src/SemanticKernel.IntegrationTests/testsettings.json b/dotnet/src/SemanticKernel.IntegrationTests/testsettings.json index 2954fa8de7d1..3e80c2f596ad 100644 --- a/dotnet/src/SemanticKernel.IntegrationTests/testsettings.json +++ b/dotnet/src/SemanticKernel.IntegrationTests/testsettings.json @@ -21,6 +21,9 @@ "Endpoint": "", "ApiKey": "" }, + "HuggingFace": { + "ApiKey": "" + }, "Bing": { "ApiKey": "" } diff --git a/dotnet/src/Connectors/Connectors.HuggingFace/TextCompletion/CompletionRequest.cs b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/CompletionRequest.cs similarity index 100% rename from dotnet/src/Connectors/Connectors.HuggingFace/TextCompletion/CompletionRequest.cs rename to dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/CompletionRequest.cs diff --git a/dotnet/src/Connectors/Connectors.HuggingFace/TextCompletion/CompletionResponse.cs b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/CompletionResponse.cs similarity index 100% rename from dotnet/src/Connectors/Connectors.HuggingFace/TextCompletion/CompletionResponse.cs rename to dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/CompletionResponse.cs diff --git a/dotnet/src/Connectors/Connectors.HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs similarity index 100% rename from dotnet/src/Connectors/Connectors.HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs rename to dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs diff --git a/dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/EmbeddingRequest.cs b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/EmbeddingRequest.cs similarity index 100% rename from dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/EmbeddingRequest.cs rename to dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/EmbeddingRequest.cs diff --git a/dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/EmbeddingResponse.cs b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/EmbeddingResponse.cs similarity index 100% rename from dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/EmbeddingResponse.cs rename to dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/EmbeddingResponse.cs diff --git a/dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/HuggingFaceEmbeddingGeneration.cs b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGeneration.cs similarity index 99% rename from dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/HuggingFaceEmbeddingGeneration.cs rename to dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGeneration.cs index 53d1d4acb33f..fa087e226c2e 100644 --- a/dotnet/src/Connectors/Connectors.HuggingFace/TextEmbedding/HuggingFaceEmbeddingGeneration.cs +++ b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGeneration.cs @@ -15,7 +15,7 @@ namespace Microsoft.SemanticKernel.Connectors.HuggingFace.TextEmbedding; /// /// HuggingFace embedding generation service. /// -public sealed class HuggingFaceEmbeddingGeneration : IEmbeddingGenerator, IDisposable +public sealed class HuggingFaceEmbeddingGeneration : IEmbeddingGeneration, IDisposable { private const string HttpUserAgent = "Microsoft Semantic Kernel"; private const string EmbeddingEndpoint = "/embeddings"; diff --git a/dotnet/src/SemanticKernel/SemanticKernel.csproj b/dotnet/src/SemanticKernel/SemanticKernel.csproj index f6a5b7f212ae..74a526e97cb6 100644 --- a/dotnet/src/SemanticKernel/SemanticKernel.csproj +++ b/dotnet/src/SemanticKernel/SemanticKernel.csproj @@ -58,4 +58,8 @@ PreserveNewest + + + + \ No newline at end of file diff --git a/samples/dotnet/kernel-syntax-examples/Example20_HuggingFace.cs b/samples/dotnet/kernel-syntax-examples/Example20_HuggingFace.cs new file mode 100644 index 000000000000..68fca8c88557 --- /dev/null +++ b/samples/dotnet/kernel-syntax-examples/Example20_HuggingFace.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Threading.Tasks; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Connectors.HuggingFace.TextCompletion; +using RepoUtils; + +/** + * The following example shows how to use Semantic Kernel with HuggingFace API. + */ + +// ReSharper disable once InconsistentNaming +public static class Example20_HuggingFace +{ + public static async Task RunAsync() + { + Console.WriteLine("======== HuggingFace text completion AI ========"); + + IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Log).Build(); + + // Add HuggingFace text completion service + kernel.Config.AddTextCompletionService("hf-text-completion", (kernel) => new HuggingFaceTextCompletion(Env.Var("HF_API_KEY"), "gpt2")); + + const string FUNCTION_DEFINITION = "Question: {{$input}}; Answer:"; + + var questionAnswerFunction = kernel.CreateSemanticFunction(FUNCTION_DEFINITION); + + var result = await questionAnswerFunction.InvokeAsync("What is New York?"); + + Console.WriteLine(result); + } +} diff --git a/samples/dotnet/kernel-syntax-examples/Program.cs b/samples/dotnet/kernel-syntax-examples/Program.cs index 8799c3c1507e..4e0c993d56ea 100644 --- a/samples/dotnet/kernel-syntax-examples/Program.cs +++ b/samples/dotnet/kernel-syntax-examples/Program.cs @@ -65,6 +65,9 @@ public static async Task Main() await Example19_Qdrant.RunAsync(); Console.WriteLine("== DONE =="); + + await Example20_HuggingFace.RunAsync(); + Console.WriteLine("== DONE =="); } } #pragma warning restore CS1591 From c04b8bd8131a4a6c2701affa8e556977ad7b99d9 Mon Sep 17 00:00:00 2001 From: Devis Lucato Date: Thu, 30 Mar 2023 12:11:39 -0700 Subject: [PATCH 05/12] rename folder --- .../Dockerfile | 0 .../README.md | 0 .../inference_app.py | 0 .../requirements.txt | Bin .../static/css/styles.css | 0 .../templates/completions.html | 0 .../templates/documentation.html | 0 .../templates/embeddings.html | 0 .../templates/home.html | 0 .../templates/images.html | 0 .../utils/CompletionGenerator.py | 0 .../utils/EmbeddingGenerator.py | 0 .../utils/ImageGenerator.py | 0 .../utils/InferenceGenerator.py | 0 .../utils/create_responses.py | 0 15 files changed, 0 insertions(+), 0 deletions(-) rename samples/apps/{local_inference_server => local-inference-server}/Dockerfile (100%) rename samples/apps/{local_inference_server => local-inference-server}/README.md (100%) rename samples/apps/{local_inference_server => local-inference-server}/inference_app.py (100%) rename samples/apps/{local_inference_server => local-inference-server}/requirements.txt (100%) rename samples/apps/{local_inference_server => local-inference-server}/static/css/styles.css (100%) rename samples/apps/{local_inference_server => local-inference-server}/templates/completions.html (100%) rename samples/apps/{local_inference_server => local-inference-server}/templates/documentation.html (100%) rename samples/apps/{local_inference_server => local-inference-server}/templates/embeddings.html (100%) rename samples/apps/{local_inference_server => local-inference-server}/templates/home.html (100%) rename samples/apps/{local_inference_server => local-inference-server}/templates/images.html (100%) rename samples/apps/{local_inference_server => local-inference-server}/utils/CompletionGenerator.py (100%) rename samples/apps/{local_inference_server => local-inference-server}/utils/EmbeddingGenerator.py (100%) rename samples/apps/{local_inference_server => local-inference-server}/utils/ImageGenerator.py (100%) rename samples/apps/{local_inference_server => local-inference-server}/utils/InferenceGenerator.py (100%) rename samples/apps/{local_inference_server => local-inference-server}/utils/create_responses.py (100%) diff --git a/samples/apps/local_inference_server/Dockerfile b/samples/apps/local-inference-server/Dockerfile similarity index 100% rename from samples/apps/local_inference_server/Dockerfile rename to samples/apps/local-inference-server/Dockerfile diff --git a/samples/apps/local_inference_server/README.md b/samples/apps/local-inference-server/README.md similarity index 100% rename from samples/apps/local_inference_server/README.md rename to samples/apps/local-inference-server/README.md diff --git a/samples/apps/local_inference_server/inference_app.py b/samples/apps/local-inference-server/inference_app.py similarity index 100% rename from samples/apps/local_inference_server/inference_app.py rename to samples/apps/local-inference-server/inference_app.py diff --git a/samples/apps/local_inference_server/requirements.txt b/samples/apps/local-inference-server/requirements.txt similarity index 100% rename from samples/apps/local_inference_server/requirements.txt rename to samples/apps/local-inference-server/requirements.txt diff --git a/samples/apps/local_inference_server/static/css/styles.css b/samples/apps/local-inference-server/static/css/styles.css similarity index 100% rename from samples/apps/local_inference_server/static/css/styles.css rename to samples/apps/local-inference-server/static/css/styles.css diff --git a/samples/apps/local_inference_server/templates/completions.html b/samples/apps/local-inference-server/templates/completions.html similarity index 100% rename from samples/apps/local_inference_server/templates/completions.html rename to samples/apps/local-inference-server/templates/completions.html diff --git a/samples/apps/local_inference_server/templates/documentation.html b/samples/apps/local-inference-server/templates/documentation.html similarity index 100% rename from samples/apps/local_inference_server/templates/documentation.html rename to samples/apps/local-inference-server/templates/documentation.html diff --git a/samples/apps/local_inference_server/templates/embeddings.html b/samples/apps/local-inference-server/templates/embeddings.html similarity index 100% rename from samples/apps/local_inference_server/templates/embeddings.html rename to samples/apps/local-inference-server/templates/embeddings.html diff --git a/samples/apps/local_inference_server/templates/home.html b/samples/apps/local-inference-server/templates/home.html similarity index 100% rename from samples/apps/local_inference_server/templates/home.html rename to samples/apps/local-inference-server/templates/home.html diff --git a/samples/apps/local_inference_server/templates/images.html b/samples/apps/local-inference-server/templates/images.html similarity index 100% rename from samples/apps/local_inference_server/templates/images.html rename to samples/apps/local-inference-server/templates/images.html diff --git a/samples/apps/local_inference_server/utils/CompletionGenerator.py b/samples/apps/local-inference-server/utils/CompletionGenerator.py similarity index 100% rename from samples/apps/local_inference_server/utils/CompletionGenerator.py rename to samples/apps/local-inference-server/utils/CompletionGenerator.py diff --git a/samples/apps/local_inference_server/utils/EmbeddingGenerator.py b/samples/apps/local-inference-server/utils/EmbeddingGenerator.py similarity index 100% rename from samples/apps/local_inference_server/utils/EmbeddingGenerator.py rename to samples/apps/local-inference-server/utils/EmbeddingGenerator.py diff --git a/samples/apps/local_inference_server/utils/ImageGenerator.py b/samples/apps/local-inference-server/utils/ImageGenerator.py similarity index 100% rename from samples/apps/local_inference_server/utils/ImageGenerator.py rename to samples/apps/local-inference-server/utils/ImageGenerator.py diff --git a/samples/apps/local_inference_server/utils/InferenceGenerator.py b/samples/apps/local-inference-server/utils/InferenceGenerator.py similarity index 100% rename from samples/apps/local_inference_server/utils/InferenceGenerator.py rename to samples/apps/local-inference-server/utils/InferenceGenerator.py diff --git a/samples/apps/local_inference_server/utils/create_responses.py b/samples/apps/local-inference-server/utils/create_responses.py similarity index 100% rename from samples/apps/local_inference_server/utils/create_responses.py rename to samples/apps/local-inference-server/utils/create_responses.py From 1784fda6e3e44920113703ff9f84056ae3229e36 Mon Sep 17 00:00:00 2001 From: Devis Lucato Date: Thu, 30 Mar 2023 12:14:43 -0700 Subject: [PATCH 06/12] Markdown format --- samples/apps/local-inference-server/README.md | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/samples/apps/local-inference-server/README.md b/samples/apps/local-inference-server/README.md index b1ef03ab1326..1108cc4cd3a6 100644 --- a/samples/apps/local-inference-server/README.md +++ b/samples/apps/local-inference-server/README.md @@ -5,27 +5,37 @@ > use case. It is intended to make Semantic Kernel features more accessible for scenarios that > do not require an OpenAI or Azure OpenAI endpoint. -This application provides an API service for interacting with models available through [Hugging Face](https://huggingface.co/). The request bodies and responses are modeled after OpenAI and Azure OpenAI for smooth transition to more capable LLMs. +This application provides an API service for interacting with models available +through [Hugging Face](https://huggingface.co/). The request bodies and responses +are modeled after OpenAI and Azure OpenAI for smooth transition to more capable LLMs. ## Building the Sample Container + `docker image build -t hf_model_server .` ## Running the Sample Container + `docker run -p 5000:5000 -d hf_model_server` -This will run the service at **`http://localhost:5000`**. Navigating to **`http://localhost:5000`** in a browser window will provide instruction on how to construct requests to the service. +This will run the service at **`http://localhost:5000`**. Navigating to +**`http://localhost:5000`** in a browser window will provide instruction on how +to construct requests to the service. > [!IMPORTANT] -> If the model has not been cached (ex: first time calling it) the response can take some time -> due to the model being downloaded. -> Using this service to generate images can also take a very long time - a factor that scales with your hardware. +> If the model has not been cached (ex: first time calling it) the response can +> take some time due to the model being downloaded. +> Using this service to generate images can also take a very long time - a factor +> that scales with your hardware. ## Alternative: Bare-Metal -Alternatively, the service can be started on bare-metal. To do this, you will need to have Python 3.9 installed. -Before proceeding, it is highly recommended that you create a Python 3.9 virtual environment. +Alternatively, the service can be started on bare-metal. To do this, you will +need to have Python 3.9 installed. + +Before proceeding, it is highly recommended that you create a Python 3.9 virtual +environment. -Example: `python -m venv myvenv` or `python3 -m venv myvenv`. +Example: `python -m venv myvenv` or `python3 -m venv myvenv`. Make sure your environment is activated: @@ -34,4 +44,6 @@ For Linux/macOS, run: `source myvenv/bin/activate`. Then, run `pip install -r requirements.txt`. -Once all the required dependencies have been installed, you can run the service using `python inference_app.py`. Navigating to **`http://localhost:5000`** in a browser window will provide instruction on how to construct requests to the service. \ No newline at end of file +Once all the required dependencies have been installed, you can run the service +using `python inference_app.py`. Navigating to **`http://localhost:5000`** in a +browser window will provide instruction on how to construct requests to the service. From 9f85a3b491e42320ffed606f631558eef3f5efce Mon Sep 17 00:00:00 2001 From: Devis Lucato Date: Thu, 30 Mar 2023 12:21:01 -0700 Subject: [PATCH 07/12] Add note to README --- samples/apps/local-inference-server/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/apps/local-inference-server/README.md b/samples/apps/local-inference-server/README.md index 1108cc4cd3a6..b6fd3a34b2b4 100644 --- a/samples/apps/local-inference-server/README.md +++ b/samples/apps/local-inference-server/README.md @@ -13,6 +13,8 @@ are modeled after OpenAI and Azure OpenAI for smooth transition to more capable `docker image build -t hf_model_server .` +Note that this step will take some minutes to download Docker image dependencies. + ## Running the Sample Container `docker run -p 5000:5000 -d hf_model_server` From 9b0a85b4769dc7527a7eec660654d231771253a1 Mon Sep 17 00:00:00 2001 From: Devis Lucato Date: Thu, 30 Mar 2023 12:37:55 -0700 Subject: [PATCH 08/12] Rename classes --- .../HuggingFaceEmbeddingGenerationTests.cs | 10 +++++----- .../TextCompletion/HuggingFaceTextCompletion.cs | 4 ++-- ...mpletionRequest.cs => TextCompletionRequest.cs} | 2 +- ...letionResponse.cs => TextCompletionResponse.cs} | 2 +- ...on.cs => HuggingFaceTextEmbeddingGeneration.cs} | 14 +++++++------- ...EmbeddingRequest.cs => TextEmbeddingRequest.cs} | 2 +- ...beddingResponse.cs => TextEmbeddingResponse.cs} | 2 +- dotnet/src/SemanticKernel/SemanticKernel.csproj | 8 -------- samples/apps/local-inference-server/README.md | 2 +- 9 files changed, 19 insertions(+), 27 deletions(-) rename dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/{CompletionRequest.cs => TextCompletionRequest.cs} (91%) rename dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/{CompletionResponse.cs => TextCompletionResponse.cs} (89%) rename dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/{HuggingFaceEmbeddingGeneration.cs => HuggingFaceTextEmbeddingGeneration.cs} (88%) rename dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/{EmbeddingRequest.cs => TextEmbeddingRequest.cs} (91%) rename dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/{EmbeddingResponse.cs => TextEmbeddingResponse.cs} (93%) diff --git a/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs index a87242f31625..07da74ea42da 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs @@ -12,7 +12,7 @@ namespace SemanticKernel.Connectors.UnitTests.HuggingFace.TextEmbedding; /// -/// Unit tests for class. +/// Unit tests for class. /// public class HuggingFaceEmbeddingGenerationTests : IDisposable { @@ -25,7 +25,7 @@ public class HuggingFaceEmbeddingGenerationTests : IDisposable }; /// - /// Verifies that + /// Verifies that /// returns expected list of generated embeddings without errors. /// [Fact] @@ -48,16 +48,16 @@ public async Task ItReturnsEmbeddingsCorrectlyAsync() } /// - /// Initializes with mocked . + /// Initializes with mocked . /// /// Test response for to return. - private HuggingFaceEmbeddingGeneration CreateService(string testResponse) + private HuggingFaceTextEmbeddingGeneration CreateService(string testResponse) { this._response.Content = new StringContent(testResponse); var httpClientHandler = HuggingFaceTestHelper.GetHttpClientHandlerMock(this._response); - return new HuggingFaceEmbeddingGeneration(new Uri(BaseUri), Model, httpClientHandler); + return new HuggingFaceTextEmbeddingGeneration(new Uri(BaseUri), Model, httpClientHandler); } public void Dispose() diff --git a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs index 0132bd65cce4..d748e0c61acf 100644 --- a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs +++ b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs @@ -131,7 +131,7 @@ private async Task ExecuteCompleteRequestAsync(string text, Cancellation { try { - var completionRequest = new CompletionRequest + var completionRequest = new TextCompletionRequest { Input = text }; @@ -146,7 +146,7 @@ private async Task ExecuteCompleteRequestAsync(string text, Cancellation var response = await this._httpClient.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false); var body = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - var completionResponse = JsonSerializer.Deserialize>(body); + var completionResponse = JsonSerializer.Deserialize>(body); return completionResponse.First().Text!; } diff --git a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/CompletionRequest.cs b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/TextCompletionRequest.cs similarity index 91% rename from dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/CompletionRequest.cs rename to dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/TextCompletionRequest.cs index a17c4dcf5d9c..b2a656744266 100644 --- a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/CompletionRequest.cs +++ b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/TextCompletionRequest.cs @@ -9,7 +9,7 @@ namespace Microsoft.SemanticKernel.Connectors.HuggingFace.TextCompletion; /// HTTP schema to perform completion request. /// [Serializable] -public sealed class CompletionRequest +public sealed class TextCompletionRequest { /// /// Prompt to complete. diff --git a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/CompletionResponse.cs b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/TextCompletionResponse.cs similarity index 89% rename from dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/CompletionResponse.cs rename to dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/TextCompletionResponse.cs index 1a267ba2b260..2678ec93808e 100644 --- a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/CompletionResponse.cs +++ b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/TextCompletionResponse.cs @@ -7,7 +7,7 @@ namespace Microsoft.SemanticKernel.Connectors.HuggingFace.TextCompletion; /// /// HTTP Schema for completion response. /// -public sealed class CompletionResponse +public sealed class TextCompletionResponse { /// /// Completed text. diff --git a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGeneration.cs b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/HuggingFaceTextEmbeddingGeneration.cs similarity index 88% rename from dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGeneration.cs rename to dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/HuggingFaceTextEmbeddingGeneration.cs index fa087e226c2e..fc789d0f5be7 100644 --- a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGeneration.cs +++ b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/HuggingFaceTextEmbeddingGeneration.cs @@ -15,7 +15,7 @@ namespace Microsoft.SemanticKernel.Connectors.HuggingFace.TextEmbedding; /// /// HuggingFace embedding generation service. /// -public sealed class HuggingFaceEmbeddingGeneration : IEmbeddingGeneration, IDisposable +public sealed class HuggingFaceTextEmbeddingGeneration : IEmbeddingGeneration, IDisposable { private const string HttpUserAgent = "Microsoft Semantic Kernel"; private const string EmbeddingEndpoint = "/embeddings"; @@ -25,12 +25,12 @@ public sealed class HuggingFaceEmbeddingGeneration : IEmbeddingGeneration - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Base URI for service API call. /// Model to use for service API call. /// Instance of to setup specific scenarios. - public HuggingFaceEmbeddingGeneration(Uri baseUri, string model, HttpClientHandler httpClientHandler) + public HuggingFaceTextEmbeddingGeneration(Uri baseUri, string model, HttpClientHandler httpClientHandler) { Verify.NotNull(baseUri, "Base URI cannot be null."); Verify.NotEmpty(model, "Model cannot be empty."); @@ -44,12 +44,12 @@ public HuggingFaceEmbeddingGeneration(Uri baseUri, string model, HttpClientHandl } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// Using default implementation. /// /// Base URI for service API call. /// Model to use for service API call. - public HuggingFaceEmbeddingGeneration(Uri baseUri, string model) + public HuggingFaceTextEmbeddingGeneration(Uri baseUri, string model) { Verify.NotNull(baseUri, "Base URI cannot be null."); Verify.NotEmpty(model, "Model cannot be empty."); @@ -88,7 +88,7 @@ private async Task>> ExecuteEmbeddingRequestAsync(IList>> ExecuteEmbeddingRequestAsync(IList(body); + var embeddingResponse = JsonSerializer.Deserialize(body); return embeddingResponse?.Embeddings?.Select(l => new Embedding(l.Embedding.ToArray())).ToList()!; } diff --git a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/EmbeddingRequest.cs b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/TextEmbeddingRequest.cs similarity index 91% rename from dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/EmbeddingRequest.cs rename to dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/TextEmbeddingRequest.cs index fa127a545504..2b8cd227e179 100644 --- a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/EmbeddingRequest.cs +++ b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/TextEmbeddingRequest.cs @@ -10,7 +10,7 @@ namespace Microsoft.SemanticKernel.Connectors.HuggingFace.TextEmbedding; /// HTTP schema to perform embedding request. /// [Serializable] -public sealed class EmbeddingRequest +public sealed class TextEmbeddingRequest { /// /// Data to embed. diff --git a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/EmbeddingResponse.cs b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/TextEmbeddingResponse.cs similarity index 93% rename from dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/EmbeddingResponse.cs rename to dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/TextEmbeddingResponse.cs index a5c8467402b2..ee25f69ad9b4 100644 --- a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/EmbeddingResponse.cs +++ b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/TextEmbeddingResponse.cs @@ -8,7 +8,7 @@ namespace Microsoft.SemanticKernel.Connectors.HuggingFace.TextEmbedding; /// /// HTTP Schema for embedding response. /// -public sealed class EmbeddingResponse +public sealed class TextEmbeddingResponse { /// /// Model containing embedding. diff --git a/dotnet/src/SemanticKernel/SemanticKernel.csproj b/dotnet/src/SemanticKernel/SemanticKernel.csproj index 74a526e97cb6..6dcfdcc3616f 100644 --- a/dotnet/src/SemanticKernel/SemanticKernel.csproj +++ b/dotnet/src/SemanticKernel/SemanticKernel.csproj @@ -39,10 +39,6 @@ <_Parameter1>SemanticKernel.UnitTests - - <_Parameter1>Microsoft.SemanticKernel.Connectors.OpenAI - <_Parameter1>Microsoft.SemanticKernel.Connectors.HuggingFace - <_Parameter1>DynamicProxyGenAssembly2 @@ -58,8 +54,4 @@ PreserveNewest - - - - \ No newline at end of file diff --git a/samples/apps/local-inference-server/README.md b/samples/apps/local-inference-server/README.md index b6fd3a34b2b4..ababcc8e248d 100644 --- a/samples/apps/local-inference-server/README.md +++ b/samples/apps/local-inference-server/README.md @@ -13,7 +13,7 @@ are modeled after OpenAI and Azure OpenAI for smooth transition to more capable `docker image build -t hf_model_server .` -Note that this step will take some minutes to download Docker image dependencies. +This step will take some minutes to download Docker image dependencies. ## Running the Sample Container From 001c98fdfe2483e7a2378526039ee9dbe66c4b03 Mon Sep 17 00:00:00 2001 From: Dmytro Struk Date: Fri, 31 Mar 2023 00:06:58 +0100 Subject: [PATCH 09/12] Addressed PR comments --- .../HuggingFaceTextCompletionTests.cs | 2 +- .../HuggingFaceEmbeddingGenerationTests.cs | 2 +- .../HuggingFaceTextCompletionTests.cs | 2 +- .../HuggingFaceTextCompletion.cs | 45 ++++++++----------- .../HuggingFaceTextEmbeddingGeneration.cs | 23 +++++----- 5 files changed, 32 insertions(+), 42 deletions(-) diff --git a/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs index 9f6b1c4062cf..5a55d148370d 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs @@ -16,7 +16,7 @@ namespace SemanticKernel.Connectors.UnitTests.HuggingFace.TextCompletion; /// public class HuggingFaceTextCompletionTests : IDisposable { - private const string BaseUri = "http://localhost:5000"; + private const string BaseUri = "http://localhost:5000/completions"; private const string Model = "gpt2"; private readonly HttpResponseMessage _response = new() diff --git a/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs index 07da74ea42da..3ca5c096816a 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs @@ -16,7 +16,7 @@ namespace SemanticKernel.Connectors.UnitTests.HuggingFace.TextEmbedding; /// public class HuggingFaceEmbeddingGenerationTests : IDisposable { - private const string BaseUri = "http://localhost:5000"; + private const string BaseUri = "http://localhost:5000/embeddings"; private const string Model = "gpt2"; private readonly HttpResponseMessage _response = new() diff --git a/dotnet/src/SemanticKernel.IntegrationTests/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs b/dotnet/src/SemanticKernel.IntegrationTests/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs index b82ad442278e..41b5e6e38fe8 100644 --- a/dotnet/src/SemanticKernel.IntegrationTests/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs +++ b/dotnet/src/SemanticKernel.IntegrationTests/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs @@ -14,7 +14,7 @@ namespace SemanticKernel.IntegrationTests.Connectors.HuggingFace.TextCompletion; /// public sealed class HuggingFaceTextCompletionTests { - private const string BaseUri = "http://localhost:5000"; + private const string BaseUri = "http://localhost:5000/completions"; private const string Model = "gpt2"; private readonly IConfigurationRoot _configuration; diff --git a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs index d748e0c61acf..a7619a81702b 100644 --- a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs +++ b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs @@ -19,11 +19,8 @@ namespace Microsoft.SemanticKernel.Connectors.HuggingFace.TextCompletion; public sealed class HuggingFaceTextCompletion : ITextCompletion, IDisposable { private const string HttpUserAgent = "Microsoft Semantic Kernel"; - private const string HuggingFaceApiBaseUri = "https://api-inference.huggingface.co"; - private const string CompletionRemoteApiEndpoint = "/models"; - private const string CompletionLocalApiEndpoint = "/completions"; + private const string HuggingFaceApiBaseUri = "https://api-inference.huggingface.co/models"; - private readonly string _completionEndpoint; private readonly string _model; private readonly HttpClient _httpClient; private readonly HttpClientHandler? _httpClientHandler; @@ -31,33 +28,31 @@ public sealed class HuggingFaceTextCompletion : ITextCompletion, IDisposable /// /// Initializes a new instance of the class. /// - /// Base URI for service API call. + /// Endpoint for service API call. /// Model to use for service API call. /// Instance of to setup specific scenarios. - public HuggingFaceTextCompletion(Uri baseUri, string model, HttpClientHandler httpClientHandler) + public HuggingFaceTextCompletion(Uri endpoint, string model, HttpClientHandler httpClientHandler) { - Verify.NotNull(baseUri, "Base URI cannot be null."); + Verify.NotNull(endpoint, "Endpoint cannot be null."); Verify.NotEmpty(model, "Model cannot be empty."); this._model = model; this._httpClient = new(httpClientHandler); - this._httpClient.BaseAddress = baseUri; + this._httpClient.BaseAddress = endpoint; this._httpClient.DefaultRequestHeaders.Add("User-Agent", HttpUserAgent); - - this._completionEndpoint = CompletionLocalApiEndpoint; } /// /// Initializes a new instance of the class. /// Using default implementation. /// - /// Base URI for service API call. + /// Endpoint for service API call. /// Model to use for service API call. - public HuggingFaceTextCompletion(Uri baseUri, string model) + public HuggingFaceTextCompletion(Uri endpoint, string model) { - Verify.NotNull(baseUri, "Base URI cannot be null."); + Verify.NotNull(endpoint, "Endpoint cannot be null."); Verify.NotEmpty(model, "Model cannot be empty."); this._model = model; @@ -65,10 +60,8 @@ public HuggingFaceTextCompletion(Uri baseUri, string model) this._httpClientHandler = new() { CheckCertificateRevocationList = true }; this._httpClient = new(this._httpClientHandler); - this._httpClient.BaseAddress = baseUri; + this._httpClient.BaseAddress = endpoint; this._httpClient.DefaultRequestHeaders.Add("User-Agent", HttpUserAgent); - - this._completionEndpoint = CompletionLocalApiEndpoint; } /// @@ -78,14 +71,13 @@ public HuggingFaceTextCompletion(Uri baseUri, string model) /// HuggingFace API key, see https://huggingface.co/docs/api-inference/quicktour#running-inference-with-api-requests. /// Model to use for service API call. /// Instance of to setup specific scenarios. - public HuggingFaceTextCompletion(string apiKey, string model, HttpClientHandler httpClientHandler) - : this(new Uri(HuggingFaceApiBaseUri), model, httpClientHandler) + /// Endpoint for service API call. + public HuggingFaceTextCompletion(string apiKey, string model, HttpClientHandler httpClientHandler, string endpoint = HuggingFaceApiBaseUri) + : this(new Uri(endpoint), model, httpClientHandler) { Verify.NotEmpty(apiKey, "HuggingFace API key cannot be empty."); this._httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}"); - - this._completionEndpoint = CompletionRemoteApiEndpoint; } /// @@ -95,14 +87,13 @@ public HuggingFaceTextCompletion(string apiKey, string model, HttpClientHandler /// /// HuggingFace API key, see https://huggingface.co/docs/api-inference/quicktour#running-inference-with-api-requests. /// Model to use for service API call. - public HuggingFaceTextCompletion(string apiKey, string model) - : this(new Uri(HuggingFaceApiBaseUri), model) + /// Endpoint for service API call. + public HuggingFaceTextCompletion(string apiKey, string model, string endpoint = HuggingFaceApiBaseUri) + : this(new Uri(endpoint), model) { Verify.NotEmpty(apiKey, "HuggingFace API key cannot be empty."); this._httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}"); - - this._completionEndpoint = CompletionRemoteApiEndpoint; } /// @@ -121,7 +112,7 @@ public void Dispose() #region private ================================================================================ /// - /// Performs HTTP request to given base URI for text completion. + /// Performs HTTP request to given endpoint for text completion. /// /// Text to complete. /// Cancellation token. @@ -139,7 +130,7 @@ private async Task ExecuteCompleteRequestAsync(string text, Cancellation using var httpRequestMessage = new HttpRequestMessage() { Method = HttpMethod.Post, - RequestUri = new Uri($"{this._completionEndpoint}/{this._model}", UriKind.Relative), + RequestUri = new Uri($"/{this._model}", UriKind.Relative), Content = new StringContent(JsonSerializer.Serialize(completionRequest)) }; @@ -150,7 +141,7 @@ private async Task ExecuteCompleteRequestAsync(string text, Cancellation return completionResponse.First().Text!; } - catch (Exception e) when (e is not AIException) + catch (Exception e) when (e is not AIException && !e.IsCriticalException()) { throw new AIException( AIException.ErrorCodes.UnknownError, diff --git a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/HuggingFaceTextEmbeddingGeneration.cs b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/HuggingFaceTextEmbeddingGeneration.cs index fc789d0f5be7..d1709fbd885e 100644 --- a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/HuggingFaceTextEmbeddingGeneration.cs +++ b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/HuggingFaceTextEmbeddingGeneration.cs @@ -18,7 +18,6 @@ namespace Microsoft.SemanticKernel.Connectors.HuggingFace.TextEmbedding; public sealed class HuggingFaceTextEmbeddingGeneration : IEmbeddingGeneration, IDisposable { private const string HttpUserAgent = "Microsoft Semantic Kernel"; - private const string EmbeddingEndpoint = "/embeddings"; private readonly string _model; private readonly HttpClient _httpClient; @@ -27,19 +26,19 @@ public sealed class HuggingFaceTextEmbeddingGeneration : IEmbeddingGeneration /// Initializes a new instance of the class. /// - /// Base URI for service API call. + /// Endpoint for service API call. /// Model to use for service API call. /// Instance of to setup specific scenarios. - public HuggingFaceTextEmbeddingGeneration(Uri baseUri, string model, HttpClientHandler httpClientHandler) + public HuggingFaceTextEmbeddingGeneration(Uri endpoint, string model, HttpClientHandler httpClientHandler) { - Verify.NotNull(baseUri, "Base URI cannot be null."); + Verify.NotNull(endpoint, "Endpoint cannot be null."); Verify.NotEmpty(model, "Model cannot be empty."); this._model = model; this._httpClient = new(httpClientHandler); - this._httpClient.BaseAddress = baseUri; + this._httpClient.BaseAddress = endpoint; this._httpClient.DefaultRequestHeaders.Add("User-Agent", HttpUserAgent); } @@ -47,11 +46,11 @@ public HuggingFaceTextEmbeddingGeneration(Uri baseUri, string model, HttpClientH /// Initializes a new instance of the class. /// Using default implementation. /// - /// Base URI for service API call. + /// Endpoint for service API call. /// Model to use for service API call. - public HuggingFaceTextEmbeddingGeneration(Uri baseUri, string model) + public HuggingFaceTextEmbeddingGeneration(Uri endpoint, string model) { - Verify.NotNull(baseUri, "Base URI cannot be null."); + Verify.NotNull(endpoint, "Endpoint cannot be null."); Verify.NotEmpty(model, "Model cannot be empty."); this._model = model; @@ -59,7 +58,7 @@ public HuggingFaceTextEmbeddingGeneration(Uri baseUri, string model) this._httpClientHandler = new() { CheckCertificateRevocationList = true }; this._httpClient = new(this._httpClientHandler); - this._httpClient.BaseAddress = baseUri; + this._httpClient.BaseAddress = endpoint; this._httpClient.DefaultRequestHeaders.Add("User-Agent", HttpUserAgent); } @@ -79,7 +78,7 @@ public void Dispose() #region private ================================================================================ /// - /// Performs HTTP request to given base URI for embedding generation. + /// Performs HTTP request to given endpoint for embedding generation. /// /// Data to embed. /// List of generated embeddings. @@ -96,7 +95,7 @@ private async Task>> ExecuteEmbeddingRequestAsync(IList>> ExecuteEmbeddingRequestAsync(IList new Embedding(l.Embedding.ToArray())).ToList()!; } - catch (Exception e) when (e is not AIException) + catch (Exception e) when (e is not AIException && !e.IsCriticalException()) { throw new AIException( AIException.ErrorCodes.UnknownError, From 233f8a394be847a6b93612da0bf3eef541ed6a8b Mon Sep 17 00:00:00 2001 From: Dmytro Struk Date: Fri, 31 Mar 2023 00:29:40 +0100 Subject: [PATCH 10/12] Fixed small issues --- .../HuggingFaceTextCompletionTests.cs | 4 ++-- .../HuggingFaceEmbeddingGenerationTests.cs | 4 ++-- .../HuggingFaceTextCompletionTests.cs | 4 ++-- .../TextCompletion/HuggingFaceTextCompletion.cs | 13 +++++++------ .../HuggingFaceTextEmbeddingGeneration.cs | 7 ++++--- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs index 5a55d148370d..cec57a896a87 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs @@ -16,7 +16,7 @@ namespace SemanticKernel.Connectors.UnitTests.HuggingFace.TextCompletion; /// public class HuggingFaceTextCompletionTests : IDisposable { - private const string BaseUri = "http://localhost:5000/completions"; + private const string Endpoint = "http://localhost:5000/completions"; private const string Model = "gpt2"; private readonly HttpResponseMessage _response = new() @@ -54,7 +54,7 @@ private HuggingFaceTextCompletion CreateService(string testResponse) var httpClientHandler = HuggingFaceTestHelper.GetHttpClientHandlerMock(this._response); - return new HuggingFaceTextCompletion(new Uri(BaseUri), Model, httpClientHandler); + return new HuggingFaceTextCompletion(new Uri(Endpoint), Model, httpClientHandler); } public void Dispose() diff --git a/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs index 3ca5c096816a..f66021336b71 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs @@ -16,7 +16,7 @@ namespace SemanticKernel.Connectors.UnitTests.HuggingFace.TextEmbedding; /// public class HuggingFaceEmbeddingGenerationTests : IDisposable { - private const string BaseUri = "http://localhost:5000/embeddings"; + private const string Endpoint = "http://localhost:5000/embeddings"; private const string Model = "gpt2"; private readonly HttpResponseMessage _response = new() @@ -57,7 +57,7 @@ private HuggingFaceTextEmbeddingGeneration CreateService(string testResponse) var httpClientHandler = HuggingFaceTestHelper.GetHttpClientHandlerMock(this._response); - return new HuggingFaceTextEmbeddingGeneration(new Uri(BaseUri), Model, httpClientHandler); + return new HuggingFaceTextEmbeddingGeneration(new Uri(Endpoint), Model, httpClientHandler); } public void Dispose() diff --git a/dotnet/src/SemanticKernel.IntegrationTests/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs b/dotnet/src/SemanticKernel.IntegrationTests/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs index 41b5e6e38fe8..c63e83160a13 100644 --- a/dotnet/src/SemanticKernel.IntegrationTests/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs +++ b/dotnet/src/SemanticKernel.IntegrationTests/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletionTests.cs @@ -14,7 +14,7 @@ namespace SemanticKernel.IntegrationTests.Connectors.HuggingFace.TextCompletion; /// public sealed class HuggingFaceTextCompletionTests { - private const string BaseUri = "http://localhost:5000/completions"; + private const string Endpoint = "http://localhost:5000/completions"; private const string Model = "gpt2"; private readonly IConfigurationRoot _configuration; @@ -35,7 +35,7 @@ public async Task HuggingFaceLocalAndRemoteTextCompletionAsync() // Arrange const string input = "This is test"; - using var huggingFaceLocal = new HuggingFaceTextCompletion(new Uri(BaseUri), Model); + using var huggingFaceLocal = new HuggingFaceTextCompletion(new Uri(Endpoint), Model); using var huggingFaceRemote = new HuggingFaceTextCompletion(this.GetApiKey(), Model); // Act diff --git a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs index a7619a81702b..d16d93021165 100644 --- a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs +++ b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextCompletion/HuggingFaceTextCompletion.cs @@ -19,9 +19,10 @@ namespace Microsoft.SemanticKernel.Connectors.HuggingFace.TextCompletion; public sealed class HuggingFaceTextCompletion : ITextCompletion, IDisposable { private const string HttpUserAgent = "Microsoft Semantic Kernel"; - private const string HuggingFaceApiBaseUri = "https://api-inference.huggingface.co/models"; + private const string HuggingFaceApiEndpoint = "https://api-inference.huggingface.co/models"; private readonly string _model; + private readonly Uri _endpoint; private readonly HttpClient _httpClient; private readonly HttpClientHandler? _httpClientHandler; @@ -36,11 +37,11 @@ public HuggingFaceTextCompletion(Uri endpoint, string model, HttpClientHandler h Verify.NotNull(endpoint, "Endpoint cannot be null."); Verify.NotEmpty(model, "Model cannot be empty."); + this._endpoint = endpoint; this._model = model; this._httpClient = new(httpClientHandler); - this._httpClient.BaseAddress = endpoint; this._httpClient.DefaultRequestHeaders.Add("User-Agent", HttpUserAgent); } @@ -55,12 +56,12 @@ public HuggingFaceTextCompletion(Uri endpoint, string model) Verify.NotNull(endpoint, "Endpoint cannot be null."); Verify.NotEmpty(model, "Model cannot be empty."); + this._endpoint = endpoint; this._model = model; this._httpClientHandler = new() { CheckCertificateRevocationList = true }; this._httpClient = new(this._httpClientHandler); - this._httpClient.BaseAddress = endpoint; this._httpClient.DefaultRequestHeaders.Add("User-Agent", HttpUserAgent); } @@ -72,7 +73,7 @@ public HuggingFaceTextCompletion(Uri endpoint, string model) /// Model to use for service API call. /// Instance of to setup specific scenarios. /// Endpoint for service API call. - public HuggingFaceTextCompletion(string apiKey, string model, HttpClientHandler httpClientHandler, string endpoint = HuggingFaceApiBaseUri) + public HuggingFaceTextCompletion(string apiKey, string model, HttpClientHandler httpClientHandler, string endpoint = HuggingFaceApiEndpoint) : this(new Uri(endpoint), model, httpClientHandler) { Verify.NotEmpty(apiKey, "HuggingFace API key cannot be empty."); @@ -88,7 +89,7 @@ public HuggingFaceTextCompletion(string apiKey, string model, HttpClientHandler /// HuggingFace API key, see https://huggingface.co/docs/api-inference/quicktour#running-inference-with-api-requests. /// Model to use for service API call. /// Endpoint for service API call. - public HuggingFaceTextCompletion(string apiKey, string model, string endpoint = HuggingFaceApiBaseUri) + public HuggingFaceTextCompletion(string apiKey, string model, string endpoint = HuggingFaceApiEndpoint) : this(new Uri(endpoint), model) { Verify.NotEmpty(apiKey, "HuggingFace API key cannot be empty."); @@ -130,7 +131,7 @@ private async Task ExecuteCompleteRequestAsync(string text, Cancellation using var httpRequestMessage = new HttpRequestMessage() { Method = HttpMethod.Post, - RequestUri = new Uri($"/{this._model}", UriKind.Relative), + RequestUri = new Uri($"{this._endpoint}/{this._model}"), Content = new StringContent(JsonSerializer.Serialize(completionRequest)) }; diff --git a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/HuggingFaceTextEmbeddingGeneration.cs b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/HuggingFaceTextEmbeddingGeneration.cs index d1709fbd885e..4cc928d55066 100644 --- a/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/HuggingFaceTextEmbeddingGeneration.cs +++ b/dotnet/src/SemanticKernel/Connectors/HuggingFace/TextEmbedding/HuggingFaceTextEmbeddingGeneration.cs @@ -20,6 +20,7 @@ public sealed class HuggingFaceTextEmbeddingGeneration : IEmbeddingGeneration>> ExecuteEmbeddingRequestAsync(IList Date: Fri, 31 Mar 2023 00:34:48 +0100 Subject: [PATCH 11/12] Added copyright header to inference server --- samples/apps/local-inference-server/Dockerfile | 2 ++ samples/apps/local-inference-server/inference_app.py | 2 ++ samples/apps/local-inference-server/static/css/styles.css | 2 ++ samples/apps/local-inference-server/templates/completions.html | 2 ++ .../apps/local-inference-server/templates/documentation.html | 2 ++ samples/apps/local-inference-server/templates/embeddings.html | 2 ++ samples/apps/local-inference-server/templates/home.html | 2 ++ samples/apps/local-inference-server/templates/images.html | 2 ++ .../apps/local-inference-server/utils/CompletionGenerator.py | 1 + samples/apps/local-inference-server/utils/EmbeddingGenerator.py | 2 ++ samples/apps/local-inference-server/utils/ImageGenerator.py | 2 ++ samples/apps/local-inference-server/utils/InferenceGenerator.py | 1 + samples/apps/local-inference-server/utils/create_responses.py | 2 ++ 13 files changed, 24 insertions(+) diff --git a/samples/apps/local-inference-server/Dockerfile b/samples/apps/local-inference-server/Dockerfile index 5518deebe246..28a07b9365c4 100644 --- a/samples/apps/local-inference-server/Dockerfile +++ b/samples/apps/local-inference-server/Dockerfile @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + FROM huggingface/transformers-pytorch-gpu COPY ./requirements.txt /app/requirements.txt diff --git a/samples/apps/local-inference-server/inference_app.py b/samples/apps/local-inference-server/inference_app.py index 26598d24158d..9b140c000211 100644 --- a/samples/apps/local-inference-server/inference_app.py +++ b/samples/apps/local-inference-server/inference_app.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + # Importing flask module in the project is mandatory # An object of Flask class is our WSGI application. from flask import Flask, request, json, redirect, url_for, render_template, jsonify diff --git a/samples/apps/local-inference-server/static/css/styles.css b/samples/apps/local-inference-server/static/css/styles.css index ec0717e79b91..eea906c30f2d 100644 --- a/samples/apps/local-inference-server/static/css/styles.css +++ b/samples/apps/local-inference-server/static/css/styles.css @@ -1,3 +1,5 @@ +/* Copyright (c) Microsoft. All rights reserved. */ + .code { background-color: black; height: auto; diff --git a/samples/apps/local-inference-server/templates/completions.html b/samples/apps/local-inference-server/templates/completions.html index d0a4d15981aa..bf88cc859abd 100644 --- a/samples/apps/local-inference-server/templates/completions.html +++ b/samples/apps/local-inference-server/templates/completions.html @@ -1,3 +1,5 @@ + + {% extends 'documentation.html' %} {% block body %} diff --git a/samples/apps/local-inference-server/templates/documentation.html b/samples/apps/local-inference-server/templates/documentation.html index 8f32cd1657ca..84d1384067a0 100644 --- a/samples/apps/local-inference-server/templates/documentation.html +++ b/samples/apps/local-inference-server/templates/documentation.html @@ -1,3 +1,5 @@ + + diff --git a/samples/apps/local-inference-server/templates/embeddings.html b/samples/apps/local-inference-server/templates/embeddings.html index ecd38f22b93f..15d81cf0d3b4 100644 --- a/samples/apps/local-inference-server/templates/embeddings.html +++ b/samples/apps/local-inference-server/templates/embeddings.html @@ -1,3 +1,5 @@ + + {% extends 'documentation.html' %} {% block body %} diff --git a/samples/apps/local-inference-server/templates/home.html b/samples/apps/local-inference-server/templates/home.html index 716f912d25c8..e71ec5a53878 100644 --- a/samples/apps/local-inference-server/templates/home.html +++ b/samples/apps/local-inference-server/templates/home.html @@ -1,3 +1,5 @@ + + diff --git a/samples/apps/local-inference-server/templates/images.html b/samples/apps/local-inference-server/templates/images.html index dff5ccf03164..eec18e540c35 100644 --- a/samples/apps/local-inference-server/templates/images.html +++ b/samples/apps/local-inference-server/templates/images.html @@ -1,3 +1,5 @@ + + {% extends 'documentation.html' %} {% block body %} diff --git a/samples/apps/local-inference-server/utils/CompletionGenerator.py b/samples/apps/local-inference-server/utils/CompletionGenerator.py index fee59d256c7b..42369d1a0e0a 100644 --- a/samples/apps/local-inference-server/utils/CompletionGenerator.py +++ b/samples/apps/local-inference-server/utils/CompletionGenerator.py @@ -1,3 +1,4 @@ +# Copyright (c) Microsoft. All rights reserved. from . import InferenceGenerator from transformers import AutoTokenizer, AutoModelForCausalLM diff --git a/samples/apps/local-inference-server/utils/EmbeddingGenerator.py b/samples/apps/local-inference-server/utils/EmbeddingGenerator.py index 7dcc7030730d..47495ca56fe5 100644 --- a/samples/apps/local-inference-server/utils/EmbeddingGenerator.py +++ b/samples/apps/local-inference-server/utils/EmbeddingGenerator.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + import torch from . import InferenceGenerator from transformers import AutoModel, AutoTokenizer diff --git a/samples/apps/local-inference-server/utils/ImageGenerator.py b/samples/apps/local-inference-server/utils/ImageGenerator.py index b67bedca1549..3a6027ae1bff 100644 --- a/samples/apps/local-inference-server/utils/ImageGenerator.py +++ b/samples/apps/local-inference-server/utils/ImageGenerator.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + from diffusers import DiffusionPipeline import base64 from . import InferenceGenerator diff --git a/samples/apps/local-inference-server/utils/InferenceGenerator.py b/samples/apps/local-inference-server/utils/InferenceGenerator.py index 48fda4e5fea0..db5ba93eb9d4 100644 --- a/samples/apps/local-inference-server/utils/InferenceGenerator.py +++ b/samples/apps/local-inference-server/utils/InferenceGenerator.py @@ -1,3 +1,4 @@ +# Copyright (c) Microsoft. All rights reserved. import os import torch diff --git a/samples/apps/local-inference-server/utils/create_responses.py b/samples/apps/local-inference-server/utils/create_responses.py index e1e58c32972e..d92043e54761 100644 --- a/samples/apps/local-inference-server/utils/create_responses.py +++ b/samples/apps/local-inference-server/utils/create_responses.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + from datetime import datetime # These responses are modeled after the OpenAI REST API From e681cd014e791d036a07afa2881d48ab6abb1d47 Mon Sep 17 00:00:00 2001 From: Devis Lucato Date: Thu, 30 Mar 2023 18:41:09 -0700 Subject: [PATCH 12/12] rename folder --- .../Dockerfile | 0 .../README.md | 0 .../inference_app.py | 0 .../requirements.txt | Bin .../static/css/styles.css | 0 .../templates/completions.html | 0 .../templates/documentation.html | 0 .../templates/embeddings.html | 0 .../templates/home.html | 0 .../templates/images.html | 0 .../utils/CompletionGenerator.py | 0 .../utils/EmbeddingGenerator.py | 0 .../utils/ImageGenerator.py | 0 .../utils/InferenceGenerator.py | 0 .../utils/create_responses.py | 0 15 files changed, 0 insertions(+), 0 deletions(-) rename samples/apps/{local-inference-server => hugging-face-http-server}/Dockerfile (100%) rename samples/apps/{local-inference-server => hugging-face-http-server}/README.md (100%) rename samples/apps/{local-inference-server => hugging-face-http-server}/inference_app.py (100%) rename samples/apps/{local-inference-server => hugging-face-http-server}/requirements.txt (100%) rename samples/apps/{local-inference-server => hugging-face-http-server}/static/css/styles.css (100%) rename samples/apps/{local-inference-server => hugging-face-http-server}/templates/completions.html (100%) rename samples/apps/{local-inference-server => hugging-face-http-server}/templates/documentation.html (100%) rename samples/apps/{local-inference-server => hugging-face-http-server}/templates/embeddings.html (100%) rename samples/apps/{local-inference-server => hugging-face-http-server}/templates/home.html (100%) rename samples/apps/{local-inference-server => hugging-face-http-server}/templates/images.html (100%) rename samples/apps/{local-inference-server => hugging-face-http-server}/utils/CompletionGenerator.py (100%) rename samples/apps/{local-inference-server => hugging-face-http-server}/utils/EmbeddingGenerator.py (100%) rename samples/apps/{local-inference-server => hugging-face-http-server}/utils/ImageGenerator.py (100%) rename samples/apps/{local-inference-server => hugging-face-http-server}/utils/InferenceGenerator.py (100%) rename samples/apps/{local-inference-server => hugging-face-http-server}/utils/create_responses.py (100%) diff --git a/samples/apps/local-inference-server/Dockerfile b/samples/apps/hugging-face-http-server/Dockerfile similarity index 100% rename from samples/apps/local-inference-server/Dockerfile rename to samples/apps/hugging-face-http-server/Dockerfile diff --git a/samples/apps/local-inference-server/README.md b/samples/apps/hugging-face-http-server/README.md similarity index 100% rename from samples/apps/local-inference-server/README.md rename to samples/apps/hugging-face-http-server/README.md diff --git a/samples/apps/local-inference-server/inference_app.py b/samples/apps/hugging-face-http-server/inference_app.py similarity index 100% rename from samples/apps/local-inference-server/inference_app.py rename to samples/apps/hugging-face-http-server/inference_app.py diff --git a/samples/apps/local-inference-server/requirements.txt b/samples/apps/hugging-face-http-server/requirements.txt similarity index 100% rename from samples/apps/local-inference-server/requirements.txt rename to samples/apps/hugging-face-http-server/requirements.txt diff --git a/samples/apps/local-inference-server/static/css/styles.css b/samples/apps/hugging-face-http-server/static/css/styles.css similarity index 100% rename from samples/apps/local-inference-server/static/css/styles.css rename to samples/apps/hugging-face-http-server/static/css/styles.css diff --git a/samples/apps/local-inference-server/templates/completions.html b/samples/apps/hugging-face-http-server/templates/completions.html similarity index 100% rename from samples/apps/local-inference-server/templates/completions.html rename to samples/apps/hugging-face-http-server/templates/completions.html diff --git a/samples/apps/local-inference-server/templates/documentation.html b/samples/apps/hugging-face-http-server/templates/documentation.html similarity index 100% rename from samples/apps/local-inference-server/templates/documentation.html rename to samples/apps/hugging-face-http-server/templates/documentation.html diff --git a/samples/apps/local-inference-server/templates/embeddings.html b/samples/apps/hugging-face-http-server/templates/embeddings.html similarity index 100% rename from samples/apps/local-inference-server/templates/embeddings.html rename to samples/apps/hugging-face-http-server/templates/embeddings.html diff --git a/samples/apps/local-inference-server/templates/home.html b/samples/apps/hugging-face-http-server/templates/home.html similarity index 100% rename from samples/apps/local-inference-server/templates/home.html rename to samples/apps/hugging-face-http-server/templates/home.html diff --git a/samples/apps/local-inference-server/templates/images.html b/samples/apps/hugging-face-http-server/templates/images.html similarity index 100% rename from samples/apps/local-inference-server/templates/images.html rename to samples/apps/hugging-face-http-server/templates/images.html diff --git a/samples/apps/local-inference-server/utils/CompletionGenerator.py b/samples/apps/hugging-face-http-server/utils/CompletionGenerator.py similarity index 100% rename from samples/apps/local-inference-server/utils/CompletionGenerator.py rename to samples/apps/hugging-face-http-server/utils/CompletionGenerator.py diff --git a/samples/apps/local-inference-server/utils/EmbeddingGenerator.py b/samples/apps/hugging-face-http-server/utils/EmbeddingGenerator.py similarity index 100% rename from samples/apps/local-inference-server/utils/EmbeddingGenerator.py rename to samples/apps/hugging-face-http-server/utils/EmbeddingGenerator.py diff --git a/samples/apps/local-inference-server/utils/ImageGenerator.py b/samples/apps/hugging-face-http-server/utils/ImageGenerator.py similarity index 100% rename from samples/apps/local-inference-server/utils/ImageGenerator.py rename to samples/apps/hugging-face-http-server/utils/ImageGenerator.py diff --git a/samples/apps/local-inference-server/utils/InferenceGenerator.py b/samples/apps/hugging-face-http-server/utils/InferenceGenerator.py similarity index 100% rename from samples/apps/local-inference-server/utils/InferenceGenerator.py rename to samples/apps/hugging-face-http-server/utils/InferenceGenerator.py diff --git a/samples/apps/local-inference-server/utils/create_responses.py b/samples/apps/hugging-face-http-server/utils/create_responses.py similarity index 100% rename from samples/apps/local-inference-server/utils/create_responses.py rename to samples/apps/hugging-face-http-server/utils/create_responses.py