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": { "application/json": {
example: { example: {
message: "What is AnythingLLM?", 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 { try {
const { slug } = request.params; 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) }); const workspace = await Workspace.get({ slug: String(slug) });
if (!workspace) { if (!workspace) {
@ -615,6 +616,7 @@ function apiWorkspaceEndpoints(app) {
mode, mode,
user: null, user: null,
thread: null, thread: null,
sessionId: !!sessionId ? String(sessionId) : null,
}); });
await Telemetry.sendTelemetry("sent_chat", { await Telemetry.sendTelemetry("sent_chat", {
@ -658,7 +660,8 @@ function apiWorkspaceEndpoints(app) {
"application/json": { "application/json": {
example: { example: {
message: "What is AnythingLLM?", 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 { try {
const { slug } = request.params; 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) }); const workspace = await Workspace.get({ slug: String(slug) });
if (!workspace) { if (!workspace) {
@ -748,6 +751,7 @@ function apiWorkspaceEndpoints(app) {
mode, mode,
user: null, user: null,
thread: null, thread: null,
sessionId: !!sessionId ? String(sessionId) : null,
}); });
await Telemetry.sendTelemetry("sent_chat", { await Telemetry.sendTelemetry("sent_chat", {
LLMSelection: LLMSelection:

View File

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

View File

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

View File

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

View File

@ -8,6 +8,7 @@ const WorkspaceChats = {
user = null, user = null,
threadId = null, threadId = null,
include = true, include = true,
apiSessionId = null,
}) { }) {
try { try {
const chat = await prisma.workspace_chats.create({ const chat = await prisma.workspace_chats.create({
@ -17,6 +18,7 @@ const WorkspaceChats = {
response: JSON.stringify(response), response: JSON.stringify(response),
user_id: user?.id || null, user_id: user?.id || null,
thread_id: threadId, thread_id: threadId,
api_session_id: apiSessionId,
include, include,
}, },
}); });
@ -40,6 +42,7 @@ const WorkspaceChats = {
workspaceId, workspaceId,
user_id: userId, user_id: userId,
thread_id: null, // this function is now only used for the default thread on workspaces and users 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, include: true,
}, },
...(limit !== null ? { take: limit } : {}), ...(limit !== null ? { take: limit } : {}),
@ -63,6 +66,7 @@ const WorkspaceChats = {
where: { where: {
workspaceId, workspaceId,
thread_id: null, // this function is now only used for the default thread on workspaces 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, include: true,
}, },
...(limit !== null ? { take: limit } : {}), ...(limit !== null ? { take: limit } : {}),
@ -196,7 +200,7 @@ const WorkspaceChats = {
const user = res.user_id ? await User.get({ id: res.user_id }) : null; const user = res.user_id ? await User.get({ id: res.user_id }) : null;
res.user = user res.user = user
? { username: user.username } ? { username: user.username }
: { username: "unknown user" }; : { username: res.api_session_id !== null ? "API" : "unknown user" };
} }
return results; 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) include Boolean @default(true)
user_id Int? user_id Int?
thread_id Int? // No relation to prevent whole table migration 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()) createdAt DateTime @default(now())
lastUpdatedAt DateTime @default(now()) lastUpdatedAt DateTime @default(now())
feedbackScore Boolean? feedbackScore Boolean?

View File

@ -1972,7 +1972,8 @@
"application/json": { "application/json": {
"example": { "example": {
"message": "What is AnythingLLM?", "message": "What is AnythingLLM?",
"mode": "query | chat" "mode": "query | chat",
"sessionId": "identifier-to-partition-chats-by-external-id"
} }
} }
} }
@ -2064,7 +2065,8 @@
"application/json": { "application/json": {
"example": { "example": {
"message": "What is AnythingLLM?", "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, workspaceId: this.invocation.workspace_id,
user_id: this.invocation.user_id || null, user_id: this.invocation.user_id || null,
thread_id: this.invocation.thread_id || null, thread_id: this.invocation.thread_id || null,
api_session_id: null,
include: true, include: true,
}, },
limit, limit,

View File

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

View File

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

View File

@ -50,7 +50,11 @@ async function prepareWorkspaceChatsForExport(format = "jsonl") {
const responseJson = JSON.parse(chat.response); const responseJson = JSON.parse(chat.response);
return { return {
id: chat.id, 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", workspace: chat.workspace ? chat.workspace.name : "unknown workspace",
prompt: chat.prompt, prompt: chat.prompt,
response: responseJson.text, response: responseJson.text,