Add selectable scopes and models to RAG playground
This commit is contained in:
parent
1847eaefbe
commit
93985976d6
5 changed files with 135 additions and 2 deletions
|
|
@ -14,6 +14,7 @@ const queryMode = document.getElementById("queryMode");
|
|||
const queryIntent = document.getElementById("queryIntent");
|
||||
const queryOperation = document.getElementById("queryOperation");
|
||||
const answerModel = document.getElementById("answerModel");
|
||||
const useModelInRetrieve = document.getElementById("useModelInRetrieve");
|
||||
const scopePresetSelect = document.getElementById("scopePresetSelect");
|
||||
const scopeSourceRef = document.getElementById("scopeSourceRef");
|
||||
const scopeTags = document.getElementById("scopeTags");
|
||||
|
|
@ -83,6 +84,30 @@ async function loadScopes() {
|
|||
}
|
||||
}
|
||||
|
||||
async function loadAnswerModels() {
|
||||
try {
|
||||
const payload = await fetch("/models/answer").then((response) => response.json());
|
||||
answerModel.innerHTML = "";
|
||||
|
||||
for (const model of payload.models || []) {
|
||||
const option = document.createElement("option");
|
||||
option.value = model;
|
||||
option.textContent = model;
|
||||
answerModel.appendChild(option);
|
||||
}
|
||||
|
||||
if (payload.defaultModel) {
|
||||
answerModel.value = payload.defaultModel;
|
||||
}
|
||||
} catch {
|
||||
answerModel.innerHTML = "";
|
||||
const option = document.createElement("option");
|
||||
option.value = "openai/gpt-4.1-mini";
|
||||
option.textContent = "openai/gpt-4.1-mini";
|
||||
answerModel.appendChild(option);
|
||||
}
|
||||
}
|
||||
|
||||
scopePresetSelect.addEventListener("change", () => {
|
||||
if (!scopePresetSelect.value) {
|
||||
return;
|
||||
|
|
@ -134,6 +159,7 @@ queryButton.addEventListener("click", async () => {
|
|||
intent: queryIntent.value,
|
||||
query: queryInput.value,
|
||||
model: answerModel.value,
|
||||
useModelInRetrieve: useModelInRetrieve.checked,
|
||||
scope: {
|
||||
sourceRef: scopeSourceRef.value,
|
||||
tags: splitTags(scopeTags.value)
|
||||
|
|
@ -186,3 +212,4 @@ presetCode.addEventListener("click", () => {
|
|||
});
|
||||
|
||||
loadScopes();
|
||||
loadAnswerModels();
|
||||
|
|
|
|||
|
|
@ -68,7 +68,9 @@
|
|||
</select>
|
||||
</label>
|
||||
<label>Modelo answer
|
||||
<input id="answerModel" value="openai/gpt-4.1-mini" />
|
||||
<select id="answerModel">
|
||||
<option value="openai/gpt-4.1-mini">openai/gpt-4.1-mini</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
|
@ -91,6 +93,11 @@
|
|||
Comparar tambien con respuesta sin RAG
|
||||
</label>
|
||||
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="useModelInRetrieve" />
|
||||
Usar tambien un modelo para sintetizar el resultado de retrieve
|
||||
</label>
|
||||
|
||||
<div class="actions">
|
||||
<button id="queryButton">Ejecutar consulta</button>
|
||||
<button id="presetDocs" class="secondary">Preset docs</button>
|
||||
|
|
|
|||
|
|
@ -58,6 +58,18 @@ export function createApp() {
|
|||
}
|
||||
});
|
||||
|
||||
app.get("/models/answer", async (_req, res) => {
|
||||
try {
|
||||
const models = await answerService.listAvailableAnswerModels();
|
||||
res.json({
|
||||
defaultModel: env.answerModel,
|
||||
models
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({ ok: false, error: error instanceof Error ? error.message : "Unknown answer models error" });
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/ingest", async (req, res) => {
|
||||
try {
|
||||
const result = await ingestService.ingest(req.body);
|
||||
|
|
@ -72,6 +84,8 @@ export function createApp() {
|
|||
const mode = (req.body.mode ?? "auto") as ChunkMode;
|
||||
const intent = (req.body.intent ?? "specific") as RetrieveIntent;
|
||||
const query = String(req.body.query ?? "");
|
||||
const model = req.body.model ? String(req.body.model) : undefined;
|
||||
const useModel = Boolean(req.body.useModelInRetrieve);
|
||||
const scope: RetrieveScope | undefined = req.body.scope
|
||||
? {
|
||||
sourceId: req.body.scope.sourceId,
|
||||
|
|
@ -80,6 +94,15 @@ export function createApp() {
|
|||
}
|
||||
: undefined;
|
||||
const result = await retrieveService.retrieve(mode, intent, query, scope);
|
||||
if (useModel) {
|
||||
const modelSummary = await answerService.summarizeRetrieve(query, result, model);
|
||||
res.json({
|
||||
...result,
|
||||
model: modelSummary.model,
|
||||
modelSummary: modelSummary.summary
|
||||
});
|
||||
return;
|
||||
}
|
||||
res.json(result);
|
||||
} catch (error) {
|
||||
res.status(500).json({ ok: false, error: error instanceof Error ? error.message : "Unknown retrieve error" });
|
||||
|
|
|
|||
|
|
@ -1,8 +1,16 @@
|
|||
import OpenAI from "openai";
|
||||
import { env } from "../../config/env.js";
|
||||
import type { AnswerResponse, ChunkMode, DirectAnswerResponse, RetrieveIntent, RetrieveScope, RetrievedItem } from "../../shared/types/rag.js";
|
||||
import type { AnswerResponse, ChunkMode, DirectAnswerResponse, RetrieveIntent, RetrieveResponse, RetrieveScope, RetrievedItem } from "../../shared/types/rag.js";
|
||||
import { RetrieveService } from "../retrieve/service.js";
|
||||
|
||||
const answerModelFallbacks = [
|
||||
"openai/gpt-4.1-mini",
|
||||
"qwen/qwen-2.5-72b-instruct",
|
||||
"google/gemini-2.0-flash-001",
|
||||
"anthropic/claude-3.5-haiku",
|
||||
"mistralai/mistral-small-3.1-24b-instruct"
|
||||
];
|
||||
|
||||
function buildPrompt(query: string, summary: string, items: RetrievedItem[]): string {
|
||||
const context = items
|
||||
.map((item, index) => [
|
||||
|
|
@ -104,4 +112,70 @@ export class AnswerService {
|
|||
answer: completion.choices[0]?.message?.content?.trim() || "No se pudo generar respuesta."
|
||||
};
|
||||
}
|
||||
|
||||
async summarizeRetrieve(query: string, retrieved: RetrieveResponse, modelOverride?: string): Promise<{ model: string; summary: string }> {
|
||||
if (!env.answerApiKey) {
|
||||
throw new Error("Missing ANSWER_API_KEY for answer provider");
|
||||
}
|
||||
|
||||
const answerModel = modelOverride?.trim() || env.answerModel;
|
||||
const completion = await this.client.chat.completions.create({
|
||||
model: answerModel,
|
||||
temperature: 0.2,
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: "Eres un sintetizador de contexto RAG. No des una respuesta final al usuario; devuelve una panoramica util y breve del contexto recuperado para que otro agente pueda continuar trabajando con el."
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
`Consulta original: ${query}`,
|
||||
`Intent: ${retrieved.intent}`,
|
||||
`Resumen base: ${retrieved.summary}`,
|
||||
`Temas: ${retrieved.topics.join(", ")}`,
|
||||
`Puntos criticos: ${retrieved.criticalPoints.join(", ")}`,
|
||||
"Fragmentos recuperados:",
|
||||
retrieved.items.map((item, index) => [
|
||||
`Fuente ${index + 1}: ${item.title}`,
|
||||
item.sectionTitle ? `section_title: ${item.sectionTitle}` : undefined,
|
||||
item.startLine ? `line_range: ${item.startLine}-${item.endLine ?? item.startLine}` : undefined,
|
||||
item.content
|
||||
].filter(Boolean).join("\n")).join("\n\n---\n\n")
|
||||
].join("\n\n")
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return {
|
||||
model: answerModel,
|
||||
summary: completion.choices[0]?.message?.content?.trim() || retrieved.summary
|
||||
};
|
||||
}
|
||||
|
||||
async listAvailableAnswerModels(): Promise<string[]> {
|
||||
try {
|
||||
const response = await fetch(`${env.answerBaseUrl}/models`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${env.answerApiKey}`
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Model list request failed with ${response.status}`);
|
||||
}
|
||||
|
||||
const payload = await response.json() as { data?: Array<{ id?: string }> };
|
||||
const ids = (payload.data ?? [])
|
||||
.map((item) => item.id)
|
||||
.filter((id): id is string => Boolean(id))
|
||||
.filter((id) => !/embed/i.test(id));
|
||||
|
||||
const preferred = answerModelFallbacks.filter((id) => ids.includes(id));
|
||||
const others = ids.filter((id) => !preferred.includes(id)).sort();
|
||||
return [...preferred, ...others].slice(0, 80);
|
||||
} catch {
|
||||
return answerModelFallbacks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,9 @@ export interface AvailableScope {
|
|||
export interface RetrieveResponse {
|
||||
mode: ChunkMode;
|
||||
intent: RetrieveIntent;
|
||||
model?: string;
|
||||
summary: string;
|
||||
modelSummary?: string;
|
||||
topics: string[];
|
||||
criticalPoints: string[];
|
||||
items: RetrievedItem[];
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue