File size: 12,200 Bytes
e97be0e 2a7f67c e97be0e 8a5bedd 4fd597d e97be0e 2a7f67c e97be0e 2a7f67c e97be0e 2a7f67c e97be0e 6edfd36 8c42385 e97be0e 6edfd36 e97be0e c9e8a3f 6edfd36 e97be0e 2a7f67c 4fd597d 2a7f67c 4fd597d c203651 2a7f67c c203651 2a7f67c 4fd597d 620e712 4fd597d c203651 4fd597d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
import zod from 'https://cdn.jsdelivr.net/npm/zod@4.0.10/+esm'
/**
* Met en forme le prompt template passé en paramètres avec les arguments
* @param {String} template
* @param {Object} args
*/
export function formatTemplate(template, args) {
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => {
// 'match' est la correspondance complète (ex: "{nom}")
// 'key' est le contenu du premier groupe capturé (ex: "nom")
if (key in args)
return args[key];
// Si la clé n'est pas trouvée dans args, on laisse le placeholder tel quel.
return "";
});
}
/**
* Recupère le prompt pour la tâche spécifiée.
* @param {String} task
*/
export async function retrieveTemplate(task) {
const req = await fetch(`/prompt/${task}`)
return await req.text();
}
/**
* Lance un deep search sur le serveur pour les topics donnés.
* @param {Array} topics
*/
export async function performDeepSearch(topics) {
const response = await fetch('/solutions/search_prior_art', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ topics: topics })
});
const results = await response.json();
console.log(results);
return results.content;
}
/**
* Genère une completion avec le LLM specifié
* @param {String} providerUrl - URL du provider du LLM
* @param {String} modelName - Nom du modèle à appeler
* @param {String} apiKey - API key a utiliser
* @param {Array<{role: string, content: string}>} messages - Liste de messages à passer au modèle
* @param {Number} temperature - Température à utiliser pour la génération
*/
export async function generateCompletion(providerUrl, modelName, apiKey, messages, temperature = 0.5) {
const genEndpoint = providerUrl + "/chat/completions"
try {
const response = await fetch(genEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`, // OpenAI-like authorization header
},
body: JSON.stringify({
model: modelName,
messages: messages,
temperature: temperature,
}),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(`API request failed with status ${response.status}: ${errorData.error?.message || 'Unknown error'}`);
}
const data = await response.json();
if (data.choices && data.choices.length > 0 && data.choices[0].message && data.choices[0].message.content)
return data.choices[0].message.content;
} catch (error) {
console.error("Error calling private LLM :", error);
throw error;
}
}
/**
* Genère une completion structurée avec le LLM specifié
* @param {String} providerUrl - URL du provider du LLM
* @param {String} modelName - Nom du modèle à appeler
* @param {String} apiKey - API key a utiliser
* @param {Array<{role: string, content: string}>} messages - Liste de messages à passer au modèle
* @param {Object} schema - Zod schema to use for structured generation
* @param {Number} temperature - Température à utiliser pour la génération
*/
//TODO: Find the correct args to constrain the LLM to the json schema instead of enforcing json correct parsing
export async function generateStructuredCompletion(providerUrl, modelName, apiKey, messages, schema, temperature = 0.5) {
const genEndpoint = providerUrl + "/chat/completions";
try {
const response = await fetch(genEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
body: JSON.stringify({
model: modelName,
messages: messages,
temperature: temperature,
response_format: { type: "json_object" }
}),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(`API request failed with status ${response.status}: ${errorData.error?.message || 'Unknown error'}`);
}
const data = await response.json();
console.log(data.choices[0].message.content);
// parse json output
const parsedJSON = JSON.parse(data.choices[0].message.content.replace('```json', '').replace("```", ""));
// validate output with zod
const validatedSchema = schema.parse(parsedJSON);
return validatedSchema;
} catch (error) {
console.error("Error calling private LLM :", error);
throw error;
}
}
/**
* Retrieves a list of available models from an OpenAI-compatible API using fetch.
*
* @param {string} providerUrl The base URL of the OpenAI-compatible API endpoint (e.g., "http://localhost:8000/v1").
* @param {string} apiKey The API key for authentication.
* @returns {Promise<Array<string>>} A promise that resolves with an array of model names, or rejects with an error.
*/
export async function getModelList(providerUrl, apiKey) {
try {
// Construct the full URL for the models endpoint
const modelsUrl = `${providerUrl}/models`;
console.log(modelsUrl);
// Make a GET request to the models endpoint using fetch
const response = await fetch(modelsUrl, {
method: 'GET', // Explicitly state the method
headers: {
'Authorization': `Bearer ${apiKey}`, // OpenAI-compatible authorization header
'Content-Type': 'application/json',
},
});
// Check if the request was successful (status code 200-299)
if (!response.ok) {
// If the response is not OK, try to get more error details
const errorData = await response.json().catch(() => ({})); // Attempt to parse JSON error, fallback to empty object
throw new Error(`HTTP error! Status: ${response.status}, Message: ${errorData.message || response.statusText}`);
}
// Parse the JSON response body
const data = await response.json();
// The response data structure for OpenAI-compatible APIs usually contains a 'data' array
// where each item represents a model and has an 'id' property.
if (data && Array.isArray(data.data)) {
const allModelNames = data.data.map(model => model.id);
// Filter out models containing "embedding" (case-insensitive)
const filteredModelNames = allModelNames.filter(modelName =>
!modelName.toLowerCase().includes('embedding')
);
return filteredModelNames;
} else {
// Handle cases where the response format is unexpected
throw new Error('Unexpected response format from the API. Could not find model list.');
}
} catch (error) {
console.error('Error fetching model list:', error.message);
// Re-throw the error to allow the caller to handle it
throw error;
}
}
// # ========================================================================================== Idea assessment logic ==================================================================
// JS schema for the assessment output.
// keep in sync with contents of "extract" prompt
const StructuredAssessmentOutput = zod.object({
final_verdict: zod.string(),
summary: zod.string(),
insights: zod.array(zod.string()),
});
export async function assessSolution(providerUrl, modelName, apiKey, solution, assessment_rules, portfolio_info) {
const template = await retrieveTemplate("assess");
const assessment_template = formatTemplate(template, {
notation_criterias: assessment_rules,
business: portfolio_info,
problem_description: solution.problem_description,
solution_description: solution.solution_description,
});
const assessment_full = await generateCompletion(providerUrl, modelName, apiKey, [
{ role: "user", content: assessment_template }
]);
const structured_template = await retrieveTemplate("extract");
const structured_filled_template = formatTemplate(structured_template, {
"report": assessment_full,
"response_schema": zod.toJSONSchema(StructuredAssessmentOutput)
})
const extracted_info = await generateStructuredCompletion(providerUrl, modelName, apiKey, [{ role: "user", content: structured_filled_template }], StructuredAssessmentOutput);
return { assessment_full, extracted_info };
}
export async function refineSolution(providerUrl, modelName, apiKey, solution, insights, user_insights, assessment_rules, portfolio_info) {
const template = await retrieveTemplate("refine");
const refine_template = formatTemplate(template, {
"problem_description": solution.problem_description,
"solution_description": solution.solution_description,
"insights": insights.join("\n -"),
"user_insights": user_insights,
"business_info": portfolio_info,
});
console.log(refine_template);
const refined_idea = await generateCompletion(providerUrl, modelName, apiKey, [{ role: "user", content: refine_template }]);
const newSolution = structuredClone(solution);
newSolution.solution_description = refined_idea;
return newSolution;
}
// FTO analysis
// JS schema for FTO analysis topic extraction
const FTOAnalysisTopicsSchema = zod.object({
topics: zod.array(zod.string())
});
/**
* Extract the topics to search for FTO
*/
async function getFtoAnalysisTopics(providerUrl, modelName, apiKey, idea, count) {
const template = await retrieveTemplate("fto_topics");
const structured_template = formatTemplate(template, {
"problem_description": idea.problem_description,
"solution_description": idea.solution_description,
"response_schema": zod.toJSONSchema(FTOAnalysisTopicsSchema),
"max_topic_count": count
});
const topics = await generateStructuredCompletion(providerUrl, modelName, apiKey, [{ role: "user", content: structured_template }], FTOAnalysisTopicsSchema);
return topics;
}
/*
* Assess the infringement of the idea wrt
*/
async function assessFTOReport(providerUrl, modelName, apiKey, solution, fto_report, portfolio_info) {
const template = await retrieveTemplate("fto_assess");
const assessment_template = formatTemplate(template, {
business: portfolio_info,
fto_report: fto_report,
problem_description: solution.problem_description,
solution_description: solution.solution_description,
});
console.log("FTO Length: " + assessment_template.length);
const assessment_full = await generateCompletion(providerUrl, modelName, apiKey, [
{ role: "user", content: assessment_template }
]);
const structured_template = await retrieveTemplate("extract");
const structured_filled_template = formatTemplate(structured_template, {
"report": assessment_full,
"response_schema": zod.toJSONSchema(StructuredAssessmentOutput)
})
const extracted_info = await generateStructuredCompletion(providerUrl, modelName, apiKey, [{ role: "user", content: structured_filled_template }], StructuredAssessmentOutput);
return { assessment_full, extracted_info };
}
export async function runFTOAnalysis(providerUrl, providerModel, apiKey, solution, portfolio_info, ftoTopicCount) {
const fto_topics = await getFtoAnalysisTopics(providerUrl, providerModel, apiKey, solution, ftoTopicCount);
console.log(fto_topics);
const fto_report = await performDeepSearch(fto_topics.topics);
const assess_results = await assessFTOReport(providerUrl, providerModel, apiKey, solution, fto_report, portfolio_info);
console.log(assess_results.extracted_info);
return {
fto_topics: fto_topics,
fto_report: fto_report,
assessment_full: assess_results.assessment_full,
extracted_info: assess_results.extracted_info,
};
}
|