Skip to content

Commit

Permalink
feat: Allow users to control the number of generated requirements (#48)
Browse files Browse the repository at this point in the history
  • Loading branch information
neeraj-presidio authored Mar 3, 2025
1 parent a1f1422 commit 5ced776
Show file tree
Hide file tree
Showing 14 changed files with 346 additions and 20 deletions.
5 changes: 5 additions & 0 deletions .changeset/shy-towns-kick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"specif-ai": patch
---

Introduced a configurable option to control the number of generated requirements.
38 changes: 31 additions & 7 deletions backend/api/solution_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,17 @@ def create_solutions():
errors = []
templates = []

def get_llm_response(template_path):
logger.info(f"Request {g.request_id}: Fetching LLM response for template: {template_path}")
template = jinja_template_env.get_template(template_path)
template = template.render(name=data["name"], description=data["description"])
def get_llm_response(template_config):
logger.info(
f"Request {g.request_id}: Fetching LLM response for template: {template_config['template_path']}")
template = jinja_template_env.get_template(
template_config['template_path'])

template = template.render(
name=data["name"],
description=data["description"],
max_count=template_config['max_count']
)
try:
# Prepare message for LLM
llm_message = LLMUtils.prepare_messages(prompt=template)
Expand All @@ -96,18 +103,35 @@ def get_llm_response(template_path):
)
llm_response = llm_handler.invoke(messages=llm_message)

logger.info(f"Request {g.request_id}: Successfully received LLM response for template: {template_path}")
logger.info(
f"Request {g.request_id}: Successfully received LLM response for template: {template_config['template_path']}")
return json.loads(llm_response)
except json.JSONDecodeError:
logger.error(f"Request {g.request_id}: Failed to parse LLM response for template: {template_path}")
logger.error(
f"Request {g.request_id}: Failed to parse LLM response for template: {template_config['template_path']}")
abort(500, description="Invalid JSON format. Please try again.")

if data["createReqt"]:
logger.info(f"Request {g.request_id}: Creating requirements using LLM")
clean_solution = data['cleanSolution'] if ('cleanSolution' in data) and isinstance(data['cleanSolution'],
bool) else False
if clean_solution is False:
templates = ['create_brd.jinja2', 'create_prd.jinja2', 'create_nfr.jinja2', 'create_uir.jinja2']
preference_mapping = {
'brdPreferences': {'type': 'brd', 'template': 'create_brd.jinja2'},
'prdPreferences': {'type': 'prd', 'template': 'create_prd.jinja2'},
'uirPreferences': {'type': 'uir', 'template': 'create_uir.jinja2'},
'nfrPreferences': {'type': 'nfr', 'template': 'create_nfr.jinja2'},
}

templates = [
{
'type': config['type'],
'template_path': config['template'],
'max_count': data[pref]['max_count']
}
for pref, config in preference_mapping.items()
if data.get(pref, {}).get('isEnabled')
]
executor = ExecutorConfig().get_executor()
futures = [executor.submit(get_llm_response, template) for template in templates]
for future in concurrent.futures.as_completed(futures):
Expand Down
2 changes: 1 addition & 1 deletion backend/llm/prompts/solution/create_brd.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ Output Structure should be a valid JSON: Here is the sample Structure:
}

Please ensure the requirements are clear and comprehensive. Output only valid JSON. Do not include ```json ``` on the start and end of the response.
Generate the required number of Business requirements to meet business needs or generate at least 15 BRDs and sort them based on business impact.
Generate Business Requirements with a maximum count of {{ max_count|default(30) }}. Sort all requirements based on business impact (High to Medium to Low).
3 changes: 2 additions & 1 deletion backend/llm/prompts/solution/create_nfr.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ Output Structure should be a valid JSON: Here is the sample Structure:
]
}

Please ensure the requirements are clear, concise, and comprehensive. Output only valid JSON. Do not include ```json ``` on start and end of the response.
Please ensure the requirements are clear, concise, and comprehensive. Output only valid JSON. Do not include ```json ``` on start and end of the response.
Generate Non-Functional Requirements with a maximum count of {{ max_count|default(30) }}. Sort all requirements based on business impact (High to Medium to Low).
4 changes: 2 additions & 2 deletions backend/llm/prompts/solution/create_prd.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ App Name: {{name}}
App Description: {{description}}

{% include 'prd.jinja2' %}
- Generate 15 PRD

Output Structure should be a valid JSON: Here is the sample Structure:

Expand All @@ -19,4 +18,5 @@ Output Structure should be a valid JSON: Here is the sample Structure:
]
}

Please ensure the requirements are descriptive and also clear, concise. Output only valid JSON. Do not include ```json ``` on start and end of the response.
Please ensure the requirements are descriptive and also clear, concise. Output only valid JSON. Do not include ```json ``` on start and end of the response.
Generate Product Requirements with a maximum count of {{ max_count|default(30) }}. Sort all requirements based on business impact (High to Medium to Low).
3 changes: 2 additions & 1 deletion backend/llm/prompts/solution/create_uir.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ Output Structure should be a valid JSON: Here is the sample Structure:
]
}

Please ensure the requirements are clear, concise, and comprehensive. Output only valid JSON. Do not include ```json ``` on start and end of the response.
Please ensure the requirements are clear, concise, and comprehensive. Output only valid JSON. Do not include ```json ``` on start and end of the response.
Generate User Interface Requirements with a maximum count of {{ max_count|default(30) }}. Sort all requirements based on business impact (High to Medium to Low).
15 changes: 14 additions & 1 deletion backend/schemas/solution_schema.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
from marshmallow import Schema, ValidationError, fields, validate
from marshmallow import Schema, fields, validate


class RequirementGenerationLimitSchema(Schema):
max_count = fields.Integer(required=True)
isEnabled = fields.Boolean(required=True)


class CreateSolutionSchema(Schema):
Expand All @@ -10,6 +15,14 @@ class CreateSolutionSchema(Schema):
deployment = fields.Boolean(required=False)
createReqt = fields.Boolean(required=False)
created_on = fields.DateTime(required=True)
brdPreferences = fields.Nested(
RequirementGenerationLimitSchema, required=True)
prdPreferences = fields.Nested(
RequirementGenerationLimitSchema, required=True)
nfrPreferences = fields.Nested(
RequirementGenerationLimitSchema, required=True)
uirPreferences = fields.Nested(
RequirementGenerationLimitSchema, required=True)


class SolutionIdSchema(Schema):
Expand Down
26 changes: 26 additions & 0 deletions ui/src/app/components/core/slider/slider.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<div class="flex flex-col gap-2 max-w-sm">
<label
*ngIf="label"
class="block text-sm font-medium text-secondary-700 mb-2"
>{{ label }}</label
>
<div class="flex items-center gap-4 w-full">
<mat-slider
class="flex-1"
[min]="min"
[max]="max"
[step]="step"
[disabled]="disabled"
>
<input
matSliderThumb
[value]="value"
(valueChange)="onSliderChange($event)"
(input)="onSliderChange($event)"
/>
</mat-slider>
<span class="text-sm text-primary-600 font-semibold whitespace-nowrap">{{
value
}}</span>
</div>
</div>
52 changes: 52 additions & 0 deletions ui/src/app/components/core/slider/slider.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Component, Input, forwardRef } from '@angular/core';
import { MatSliderModule } from '@angular/material/slider';
import { NgIf } from '@angular/common';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
selector: 'app-slider',
templateUrl: './slider.component.html',
standalone: true,
imports: [MatSliderModule, NgIf],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AppSliderComponent),
multi: true,
},
],
})
export class AppSliderComponent implements ControlValueAccessor {
@Input() min: number = 1;
@Input() max: number = 30;
@Input() step: number = 1;
@Input() label: string = '';
@Input() disabled: boolean = false;

value: number = 15;
onChange: (value: number) => void = () => {};
onTouch: () => void = () => {};

writeValue(value: number): void {
this.value = value;
}

registerOnChange(fn: (value: number) => void): void {
this.onChange = fn;
}

registerOnTouched(fn: () => void): void {
this.onTouch = fn;
}

setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}

onSliderChange(event: Event | number): void {
const newValue = typeof event === 'number' ? event : (event.target as any).value;
this.value = newValue;
this.onChange(newValue);
this.onTouch();
}
}
16 changes: 16 additions & 0 deletions ui/src/app/model/interfaces/projects.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ export interface IProject {
metadata: IProjectMetadata;
}

export interface IGenerationRange {
max_count: number;
isEnabled: boolean;
}

export interface IProjectMetadata {
name?: string;
description: string;
Expand All @@ -15,6 +20,17 @@ export interface IProjectMetadata {
createdAt: string;
}

export interface ICreateSolutionRequest {
name: string;
description: string;
createReqt: boolean;
cleanSolution: boolean;
brdPreferences: IGenerationRange;
prdPreferences: IGenerationRange;
uirPreferences: IGenerationRange;
nfrPreferences: IGenerationRange;
}

export interface ISolutionResponse {
brd?: { [key in string]: string }[];
nfr?: { [key in string]: string }[];
Expand Down
Loading

0 comments on commit 5ced776

Please sign in to comment.