Api session id support (#2158)

* Refactor api endpoint chat handler to its own function
remove legacy `chatWithWorkspace` and cleanup `index.js`

* Add `sessionId` in dev API to partition chats logically statelessly
This commit is contained in:
Timothy Carambat 2024-08-21 15:25:47 -07:00 committed by GitHub
parent 2d2e49bc00
commit fdc3add53c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 54 additions and 20 deletions

View File

@ -550,7 +550,8 @@ function apiWorkspaceEndpoints(app) {
"application/json": {
example: {
message: "What is AnythingLLM?",
mode: "query | chat"
mode: "query | chat",
sessionId: "identifier-to-partition-chats-by-external-id"
}
}
}
@ -580,7 +581,7 @@ function apiWorkspaceEndpoints(app) {
*/
try {
const { slug } = request.params;
const { message, mode = "query" } = reqBody(request);
const { message, mode = "query", sessionId = null } = reqBody(request);
const workspace = await Workspace.get({ slug: String(slug) });
if (!workspace) {
@ -615,6 +616,7 @@ function apiWorkspaceEndpoints(app) {
mode,
user: null,
thread: null,
sessionId: !!sessionId ? String(sessionId) : null,
});
await Telemetry.sendTelemetry("sent_chat", {
@ -658,7 +660,8 @@ function apiWorkspaceEndpoints(app) {
"application/json": {
example: {
message: "What is AnythingLLM?",
mode: "query | chat"
mode: "query | chat",
sessionId: "identifier-to-partition-chats-by-external-id"
}
}
}
@ -706,7 +709,7 @@ function apiWorkspaceEndpoints(app) {
*/
try {
const { slug } = request.params;
const { message, mode = "query" } = reqBody(request);
const { message, mode = "query", sessionId = null } = reqBody(request);
const workspace = await Workspace.get({ slug: String(slug) });
if (!workspace) {
@ -748,6 +751,7 @@ function apiWorkspaceEndpoints(app) {
mode,
user: null,
thread: null,
sessionId: !!sessionId ? String(sessionId) : null,
});
await Telemetry.sendTelemetry("sent_chat", {
LLMSelection:

View File

@ -299,6 +299,7 @@ function apiWorkspaceThreadEndpoints(app) {
{
workspaceId: workspace.id,
thread_id: thread.id,
api_session_id: null, // Do not include API session chats.
include: true,
},
null,

View File

@ -138,6 +138,7 @@ function workspaceThreadEndpoints(app) {
workspaceId: workspace.id,
user_id: user?.id || null,
thread_id: thread.id,
api_session_id: null, // Do not include API session chats.
include: true,
},
null,

View File

@ -793,6 +793,7 @@ function workspaceEndpoints(app) {
user_id: user?.id,
include: true, // only duplicate visible chats
thread_id: threadId,
api_session_id: null, // Do not include API session chats.
id: { lte: Number(chatId) },
},
null,

View File

@ -8,6 +8,7 @@ const WorkspaceChats = {
user = null,
threadId = null,
include = true,
apiSessionId = null,
}) {
try {
const chat = await prisma.workspace_chats.create({
@ -17,6 +18,7 @@ const WorkspaceChats = {
response: JSON.stringify(response),
user_id: user?.id || null,
thread_id: threadId,
api_session_id: apiSessionId,
include,
},
});
@ -40,6 +42,7 @@ const WorkspaceChats = {
workspaceId,
user_id: userId,
thread_id: null, // this function is now only used for the default thread on workspaces and users
api_session_id: null, // do not include api-session chats in the frontend for anyone.
include: true,
},
...(limit !== null ? { take: limit } : {}),
@ -63,6 +66,7 @@ const WorkspaceChats = {
where: {
workspaceId,
thread_id: null, // this function is now only used for the default thread on workspaces
api_session_id: null, // do not include api-session chats in the frontend for anyone.
include: true,
},
...(limit !== null ? { take: limit } : {}),
@ -196,7 +200,7 @@ const WorkspaceChats = {
const user = res.user_id ? await User.get({ id: res.user_id }) : null;
res.user = user
? { username: user.username }
: { username: "unknown user" };
: { username: res.api_session_id !== null ? "API" : "unknown user" };
}
return results;

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "workspace_chats" ADD COLUMN "api_session_id" TEXT;

View File

@ -177,6 +177,7 @@ model workspace_chats {
include Boolean @default(true)
user_id Int?
thread_id Int? // No relation to prevent whole table migration
api_session_id String? // String identifier for only the dev API to parition chats in any mode.
createdAt DateTime @default(now())
lastUpdatedAt DateTime @default(now())
feedbackScore Boolean?

View File

@ -1972,7 +1972,8 @@
"application/json": {
"example": {
"message": "What is AnythingLLM?",
"mode": "query | chat"
"mode": "query | chat",
"sessionId": "identifier-to-partition-chats-by-external-id"
}
}
}
@ -2064,7 +2065,8 @@
"application/json": {
"example": {
"message": "What is AnythingLLM?",
"mode": "query | chat"
"mode": "query | chat",
"sessionId": "identifier-to-partition-chats-by-external-id"
}
}
}

View File

@ -42,6 +42,7 @@ class AgentHandler {
workspaceId: this.invocation.workspace_id,
user_id: this.invocation.user_id || null,
thread_id: this.invocation.thread_id || null,
api_session_id: null,
include: true,
},
limit,

View File

@ -23,6 +23,7 @@ const { chatPrompt, sourceIdentifier, recentChatHistory } = require("./index");
* mode: "chat"|"query",
* user: import("@prisma/client").users|null,
* thread: import("@prisma/client").workspace_threads|null,
* sessionId: string|null,
* }} parameters
* @returns {Promise<ResponseObject>}
*/
@ -32,6 +33,7 @@ async function chatSync({
mode = "chat",
user = null,
thread = null,
sessionId = null,
}) {
const uuid = uuidv4();
const chatMode = mode ?? "chat";
@ -60,6 +62,7 @@ async function chatSync({
type: chatMode,
},
include: false,
apiSessionId: sessionId,
});
return {
@ -83,7 +86,7 @@ async function chatSync({
workspace,
thread,
messageLimit,
chatMode,
apiSessionId: sessionId,
});
await new DocumentManager({
@ -168,6 +171,7 @@ async function chatSync({
},
threadId: thread?.id || null,
include: false,
apiSessionId: sessionId,
user,
});
@ -214,6 +218,7 @@ async function chatSync({
prompt: message,
response: { text: textResponse, sources, type: chatMode },
threadId: thread?.id || null,
apiSessionId: sessionId,
user,
});
@ -237,6 +242,7 @@ async function chatSync({
* mode: "chat"|"query",
* user: import("@prisma/client").users|null,
* thread: import("@prisma/client").workspace_threads|null,
* sessionId: string|null,
* }} parameters
* @returns {Promise<VoidFunction>}
*/
@ -247,6 +253,7 @@ async function streamChat({
mode = "chat",
user = null,
thread = null,
sessionId = null,
}) {
const uuid = uuidv4();
const chatMode = mode ?? "chat";
@ -285,6 +292,7 @@ async function streamChat({
attachments: [],
},
threadId: thread?.id || null,
apiSessionId: sessionId,
include: false,
user,
});
@ -303,6 +311,7 @@ async function streamChat({
workspace,
thread,
messageLimit,
apiSessionId: sessionId,
});
// Look for pinned documents and see if the user decided to use this feature. We will also do a vector search
@ -402,6 +411,7 @@ async function streamChat({
attachments: [],
},
threadId: thread?.id || null,
apiSessionId: sessionId,
include: false,
user,
});
@ -453,6 +463,7 @@ async function streamChat({
prompt: message,
response: { text: completeText, sources, type: chatMode },
threadId: thread?.id || null,
apiSessionId: sessionId,
user,
});

View File

@ -37,6 +37,7 @@ async function recentChatHistory({
workspace,
thread = null,
messageLimit = 20,
apiSessionId = null,
}) {
const rawHistory = (
await WorkspaceChats.where(
@ -44,6 +45,7 @@ async function recentChatHistory({
workspaceId: workspace.id,
user_id: user?.id || null,
thread_id: thread?.id || null,
api_session_id: apiSessionId || null,
include: true,
},
messageLimit,

View File

@ -50,7 +50,11 @@ async function prepareWorkspaceChatsForExport(format = "jsonl") {
const responseJson = JSON.parse(chat.response);
return {
id: chat.id,
username: chat.user ? chat.user.username : "unknown user",
username: chat.user
? chat.user.username
: chat.api_session_id !== null
? "API"
: "unknown user",
workspace: chat.workspace ? chat.workspace.name : "unknown workspace",
prompt: chat.prompt,
response: responseJson.text,