Skip to content

Commit

Permalink
add lang chain as AI layer
Browse files Browse the repository at this point in the history
  • Loading branch information
thivy committed Jul 24, 2023
1 parent 7bd9789 commit 88f5d85
Show file tree
Hide file tree
Showing 26 changed files with 1,877 additions and 660 deletions.
7 changes: 6 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ NEXTAUTH_SECRET=
NEXTAUTH_URL=

AZURE_COSMOSEDB_URI=
AZURE_COSMOSEDB_KEY=
AZURE_COSMOSEDB_KEY=

AZURE_SEARCH_API_KEY=
AZURE_SEARCH_NAME=
AZURE_SEARCH_INDEX_NAME=
AZURE_SEARCH_API_VERSION=
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ AzureChatGPT is built with the following technologies.

# 💙 One click Azure deployment

[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fmicrosoft%2Fazurechatgpt%2Fmain%2Finfra%2Fmain.json)
[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fthivy%2Fazure-chatgpt%2Fmain%2Finfra%2Fmain.json)

Click on the Deploy to Azure button and configure your settings in the Azure Portal as described in the [Environment variables](#🔑-environment-variables) section.

Expand Down
12 changes: 0 additions & 12 deletions app/api/chat/[id]/route.ts

This file was deleted.

6 changes: 6 additions & 0 deletions app/api/chat/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { PromptGPT } from "@/features/chat/chat-api";

export async function POST(req: Request) {
const body = await req.json();
return await PromptGPT(body);
}
2 changes: 0 additions & 2 deletions app/chat/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { FindChatThreadByID } from "@/features/chat/chat-thread-service";
import { ChatUI } from "@/features/chat/chat-ui";
import { notFound } from "next/navigation";

export const revalidate = 0;

export default async function Home({ params }: { params: { id: string } }) {
const items = await FindAllChats(params.id);
const thread = await FindChatThreadByID(params.id);
Expand Down
2 changes: 0 additions & 2 deletions app/chat/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { ProtectedPage } from "@/features/auth/protected-page";
import { ChatMenu } from "@/features/chat/chat-menu/chat-menu";
import { MainMenu } from "@/features/menu/menu";

export const revalidate = 0;

export const metadata = {
title: "AzureChatGPT",
description: "AzureChatGPT",
Expand Down
2 changes: 0 additions & 2 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { Card } from "@/components/ui/card";
import { userSession } from "@/features/auth/helpers";
import { redirect } from "next/navigation";

export const revalidate = 0;

export default async function Home() {
const user = await userSession();
if (user) {
Expand Down
2 changes: 0 additions & 2 deletions app/reporting/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { ProtectedPage } from "@/features/auth/protected-page";
import { MainMenu } from "@/features/menu/menu";

export const revalidate = 0;

export const metadata = {
title: "AzureChatGPT",
description: "AzureChatGPT",
Expand Down
3 changes: 2 additions & 1 deletion components/chat/chat-row.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { chatRole } from "@/features/chat/chat-service";
import { cn } from "@/lib/utils";
import { FC } from "react";
import remarkGfm from "remark-gfm";
Expand All @@ -10,7 +11,7 @@ interface ChatRowProps {
name: string;
profilePicture: string;
message: string;
type: "user" | "assistant" | "system";
type: chatRole;
}

const ChatRow: FC<ChatRowProps> = (props) => {
Expand Down
4 changes: 2 additions & 2 deletions components/hooks/use-chat-scroll-anchor.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"use client";

import { ChatMessageOutputModel } from "@/features/chat/chat-service";
import { Message } from "ai";
import { RefObject, useEffect } from "react";

export const useChatScrollAnchor = (
chats: ChatMessageOutputModel[],
chats: Message[],
ref: RefObject<HTMLDivElement>
) => {
useEffect(() => {
Expand Down
196 changes: 196 additions & 0 deletions features/azure-cog-search/azure-cog-vector-store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import { Callbacks } from "langchain/callbacks";
import { Document } from "langchain/document";
import { Embeddings } from "langchain/embeddings/base";
import { VectorStore } from "langchain/vectorstores/base";
import { nanoid } from "nanoid";

// example index model below
// export interface AzureCogDocumentIndex extends Record<string, unknown> {
// id: string;
// content: string;
// user: string;
// embedding?: number[];
// pageContent: string;
// metadata: any;
// }

interface AzureSearchConfig {
name: string;
indexName: string;
apiKey: string;
apiVersion: string;
vectorFieldName: string;
}

interface DocumentSearchResponseModel<TModel> {
value: TModel[];
}

type DocumentSearchModel = {
"@search.score": number;
};

export interface AzureCogDocument extends Record<string, unknown> {}

type AzureCogVectorField = {
value: number[];
fields: string;
k: number;
};

type AzureCogFilter = {
search?: string;
facets?: string[];
filter?: string;
top?: number;
vectorFields: string;
};

type AzureCogRequestObject = {
search: string;
facets: string[];
filter: string;
vectors: AzureCogVectorField[];
top: number;
};

export class AzureCogSearch<
TModel extends Record<string, unknown>
> extends VectorStore {
private _config: AzureSearchConfig;

constructor(embeddings: Embeddings, dbConfig: AzureSearchConfig) {
super(embeddings, dbConfig);
this._config = dbConfig;
}

_vectorstoreType(): string {
return "azure-cog-search";
}

get config(): AzureSearchConfig {
return this._config;
}

get baseUrl(): string {
return `https://${this._config.name}.search.windows.net/indexes/${this._config.indexName}/docs`;
}

async addDocuments(documents: Document<TModel>[]): Promise<string[]> {
const texts = documents.map(({ pageContent }) => pageContent);
return this.addVectors(
await this.embeddings.embedDocuments(texts),
documents
);
}

/**
* Search for the most similar documents to a query
*/
async similaritySearch(
query: string,
k?: number,
filter?: AzureCogFilter
): Promise<Document<TModel>[]> {
const results = await this.similaritySearchVectorWithScore(
await this.embeddings.embedQuery(query),
k || 4,
filter
);

return results.map(([doc, _score]) => doc);
}

/**
* Search for the most similar documents to a query,
* and return their similarity score
*/
async similaritySearchWithScore(
query: string,
k?: number,
filter?: AzureCogFilter,
_callbacks: Callbacks | undefined = undefined
): Promise<[Document, number][]> {
const embeddings = await this.embeddings.embedQuery(query);
return this.similaritySearchVectorWithScore(embeddings, k || 5, filter);
}

/**
* Advanced: Add more documents to an existing VectorStore,
* when you already have their embeddings
*/
async addVectors(
vectors: number[][],
documents: Document<TModel>[]
): Promise<string[]> {
const indexes: Array<any> = [];

documents.forEach((document, i) => {
indexes.push({
id: nanoid(),
content: document.pageContent,
...document,
[this._config.vectorFieldName]: vectors[i],
});
});

const documentIndexRequest: DocumentSearchResponseModel<TModel> = {
value: indexes,
};

const url = `${this.baseUrl}/index?api-version=${this._config.apiVersion}`;
const responseObj = await fetcher(
url,
documentIndexRequest,
this._config.apiKey
);
return responseObj.value.map((doc: any) => doc.key);
}

/**
* Advanced: Search for the most similar documents to a query,
* when you already have the embedding of the query
*/
async similaritySearchVectorWithScore(
query: number[],
k: number,
filter?: AzureCogFilter
): Promise<[Document<TModel>, number][]> {
const url = `${this.baseUrl}/search?api-version=${this._config.apiVersion}`;

const searchBody: AzureCogRequestObject = {
search: filter?.search || "*",
facets: filter?.facets || [],
filter: filter?.filter || "",
vectors: [{ value: query, fields: filter?.vectorFields || "", k: k }],
top: filter?.top || k,
};

const resultDocuments = (await fetcher(
url,
searchBody,
this._config.apiKey
)) as DocumentSearchResponseModel<Document<TModel> & DocumentSearchModel>;

return resultDocuments.value.map((doc) => [doc, doc["@search.score"] || 0]);
}
}

const fetcher = async (url: string, body: any, apiKey: string) => {
const response = await fetch(url, {
method: "POST",
body: JSON.stringify(body),
headers: {
"Content-Type": "application/json",
"api-key": apiKey,
},
});

if (!response.ok) {
const err = await response.json();
console.log(err);
throw new Error(err);
}

return await response.json();
};
Loading

0 comments on commit 88f5d85

Please sign in to comment.