sessions improved, v0.7.0

This commit is contained in:
serversdwn
2025-12-21 15:50:52 -05:00
parent 01d4811717
commit b4613ac30c
7 changed files with 424 additions and 41 deletions

View File

@@ -4,9 +4,17 @@
import express from "express";
import dotenv from "dotenv";
import cors from "cors";
import fs from "fs/promises";
import path from "path";
import { fileURLToPath } from "url";
dotenv.config();
// ES module __dirname workaround
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const SESSIONS_DIR = path.join(__dirname, "sessions");
const app = express();
app.use(cors());
app.use(express.json());
@@ -173,20 +181,182 @@ app.post("/chat", async (req, res) => {
// -----------------------------------------------------
// SESSION ENDPOINTS (for UI)
// -----------------------------------------------------
// In-memory session storage (could be replaced with a database)
const sessions = new Map();
// Helper functions for session persistence
async function ensureSessionsDir() {
try {
await fs.mkdir(SESSIONS_DIR, { recursive: true });
} catch (err) {
console.error("Failed to create sessions directory:", err);
}
}
app.get("/sessions/:id", (req, res) => {
async function loadSession(sessionId) {
try {
const sessionPath = path.join(SESSIONS_DIR, `${sessionId}.json`);
const data = await fs.readFile(sessionPath, "utf-8");
return JSON.parse(data);
} catch (err) {
// File doesn't exist or is invalid - return empty array
return [];
}
}
async function saveSession(sessionId, history, metadata = {}) {
try {
await ensureSessionsDir();
const sessionPath = path.join(SESSIONS_DIR, `${sessionId}.json`);
const metadataPath = path.join(SESSIONS_DIR, `${sessionId}.meta.json`);
// Save history
await fs.writeFile(sessionPath, JSON.stringify(history, null, 2), "utf-8");
// Save metadata (name, etc.)
await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
return true;
} catch (err) {
console.error(`Failed to save session ${sessionId}:`, err);
return false;
}
}
async function loadSessionMetadata(sessionId) {
try {
const metadataPath = path.join(SESSIONS_DIR, `${sessionId}.meta.json`);
const data = await fs.readFile(metadataPath, "utf-8");
return JSON.parse(data);
} catch (err) {
// No metadata file, return default
return { name: sessionId };
}
}
async function saveSessionMetadata(sessionId, metadata) {
try {
await ensureSessionsDir();
const metadataPath = path.join(SESSIONS_DIR, `${sessionId}.meta.json`);
await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
return true;
} catch (err) {
console.error(`Failed to save metadata for ${sessionId}:`, err);
return false;
}
}
async function listSessions() {
try {
await ensureSessionsDir();
const files = await fs.readdir(SESSIONS_DIR);
const sessions = [];
for (const file of files) {
if (file.endsWith(".json") && !file.endsWith(".meta.json")) {
const sessionId = file.replace(".json", "");
const sessionPath = path.join(SESSIONS_DIR, file);
const stats = await fs.stat(sessionPath);
// Try to read the session to get message count
let messageCount = 0;
try {
const data = await fs.readFile(sessionPath, "utf-8");
const history = JSON.parse(data);
messageCount = history.length;
} catch (e) {
// Invalid JSON, skip
}
// Load metadata (name)
const metadata = await loadSessionMetadata(sessionId);
sessions.push({
id: sessionId,
name: metadata.name || sessionId,
lastModified: stats.mtime,
messageCount
});
}
}
// Sort by last modified (newest first)
sessions.sort((a, b) => b.lastModified - a.lastModified);
return sessions;
} catch (err) {
console.error("Failed to list sessions:", err);
return [];
}
}
async function deleteSession(sessionId) {
try {
const sessionPath = path.join(SESSIONS_DIR, `${sessionId}.json`);
const metadataPath = path.join(SESSIONS_DIR, `${sessionId}.meta.json`);
// Delete session file
await fs.unlink(sessionPath);
// Delete metadata file (if exists)
try {
await fs.unlink(metadataPath);
} catch (e) {
// Metadata file doesn't exist, that's ok
}
return true;
} catch (err) {
console.error(`Failed to delete session ${sessionId}:`, err);
return false;
}
}
// GET /sessions - List all sessions
app.get("/sessions", async (req, res) => {
const sessions = await listSessions();
res.json(sessions);
});
// GET /sessions/:id - Get specific session history
app.get("/sessions/:id", async (req, res) => {
const sessionId = req.params.id;
const history = sessions.get(sessionId) || [];
const history = await loadSession(sessionId);
res.json(history);
});
app.post("/sessions/:id", (req, res) => {
// POST /sessions/:id - Save session history
app.post("/sessions/:id", async (req, res) => {
const sessionId = req.params.id;
const history = req.body;
sessions.set(sessionId, history);
res.json({ ok: true, saved: history.length });
const success = await saveSession(sessionId, history);
if (success) {
res.json({ ok: true, saved: history.length });
} else {
res.status(500).json({ error: "Failed to save session" });
}
});
// PATCH /sessions/:id/metadata - Update session metadata (name, etc.)
app.patch("/sessions/:id/metadata", async (req, res) => {
const sessionId = req.params.id;
const metadata = req.body;
const success = await saveSessionMetadata(sessionId, metadata);
if (success) {
res.json({ ok: true, metadata });
} else {
res.status(500).json({ error: "Failed to update metadata" });
}
});
// DELETE /sessions/:id - Delete a session
app.delete("/sessions/:id", async (req, res) => {
const sessionId = req.params.id;
const success = await deleteSession(sessionId);
if (success) {
res.json({ ok: true, deleted: sessionId });
} else {
res.status(500).json({ error: "Failed to delete session" });
}
});
// -----------------------------------------------------

View File

@@ -0,0 +1,3 @@
{
"name": "My Coding Session"
}

View File

@@ -0,0 +1,18 @@
[
{
"role": "user",
"content": "Hello! this is a new test session. Do you know who i am?"
},
{
"role": "assistant",
"content": "Hi! In this session, I don't have information about your previous interactions. You can tell me who you are or any other details you'd like to share. How can I assist you today?"
},
{
"role": "user",
"content": "sure im brian! i am designing you... you are a robot!"
},
{
"role": "assistant",
"content": "Hello Brian! Nice to meet you. As an AI, I don't have physical design capabilities, but I'm here to help with any information or tasks you need. How can I assist you in your design process?"
}
]

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{"name":"Session 2"}