Lucas ARRIESSE
commited on
Commit
·
46d8b46
1
Parent(s):
f6bffda
Remove old endpoints
Browse files
api/solutions.py
CHANGED
@@ -7,13 +7,13 @@ from jinja2 import Environment
|
|
7 |
from litellm.router import Router
|
8 |
from dependencies import INSIGHT_FINDER_BASE_URL, get_http_client, get_llm_router, get_prompt_templates
|
9 |
from typing import Awaitable, Callable, TypeVar
|
10 |
-
from schemas import _RefinedSolutionModel,
|
11 |
|
12 |
# Router for solution generation and critique
|
13 |
router = APIRouter(tags=["solution generation and critique"])
|
14 |
|
15 |
|
16 |
-
# ============== utilities
|
17 |
T = TypeVar("T")
|
18 |
A = TypeVar("A")
|
19 |
|
@@ -32,68 +32,65 @@ async def retry_until(
|
|
32 |
last_value = await func(arg)
|
33 |
return last_value
|
34 |
|
35 |
-
#
|
36 |
|
|
|
|
|
37 |
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
|
|
|
|
|
|
|
|
|
|
43 |
|
44 |
-
|
45 |
-
|
46 |
-
req_prompt = await prompt_env.get_template("search_solution_v2.txt").render_async(**{
|
47 |
-
"category": cat.model_dump(),
|
48 |
-
"user_constraints": params.user_constraints,
|
49 |
-
})
|
50 |
|
51 |
-
#
|
52 |
-
|
53 |
-
|
54 |
-
req_completion = await llm_router.acompletion(model="gemini-v2", messages=[
|
55 |
-
{"role": "user", "content": req_prompt}
|
56 |
-
], tools=[{"googleSearch": {}}], tool_choice="required")
|
57 |
|
58 |
-
#
|
|
|
|
|
59 |
|
60 |
-
|
61 |
-
"solution": req_completion.choices[0].message.content,
|
62 |
-
"response_schema": _SearchedSolutionModel.model_json_schema()
|
63 |
-
})
|
64 |
|
65 |
-
|
66 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
], response_format=_SearchedSolutionModel)
|
68 |
-
solution_model = _SearchedSolutionModel.model_validate_json(
|
69 |
-
structured_completion.choices[0].message.content)
|
70 |
-
|
71 |
-
# ======================== build the final solution object ================================
|
72 |
|
73 |
-
|
74 |
-
|
75 |
-
try:
|
76 |
-
sources_metadata.extend([{"name": a["web"]["title"], "url": a["web"]["uri"]}
|
77 |
-
for a in req_completion["vertex_ai_grounding_metadata"][0]['groundingChunks']])
|
78 |
-
except KeyError as ke:
|
79 |
-
pass
|
80 |
|
81 |
-
|
82 |
Context="",
|
83 |
Requirements=[
|
84 |
-
cat.requirements[i].requirement for i in
|
85 |
],
|
86 |
-
Problem_Description=
|
87 |
-
Solution_Description=
|
88 |
-
References=
|
89 |
Category_Id=cat.id,
|
90 |
)
|
91 |
-
return final_sol
|
92 |
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
|
|
|
|
97 |
|
98 |
return SolutionSearchResponse(solutions=final_solutions)
|
99 |
|
@@ -155,65 +152,3 @@ async def refine_solutions(params: CritiqueResponse, prompt_env: Environment = D
|
|
155 |
|
156 |
return SolutionSearchResponse(solutions=refined_solutions)
|
157 |
|
158 |
-
# =============================================================== Search solutions =========================================
|
159 |
-
|
160 |
-
|
161 |
-
@router.post("/search_solutions_if")
|
162 |
-
async def search_solutions_if(req: SolutionSearchV2Request, prompt_env: Environment = Depends(get_prompt_templates), llm_router: Router = Depends(get_llm_router), http_client: AsyncClient = Depends(get_http_client)) -> SolutionSearchResponse:
|
163 |
-
|
164 |
-
async def _search_solution_inner(cat: ReqGroupingCategory):
|
165 |
-
# process requirements into insight finder format
|
166 |
-
fmt_completion = await llm_router.acompletion("gemini-v2", messages=[
|
167 |
-
{
|
168 |
-
"role": "user",
|
169 |
-
"content": await prompt_env.get_template("if/format_requirements.txt").render_async(**{
|
170 |
-
"category": cat.model_dump(),
|
171 |
-
"response_schema": InsightFinderConstraintsList.model_json_schema()
|
172 |
-
})
|
173 |
-
}], response_format=InsightFinderConstraintsList)
|
174 |
-
|
175 |
-
fmt_model = InsightFinderConstraintsList.model_validate_json(
|
176 |
-
fmt_completion.choices[0].message.content)
|
177 |
-
|
178 |
-
# translate from a structured output to a dict for insights finder
|
179 |
-
formatted_constraints = {'constraints': {
|
180 |
-
cons.title: cons.description for cons in fmt_model.constraints}}
|
181 |
-
|
182 |
-
# fetch technologies from insight finder
|
183 |
-
technologies_req = await http_client.post(INSIGHT_FINDER_BASE_URL + "process-constraints", content=json.dumps(formatted_constraints))
|
184 |
-
technologies = TechnologyData.model_validate(technologies_req.json())
|
185 |
-
|
186 |
-
# =============================================================== synthesize solution using LLM =========================================
|
187 |
-
|
188 |
-
format_solution = await llm_router.acompletion("gemini-v2", messages=[{
|
189 |
-
"role": "user",
|
190 |
-
"content": await prompt_env.get_template("if/synthesize_solution.txt").render_async(**{
|
191 |
-
"category": cat.model_dump(),
|
192 |
-
"technologies": technologies.model_dump()["technologies"],
|
193 |
-
"user_constraints": req.user_constraints,
|
194 |
-
"response_schema": _SearchedSolutionModel.model_json_schema()
|
195 |
-
})}
|
196 |
-
], response_format=_SearchedSolutionModel)
|
197 |
-
|
198 |
-
format_solution_model = _SearchedSolutionModel.model_validate_json(
|
199 |
-
format_solution.choices[0].message.content)
|
200 |
-
|
201 |
-
final_solution = SolutionModel(
|
202 |
-
Context="",
|
203 |
-
Requirements=[
|
204 |
-
cat.requirements[i].requirement for i in format_solution_model.requirement_ids
|
205 |
-
],
|
206 |
-
Problem_Description=format_solution_model.problem_description,
|
207 |
-
Solution_Description=format_solution_model.solution_description,
|
208 |
-
References=[],
|
209 |
-
Category_Id=cat.id,
|
210 |
-
)
|
211 |
-
|
212 |
-
# ========================================================================================================================================
|
213 |
-
|
214 |
-
return final_solution
|
215 |
-
|
216 |
-
tasks = await asyncio.gather(*[_search_solution_inner(cat) for cat in req.categories], return_exceptions=True)
|
217 |
-
final_solutions = [sol for sol in tasks if not isinstance(sol, Exception)]
|
218 |
-
|
219 |
-
return SolutionSearchResponse(solutions=final_solutions)
|
|
|
7 |
from litellm.router import Router
|
8 |
from dependencies import INSIGHT_FINDER_BASE_URL, get_http_client, get_llm_router, get_prompt_templates
|
9 |
from typing import Awaitable, Callable, TypeVar
|
10 |
+
from schemas import _RefinedSolutionModel, _SearchedSolutionModel, _SolutionCriticismOutput, CriticizeSolutionsRequest, CritiqueResponse, InsightFinderConstraintsList, ReqGroupingCategory, ReqGroupingRequest, ReqGroupingResponse, ReqSearchLLMResponse, ReqSearchRequest, ReqSearchResponse, SolutionCriticism, SolutionModel, SolutionSearchResponse, SolutionSearchV2Request, TechnologyData
|
11 |
|
12 |
# Router for solution generation and critique
|
13 |
router = APIRouter(tags=["solution generation and critique"])
|
14 |
|
15 |
|
16 |
+
# ============== utilities ===========================
|
17 |
T = TypeVar("T")
|
18 |
A = TypeVar("A")
|
19 |
|
|
|
32 |
last_value = await func(arg)
|
33 |
return last_value
|
34 |
|
35 |
+
# =================================================== Search solutions ============================================================================
|
36 |
|
37 |
+
@router.post("/search_solutions")
|
38 |
+
async def search_solutions_if(req: SolutionSearchV2Request, prompt_env: Environment = Depends(get_prompt_templates), llm_router: Router = Depends(get_llm_router), http_client: AsyncClient = Depends(get_http_client)) -> SolutionSearchResponse:
|
39 |
|
40 |
+
async def _search_solution_inner(cat: ReqGroupingCategory):
|
41 |
+
# process requirements into insight finder format
|
42 |
+
fmt_completion = await llm_router.acompletion("gemini-v2", messages=[
|
43 |
+
{
|
44 |
+
"role": "user",
|
45 |
+
"content": await prompt_env.get_template("format_requirements.txt").render_async(**{
|
46 |
+
"category": cat.model_dump(),
|
47 |
+
"response_schema": InsightFinderConstraintsList.model_json_schema()
|
48 |
+
})
|
49 |
+
}], response_format=InsightFinderConstraintsList)
|
50 |
|
51 |
+
fmt_model = InsightFinderConstraintsList.model_validate_json(
|
52 |
+
fmt_completion.choices[0].message.content)
|
|
|
|
|
|
|
|
|
53 |
|
54 |
+
# translate from a structured output to a dict for insights finder
|
55 |
+
formatted_constraints = {'constraints': {
|
56 |
+
cons.title: cons.description for cons in fmt_model.constraints}}
|
|
|
|
|
|
|
57 |
|
58 |
+
# fetch technologies from insight finder
|
59 |
+
technologies_req = await http_client.post(INSIGHT_FINDER_BASE_URL + "process-constraints", content=json.dumps(formatted_constraints))
|
60 |
+
technologies = TechnologyData.model_validate(technologies_req.json())
|
61 |
|
62 |
+
# =============================================================== synthesize solution using LLM =========================================
|
|
|
|
|
|
|
63 |
|
64 |
+
format_solution = await llm_router.acompletion("gemini-v2", messages=[{
|
65 |
+
"role": "user",
|
66 |
+
"content": await prompt_env.get_template("synthesize_solution.txt").render_async(**{
|
67 |
+
"category": cat.model_dump(),
|
68 |
+
"technologies": technologies.model_dump()["technologies"],
|
69 |
+
"user_constraints": req.user_constraints,
|
70 |
+
"response_schema": _SearchedSolutionModel.model_json_schema()
|
71 |
+
})}
|
72 |
], response_format=_SearchedSolutionModel)
|
|
|
|
|
|
|
|
|
73 |
|
74 |
+
format_solution_model = _SearchedSolutionModel.model_validate_json(
|
75 |
+
format_solution.choices[0].message.content)
|
|
|
|
|
|
|
|
|
|
|
76 |
|
77 |
+
final_solution = SolutionModel(
|
78 |
Context="",
|
79 |
Requirements=[
|
80 |
+
cat.requirements[i].requirement for i in format_solution_model.requirement_ids
|
81 |
],
|
82 |
+
Problem_Description=format_solution_model.problem_description,
|
83 |
+
Solution_Description=format_solution_model.solution_description,
|
84 |
+
References=[],
|
85 |
Category_Id=cat.id,
|
86 |
)
|
|
|
87 |
|
88 |
+
# ========================================================================================================================================
|
89 |
+
|
90 |
+
return final_solution
|
91 |
+
|
92 |
+
tasks = await asyncio.gather(*[_search_solution_inner(cat) for cat in req.categories], return_exceptions=True)
|
93 |
+
final_solutions = [sol for sol in tasks if not isinstance(sol, Exception)]
|
94 |
|
95 |
return SolutionSearchResponse(solutions=final_solutions)
|
96 |
|
|
|
152 |
|
153 |
return SolutionSearchResponse(solutions=refined_solutions)
|
154 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
prompts/{if/format_requirements.txt → format_requirements.txt}
RENAMED
File without changes
|
prompts/search_solution.txt
DELETED
@@ -1,23 +0,0 @@
|
|
1 |
-
<role>You are an expert system designer</role>
|
2 |
-
<task>
|
3 |
-
Your task is to create a solution that should cover the maximum possible of requirements at once.
|
4 |
-
The solution should be composed of multiple mechanisms, that are defined as processes, methods, or sequences of steps using a technology that explain how something works or achieves its requirements.
|
5 |
-
You may for that search mechanisms for the different requirements.
|
6 |
-
**Please actually make searches and do not simulate them.**
|
7 |
-
|
8 |
-
You must aim for the following goals:
|
9 |
-
- The solution must aim to maximize requirement satisfaction while respecting the context.
|
10 |
-
- Provide a list of requirements addressed by the solution (provide only the requirement IDs)
|
11 |
-
- Please also detail each mechanism and how they work in the final solution description.
|
12 |
-
- Please describe the solution description using markdown in a consistent format.
|
13 |
-
</task>
|
14 |
-
|
15 |
-
Here is the category item and the associated requirements:
|
16 |
-
<requirements>
|
17 |
-
Category Title: {{category["title"]}}
|
18 |
-
Context: {{category["requirements"][0]["context"]}}
|
19 |
-
Requirements:
|
20 |
-
{% for req in category["requirements"] -%}
|
21 |
-
- {{loop.index0}} {{req["requirement"]}}
|
22 |
-
{% endfor -%}
|
23 |
-
</requirements>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
prompts/search_solution_v2.txt
DELETED
@@ -1,31 +0,0 @@
|
|
1 |
-
<role>You are an expert system designer</role>
|
2 |
-
<task>
|
3 |
-
Your task is to create a solution that should cover the maximum possible of requirements at once.
|
4 |
-
The solution should be composed of multiple mechanisms, that are defined as processes, methods, or sequences of steps using a technology that explain how something works or achieves its requirements.
|
5 |
-
You may for that search mechanisms for the different requirements.
|
6 |
-
|
7 |
-
**Please actually make searches and do not simulate them.**
|
8 |
-
|
9 |
-
You must aim for the following goals:
|
10 |
-
- The solution must aim to maximize requirement satisfaction while respecting the context.
|
11 |
-
- Provide a list of requirements addressed by the solution (provide only the requirement IDs)
|
12 |
-
- Please also detail each mechanism and how they work in the final solution description.
|
13 |
-
- Please describe the solution description using markdown in a consistent format.
|
14 |
-
</task>
|
15 |
-
|
16 |
-
Here is the category item and the associated requirements:
|
17 |
-
<requirements>
|
18 |
-
Category Title: {{category["title"]}}
|
19 |
-
Context: {{category["requirements"][0]["context"]}}
|
20 |
-
Requirements:
|
21 |
-
{% for req in category["requirements"] -%}
|
22 |
-
- {{loop.index0}} {{req["requirement"]}}
|
23 |
-
{% endfor -%}
|
24 |
-
</requirements>
|
25 |
-
|
26 |
-
{% if user_constraints is not none %}
|
27 |
-
Here are additional user constraints the solution must respect:
|
28 |
-
<user_constraints>
|
29 |
-
{{user_constraints}}
|
30 |
-
</user_constraints>
|
31 |
-
{% endif %}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
prompts/{if/synthesize_solution.txt → synthesize_solution.txt}
RENAMED
File without changes
|
static/index.html
CHANGED
@@ -270,7 +270,7 @@
|
|
270 |
Categorize
|
271 |
</button>
|
272 |
</div>
|
273 |
-
<div class="flex flex-wrap justify-end items-center gap-6">
|
274 |
<div class="tooltip" data-tip="Use Insight Finder's API to generate solutions">
|
275 |
<label class="label">
|
276 |
<span class="label-text text-base-content">Use insight finder solver</span>
|
@@ -278,7 +278,7 @@
|
|
278 |
<input type="checkbox" class="toggle toggle-primary" id="use-insight-finder-solver"
|
279 |
checked="false" />
|
280 |
</div>
|
281 |
-
</div>
|
282 |
</div>
|
283 |
<div id="categorized-requirements-container pt-10" class="mb-6">
|
284 |
<div class="flex mb-4 pt-10">
|
|
|
270 |
Categorize
|
271 |
</button>
|
272 |
</div>
|
273 |
+
<!-- <div class="flex flex-wrap justify-end items-center gap-6">
|
274 |
<div class="tooltip" data-tip="Use Insight Finder's API to generate solutions">
|
275 |
<label class="label">
|
276 |
<span class="label-text text-base-content">Use insight finder solver</span>
|
|
|
278 |
<input type="checkbox" class="toggle toggle-primary" id="use-insight-finder-solver"
|
279 |
checked="false" />
|
280 |
</div>
|
281 |
+
</div> -->
|
282 |
</div>
|
283 |
<div id="categorized-requirements-container pt-10" class="mb-6">
|
284 |
<div class="flex mb-4 pt-10">
|
static/js/script.js
CHANGED
@@ -335,13 +335,6 @@ async function extractRequirements() {
|
|
335 |
}
|
336 |
});
|
337 |
|
338 |
-
|
339 |
-
// const response = await fetch('/generate_requirements/', {
|
340 |
-
// method: 'POST',
|
341 |
-
// headers: { 'Content-Type': 'application/json' },
|
342 |
-
// body: req
|
343 |
-
// });
|
344 |
-
|
345 |
const data = response.data; // data in the SSE message contains the requirements response
|
346 |
requirements = data.requirements;
|
347 |
let req_id = 0;
|
@@ -1047,33 +1040,30 @@ function clearAllSolutions() {
|
|
1047 |
document.querySelectorAll('.solution-accordion').forEach(a => a.remove());
|
1048 |
}
|
1049 |
|
1050 |
-
async function
|
1051 |
console.log(selected_categories);
|
1052 |
|
1053 |
let input_req = structuredClone(selected_categories);
|
1054 |
input_req.user_constraints = user_constraints;
|
1055 |
-
console.log(useIFSolver);
|
1056 |
-
|
1057 |
-
const base_url = useIFSolver ? "/solutions/search_solutions_if" : "/solutions/search_solutions_gemini/v2"
|
1058 |
|
1059 |
-
let response = await fetch(
|
1060 |
let responseObj = await response.json()
|
1061 |
return responseObj;
|
1062 |
}
|
1063 |
|
1064 |
-
async function
|
1065 |
let response = await fetch('/solutions/criticize_solution', { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(solutions) })
|
1066 |
let responseObj = await response.json()
|
1067 |
solutionsCriticizedVersions.push(responseObj)
|
1068 |
}
|
1069 |
|
1070 |
-
async function
|
1071 |
let response = await fetch('/solutions/refine_solutions', { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(critiques) })
|
1072 |
let responseObj = await response.json()
|
1073 |
-
await
|
1074 |
}
|
1075 |
|
1076 |
-
async function workflow(steps = 1
|
1077 |
let soluce;
|
1078 |
showLoadingOverlay('Génération des solutions & critiques ....');
|
1079 |
|
@@ -1093,11 +1083,11 @@ async function workflow(steps = 1, useIFSolver = false) {
|
|
1093 |
}
|
1094 |
|
1095 |
if (solutionsCriticizedVersions.length == 0) {
|
1096 |
-
soluce = await
|
1097 |
-
await
|
1098 |
} else {
|
1099 |
let prevSoluce = solutionsCriticizedVersions[solutionsCriticizedVersions.length - 1];
|
1100 |
-
await
|
1101 |
}
|
1102 |
}
|
1103 |
hideLoadingOverlay();
|
@@ -1129,10 +1119,10 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
1129 |
// Événements pour les boutons de solutions (à implémenter plus tard)
|
1130 |
document.getElementById('get-solutions-btn').addEventListener('click', () => {
|
1131 |
const n_steps = document.getElementById('solution-gen-nsteps').value;
|
1132 |
-
workflow(n_steps
|
1133 |
});
|
1134 |
-
|
1135 |
-
workflow(1
|
1136 |
});
|
1137 |
});
|
1138 |
|
|
|
335 |
}
|
336 |
});
|
337 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
338 |
const data = response.data; // data in the SSE message contains the requirements response
|
339 |
requirements = data.requirements;
|
340 |
let req_id = 0;
|
|
|
1040 |
document.querySelectorAll('.solution-accordion').forEach(a => a.remove());
|
1041 |
}
|
1042 |
|
1043 |
+
async function generateSolutions(selected_categories, user_constraints = null) {
|
1044 |
console.log(selected_categories);
|
1045 |
|
1046 |
let input_req = structuredClone(selected_categories);
|
1047 |
input_req.user_constraints = user_constraints;
|
|
|
|
|
|
|
1048 |
|
1049 |
+
let response = await fetch("/solutions/search_solutions", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(input_req) })
|
1050 |
let responseObj = await response.json()
|
1051 |
return responseObj;
|
1052 |
}
|
1053 |
|
1054 |
+
async function generateCriticisms(solutions) {
|
1055 |
let response = await fetch('/solutions/criticize_solution', { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(solutions) })
|
1056 |
let responseObj = await response.json()
|
1057 |
solutionsCriticizedVersions.push(responseObj)
|
1058 |
}
|
1059 |
|
1060 |
+
async function refineSolutions(critiques) {
|
1061 |
let response = await fetch('/solutions/refine_solutions', { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(critiques) })
|
1062 |
let responseObj = await response.json()
|
1063 |
+
await generateCriticisms(responseObj)
|
1064 |
}
|
1065 |
|
1066 |
+
async function workflow(steps = 1) {
|
1067 |
let soluce;
|
1068 |
showLoadingOverlay('Génération des solutions & critiques ....');
|
1069 |
|
|
|
1083 |
}
|
1084 |
|
1085 |
if (solutionsCriticizedVersions.length == 0) {
|
1086 |
+
soluce = await generateSolutions(selected_requirements, user_constraints ? user_constraints : null);
|
1087 |
+
await generateCriticisms(soluce)
|
1088 |
} else {
|
1089 |
let prevSoluce = solutionsCriticizedVersions[solutionsCriticizedVersions.length - 1];
|
1090 |
+
await refineSolutions(prevSoluce)
|
1091 |
}
|
1092 |
}
|
1093 |
hideLoadingOverlay();
|
|
|
1119 |
// Événements pour les boutons de solutions (à implémenter plus tard)
|
1120 |
document.getElementById('get-solutions-btn').addEventListener('click', () => {
|
1121 |
const n_steps = document.getElementById('solution-gen-nsteps').value;
|
1122 |
+
workflow(n_steps);
|
1123 |
});
|
1124 |
+
document.getElementById('get-solutions-step-btn').addEventListener('click', () => {
|
1125 |
+
workflow(1);
|
1126 |
});
|
1127 |
});
|
1128 |
|