avatarmatteo

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

4626

Abstract

s-comment">//dataSources: NOT POSSIBLE TO QUERY COGNITIVE SEARCH</span> }); <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">"createChatCompletion response: "</span> + completion.<span class="hljs-property">data</span>.<span class="hljs-property">choices</span>[<span class="hljs-number">0</span>].<span class="hljs-property">message</span>.<span class="hljs-property">content</span>); } <span class="hljs-keyword">catch</span> (error) { ... }</pre></div><p id="9a0f">This example uses the <code>ChatCompletions</code>endpoint of the Azure OpenAI deployment and works without any hassle. The issue occurs when we want to query our own data from Cognitive search. To do so, we need to call a different endpoint, <code>/extensions</code> .</p><p id="2818">Unfortunately, as of August 2023, <b>it is not possible to query data from Cognitive Search using this library. </b>For this reason, a different approach is necessary.</p><h1 id="fee3">Solution: Using the Azure OpenAI ReST endpoint</h1><p id="b009">Fortunately, using the official library is not necessary. Microsoft also enables developers to access their OpenAI instances using a ReST API. Find out more on this <a href="https://learn.microsoft.com/en-us/azure/ai-services/openai/reference">link</a>.</p><p id="45c5">To set our Cognitive Search as a data source parameter, we have to use the <code>extensions</code> endpoint.</p><div id="51d1"><pre>POST {your-resource-name}/openai/deployments/{deployment-id}/extensions/chat/completions?api-version={api-version}</pre></div><p id="289f">Find the specs <a href="https://github.com/Azure/azure-rest-api-specs/blob/main/specification/cognitiveservices/data-plane/AzureOpenAI/inference/preview/2023-06-01-preview/inference.json">here</a>. When calling this endpoint, we must send the user prompt (message) with it. However, it is recommended to not only send one message but a history of messages, to make GPT aware of the context of the message.</p><p id="01c5">A message in our example is of type <code>ChatMessage</code> and consist of the <code>message</code>, the prompt, and a <code>role</code> , which basically tells GTP who the creator of the message is. This could be the <code>assistent</code> , meaning GPT itself, the <code>system</code> or <code>user</code> . Find out more <a href="https://help.openai.com/en/articles/7042661-chatgpt-api-transition-guide">here</a>.</p><p id="d1f4">Our first task involves creating a function to access our OpenAI deployment, by using the straightforward <code>fetch</code> method.</p><div id="16aa"><pre>export type ChatMessage = { role: <span class="hljs-keyword">string</span>; content: <span class="hljs-keyword">string</span>; };

<span class="hljs-comment">/*

  • Calls the Azure OpenAI and Cognitive Search resource and returns the completion
  • Params: msg ... Array of previous messages for the context, chat history */</span> async <span class="hljs-title function_ invoke__">fetchCompletions</span>(<span class="hljs-attr">msg</span>: ChatMessage[]): Promise<Response> { <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">fetch</span>(<span class="hljs-string">'https://{AZURE_OPENAI_ENDPOINT/openai/deployments/{YOUR_AZURE_OPENAI_DEPLOYMENT}/extensions/chat/completions?api-version=2023-06-01-preview'</span>, { <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>, <span class="hljs-attr">headers</span>: { <span class="hljs-string">'content-type'</span>: <span class="hljs-string">'application/json'</span>, <span class="hljs-string">'api-key'</span>: <span class="hljs-string">'{YOUR_AZURE_OPENAI_KEY}'</span> }, <span class="hljs-attr">body</span>: JSON.<span class="hljs-title function_ invoke__">stringify</span>({ <span class="hljs-attr">messages</span>: msg, <span class="hljs-attr">dataSources</span>: [ { <span class="hljs-attr">type</span>: <span class="hljs-string">"AzureCognitiveSearch"</span>, <span class="hljs-attr">max_tokens</span>: <span class="hljs-number">1000</span> // optional <span class="hljs-attr">temperature</span>: <span class="hljs-string">"0.7"</span>, // optional <span class="hljs-attr">top_p</span>: <span class="hljs-number">0.95</span>, // optional <span class="hljs-attr">parameters</span>: { <span class="hljs-attr">endpoint</span>: <span class="hljs-string">"{YOUR_COGNITIVE_SEARCH_ENDPOINT}"</span>, <span class="hljs-attr">key</

Options

span>: <span class="hljs-string">"{YOUR_COGNITIVE_SEARCH_KEY}"</span>, <span class="hljs-attr">indexName</span>: <span class="hljs-string">"{YOUR_COGNITIVE_SEARCH_INDEX}"</span>, }, <span class="hljs-attr">topNDocuments</span>: <span class="hljs-string">"5"</span> // optional, set amount of search results } ] // <span class="hljs-attr">Optional</span>: only available with gpt model versions >= <span class="hljs-number">0613</span>. <span class="hljs-attr">function_call</span>: <span class="hljs-string">"auto"</span>, <span class="hljs-attr">functions</span>: [ ... ] }), }) } </pre></div><p id="abb4">The keys can be found on Azure and/or in OpenAI Studio. Next, we have to use our written function and then process the call.</p><div id="2a71"><pre><span class="hljs-keyword">const</span> messages = [ ... ]; <span class="hljs-keyword">const</span> <span class="hljs-attr">response</span>: <span class="hljs-title class_">Response</span> = <span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">fetchCompletions</span>(messages); <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.<span class="hljs-title function_">text</span>(); <span class="hljs-keyword">const</span> jsonData = <span class="hljs-keyword">await</span> <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">parse</span>(data);
<span class="hljs-keyword">if</span> (response.<span class="hljs-property">ok</span>) { <span class="hljs-keyword">const</span> msg = jsonData.<span class="hljs-property">choices</span>[<span class="hljs-number">0</span>]?.<span class="hljs-property">messages</span>[<span class="hljs-number">1</span>]?.<span class="hljs-property">content</span>; <span class="hljs-comment">// messages[1] when using extensions endpoint, otherwise [0]</span> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(msg) }</pre></div><p id="bac0">Note that our endpoint returns a stream and not a simple JSON object. We can’t simply parse the response using <code>response.json()</code> but have to use the <code>text()</code> method before parsing it to a JSON.</p><p id="cf10">As of now, the code is working and you’ll see results containing your internal data on Cognitive Search transformed into a readable text by OpenAI. However, the response might include references to documents and contain strings in the following format: <code>doc[NUMBER]</code> .</p><p id="9512">Instead of these references, we can put the link into the answer. To do so, use the following function.</p><div id="4d11"><pre><span class="hljs-keyword">if</span> (response.<span class="hljs-property">ok</span>) { ... msg = <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">replaceDocNumber</span>(msg, <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">parse</span>(jsonData.<span class="hljs-property">choices</span>[<span class="hljs-number">0</span>]?.<span class="hljs-property">messages</span>[<span class="hljs-number">0</span>]?.<span class="hljs-property">content</span>)); ... }

<span class="hljs-title function_">replaceDocNumber</span>(<span class="hljs-params">msg: <span class="hljs-built_in">string</span>, citations: <span class="hljs-built_in">any</span></span>) { <span class="hljs-keyword">const</span> replacedString = msg.<span class="hljs-title function_">replace</span>(<span class="hljs-regexp">/[doc\d+]/g</span>, <span class="hljs-function">(<span class="hljs-params">match, index</span>) =></span> { <span class="hljs-comment">// "match" is the entire matched string</span> <span class="hljs-comment">// find [doc{NUMBER}] in citations and replace with doc urls</span> <span class="hljs-keyword">const</span> num = <span class="hljs-built_in">parseInt</span>(match.<span class="hljs-title function_">match</span>(<span class="hljs-regexp">/\d+/</span>)[<span class="hljs-number">0</span>]); <span class="hljs-keyword">if</span> (num > <span class="hljs-number">0</span>) { <span class="hljs-keyword">return</span> citations.<span class="hljs-property">citations</span>[num-<span class="hljs-number">1</span>].<span class="hljs-property">url</span>; } }); <span class="hljs-keyword">return</span> replacedString; }</pre></div><p id="7112">Voilà, that’s it! Let me know in the comments if you found this tutorial helpful.</p></article></body>

How To Use Azure OpenAI With Cognitive Search in TypeScript

A workaround using Fetch

Original Image

Disclaimer: This is not an introduction to Azure OpenAI & Cognitive Search

TL;DR

Are you looking to build your own version of ChatGPT? Do you want a tool that’s able to hold a conversation with your customers? Well, you’re in luck.

Microsoft has partnered with OpenAI to allow developers to use their GPT models in Azure with OpenAI Studio. Using Cognitive Search, businesses are able to connect the GPT-35-Turbo and GPT-4 models to their own data, and many are now considering using it as a chatbot for internal data.

In this blog, I’ll show you can utilize your Azure OpenAI deployment and Cognitive Search together with TypeScript, allowing you to combine the natural language skills of OpenAI with the search capabilities of Cognitive Search.

Prerequisites

Before coding it up in TypeScript, you need to make sure you’ve got a couple of things set up:

  • An Azure account with an active subscription. Create an account for free.
  • An Azure OpenAI (AOAI) service and model deployment. To create a deployment, simply use OpenAI Studio. Azure OpenAI requires registration and is currently only available to approved enterprise customers. You are required to submit a registration form
  • An Azure Cognitive Search service (running at least on plan ‘basic’, which costs €0,10/hour.) and an index filled with some data. Find a well-written guide on inserting data here.

The Azure OpenAI client library

Microsoft released an official npm package for developers to use Azure OpenAI in Typescript. It allows easy access to your OpenAI instance and has endpoints for chat completions, completions, and embeddings.

Implementation is very simple. Create an OpenAiApi object reference your Azure resources, before you can use it to create the completion.

import { 
  Configuration, 
  OpenAIApi, 
  ChatCompletionRequestMessageRoleEnum
} from "azure-openai";

this.openAiApi = new OpenAIApi(
  new Configuration({
     apiKey: "{YOUR_AZURE_OPENAI_KEY}",
     // add azure info into configuration
     azure: {
        apiKey: "{YOUR_OPENAI_AZURE_KEY}",
        endpoint: "{YOUR_AZURE_OPENAI_ENDPOINT}",
        deploymentName: "{YOUR_OPENAI_MODEL_NAME}" // created in AOAI studio
     }
  }),
);

try {
  const completion = await this.openAiApi.createChatCompletion({
    model: process.env.AOAI_MODEL,
    messages: this.chatHistory[sender],
    temperature: 0.7,
    max_tokens: 1000,
    top_p: 0.7,
    //dataSources: NOT POSSIBLE TO QUERY COGNITIVE SEARCH
  });
  console.log("createChatCompletion response: " + completion.data.choices[0].message.content);
  } catch (error) {
    ...
  }

This example uses the ChatCompletionsendpoint of the Azure OpenAI deployment and works without any hassle. The issue occurs when we want to query our own data from Cognitive search. To do so, we need to call a different endpoint, /extensions .

Unfortunately, as of August 2023, it is not possible to query data from Cognitive Search using this library. For this reason, a different approach is necessary.

Solution: Using the Azure OpenAI ReST endpoint

Fortunately, using the official library is not necessary. Microsoft also enables developers to access their OpenAI instances using a ReST API. Find out more on this link.

To set our Cognitive Search as a data source parameter, we have to use the extensions endpoint.

POST {your-resource-name}/openai/deployments/{deployment-id}/extensions/chat/completions?api-version={api-version}

Find the specs here. When calling this endpoint, we must send the user prompt (message) with it. However, it is recommended to not only send one message but a history of messages, to make GPT aware of the context of the message.

A message in our example is of type ChatMessage and consist of the message, the prompt, and a role , which basically tells GTP who the creator of the message is. This could be the assistent , meaning GPT itself, the system or user . Find out more here.

Our first task involves creating a function to access our OpenAI deployment, by using the straightforward fetch method.

export type ChatMessage = {
  role: string;
  content: string;
};

/*
* Calls the Azure OpenAI and Cognitive Search resource and returns the completion 
* Params: msg ... Array of previous messages for the context, chat history 
*/
async fetchCompletions(msg: ChatMessage[]): Promise<Response> {
  return fetch('https://{AZURE_OPENAI_ENDPOINT/openai/deployments/{YOUR_AZURE_OPENAI_DEPLOYMENT}/extensions/chat/completions?api-version=2023-06-01-preview', {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      'api-key': '{YOUR_AZURE_OPENAI_KEY}'
    },
    body: JSON.stringify({
      messages: msg,
       dataSources: [
        {
          type: "AzureCognitiveSearch",
          max_tokens: 1000   // optional
          temperature: "0.7",   // optional
          top_p: 0.95,   // optional
          parameters: {
              endpoint: "{YOUR_COGNITIVE_SEARCH_ENDPOINT}",
              key: "{YOUR_COGNITIVE_SEARCH_KEY}",
              indexName: "{YOUR_COGNITIVE_SEARCH_INDEX}",
          },
          topNDocuments: "5"  // optional, set amount of search results
        }
      ] 
      // Optional: only available with gpt model versions >= 0613. 
      function_call: "auto",
      functions:  [ ... ]
    }),
  })
}

The keys can be found on Azure and/or in OpenAI Studio. Next, we have to use our written function and then process the call.

const messages = [ ... ];
const response: Response = await this.fetchCompletions(messages);
const data = await response.text();
const jsonData = await JSON.parse(data);  
if (response.ok) {
  const msg = jsonData.choices[0]?.messages[1]?.content; // messages[1] when using extensions endpoint, otherwise [0]
  console.log(msg)
}

Note that our endpoint returns a stream and not a simple JSON object. We can’t simply parse the response using response.json() but have to use the text() method before parsing it to a JSON.

As of now, the code is working and you’ll see results containing your internal data on Cognitive Search transformed into a readable text by OpenAI. However, the response might include references to documents and contain strings in the following format: doc[NUMBER] .

Instead of these references, we can put the link into the answer. To do so, use the following function.

if (response.ok) {
  ...
  msg = this.replaceDocNumber(msg, JSON.parse(jsonData.choices[0]?.messages[0]?.content));
  ...
}

replaceDocNumber(msg: string, citations: any) {
    const replacedString = msg.replace(/\[doc\d+\]/g, (match, index) => {
      // "match" is the entire matched string
      // find [doc{NUMBER}] in citations and replace with doc urls
      const num = parseInt(match.match(/\d+/)[0]);
      if (num > 0) {
        return citations.citations[num-1].url;
      }
    });
    return replacedString;
  }

Voilà, that’s it! Let me know in the comments if you found this tutorial helpful.

Azureopenai
OpenAI
Cognitive Search
Typescript
ChatGPT
Recommended from ReadMedium