|
|
|
@@ -1,3 +1,5 @@
|
|
|
|
|
import { learningLanguageInstruction } from "../core/i18n.ts";
|
|
|
|
|
import { applicationLanguage, chatLanguage } from "./language.ts";
|
|
|
|
|
import type { Usage } from "@earendil-works/pi-ai";
|
|
|
|
|
import {
|
|
|
|
|
createPracticeAgent,
|
|
|
|
@@ -80,6 +82,7 @@ import type {CallUsage, SteerMessage} from "../core/contracts.ts";
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
const jobRunner = createJobRunner({
|
|
|
|
|
languageForChat: chatLanguage,
|
|
|
|
|
chatStore: stores.chatStore,
|
|
|
|
|
jobStore: stores.jobStore,
|
|
|
|
|
deliver: deliverJobResult,
|
|
|
|
@@ -105,6 +108,7 @@ const jobRunner = createJobRunner({
|
|
|
|
|
* quiz-triggered session runs (turn-service) and notes imports
|
|
|
|
|
* (/api/graph/import) hit the same single-flight registry. */
|
|
|
|
|
export const extraction = createExtractionPipeline({
|
|
|
|
|
languageForSource: (id) => chatLanguage(id),
|
|
|
|
|
...stores,
|
|
|
|
|
// BYOK (owner, 2026-09-22): the extraction stages resolve the chain —
|
|
|
|
|
// role override → slot → NOTHING — read FRESH per stage call; an empty
|
|
|
|
@@ -164,7 +168,7 @@ export async function handleChatRequest(
|
|
|
|
|
}
|
|
|
|
|
throw err;
|
|
|
|
|
}
|
|
|
|
|
return handleChatInput(chatId, input, buildAgent);
|
|
|
|
|
return handleChatInput(chatId, input, buildAgent, req.headers.get("accept-language"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@@ -176,6 +180,7 @@ export async function handleChatInput(
|
|
|
|
|
chatId: string,
|
|
|
|
|
input: TurnInput,
|
|
|
|
|
buildAgent?: (opts: AgentBuildOptions & { teaching: boolean }) => TeachingAgent,
|
|
|
|
|
browserLanguages?: string | null,
|
|
|
|
|
): Promise<Response> {
|
|
|
|
|
let record = stores.chatStore.getChat(chatId);
|
|
|
|
|
if (!record) {
|
|
|
|
@@ -191,6 +196,7 @@ export async function handleChatInput(
|
|
|
|
|
// write-ahead persist (nothing durable changes), and every downstream
|
|
|
|
|
// resolution (jobs/extraction gate per-need on their own tiers).
|
|
|
|
|
const effective = effectiveSettings();
|
|
|
|
|
const language = applicationLanguage(browserLanguages ?? record.language);
|
|
|
|
|
const conversation = resolveConversationSpec(record.settings, effective.models.strong);
|
|
|
|
|
if (!conversation) {
|
|
|
|
|
const envelope = modelsNotConfigured(["strong"]);
|
|
|
|
@@ -250,7 +256,7 @@ export async function handleChatInput(
|
|
|
|
|
effective.compaction?.thresholdTokens,
|
|
|
|
|
catalogContextLimits().get(modelIdForCompaction),
|
|
|
|
|
),
|
|
|
|
|
systemPromptTokens: Math.ceil(composeInstructions(record.kind === "practice" ? "practice" : "teacher").length / 4),
|
|
|
|
|
systemPromptTokens: Math.ceil((composeInstructions(record.kind === "practice" ? "practice" : "teacher").length + learningLanguageInstruction(language).length) / 4),
|
|
|
|
|
},
|
|
|
|
|
chatId,
|
|
|
|
|
);
|
|
|
|
@@ -312,6 +318,7 @@ export async function handleChatInput(
|
|
|
|
|
const stagedAutomatic = isSubscriptionProvider(conversation.provider) && input.type === "steers";
|
|
|
|
|
let persistBaseCount = stagedAutomatic ? record.messages.length : merged.length;
|
|
|
|
|
try {
|
|
|
|
|
stores.chatStore.saveLanguage?.(chatId, language);
|
|
|
|
|
if (!stagedAutomatic) stores.chatStore.saveMessages(chatId, merged);
|
|
|
|
|
// Dock fix (owner ruling 2026-09-26): persisted markers are visible
|
|
|
|
|
// in the record — flip their jobs now (the wake's steers ride this
|
|
|
|
@@ -374,6 +381,7 @@ export async function handleChatInput(
|
|
|
|
|
restoreAutomatic: (automaticIds, retainedIds) => restoreUncommittedAutomatic(chatId, constructed, automaticIds, retainedIds),
|
|
|
|
|
});
|
|
|
|
|
const agentOpts: AgentBuildOptions = {
|
|
|
|
|
language,
|
|
|
|
|
engine,
|
|
|
|
|
callPurpose,
|
|
|
|
|
requestCycle,
|
|
|
|
@@ -579,7 +587,7 @@ export async function handleChatInput(
|
|
|
|
|
fresh.compaction?.thresholdTokens,
|
|
|
|
|
catalogContextLimits().get(kickModelId),
|
|
|
|
|
),
|
|
|
|
|
systemPromptTokens: Math.ceil(composeInstructions(freshRecord.kind === "practice" ? "practice" : "teacher").length / 4),
|
|
|
|
|
systemPromptTokens: Math.ceil((composeInstructions(freshRecord.kind === "practice" ? "practice" : "teacher").length + learningLanguageInstruction(chatLanguage(chatId)).length) / 4),
|
|
|
|
|
};
|
|
|
|
|
}, chatId);
|
|
|
|
|
},
|
|
|
|
|