feat(FN-1621): merge fusion/fn-1621
This commit is contained in:
@@ -2377,6 +2377,8 @@ describe("Mission API", () => {
|
||||
"Scoped Mission",
|
||||
scopedRootDir,
|
||||
{ "mission-interview-system": "Scoped mission interview prompt" },
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2462,6 +2464,8 @@ describe("Mission API", () => {
|
||||
"Default Mission",
|
||||
"/fake/root",
|
||||
{},
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -196,6 +196,9 @@ interface MissionInterviewSession {
|
||||
thinkingOutput: string;
|
||||
/** Thinking output generated while producing currentQuestion */
|
||||
lastGeneratedThinking: string;
|
||||
/** Model override for this interview session */
|
||||
modelProvider?: string;
|
||||
modelId?: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -274,6 +277,8 @@ function persistMissionSession(session: MissionInterviewSession, status: "genera
|
||||
ip: session.ip,
|
||||
missionTitle: session.missionTitle,
|
||||
missionId: session.missionId,
|
||||
modelProvider: session.modelProvider,
|
||||
modelId: session.modelId,
|
||||
}),
|
||||
conversationHistory: JSON.stringify(session.history),
|
||||
currentQuestion: session.currentQuestion ? JSON.stringify(session.currentQuestion) : null,
|
||||
@@ -300,7 +305,7 @@ function unpersistMissionSession(sessionId: string): void {
|
||||
}
|
||||
|
||||
function buildMissionInterviewSessionFromRow(row: AiSessionRow): MissionInterviewSession {
|
||||
const payload = safeParseJson<{ ip?: string; missionId?: string; missionTitle?: string }>(
|
||||
const payload = safeParseJson<{ ip?: string; missionId?: string; missionTitle?: string; modelProvider?: string; modelId?: string }>(
|
||||
row.inputPayload,
|
||||
{},
|
||||
{ throwOnError: true, fieldName: "inputPayload" },
|
||||
@@ -338,6 +343,8 @@ function buildMissionInterviewSessionFromRow(row: AiSessionRow): MissionIntervie
|
||||
thinkingOutput: row.thinkingOutput,
|
||||
lastGeneratedThinking: row.thinkingOutput || "",
|
||||
error: row.error ?? undefined,
|
||||
modelProvider: payload.modelProvider,
|
||||
modelId: payload.modelId,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
agent: undefined,
|
||||
@@ -749,6 +756,12 @@ async function createMissionInterviewAgent(
|
||||
cwd: rootDir,
|
||||
systemPrompt: effectivePrompt,
|
||||
tools: "readonly",
|
||||
...(session.modelProvider && session.modelId
|
||||
? {
|
||||
defaultProvider: session.modelProvider,
|
||||
defaultModelId: session.modelId,
|
||||
}
|
||||
: {}),
|
||||
onThinking: (delta: string) => {
|
||||
session.thinkingOutput += delta;
|
||||
persistMissionThinking(session.id, session.thinkingOutput);
|
||||
@@ -963,6 +976,8 @@ export async function createMissionInterviewSession(
|
||||
missionTitle: string,
|
||||
rootDir: string,
|
||||
promptOverrides?: PromptOverrideMap,
|
||||
modelProvider?: string,
|
||||
modelId?: string,
|
||||
): Promise<string> {
|
||||
if (!checkRateLimit(ip)) {
|
||||
const resetTime = getRateLimitResetTime(ip);
|
||||
@@ -982,6 +997,8 @@ export async function createMissionInterviewSession(
|
||||
history: [],
|
||||
thinkingOutput: "",
|
||||
lastGeneratedThinking: "",
|
||||
modelProvider,
|
||||
modelId,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
@@ -354,13 +354,13 @@ export function createMissionRouter(
|
||||
/**
|
||||
* POST /api/missions/interview/start
|
||||
* Start a mission interview session with AI agent streaming.
|
||||
* Body: { missionTitle: string }
|
||||
* Body: { missionTitle: string, modelProvider?: string, modelId?: string }
|
||||
* Returns: { sessionId: string }
|
||||
*/
|
||||
router.post(
|
||||
"/interview/start",
|
||||
catchTypedHandler(async (req, res) => {
|
||||
const { missionTitle } = req.body;
|
||||
const { missionTitle, modelProvider, modelId } = req.body;
|
||||
|
||||
if (!missionTitle || typeof missionTitle !== "string" || !missionTitle.trim()) {
|
||||
throw badRequest("missionTitle is required and must be a non-empty string");
|
||||
@@ -370,6 +370,19 @@ export function createMissionRouter(
|
||||
throw badRequest("missionTitle must be 500 characters or less");
|
||||
}
|
||||
|
||||
// Validate model parameters - if one is provided, both must be provided
|
||||
if (modelProvider !== undefined && typeof modelProvider !== "string") {
|
||||
throw badRequest("modelProvider must be a string when provided");
|
||||
}
|
||||
|
||||
if (modelId !== undefined && typeof modelId !== "string") {
|
||||
throw badRequest("modelId must be a string when provided");
|
||||
}
|
||||
|
||||
if ((modelProvider && !modelId) || (!modelProvider && modelId)) {
|
||||
throw badRequest("Both modelProvider and modelId must be provided together, or neither should be provided");
|
||||
}
|
||||
|
||||
try {
|
||||
const ip = req.ip || req.socket.remoteAddress || "unknown";
|
||||
const scopedStore = await getScopedStoreForRequest(req);
|
||||
@@ -386,6 +399,8 @@ export function createMissionRouter(
|
||||
missionTitle.trim(),
|
||||
rootDir,
|
||||
settings.promptOverrides,
|
||||
modelProvider,
|
||||
modelId,
|
||||
);
|
||||
res.status(201).json({ sessionId });
|
||||
} catch (err: any) {
|
||||
|
||||
Reference in New Issue
Block a user