Spaces:
Sleeping
Sleeping
| 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, | |
| }; | |
| } | |