Lucas ARRIESSE commited on
Commit
36dc4ec
·
1 Parent(s): ae51a9d

Add endpoints to use Insight Finder API

Browse files
app.py CHANGED
@@ -4,11 +4,12 @@ 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, SolutionSearchV2Request
8
  from jinja2 import Environment, FileSystemLoader, StrictUndefined
9
  from litellm.router import Router
10
  from dotenv import load_dotenv
11
  from util import retry_until
 
12
 
13
  logging.basicConfig(
14
  level=logging.INFO,
@@ -40,6 +41,11 @@ llm_router = Router(model_list=[
40
  }
41
  ], num_retries=10, retry_after=30)
42
 
 
 
 
 
 
43
  # Jinja2 environment to load prompt templates
44
  prompt_env = Environment(loader=FileSystemLoader(
45
  'prompts'), enable_async=True, undefined=StrictUndefined)
@@ -54,6 +60,11 @@ requirements_router = APIRouter(prefix="/reqs", tags=["requirements"])
54
  solution_router = APIRouter(prefix="/solution", tags=["solution"])
55
 
56
 
 
 
 
 
 
57
  @requirements_router.post("/categorize_requirements")
58
  async def categorize_reqs(params: ReqGroupingRequest) -> ReqGroupingResponse:
59
  """Categorize the given service requirements into categories"""
@@ -300,6 +311,65 @@ async def refine_solutions(params: CritiqueResponse) -> SolutionSearchResponse:
300
  return SolutionSearchResponse(solutions=refined_solutions)
301
 
302
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  api.include_router(requirements_router)
304
  api.include_router(solution_router)
305
 
 
4
  import sys
5
  import uvicorn
6
  from fastapi import APIRouter, FastAPI
7
+ from schemas import _RefinedSolutionModel, _SearchedSolutionModel, _SolutionCriticismOutput, CriticizeSolutionsRequest, CritiqueResponse, InsightFinderConstraintsList, RequirementInfo, ReqGroupingCategory, ReqGroupingResponse, ReqGroupingRequest, _ReqGroupingCategory, _ReqGroupingOutput, SolutionCriticism, SolutionModel, SolutionSearchResponse, SolutionSearchV2Request, TechnologyData
8
  from jinja2 import Environment, FileSystemLoader, StrictUndefined
9
  from litellm.router import Router
10
  from dotenv import load_dotenv
11
  from util import retry_until
12
+ from httpx import AsyncClient
13
 
14
  logging.basicConfig(
15
  level=logging.INFO,
 
41
  }
42
  ], num_retries=10, retry_after=30)
43
 
44
+ # HTTP client
45
+ INSIGHT_FINDER_BASE_URL = "https://organizedprogrammers-insight-finder.hf.space/"
46
+ http_client = AsyncClient(verify=os.environ.get(
47
+ "NO_SSL", "0") == "1", timeout=None)
48
+
49
  # Jinja2 environment to load prompt templates
50
  prompt_env = Environment(loader=FileSystemLoader(
51
  'prompts'), enable_async=True, undefined=StrictUndefined)
 
60
  solution_router = APIRouter(prefix="/solution", tags=["solution"])
61
 
62
 
63
+ async def format_prompt(prompt_name: str, **args) -> str:
64
+ """Helper to format a prompt"""
65
+ return await prompt_env.get_template(prompt_name).render_async(args)
66
+
67
+
68
  @requirements_router.post("/categorize_requirements")
69
  async def categorize_reqs(params: ReqGroupingRequest) -> ReqGroupingResponse:
70
  """Categorize the given service requirements into categories"""
 
311
  return SolutionSearchResponse(solutions=refined_solutions)
312
 
313
 
314
+ # ======================================== Solution generation using Insights Finder ==================
315
+
316
+ @solution_router.post("/search_solutions_if")
317
+ async def search_solutions_if(req: SolutionSearchV2Request) -> SolutionSearchResponse:
318
+
319
+ async def _search_solution_inner(cat: ReqGroupingCategory):
320
+ # process requirements into insight finder format
321
+ fmt_completion = await llm_router.acompletion("chat", messages=[
322
+ {
323
+ "role": "user",
324
+ "content": await format_prompt("if/format_requirements.txt", **{
325
+ "category": cat.model_dump(),
326
+ "response_schema": InsightFinderConstraintsList.model_json_schema()
327
+ })
328
+ }], response_format=InsightFinderConstraintsList)
329
+
330
+ fmt_model = InsightFinderConstraintsList.model_validate_json(
331
+ fmt_completion.choices[0].message.content)
332
+
333
+ # fetch technologies from insight finder
334
+ technologies_req = await http_client.post(INSIGHT_FINDER_BASE_URL + "process-constraints", content=fmt_model.model_dump_json())
335
+ technologies = TechnologyData.model_validate(technologies_req.json())
336
+
337
+ # =============================================================== synthesize solution using LLM =========================================
338
+
339
+ format_solution = await llm_router.acompletion("chat", messages=[{
340
+ "role": "user",
341
+ "content": await format_prompt("if/synthesize_solution.txt", **{
342
+ "category": cat.model_dump(),
343
+ "technologies": technologies.model_dump()["technologies"],
344
+ "user_constraints": None,
345
+ "response_schema": _SearchedSolutionModel.model_json_schema()
346
+ })}
347
+ ], response_format=_SearchedSolutionModel)
348
+
349
+ format_solution_model = _SearchedSolutionModel.model_validate_json(
350
+ format_solution.choices[0].message.content)
351
+
352
+ final_solution = SolutionModel(
353
+ Context="",
354
+ Requirements=[
355
+ cat.requirements[i].requirement for i in format_solution_model.requirement_ids
356
+ ],
357
+ Problem_Description=format_solution_model.problem_description,
358
+ Solution_Description=format_solution_model.solution_description,
359
+ References=[],
360
+ Category_Id=cat.id,
361
+ )
362
+
363
+ # ========================================================================================================================================
364
+
365
+ return final_solution
366
+
367
+ tasks = await asyncio.gather(*[_search_solution_inner(cat) for cat in req.categories])
368
+ final_solutions = [sol for sol in tasks if not isinstance(sol, Exception)]
369
+
370
+ return SolutionSearchResponse(solutions=final_solutions)
371
+
372
+
373
  api.include_router(requirements_router)
374
  api.include_router(solution_router)
375
 
prompts/if/format_requirements.txt ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <role>You are an expert system designer</role>
2
+ <task>
3
+ Your task is to transform a category of requirements into a JSON object where the object key is a short title (with spaces) for the requirement and the value is the requirement.
4
+ Give the the 10 most important constraints that must be solved to address the general problem of the category.
5
+ </task>
6
+
7
+ <requirements>
8
+ Here is the category item and the associated requirements:
9
+ Category Title: {{category["title"]}}
10
+ Context: {{category["requirements"][0]["context"]}}
11
+ Requirements:
12
+ {% for req in category["requirements"] -%}
13
+ - {{loop.index0}} {{req["requirement"]}}
14
+ {% endfor -%}
15
+ </requirements>
16
+
17
+ <response_format>
18
+ Reply in JSON using the following schema:
19
+ {{response_schema}}
20
+ </response_format>
21
+
22
+
prompts/if/synthesize_solution.txt ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 one or multiple technologies that explain how something works or achieves its requirements.
5
+
6
+ You may use the technologies listed below for the mechanisms of your solution.
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
+ <requirements>
16
+ Here is the category item and the associated 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>
24
+
25
+ <available_technologies>
26
+ Here are the technologies you may use for the mechanisms that compose the solution:
27
+ {% for tech in technologies -%}
28
+ - {{tech["title"]}} : {{tech["purpose"]}}
29
+ * Key Components : {{tech["key_components"]}}
30
+ {% endfor %}
31
+ </available_technologies>
32
+
33
+ {% if user_constraints is not none %}
34
+ <user_constraints>
35
+ Here are additional user constraints the solution must respect:
36
+ {{user_constraints}}
37
+ </user_constraints>
38
+ {% endif %}
39
+
40
+ <response_format>
41
+ Reply in JSON using the following schema:
42
+ {{response_schema}}
43
+ </response_format>
schemas.py CHANGED
@@ -134,3 +134,27 @@ class _RefinedSolutionModel(BaseModel):
134
  description="New description of the problem being solved.")
135
  solution_description: str = Field(...,
136
  description="New detailed description of the solution.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  description="New description of the problem being solved.")
135
  solution_description: str = Field(...,
136
  description="New detailed description of the solution.")
137
+
138
+
139
+ # ================================================================= search solutions using insight finder endpoints
140
+
141
+ class InsightFinderConstraintsList(BaseModel):
142
+ constraints: dict = Field(
143
+ description="A dict of constraints where the key is a short title for the constraint and the value the whole constraint description")
144
+
145
+
146
+ class Technology(BaseModel):
147
+ """Represents a single technology entry with its details."""
148
+ title: str
149
+ purpose: str
150
+ key_components: str
151
+ advantages: str
152
+ limitations: str
153
+ id: int
154
+
155
+ # This schema defines the root structure of the JSON
156
+
157
+
158
+ class TechnologyData(BaseModel):
159
+ """Represents the top-level object containing a list of technologies."""
160
+ technologies: List[Technology]