sd / src /session.js
pigracing's picture
Upload 22 files
bd27f3c verified
Raw History Blame Contribute Delete
3.4 kB
import { reportTokenError, reportTokenSuccess, setRequestToken, getRequestToken } from './auth.js';
import { apiHeaders, proxiedFetch } from './headers.js';
import { recordSessionHit } from './metrics.js';
const BASE_URL = 'https://chat.deepseek.com';
const SESSION_TTL = 259200; // 3 days in seconds
const sessionPool = new Map(); // key: token:model_type, value: { id, model_type, createdAt, token }
export async function createSession(token, modelType = 'default') {
const res = await proxiedFetch(`${BASE_URL}/api/v0/chat_session/create`, {
method: 'POST',
headers: await apiHeaders(token),
body: JSON.stringify({}),
});
const json = await res.json();
// Token invalid — report error so auth.js can mark it dead
if (json.code === 40003) {
reportTokenError(token);
throw new Error('Token invalid (40003)');
}
const session = json.data?.biz_data?.chat_session;
if (!session) {
reportTokenError(token);
throw new Error(`Session create failed: ${json.msg || JSON.stringify(json)}`);
}
reportTokenSuccess(token);
return session;
}
export async function getSession(token, modelType) {
const cacheKey = `${token.slice(0, 12)}:${modelType}`;
const now = Date.now() / 1000;
const cached = sessionPool.get(cacheKey);
if (cached && (now - cached.createdAt) < SESSION_TTL) {
recordSessionHit(true);
return cached;
}
const session = await createSession(token, modelType);
recordSessionHit(false);
session.createdAt = now;
session.token = token;
sessionPool.set(cacheKey, session);
return session;
}
// Remove cached sessions for a specific token prefix (used after token refresh)
export function invalidateTokenSessions(tokenPrefix) {
for (const key of sessionPool.keys()) {
if (key.startsWith(tokenPrefix + ':')) {
sessionPool.delete(key);
}
}
}
export function getSessionInfo() {
const now = Date.now() / 1000;
const entries = [];
for (const [key, val] of sessionPool) {
const age = now - val.createdAt;
entries.push({
key,
modelType: val.model_type,
ageSeconds: Math.floor(age),
ttlRemainingSeconds: Math.max(0, Math.floor(SESSION_TTL - age)),
});
}
return { count: sessionPool.size, ttl: SESSION_TTL, sessions: entries };
}
export async function prewarmSessions(tokens, modelTypes = ['default', 'expert']) {
const { getPoolInfo } = await import('./auth.js');
const poolInfo = getPoolInfo();
const alivePrefixes = poolInfo.filter(t => !t.dead && t.token !== 'NONE').map(t => t.token.replace('...', ''));
console.log(`Pre-warming sessions for ${alivePrefixes.length} alive tokens × ${modelTypes.length} model types...`);
const promises = [];
for (const token of tokens) {
const prefix = token.slice(0, 12);
if (!alivePrefixes.includes(prefix)) continue;
for (const modelType of modelTypes) {
const cacheKey = `${prefix}:${modelType}`;
if (!sessionPool.has(cacheKey)) {
promises.push(
getSession(token, modelType).catch(() => {})
);
}
}
if (promises.length >= 6) {
await Promise.allSettled(promises.splice(0));
}
}
if (promises.length > 0) {
await Promise.allSettled(promises);
}
console.log(`Session pool: ${sessionPool.size} cached sessions`);
}