Lucas ARRIESSE commited on
Commit
2334311
·
1 Parent(s): fb16ff6

Add search_solution/v2 endpoint

Browse files
app.py CHANGED
@@ -4,7 +4,7 @@ import os
4
  import sys
5
  import uvicorn
6
  from fastapi import APIRouter, FastAPI
7
- from schemas import _RefinedSolutionModel, _SearchedSolutionModel, _SolutionCriticismOutput, CriticizeSolutionsRequest, CritiqueResponse, RequirementInfo, ReqGroupingCategory, ReqGroupingResponse, ReqGroupingRequest, _ReqGroupingCategory, _ReqGroupingOutput, SolutionCriticism, SolutionModel, SolutionSearchResponse
8
  from jinja2 import Environment, FileSystemLoader, StrictUndefined
9
  from litellm.router import Router
10
  from dotenv import load_dotenv
@@ -178,6 +178,72 @@ async def search_solutions(params: ReqGroupingResponse) -> SolutionSearchRespons
178
  return SolutionSearchResponse(solutions=final_solutions)
179
 
180
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  @solution_router.post("/criticize_solution", response_model=CritiqueResponse)
182
  async def criticize_solution(params: CriticizeSolutionsRequest) -> CritiqueResponse:
183
  """Criticize the challenges, weaknesses and limitations of the provided solutions."""
 
4
  import sys
5
  import uvicorn
6
  from fastapi import APIRouter, FastAPI
7
+ from schemas import _RefinedSolutionModel, _SearchedSolutionModel, _SolutionCriticismOutput, CriticizeSolutionsRequest, CritiqueResponse, RequirementInfo, ReqGroupingCategory, ReqGroupingResponse, ReqGroupingRequest, _ReqGroupingCategory, _ReqGroupingOutput, SolutionCriticism, SolutionModel, SolutionSearchResponse, SolutionSearchV2Request
8
  from jinja2 import Environment, FileSystemLoader, StrictUndefined
9
  from litellm.router import Router
10
  from dotenv import load_dotenv
 
178
  return SolutionSearchResponse(solutions=final_solutions)
179
 
180
 
181
+ @solution_router.post("/search_solutions_gemini/v2", response_model=SolutionSearchResponse)
182
+ async def search_solutions(params: SolutionSearchV2Request) -> SolutionSearchResponse:
183
+ """Searches solutions solving the given grouping params and respecting the user constraints using Gemini and grounded on google search"""
184
+
185
+ logging.info(f"Searching solutions for categories: {params.categories}")
186
+
187
+ async def _search_inner(cat: ReqGroupingCategory) -> SolutionModel:
188
+ # ================== generate the solution with web grounding
189
+ req_prompt = await prompt_env.get_template("search_solution_v2.txt").render_async(**{
190
+ "category": cat.model_dump(),
191
+ "user_constraints": params.user_constraints,
192
+ })
193
+
194
+ # generate the completion in non-structured mode.
195
+ # the googleSearch tool enables grounding gemini with google search
196
+ # this also forces gemini to perform a tool call
197
+ req_completion = await llm_router.acompletion(model="chat", messages=[
198
+ {"role": "user", "content": req_prompt}
199
+ ], tools=[{"googleSearch": {}}], tool_choice="required")
200
+
201
+ # ==================== structure the solution as a json ===================================
202
+
203
+ structured_prompt = await prompt_env.get_template("structure_solution.txt").render_async(**{
204
+ "solution": req_completion.choices[0].message.content,
205
+ "response_schema": _SearchedSolutionModel.model_json_schema()
206
+ })
207
+
208
+ structured_completion = await llm_router.acompletion(model="chat", messages=[
209
+ {"role": "user", "content": structured_prompt}
210
+ ], response_format=_SearchedSolutionModel)
211
+ solution_model = _SearchedSolutionModel.model_validate_json(
212
+ structured_completion.choices[0].message.content)
213
+
214
+ # ======================== build the final solution object ================================
215
+
216
+ sources_metadata = []
217
+ # extract the source metadata from the search items, if gemini actually called the tools to search .... and didn't hallucinated
218
+ try:
219
+ sources_metadata.extend([{"name": a["web"]["title"], "url": a["web"]["uri"]}
220
+ for a in req_completion["vertex_ai_grounding_metadata"][0]['groundingChunks']])
221
+ except KeyError as ke:
222
+ pass
223
+
224
+ final_sol = SolutionModel(
225
+ Context="",
226
+ Requirements=[
227
+ cat.requirements[i].requirement for i in solution_model.requirement_ids
228
+ ],
229
+ Problem_Description=solution_model.problem_description,
230
+ Solution_Description=solution_model.solution_description,
231
+ References=sources_metadata,
232
+ Category_Id=cat.id,
233
+ )
234
+ return final_sol
235
+
236
+ solutions = await asyncio.gather(*[retry_until(_search_inner, cat, lambda v: len(v.References) > 0, 2) for cat in params.categories], return_exceptions=True)
237
+ logging.info(solutions)
238
+ final_solutions = [
239
+ sol for sol in solutions if not isinstance(sol, Exception)]
240
+
241
+ return SolutionSearchResponse(solutions=final_solutions)
242
+
243
+
244
+ # =================================================================================================================
245
+
246
+
247
  @solution_router.post("/criticize_solution", response_model=CritiqueResponse)
248
  async def criticize_solution(params: CriticizeSolutionsRequest) -> CritiqueResponse:
249
  """Criticize the challenges, weaknesses and limitations of the provided solutions."""
prompts/search_solution.txt CHANGED
@@ -4,6 +4,12 @@ Your task is to create a solution that should cover the maximum possible of requ
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
  </task>
8
 
9
  Here is the category item and the associated requirements:
@@ -14,11 +20,4 @@ Requirements:
14
  {% for req in category["requirements"] -%}
15
  - {{loop.index0}} {{req["requirement"]}}
16
  {% endfor -%}
17
- </requirements>
18
-
19
- <additional_instructions>
20
- - The solution must aim to maximize requirement satisfaction while respecting the context.
21
- - Provide a list of requirements addressed by the solution (provide only the requirement IDs)
22
- - Please also detail each mechanism and how they work in the final solution description.
23
- - Please describe the solution description using markdown in a consistent format.
24
- </additional_instructions>
 
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:
 
20
  {% for req in category["requirements"] -%}
21
  - {{loop.index0}} {{req["requirement"]}}
22
  {% endfor -%}
23
+ </requirements>
 
 
 
 
 
 
 
prompts/search_solution_v2.txt ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 %}
schemas.py CHANGED
@@ -114,6 +114,15 @@ class SolutionSearchResponse(BaseModel):
114
  """Response model for solution search"""
115
  solutions: list[SolutionModel]
116
 
 
 
 
 
 
 
 
 
 
117
 
118
  # ================================================================= refine solution endpoints
119
 
@@ -124,4 +133,4 @@ class _RefinedSolutionModel(BaseModel):
124
  problem_description: str = Field(...,
125
  description="New description of the problem being solved.")
126
  solution_description: str = Field(...,
127
- description="New detailed description of the solution.")
 
114
  """Response model for solution search"""
115
  solutions: list[SolutionModel]
116
 
117
+ # ================================================================ search solution endpoint v2
118
+
119
+
120
+ class SolutionSearchV2Request(BaseModel):
121
+ """Response of a requirement grouping call."""
122
+ categories: List[ReqGroupingCategory]
123
+ user_constraints: Optional[str] = Field(
124
+ default=None, description="Additional user constraints to respect when generating the solutions.")
125
+
126
 
127
  # ================================================================= refine solution endpoints
128
 
 
133
  problem_description: str = Field(...,
134
  description="New description of the problem being solved.")
135
  solution_description: str = Field(...,
136
+ description="New detailed description of the solution.")