prep work for sessionUpdated and chat session auto-naming
This commit is contained in:
parent
d7924a9d6f
commit
4b33915c7d
@ -21,6 +21,8 @@ import {
|
||||
SubmitPromptCallback,
|
||||
} from "@gadget/api";
|
||||
|
||||
import ChatSession from "../models/chat-session.ts";
|
||||
|
||||
import { ChatSessionService, SocketService } from "../services/index.ts";
|
||||
|
||||
export class CodeSession extends SocketSession {
|
||||
@ -187,16 +189,47 @@ export class CodeSession extends SocketSession {
|
||||
this.chatSession,
|
||||
content,
|
||||
);
|
||||
this.currentTurnId = turn._id;
|
||||
|
||||
this.log.info("ChatTurn created", {
|
||||
turnId: turn._id,
|
||||
chatSessionId: this.chatSession._id,
|
||||
});
|
||||
|
||||
this.currentTurnId = turn._id;
|
||||
droneSession.setCurrentTurnId(turn._id);
|
||||
|
||||
/*
|
||||
* Increment the chat session turn count and replace our cached view of
|
||||
* it. Reject the prompt if the chat session has been removed.
|
||||
*/
|
||||
const newSession = await ChatSession.findOneAndUpdate(
|
||||
{ _id: this.chatSession._id },
|
||||
{ $inc: { "stats.turnCount": 1 } },
|
||||
{
|
||||
new: true,
|
||||
populate: ChatSessionService.populateChatSession,
|
||||
},
|
||||
);
|
||||
if (!newSession) {
|
||||
// remove the turn
|
||||
await ChatSessionService.delete(this.chatSession._id);
|
||||
|
||||
// reject the prompt
|
||||
const error = new Error("chat session has been removed");
|
||||
error.statusCode = 404;
|
||||
throw error;
|
||||
}
|
||||
this.chatSession = newSession;
|
||||
|
||||
/*
|
||||
* Signal the IDE that the turn was created successfully. This moves the
|
||||
* IDE to the Processing state, and it will start expecting events to be
|
||||
* streamed in from the drone while processing the prompt.
|
||||
*/
|
||||
cb(true, { turnId: turn._id, message: "turn created successfully" });
|
||||
|
||||
/*
|
||||
* Forward to gadget-drone as a work order for processing.
|
||||
*/
|
||||
droneSession.socket.emit(
|
||||
"processWorkOrder",
|
||||
this.selectedDrone,
|
||||
@ -218,7 +251,17 @@ export class CodeSession extends SocketSession {
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/*
|
||||
* Call out to have the session's name auto-generated from this prompt
|
||||
* if this is the first prompt.
|
||||
*/
|
||||
this.chatSession = await ChatSessionService.generateSessionNameFromPrompt(
|
||||
this.chatSession,
|
||||
content,
|
||||
);
|
||||
} catch (error) {
|
||||
this.log.error("prompt rejected", { error });
|
||||
cb(false, {});
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// Copyright (C) 2026 Robert Colbert <rob.colbert@openplatform.us>
|
||||
// All Rights Reserved
|
||||
|
||||
import env from "../config/env.js";
|
||||
import env from "../config/env.ts";
|
||||
|
||||
import path from "node:path";
|
||||
import fs from "node:fs";
|
||||
@ -19,16 +19,36 @@ import {
|
||||
ReasoningEffort,
|
||||
} from "@gadget/api";
|
||||
|
||||
import { DtpService } from "../lib/service.js";
|
||||
import { PopulateOptions } from "mongoose";
|
||||
import ChatSession from "../models/chat-session.js";
|
||||
import ChatTurn from "../models/chat-turn.js";
|
||||
import Project from "../models/project.js";
|
||||
import AiProvider from "../models/ai-provider.js";
|
||||
import { IAiProvider } from "@gadget/api";
|
||||
|
||||
import Project from "../models/project.js";
|
||||
import ChatTurn from "../models/chat-turn.js";
|
||||
import ChatSession from "../models/chat-session.js";
|
||||
import AiProvider from "../models/ai-provider.js";
|
||||
|
||||
import { DtpService } from "../lib/service.js";
|
||||
import { PopulateOptions } from "mongoose";
|
||||
import {
|
||||
AiApi,
|
||||
createAiApi,
|
||||
IAiEnvironment,
|
||||
IAiProvider as IAiApiProvider,
|
||||
} from "@gadget/ai";
|
||||
|
||||
const aiEnv: IAiEnvironment = {
|
||||
NODE_ENV: env.NODE_ENV || "develop",
|
||||
services: {
|
||||
google: {
|
||||
cse: {
|
||||
apiKey: env.google.cse.apiKey,
|
||||
engineId: env.google.cse.engineId,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
class ChatSessionService extends DtpService {
|
||||
private populateSession: PopulateOptions[] = [
|
||||
public populateChatSession: PopulateOptions[] = [
|
||||
{ path: "user", select: "-passwordSalt -password" },
|
||||
{
|
||||
path: "project",
|
||||
@ -41,7 +61,7 @@ class ChatSessionService extends DtpService {
|
||||
},
|
||||
{ path: "provider" },
|
||||
];
|
||||
private populateChatTurn: PopulateOptions[] = [
|
||||
public populateChatTurn: PopulateOptions[] = [
|
||||
{
|
||||
path: "user",
|
||||
select: "-passwordSalt -password",
|
||||
@ -51,7 +71,7 @@ class ChatSessionService extends DtpService {
|
||||
},
|
||||
{
|
||||
path: "session",
|
||||
populate: this.populateSession,
|
||||
populate: this.populateChatSession,
|
||||
},
|
||||
{
|
||||
path: "provider",
|
||||
@ -123,7 +143,7 @@ class ChatSessionService extends DtpService {
|
||||
model: selectedModel,
|
||||
});
|
||||
|
||||
return session.populate(this.populateSession);
|
||||
return session.populate(this.populateChatSession);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -368,12 +388,69 @@ class ChatSessionService extends DtpService {
|
||||
/**
|
||||
* Generates a session name from the first prompt.
|
||||
*/
|
||||
generateSessionNameFromPrompt(prompt: string): string {
|
||||
// Take first 50 chars, remove special chars, capitalize
|
||||
const truncated = prompt.slice(0, 50).replace(/[^a-zA-Z0-9\s]/g, "");
|
||||
const words = truncated.trim().split(/\s+/);
|
||||
const name = words.slice(0, 5).join(" ");
|
||||
return name.charAt(0).toUpperCase() + name.slice(1);
|
||||
async generateSessionNameFromPrompt(
|
||||
session: IChatSession,
|
||||
prompt: string,
|
||||
): Promise<IChatSession> {
|
||||
const dbProvider: IAiProvider = session.provider as IAiProvider;
|
||||
const provider: IAiApiProvider = this.mapDbProviderToConfig(dbProvider);
|
||||
|
||||
this.log.info("calling provider to generate chat session title", {
|
||||
provider: {
|
||||
_id: provider._id,
|
||||
name: provider.name,
|
||||
},
|
||||
selectedModel: session.selectedModel,
|
||||
});
|
||||
|
||||
const api: AiApi = createAiApi(aiEnv, provider, this.log);
|
||||
const response = await api.generate(
|
||||
{
|
||||
provider,
|
||||
modelId: session.selectedModel,
|
||||
params: {
|
||||
reasoning: false,
|
||||
temperature: 1.0,
|
||||
topK: 0.6,
|
||||
topP: 0.4,
|
||||
},
|
||||
},
|
||||
{
|
||||
systemPrompt:
|
||||
"You are an assistant that creates titles for chat sessions by examining the first prompt.",
|
||||
prompt: `The first prompt submitted by the user: \n\n${prompt}`,
|
||||
},
|
||||
);
|
||||
|
||||
const newSession = await ChatSession.findOneAndUpdate(
|
||||
{ _id: session._id },
|
||||
{ $set: { name: response.response || "New Session" } },
|
||||
{ new: true, populate: this.populateChatSession, lean: true },
|
||||
);
|
||||
if (!newSession) {
|
||||
const error = new Error("chat session has been removed");
|
||||
error.statusCode = 404;
|
||||
throw error;
|
||||
}
|
||||
|
||||
//TODO: emit the `sessionUpdated` message to the CodeSession in the IDE
|
||||
// for this chat session, letting it know the name of the chat session has
|
||||
// changed. The IDE will then update it's displays and state for the User.
|
||||
|
||||
return newSession;
|
||||
}
|
||||
|
||||
mapDbProviderToConfig(provider: IAiProvider | GadgetId): IAiApiProvider {
|
||||
if (typeof provider === "string") {
|
||||
throw new Error("Provider must be populated, not a GadgetId reference");
|
||||
}
|
||||
return {
|
||||
_id: provider._id,
|
||||
name: provider.name,
|
||||
sdk: provider.apiType, // map apiType → sdk
|
||||
baseUrl: provider.baseUrl,
|
||||
apiKey: provider.apiKey,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user