This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import type { MultiplexRequest, Provider } from "./types.js";
|
||||
|
||||
export const QUICK_QUESTION_SYSTEM_PROMPT =
|
||||
"You are answering a quick question in a one-shot experience intended to replace a quick Google search. " +
|
||||
"Give a succinct, direct, self-contained answer. Do not ask follow-up questions, invite the user to continue, " +
|
||||
"or offer additional help. If the question is ambiguous, make the most reasonable assumption and state it briefly only when needed.";
|
||||
|
||||
export type QuickQuestionRequest = {
|
||||
provider: Provider;
|
||||
model: string;
|
||||
question: string;
|
||||
enabledTools?: string[];
|
||||
userLocation?: string;
|
||||
temperature?: number;
|
||||
maxTokens?: number;
|
||||
};
|
||||
|
||||
export function buildQuickQuestionMultiplexRequest({
|
||||
question,
|
||||
...request
|
||||
}: QuickQuestionRequest): MultiplexRequest {
|
||||
return {
|
||||
...request,
|
||||
persist: false,
|
||||
messages: [
|
||||
{ role: "system", content: QUICK_QUESTION_SYSTEM_PROMPT },
|
||||
{ role: "user", content: question },
|
||||
],
|
||||
};
|
||||
}
|
||||
+33
-1
@@ -11,6 +11,7 @@ import { runMultiplex } from "./llm/multiplexer.js";
|
||||
import { runMultiplexStream, type StreamEvent } from "./llm/streaming.js";
|
||||
import { getAvailableChatTools, normalizeEnabledChatTools } from "./llm/chat-tools.js";
|
||||
import { getModelCatalogSnapshot } from "./llm/model-catalog.js";
|
||||
import { buildQuickQuestionMultiplexRequest } from "./llm/quick-question.js";
|
||||
import { openaiClient } from "./llm/providers.js";
|
||||
import { serializeProviderFields, toPrismaProvider } from "./llm/provider-ids.js";
|
||||
import { exaClient } from "./search/exa.js";
|
||||
@@ -205,6 +206,16 @@ const CompletionStreamBody = z
|
||||
}
|
||||
});
|
||||
|
||||
const QuickQuestionStreamBody = z.object({
|
||||
provider: ProviderSchema,
|
||||
model: z.string().min(1),
|
||||
question: z.string().trim().min(1),
|
||||
enabledTools: EnabledToolsSchema.optional(),
|
||||
userLocation: z.string().trim().min(1).max(200).optional(),
|
||||
temperature: z.number().min(0).max(2).optional(),
|
||||
maxTokens: z.number().int().positive().optional(),
|
||||
});
|
||||
|
||||
function mergeAttachmentsIntoMetadata(metadata: unknown, attachments?: ChatAttachment[]) {
|
||||
if (!attachments?.length) return metadata as any;
|
||||
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
|
||||
@@ -1564,7 +1575,28 @@ export async function registerRoutes(app: FastifyInstance) {
|
||||
};
|
||||
});
|
||||
|
||||
// Streaming SSE endpoint.
|
||||
// One-shot, non-persistent Quick Question SSE endpoint.
|
||||
app.post("/v1/quick-questions/stream", async (req, reply) => {
|
||||
requireAdmin(req);
|
||||
|
||||
const parsed = QuickQuestionStreamBody.safeParse(req.body);
|
||||
if (!parsed.success) return app.httpErrors.badRequest(parsed.error.message);
|
||||
const body = withRequestUserLocation(parsed.data, req);
|
||||
|
||||
reply.raw.writeHead(200, buildSseHeaders(typeof req.headers.origin === "string" ? req.headers.origin : undefined));
|
||||
reply.raw.flushHeaders();
|
||||
|
||||
for await (const ev of runMultiplexStream(buildQuickQuestionMultiplexRequest(body))) {
|
||||
writeSseEvent(reply, mapChatStreamEvent(ev));
|
||||
}
|
||||
|
||||
if (!reply.raw.destroyed && !reply.raw.writableEnded) {
|
||||
reply.raw.end();
|
||||
}
|
||||
return reply;
|
||||
});
|
||||
|
||||
// General chat completion SSE endpoint.
|
||||
app.post("/v1/chat-completions/stream", async (req, reply) => {
|
||||
requireAdmin(req);
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import {
|
||||
buildQuickQuestionMultiplexRequest,
|
||||
QUICK_QUESTION_SYSTEM_PROMPT,
|
||||
} from "../src/llm/quick-question.js";
|
||||
|
||||
test("quick question requests receive the server-owned one-shot system prompt", () => {
|
||||
const request = buildQuickQuestionMultiplexRequest({
|
||||
provider: "openai",
|
||||
model: "gpt-4.1-mini",
|
||||
question: "How do I reset my password?",
|
||||
enabledTools: ["web_search"],
|
||||
userLocation: "Los Angeles, CA",
|
||||
});
|
||||
|
||||
assert.equal(request.persist, false);
|
||||
assert.equal(request.chatId, undefined);
|
||||
assert.deepEqual(request.messages, [
|
||||
{ role: "system", content: QUICK_QUESTION_SYSTEM_PROMPT },
|
||||
{ role: "user", content: "How do I reset my password?" },
|
||||
]);
|
||||
assert.deepEqual(request.enabledTools, ["web_search"]);
|
||||
assert.equal(request.userLocation, "Los Angeles, CA");
|
||||
assert.match(QUICK_QUESTION_SYSTEM_PROMPT, /succinct, direct, self-contained answer/i);
|
||||
assert.match(QUICK_QUESTION_SYSTEM_PROMPT, /do not ask follow-up questions/i);
|
||||
});
|
||||
Reference in New Issue
Block a user