From 79e698ce0525f3d2d55e45837e9654008fbec8e0 Mon Sep 17 00:00:00 2001 From: Sunil Sattiraju Date: Fri, 5 Jan 2024 01:16:24 +0000 Subject: [PATCH] add option to call Promptflow Endpoint --- src/.env.example | 6 +- .../chat/chat-services/chat-api-entry.ts | 3 + .../chat/chat-services/chat-api-pf.ts | 77 +++++++++++++++++++ src/features/chat/chat-services/models.ts | 15 +++- .../chat-empty-state/chat-type-selector.tsx | 11 ++- src/package-lock.json | 39 +++++++++- src/package.json | 1 + src/type.ts | 2 + 8 files changed, 148 insertions(+), 6 deletions(-) create mode 100644 src/features/chat/chat-services/chat-api-pf.ts diff --git a/src/.env.example b/src/.env.example index 79adc5f87..4ae7f4022 100644 --- a/src/.env.example +++ b/src/.env.example @@ -50,4 +50,8 @@ AZURE_SPEECH_REGION= AZURE_SPEECH_KEY= # Enabled must be set to "true" any other value will disable the feature -PUBLIC_SPEECH_ENABLED=true \ No newline at end of file +PUBLIC_SPEECH_ENABLED=true + +# Promptflow API +PROMPT_FLOW_API_URL= +PROMPT_FLOW_API_KEY= diff --git a/src/features/chat/chat-services/chat-api-entry.ts b/src/features/chat/chat-services/chat-api-entry.ts index 76e97b57d..ff3ef8a8e 100644 --- a/src/features/chat/chat-services/chat-api-entry.ts +++ b/src/features/chat/chat-services/chat-api-entry.ts @@ -1,5 +1,6 @@ import { ChatAPIData } from "./chat-api-data"; import { ChatAPISimple } from "./chat-api-simple"; +import { ChatAPIPromptFlow } from "./chat-api-pf" import { PromptGPTProps } from "./models"; export const chatAPIEntry = async (props: PromptGPTProps) => { @@ -9,6 +10,8 @@ export const chatAPIEntry = async (props: PromptGPTProps) => { return await ChatAPIData(props); } else if (props.chatType === "mssql") { return await ChatAPIData(props); + } else if (props.chatType === "promptflow") { + return await ChatAPIPromptFlow(props); } else { return await ChatAPISimple(props); } diff --git a/src/features/chat/chat-services/chat-api-pf.ts b/src/features/chat/chat-services/chat-api-pf.ts new file mode 100644 index 000000000..7e312cecf --- /dev/null +++ b/src/features/chat/chat-services/chat-api-pf.ts @@ -0,0 +1,77 @@ +import { userHashedId } from "@/features/auth/helpers"; +import { StreamingTextResponse } from "ai"; +import { initAndGuardChatSession } from "./chat-thread-service"; +import { CosmosDBChatMessageHistory } from "./cosmosdb/cosmosdb"; +import { PromptGPTProps, PromptflowChatMessage } from "./models"; +import axios from "axios"; +import { ChatCompletionMessage } from "openai/resources"; + +export const ChatAPIPromptFlow = async (props: PromptGPTProps) => { + const { lastHumanMessage, chatThread } = await initAndGuardChatSession(props); + const userId = await userHashedId(); + + const chatHistory = new CosmosDBChatMessageHistory({ + sessionId: chatThread.id, + userId: userId, + }); + + const history = await chatHistory.getMessages(); + const topHistory = history.slice(history.length - 30, history.length); + + try { + const pfHistory = topHistory.reduce( + (acc: PromptflowChatMessage[], message: ChatCompletionMessage) => { + if (message.role === "user") { + acc.push({ + inputs: { + question: message.content || "", + }, + outputs: {}, + }); + } else if (message.role === "assistant" && acc.length > 0) { + acc[acc.length - 1].outputs.answer = message.content || ""; + } + return acc; + }, + [] + ); + + const response = await axios.post( + `${process.env.PROMPT_FLOW_API_URL}`, + { + question: lastHumanMessage.content, + chat_history: pfHistory, + }, + { + headers: { + Authorization: `Bearer ${process.env.PROMPT_FLOW_API_KEY}`, + }, + } + ); + + + await chatHistory.addMessage({ + content: lastHumanMessage.content, + role: "user", + }); + + await chatHistory.addMessage({ + content: response.data.answer, + role: "assistant", + }); + + return new StreamingTextResponse(response.data.answer); + } catch (e: unknown) { + if (e instanceof Error) { + return new Response(e.message, { + status: 500, + statusText: e.toString(), + }); + } else { + return new Response("An unknown error occurred.", { + status: 500, + statusText: "Unknown Error", + }); + } + } +}; diff --git a/src/features/chat/chat-services/models.ts b/src/features/chat/chat-services/models.ts index 8f9483286..3a2539498 100644 --- a/src/features/chat/chat-services/models.ts +++ b/src/features/chat/chat-services/models.ts @@ -17,7 +17,7 @@ export interface ChatMessageModel { } export type ConversationStyle = "creative" | "balanced" | "precise"; -export type ChatType = "simple" | "data" | "mssql"; +export type ChatType = "simple" | "data" | "mssql" | "promptflow"; export type ChatRole = "system" | "user" | "assistant" | "function"; @@ -60,3 +60,16 @@ export interface ServerActionResponse { error: string; response: T; } + +export interface PromptflowChatMessageMessageInput { + question: string; +} + +export interface PromptflowChatMessageMessageOutput { + answer?: string; +} + +export interface PromptflowChatMessage { + inputs: PromptflowChatMessageMessageInput; + outputs: PromptflowChatMessageMessageOutput; +} diff --git a/src/features/chat/chat-ui/chat-empty-state/chat-type-selector.tsx b/src/features/chat/chat-ui/chat-empty-state/chat-type-selector.tsx index 66f047e6c..7bc696ff4 100644 --- a/src/features/chat/chat-ui/chat-empty-state/chat-type-selector.tsx +++ b/src/features/chat/chat-ui/chat-empty-state/chat-type-selector.tsx @@ -1,5 +1,5 @@ import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { FileText, MessageCircle } from "lucide-react"; +import { FileText, MessageCircle, Globe } from "lucide-react"; import { FC } from "react"; import { ChatType } from "../../chat-services/models"; import { useChatContext } from "../chat-context"; @@ -16,7 +16,7 @@ export const ChatTypeSelector: FC = (props) => { defaultValue={chatBody.chatType} onValueChange={(value) => onChatTypeChange(value as ChatType)} > - + = (props) => { > File + + PF API + ); diff --git a/src/package-lock.json b/src/package-lock.json index bdfa7692b..5c16122e1 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "azure-open-ai-accelerator", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "azure-open-ai-accelerator", - "version": "1.1.0", + "version": "1.2.0", "dependencies": { "@azure/ai-form-recognizer": "^5.0.0", "@azure/cosmos": "^4.0.0", @@ -25,6 +25,7 @@ "@types/react-dom": "^18.2.14", "ai": "^2.2.20", "autoprefixer": "^10.4.16", + "axios": "^1.6.4", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "eslint": "^8.52.0", @@ -2144,6 +2145,16 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.4.tgz", + "integrity": "sha512-heJnIs6N4aa1eSthhN9M5ioILu8Wi8vmQW9iHQ9NUvfkJb0lEEDUiIdQNAuBtfUt3FxReaKdpQA5DbmMOqzF/A==", + "dependencies": { + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -3346,6 +3357,25 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==" }, + "node_modules/follow-redirects": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -5368,6 +5398,11 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", diff --git a/src/package.json b/src/package.json index 87ae29a86..f90b52b44 100644 --- a/src/package.json +++ b/src/package.json @@ -26,6 +26,7 @@ "@types/react-dom": "^18.2.14", "ai": "^2.2.20", "autoprefixer": "^10.4.16", + "axios": "^1.6.4", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "eslint": "^8.52.0", diff --git a/src/type.ts b/src/type.ts index 91cae9c36..0f243739b 100644 --- a/src/type.ts +++ b/src/type.ts @@ -25,6 +25,8 @@ const azureEnvVars = [ "AZURE_SPEECH_REGION", "AZURE_SPEECH_KEY", "PUBLIC_PUBLIC_SPEECH_ENABLED", + "PROMPT_FLOW_API_URL", + "PROMPT_FLOW_API_KEY", ] as const; type RequiredServerEnvKeys = (typeof azureEnvVars)[number];