Skip to content

Commit

Permalink
store chat metadata and chat feedback in db
Browse files Browse the repository at this point in the history
  • Loading branch information
AyushAgrawal-A2 committed Jan 10, 2025
1 parent 6ec90ff commit 0f7334b
Show file tree
Hide file tree
Showing 34 changed files with 312 additions and 154 deletions.
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"codegen",
"containerd",
"CRPXNLSKVLJFHH",
"Cyphertext",
"dashmap",
"dbgjs",
"dcell",
Expand Down Expand Up @@ -79,6 +80,7 @@
"selfhost",
"selfhosted",
"selfservice",
"sendgrid",
"serde",
"shadcn",
"Signin",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
-- CreateEnum
CREATE TYPE "AIChatSource" AS ENUM ('ai_assistant', 'ai_analyst', 'ai_researcher', 'get_chat_name', 'get_file_name');

-- CreateTable
CREATE TABLE "AnalyticsAIChat" (
"id" SERIAL NOT NULL,
"user_id" INTEGER NOT NULL,
"file_id" INTEGER NOT NULL,
"chat_id" TEXT NOT NULL,
"source" "AIChatSource" NOT NULL,
"s3_key" TEXT NOT NULL,
"created_date" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_date" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "AnalyticsAIChat_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "AnalyticsAIChatMessage" (
"id" SERIAL NOT NULL,
"chat_id" INTEGER NOT NULL,
"model" TEXT NOT NULL,
"message_index" INTEGER NOT NULL,
"like" BOOLEAN,
"undo" BOOLEAN,
"code_run_error" TEXT,
"response_error" TEXT,
"created_date" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_date" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "AnalyticsAIChatMessage_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "AnalyticsAIChat_chat_id_key" ON "AnalyticsAIChat"("chat_id");

-- CreateIndex
CREATE INDEX "AnalyticsAIChat_chat_id_idx" ON "AnalyticsAIChat"("chat_id");

-- CreateIndex
CREATE INDEX "AnalyticsAIChat_user_id_idx" ON "AnalyticsAIChat"("user_id");

-- CreateIndex
CREATE INDEX "AnalyticsAIChat_file_id_idx" ON "AnalyticsAIChat"("file_id");

-- CreateIndex
CREATE INDEX "AnalyticsAIChat_source_idx" ON "AnalyticsAIChat"("source");

-- CreateIndex
CREATE INDEX "AnalyticsAIChatMessage_chat_id_message_index_idx" ON "AnalyticsAIChatMessage"("chat_id", "message_index");

-- CreateIndex
CREATE INDEX "AnalyticsAIChatMessage_chat_id_idx" ON "AnalyticsAIChatMessage"("chat_id");

-- CreateIndex
CREATE INDEX "AnalyticsAIChatMessage_model_idx" ON "AnalyticsAIChatMessage"("model");

-- CreateIndex
CREATE UNIQUE INDEX "AnalyticsAIChatMessage_chat_id_message_index_key" ON "AnalyticsAIChatMessage"("chat_id", "message_index");

-- AddForeignKey
ALTER TABLE "AnalyticsAIChat" ADD CONSTRAINT "AnalyticsAIChat_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "AnalyticsAIChat" ADD CONSTRAINT "AnalyticsAIChat_file_id_fkey" FOREIGN KEY ("file_id") REFERENCES "File"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "AnalyticsAIChatMessage" ADD CONSTRAINT "AnalyticsAIChatMessage_chat_id_fkey" FOREIGN KEY ("chat_id") REFERENCES "AnalyticsAIChat"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
68 changes: 58 additions & 10 deletions quadratic-api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ datasource db {
}

model User {
id Int @id @default(autoincrement())
auth0Id String @unique @map("auth0_id")
eduStatus EduStatus? @map("edu_status")
ownedFiles File[] @relation(name: "OwnerRelation")
createdFiles File[] @relation(name: "CreatorRelation")
QFeedback QFeedback[]
UserTeamRole UserTeamRole[]
UserFileRole UserFileRole[]
id Int @id @default(autoincrement())
auth0Id String @unique @map("auth0_id")
eduStatus EduStatus? @map("edu_status")
ownedFiles File[] @relation(name: "OwnerRelation")
createdFiles File[] @relation(name: "CreatorRelation")
QFeedback QFeedback[]
UserTeamRole UserTeamRole[]
UserFileRole UserFileRole[]
AnalyticsAIChat AnalyticsAIChat[]
@@index([auth0Id])
}
Expand Down Expand Up @@ -62,12 +63,13 @@ model File {
ownerTeam Team @relation(fields: [ownerTeamId], references: [id])
ownerTeamId Int @map("owner_team_id")
publicLinkAccess LinkPermission @default(NOT_SHARED) @map("public_link_access")
times_updated Int @default(1) // unused to delete
publicLinkAccess LinkPermission @default(NOT_SHARED) @map("public_link_access")
times_updated Int @default(1) // unused to delete
version String? // unused to delete
FileCheckpoint FileCheckpoint[]
UserFileRole UserFileRole[]
FileInvite FileInvite[]
AnalyticsAIChat AnalyticsAIChat[]
@@index([uuid])
}
Expand Down Expand Up @@ -256,3 +258,49 @@ model QFile {
times_updated Int @default(1)
version String?
}

model AnalyticsAIChat {
id Int @id @default(autoincrement())
userId Int @map("user_id")
user User @relation(fields: [userId], references: [id])
fileId Int @map("file_id")
file File @relation(fields: [fileId], references: [id])
chatId String @unique @map("chat_id")
source AIChatSource
s3Key String @map("s3_key")
messages AnalyticsAIChatMessage[]
createdDate DateTime @default(now()) @map("created_date")
updatedDate DateTime @default(now()) @map("updated_date")
@@index([chatId])
@@index([userId])
@@index([fileId])
@@index([source])
}

enum AIChatSource {
AIAssistant @map("ai_assistant")
AIAnalyst @map("ai_analyst")
AIResearcher @map("ai_researcher")
GetChatName @map("get_chat_name")
GetFileName @map("get_file_name")
}

model AnalyticsAIChatMessage {
id Int @id @default(autoincrement())
chatId Int @map("chat_id")
chat AnalyticsAIChat @relation(fields: [chatId], references: [id])
model String
messageIndex Int @map("message_index")
like Boolean?
undo Boolean?
codeRunError String? @map("code_run_error")
responseError String? @map("response_error")
createdDate DateTime @default(now()) @map("created_date")
updatedDate DateTime @default(now()) @map("updated_date")
@@unique([chatId, messageIndex], name: "chatId_messageIndex")
@@index([chatId, messageIndex])
@@index([chatId])
@@index([model])
}
12 changes: 4 additions & 8 deletions quadratic-api/src/ai/handler/anthropic.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
import Anthropic from '@anthropic-ai/sdk';
import type { Response } from 'express';
import {
getAnthropicApiArgs,
parseAnthropicResponse,
parseAnthropicStream,
} from 'quadratic-api/src/ai/helpers/anthropic.helper';
import { ANTHROPIC_API_KEY } from 'quadratic-api/src/env-vars';
import { getModelOptions } from 'quadratic-shared/ai/helpers/model.helper';
import type { AIMessagePrompt, AIRequestBody, AnthropicModel } from 'quadratic-shared/typesAndSchemasAI';
import type { AIMessagePrompt, AIRequestHelperArgs, AnthropicModel } from 'quadratic-shared/typesAndSchemasAI';
import { ANTHROPIC_API_KEY } from '../../env-vars';
import { getAnthropicApiArgs, parseAnthropicResponse, parseAnthropicStream } from '../helpers/anthropic.helper';

const anthropic = new Anthropic({
apiKey: ANTHROPIC_API_KEY,
});

export const handleAnthropicRequest = async (
model: AnthropicModel,
args: Omit<AIRequestBody, 'model'>,
args: AIRequestHelperArgs,
response: Response
): Promise<AIMessagePrompt | undefined> => {
const { system, messages, tools, tool_choice } = getAnthropicApiArgs(args);
Expand Down
16 changes: 4 additions & 12 deletions quadratic-api/src/ai/handler/bedrock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,11 @@ import { AnthropicBedrock } from '@anthropic-ai/bedrock-sdk';
import Anthropic from '@anthropic-ai/sdk';
import { BedrockRuntimeClient, ConverseCommand, ConverseStreamCommand } from '@aws-sdk/client-bedrock-runtime';
import { type Response } from 'express';
import {
getAnthropicApiArgs,
parseAnthropicResponse,
parseAnthropicStream,
} from 'quadratic-api/src/ai/helpers/anthropic.helper';
import {
getBedrockApiArgs,
parseBedrockResponse,
parseBedrockStream,
} from 'quadratic-api/src/ai/helpers/bedrock.helper';
import { AWS_S3_ACCESS_KEY_ID, AWS_S3_REGION, AWS_S3_SECRET_ACCESS_KEY } from 'quadratic-api/src/env-vars';
import { getModelOptions, isBedrockAnthropicModel } from 'quadratic-shared/ai/helpers/model.helper';
import type { AIMessagePrompt, AIRequestBody, BedrockModel } from 'quadratic-shared/typesAndSchemasAI';
import { AWS_S3_ACCESS_KEY_ID, AWS_S3_REGION, AWS_S3_SECRET_ACCESS_KEY } from '../../env-vars';
import { getAnthropicApiArgs, parseAnthropicResponse, parseAnthropicStream } from '../helpers/anthropic.helper';
import { getBedrockApiArgs, parseBedrockResponse, parseBedrockStream } from '../helpers/bedrock.helper';

// aws-sdk for bedrock, generic for all models
const bedrock = new BedrockRuntimeClient({
Expand All @@ -31,7 +23,7 @@ const bedrock_anthropic = new AnthropicBedrock({

export const handleBedrockRequest = async (
model: BedrockModel,
args: Omit<AIRequestBody, 'model'>,
args: Omit<AIRequestBody, 'chatId' | 'fileUuid' | 'source' | 'model'>,
response: Response
): Promise<AIMessagePrompt | undefined> => {
const { stream, temperature, max_tokens } = getModelOptions(model, args);
Expand Down
8 changes: 4 additions & 4 deletions quadratic-api/src/ai/handler/openai.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { type Response } from 'express';
import OpenAI from 'openai';
import { getOpenAIApiArgs, parseOpenAIResponse, parseOpenAIStream } from 'quadratic-api/src/ai/helpers/openai.helper';
import { OPENAI_API_KEY } from 'quadratic-api/src/env-vars';
import { getModelOptions } from 'quadratic-shared/ai/helpers/model.helper';
import type { AIMessagePrompt, AIRequestBody, OpenAIModel } from 'quadratic-shared/typesAndSchemasAI';
import type { AIMessagePrompt, AIRequestHelperArgs, OpenAIModel } from 'quadratic-shared/typesAndSchemasAI';
import { OPENAI_API_KEY } from '../../env-vars';
import { getOpenAIApiArgs, parseOpenAIResponse, parseOpenAIStream } from '../helpers/openai.helper';

const openai = new OpenAI({
apiKey: OPENAI_API_KEY || '',
});

export const handleOpenAIRequest = async (
model: OpenAIModel,
args: Omit<AIRequestBody, 'model'>,
args: AIRequestHelperArgs,
response: Response
): Promise<AIMessagePrompt | undefined> => {
const { messages, tools, tool_choice } = getOpenAIApiArgs(args);
Expand Down
4 changes: 3 additions & 1 deletion quadratic-api/src/ai/helpers/anthropic.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import type {
BedrockAnthropicModel,
} from 'quadratic-shared/typesAndSchemasAI';

export function getAnthropicApiArgs(args: Omit<AIRequestBody, 'model'>): Omit<AnthropicRequestBody, 'model'> {
export function getAnthropicApiArgs(
args: Omit<AIRequestBody, 'chatId' | 'fileUuid' | 'source' | 'model'>
): Omit<AnthropicRequestBody, 'model'> {
const { messages: chatMessages, useTools, toolName } = args;

const { systemMessages, promptMessages } = getSystemPromptMessages(chatMessages);
Expand Down
4 changes: 2 additions & 2 deletions quadratic-api/src/ai/helpers/bedrock.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import type { AITool } from 'quadratic-shared/ai/specs/aiToolsSpec';
import { aiToolsSpec } from 'quadratic-shared/ai/specs/aiToolsSpec';
import type {
AIMessagePrompt,
AIRequestBody,
AIRequestHelperArgs,
BedrockModel,
BedrockPromptMessage,
BedrockRequestBody,
BedrockTool,
BedrockToolChoice,
} from 'quadratic-shared/typesAndSchemasAI';

export function getBedrockApiArgs(args: Omit<AIRequestBody, 'model'>): Omit<BedrockRequestBody, 'model'> {
export function getBedrockApiArgs(args: AIRequestHelperArgs): Omit<BedrockRequestBody, 'model'> {
const { messages: chatMessages, useTools, toolName } = args;

const { systemMessages, promptMessages } = getSystemPromptMessages(chatMessages);
Expand Down
10 changes: 5 additions & 5 deletions quadratic-api/src/ai/helpers/context.helper.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ConnectionDocs } from 'quadratic-api/src/ai/docs/ConnectionDocs';
import { FormulaDocs } from 'quadratic-api/src/ai/docs/FormulaDocs';
import { JavascriptDocs } from 'quadratic-api/src/ai/docs/JavascriptDocs';
import { PythonDocs } from 'quadratic-api/src/ai/docs/PythonDocs';
import { QuadraticDocs } from 'quadratic-api/src/ai/docs/QuadraticDocs';
import { aiToolsSpec } from 'quadratic-shared/ai/specs/aiToolsSpec';
import type { ChatMessage, CodeCellType } from 'quadratic-shared/typesAndSchemasAI';
import { ConnectionDocs } from '../docs/ConnectionDocs';
import { FormulaDocs } from '../docs/FormulaDocs';
import { JavascriptDocs } from '../docs/JavascriptDocs';
import { PythonDocs } from '../docs/PythonDocs';
import { QuadraticDocs } from '../docs/QuadraticDocs';

export const getQuadraticContext = (language?: CodeCellType): ChatMessage[] => [
{
Expand Down
4 changes: 2 additions & 2 deletions quadratic-api/src/ai/helpers/openai.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import type { AITool } from 'quadratic-shared/ai/specs/aiToolsSpec';
import { aiToolsSpec } from 'quadratic-shared/ai/specs/aiToolsSpec';
import type {
AIMessagePrompt,
AIRequestBody,
AIRequestHelperArgs,
OpenAIModel,
OpenAIPromptMessage,
OpenAIRequestBody,
OpenAITool,
OpenAIToolChoice,
} from 'quadratic-shared/typesAndSchemasAI';

export function getOpenAIApiArgs(args: Omit<AIRequestBody, 'model'>): Omit<OpenAIRequestBody, 'model'> {
export function getOpenAIApiArgs(args: AIRequestHelperArgs): Omit<OpenAIRequestBody, 'model'> {
const { messages: chatMessages, useTools, toolName } = args;

const { systemMessages, promptMessages } = getSystemPromptMessages(chatMessages);
Expand Down
4 changes: 2 additions & 2 deletions quadratic-api/src/ai/middleware/aiRateLimiter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import rateLimit from 'express-rate-limit';
import { RATE_LIMIT_AI_REQUESTS_MAX, RATE_LIMIT_AI_WINDOW_MS } from 'quadratic-api/src/env-vars';
import type { Request } from 'quadratic-api/src/types/Request';
import { RATE_LIMIT_AI_REQUESTS_MAX, RATE_LIMIT_AI_WINDOW_MS } from '../../env-vars';
import type { Request } from '../../types/Request';

export const ai_rate_limiter = rateLimit({
windowMs: Number(RATE_LIMIT_AI_WINDOW_MS) || 3 * 60 * 60 * 1000, // 3 hours
Expand Down
4 changes: 2 additions & 2 deletions quadratic-api/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import 'express-async-errors';
import fs from 'fs';
import helmet from 'helmet';
import path from 'path';
import internal_router from 'quadratic-api/src/routes/internal';
import { ApiError } from 'quadratic-api/src/utils/ApiError';
import { CORS, NODE_ENV, SENTRY_DSN } from './env-vars';
import internal_router from './routes/internal';
import { ApiError } from './utils/ApiError';
export const app = express();

// Configure Sentry
Expand Down
8 changes: 4 additions & 4 deletions quadratic-api/src/licenseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
//! Modifying this license check is violating the Quadratic Terms and Conditions and is stealing software, and we will come after you.

import axios from 'axios';
import dbClient from 'quadratic-api/src/dbClient';
import { LICENSE_API_URI, LICENSE_KEY } from 'quadratic-api/src/env-vars';
import { ApiError } from 'quadratic-api/src/utils/ApiError';
import { hash } from 'quadratic-api/src/utils/crypto';
import { LicenseSchema } from 'quadratic-shared/typesAndSchemas';
import type z from 'zod';
import dbClient from './dbClient';
import { LICENSE_API_URI, LICENSE_KEY } from './env-vars';
import { ApiError } from './utils/ApiError';
import { hash } from './utils/crypto';

type LicenseResponse = z.infer<typeof LicenseSchema>;

Expand Down
2 changes: 1 addition & 1 deletion quadratic-api/src/middleware/validateAccessToken.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Params } from 'express-jwt';
import { expressjwt } from 'express-jwt';
import { jwtConfig } from 'quadratic-api/src/auth/auth';
import { jwtConfig } from '../auth/auth';

// based on implementation from https://github.com/auth0-developer-hub/api_express_typescript_hello-world/blob/main/src/middleware/auth0.middleware.ts
export const validateAccessToken = expressjwt(jwtConfig() as Params);
6 changes: 3 additions & 3 deletions quadratic-api/src/routes/internal/checkpoint.$fileUuid.GET.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { Response } from 'express';
import express from 'express';
import { param, validationResult } from 'express-validator';
import dbClient from 'quadratic-api/src/dbClient';
import { validateM2MAuth } from 'quadratic-api/src/internal/validateM2MAuth';
import type { Request } from 'quadratic-api/src/types/Request';
import dbClient from '../../dbClient';
import { validateM2MAuth } from '../../internal/validateM2MAuth';
import type { Request } from '../../types/Request';

export const validateUUID = () => param('uuid').isUUID(4);

Expand Down
8 changes: 4 additions & 4 deletions quadratic-api/src/routes/internal/checkpoint.$fileUuid.PUT.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { Response } from 'express';
import express from 'express';
import { param, validationResult } from 'express-validator';
import dbClient from 'quadratic-api/src/dbClient';
import { validateM2MAuth } from 'quadratic-api/src/internal/validateM2MAuth';
import { validateRequestSchema } from 'quadratic-api/src/middleware/validateRequestSchema';
import type { Request } from 'quadratic-api/src/types/Request';
import { z } from 'zod';
import dbClient from '../../dbClient';
import { validateM2MAuth } from '../../internal/validateM2MAuth';
import { validateRequestSchema } from '../../middleware/validateRequestSchema';
import type { Request } from '../../types/Request';

export const validateUUID = () => param('uuid').isUUID(4);

Expand Down
Loading

0 comments on commit 0f7334b

Please sign in to comment.