elfarash commited on
Commit
eb5a3ff
·
0 Parent(s):

Initial commit with RAG, FastAPI and Gradio UI

Browse files
Files changed (6) hide show
  1. .gitignore +25 -0
  2. README.md +54 -0
  3. agent.py +107 -0
  4. api.py +47 -0
  5. gradio_app.py +71 -0
  6. requirements.txt +147 -0
.gitignore ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Virtual Environments
2
+ venv/
3
+ env/
4
+ .venv/
5
+
6
+ # Environment Variables
7
+ .env
8
+ *.env
9
+
10
+ # Python Cache
11
+ __pycache__/
12
+ *.pyc
13
+ *.pyo
14
+ *.pyd
15
+
16
+ # ChromaDB local vector storage
17
+ chroma_db/
18
+
19
+ # IDEs
20
+ .vscode/
21
+ .idea/
22
+
23
+ # OS generated files
24
+ .DS_Store
25
+ Thumbs.db
README.md ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Task Prompting Tool
2
+
3
+ A tool designed for project managers and team leaders to generate developer-ready prompts. By providing a task description, specifying a field (e.g., Backend, Frontend), and uploading relevant context files, the underlying LLM (OpenAI or Google Gemini) will construct a comprehensive prompt ready to be handed to developers.
4
+
5
+ ## Prerequisites
6
+
7
+ - Python 3.8+
8
+ - API keys for OpenAI and/or Google Gen AI.
9
+
10
+ ## Setup Instructions
11
+
12
+ **1. Create Hand-configured Keys in `.env`**
13
+ Edit the `.env` file and insert your API keys and models as preferred:
14
+ ```env
15
+ OPENAI_API_KEY="your-openai-api-key"
16
+ GOOGLE_API_KEY="your-google-api-key"
17
+ LLM_PROVIDER="google" # Options: "google" or "openai"
18
+ OPENAI_MODEL="gpt-4.1-mini"
19
+ GOOGLE_MODEL="gemini-3.1-flash-lite-preview"
20
+ ```
21
+
22
+ **2. Setup Virtual Environment & Install Dependencies**
23
+ Open a terminal and run the following commands in the project directory:
24
+
25
+ ```bash
26
+ python3 -m venv venv
27
+ source venv/bin/activate
28
+ pip install -r requirements.txt
29
+ ```
30
+
31
+ *(On Windows, activate the virtual environment using `venv\Scripts\activate`)*
32
+
33
+ ## Running the Application
34
+
35
+ This project features both a FastAPI backend (providing an API) and a Gradio frontend (providing a UI).
36
+
37
+ ### Method 1: Using the UI (Recommended)
38
+ You can directly run the Gradio application to access the user interface.
39
+
40
+ ```bash
41
+ source venv/bin/activate
42
+ python gradio_app.py
43
+ ```
44
+ After executing, an interface will open at `http://127.0.0.1:7860/` by default. You can open your browser to this URL to interact with the Task Prompting Tool.
45
+
46
+ ### Method 2: Running the API Server
47
+ If you'd like to integrate this logic into another system or frontend, run the FastAPI backend:
48
+
49
+ ```bash
50
+ source venv/bin/activate
51
+ python api.py
52
+ ```
53
+ The server will run at `http://0.0.0.0:8000`.
54
+ - View API Documentation at `http://127.0.0.1:8000/docs` to test endpoints interactively.
agent.py ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+ from langchain_openai import ChatOpenAI, OpenAIEmbeddings
4
+ from langchain_google_genai import ChatGoogleGenerativeAI, GoogleGenerativeAIEmbeddings
5
+ from langchain_core.prompts import ChatPromptTemplate
6
+ from langchain_core.output_parsers import StrOutputParser
7
+ from langchain_core.documents import Document
8
+ from langchain_community.vectorstores import Chroma
9
+ from langchain_text_splitters import RecursiveCharacterTextSplitter
10
+
11
+ load_dotenv()
12
+
13
+ def get_llm():
14
+ provider = os.getenv("LLM_PROVIDER", "google").lower()
15
+
16
+ if provider == "openai":
17
+ return ChatOpenAI(
18
+ model=os.getenv("OPENAI_MODEL", "gpt-4.1-mini"),
19
+ temperature=0.7
20
+ )
21
+ elif provider == "google":
22
+ return ChatGoogleGenerativeAI(
23
+ model=os.getenv("GOOGLE_MODEL", "gemini-3.1-flash-lite-preview"),
24
+ temperature=0.7
25
+ )
26
+ else:
27
+ raise ValueError(f"Unknown LLM Provider: {provider}")
28
+
29
+ def get_embeddings():
30
+ provider = os.getenv("LLM_PROVIDER", "google").lower()
31
+ if provider == "openai":
32
+ return OpenAIEmbeddings(model="text-embedding-3-small")
33
+ elif provider == "google":
34
+ return GoogleGenerativeAIEmbeddings(model="models/gemini-embedding-001")
35
+ else:
36
+ raise ValueError(f"Unknown LLM Provider: {provider}")
37
+
38
+ def process_and_retrieve_context(description: str, field: str, files_data: list[dict]) -> str:
39
+ """Takes a list of file dictionaries and retrieves relevant context using ChromaDB."""
40
+ if not files_data:
41
+ return "No extra files provided."
42
+
43
+ docs = []
44
+ for file in files_data:
45
+ docs.append(Document(
46
+ page_content=file["content"],
47
+ metadata={"source": file["filename"]}
48
+ ))
49
+
50
+ # Split the documents
51
+ text_splitter = RecursiveCharacterTextSplitter(
52
+ chunk_size=1000,
53
+ chunk_overlap=200
54
+ )
55
+ splits = text_splitter.split_documents(docs)
56
+
57
+ # Store locally in chromadb directory and use it to retrieve
58
+ vectorstore = Chroma.from_documents(
59
+ documents=splits,
60
+ embedding=get_embeddings(),
61
+ persist_directory="./chroma_db"
62
+ )
63
+
64
+ # Use description and field to retrieve relevant chunks
65
+ query = f"Field: {field}. Task: {description}"
66
+ retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
67
+
68
+ retrieved_docs = retriever.invoke(query)
69
+
70
+ context = ""
71
+ for idx, doc in enumerate(retrieved_docs):
72
+ context += f"\n--- Retrieved Chunk {idx+1} from {doc.metadata.get('source', 'Unknown')} ---\n{doc.page_content}\n"
73
+
74
+ return context
75
+
76
+ def generate_task_prompt(description: str, field: str, files_data: list[dict]) -> str:
77
+ llm = get_llm()
78
+
79
+ # Get filtered context via RAG
80
+ files_context = process_and_retrieve_context(description, field, files_data)
81
+
82
+ system_prompt = (
83
+ "You are an expert technical project manager and architect. "
84
+ "Your goal is to take a task description provided by a project manager, context about the field (e.g., backend, frontend), "
85
+ "and any uploaded file context, and produce a high-quality, developer-ready task prompt.\n\n"
86
+ "Return ONLY the finalized prompt ready to be handed to a developer."
87
+ )
88
+
89
+ human_prompt = (
90
+ "Field/Domain: {field}\n"
91
+ "Task Description:\n{description}\n\n"
92
+ "Relevant Code/Files Context:\n{files_context}\n\n"
93
+ "Please generate a comprehensive developer prompt."
94
+ )
95
+
96
+ prompt = ChatPromptTemplate.from_messages([
97
+ ("system", system_prompt),
98
+ ("human", human_prompt),
99
+ ])
100
+
101
+ chain = prompt | llm | StrOutputParser()
102
+
103
+ return chain.invoke({
104
+ "field": field,
105
+ "description": description,
106
+ "files_context": files_context
107
+ })
api.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, UploadFile, File, Form
2
+ from typing import List, Optional
3
+ import uvicorn
4
+ from agent import generate_task_prompt
5
+
6
+ import io
7
+ import pypdf
8
+ import docx2txt
9
+
10
+ app = FastAPI(title="Task Prompting API")
11
+
12
+ @app.post("/generate_prompt")
13
+ async def generate_prompt(
14
+ description: str = Form(...),
15
+ field: str = Form(...),
16
+ files: Optional[List[UploadFile]] = File(None)
17
+ ):
18
+ files_data = []
19
+
20
+ if files:
21
+ for file in files:
22
+ content = await file.read()
23
+ filename_lower = file.filename.lower()
24
+
25
+ try:
26
+ if filename_lower.endswith(".pdf"):
27
+ reader = pypdf.PdfReader(io.BytesIO(content))
28
+ text = "\n".join([page.extract_text() or "" for page in reader.pages])
29
+ files_data.append({"filename": file.filename, "content": text})
30
+ elif filename_lower.endswith(".docx"):
31
+ text = docx2txt.process(io.BytesIO(content))
32
+ files_data.append({"filename": file.filename, "content": text})
33
+ else:
34
+ decoded_content = content.decode('utf-8')
35
+ files_data.append({"filename": file.filename, "content": decoded_content})
36
+ except Exception as e:
37
+ print(f"Error processing {file.filename}: {e}")
38
+ pass # Skip files that fail encoding or extraction
39
+
40
+ try:
41
+ generated_prompt = generate_task_prompt(description, field, files_data)
42
+ return {"status": "success", "prompt": generated_prompt}
43
+ except Exception as e:
44
+ return {"status": "error", "message": str(e)}
45
+
46
+ if __name__ == "__main__":
47
+ uvicorn.run("api:app", host="0.0.0.0", port=8000, reload=True)
gradio_app.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import requests
3
+ import os
4
+
5
+ from agent import generate_task_prompt
6
+
7
+ import pypdf
8
+ import docx2txt
9
+
10
+ def process_request(description, field, uploaded_files):
11
+ files_data = []
12
+
13
+ if uploaded_files:
14
+ for file_path in uploaded_files:
15
+ filename = os.path.basename(file_path)
16
+ lower_name = filename.lower()
17
+ try:
18
+ if lower_name.endswith(".pdf"):
19
+ reader = pypdf.PdfReader(file_path)
20
+ content = "\n".join([page.extract_text() or "" for page in reader.pages])
21
+ elif lower_name.endswith(".docx"):
22
+ content = docx2txt.process(file_path)
23
+ else:
24
+ with open(file_path, "r", encoding="utf-8") as f:
25
+ content = f.read()
26
+ files_data.append({"filename": filename, "content": content})
27
+ except Exception as e:
28
+ print(f"Skipping binary/unreadable file: {filename}")
29
+
30
+ try:
31
+ return generate_task_prompt(description, field, files_data)
32
+ except Exception as e:
33
+ return f"Error occurred: {str(e)}"
34
+
35
+ # Gradio Interface
36
+ with gr.Blocks(title="Task Prompting Tool") as demo:
37
+ gr.Markdown("# 🚀 Developer Task Prompting Tool (RAG Enabled)")
38
+ gr.Markdown("Generate high-quality, developer-ready prompts using advanced ChromaDB chunking for very large projects.")
39
+
40
+ with gr.Row():
41
+ with gr.Column(scale=2):
42
+ field_input = gr.Dropdown(
43
+ choices=["Backend", "Frontend", "Fullstack", "DevOps", "Data Science", "Mobile", "Other"],
44
+ label="Field / Application Area",
45
+ value="Backend"
46
+ )
47
+ desc_input = gr.Textbox(
48
+ label="Task Description",
49
+ placeholder="Describe what needs to be done...",
50
+ lines=5
51
+ )
52
+ file_input = gr.File(
53
+ label="Upload Context Files (Code, MD, JSON, etc.)",
54
+ file_count="multiple"
55
+ )
56
+ submit_btn = gr.Button("Generate Prompt", variant="primary")
57
+
58
+ with gr.Column(scale=3):
59
+ output_text = gr.Textbox(
60
+ label="Generated Task Prompt",
61
+ lines=15
62
+ )
63
+
64
+ submit_btn.click(
65
+ fn=process_request,
66
+ inputs=[desc_input, field_input, file_input],
67
+ outputs=output_text
68
+ )
69
+
70
+ if __name__ == "__main__":
71
+ demo.launch(server_name="0.0.0.0", server_port=7860)
requirements.txt ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ aiofiles==24.1.0
2
+ aiohappyeyeballs==2.6.1
3
+ aiohttp==3.13.5
4
+ aiosignal==1.4.0
5
+ annotated-doc==0.0.4
6
+ annotated-types==0.7.0
7
+ anyio==4.13.0
8
+ attrs==26.1.0
9
+ bcrypt==5.0.0
10
+ beautifulsoup4==4.14.3
11
+ brotli==1.2.0
12
+ bs4==0.0.2
13
+ build==1.4.2
14
+ certifi==2026.2.25
15
+ cffi==2.0.0
16
+ charset-normalizer==3.4.7
17
+ chromadb==1.5.5
18
+ click==8.3.2
19
+ cryptography==46.0.6
20
+ dataclasses-json==0.6.7
21
+ distro==1.9.0
22
+ docx2txt==0.9
23
+ durationpy==0.10
24
+ fastapi==0.135.3
25
+ ffmpy==1.0.0
26
+ filelock==3.25.2
27
+ filetype==1.2.0
28
+ flatbuffers==25.12.19
29
+ frozenlist==1.8.0
30
+ fsspec==2026.3.0
31
+ google-auth==2.49.1
32
+ google-genai==1.70.0
33
+ googleapis-common-protos==1.74.0
34
+ gradio==6.11.0
35
+ gradio_client==2.4.0
36
+ greenlet==3.3.2
37
+ groovy==0.1.2
38
+ grpcio==1.80.0
39
+ h11==0.16.0
40
+ hf-gradio==0.3.0
41
+ hf-xet==1.4.3
42
+ httpcore==1.0.9
43
+ httptools==0.7.1
44
+ httpx==0.28.1
45
+ httpx-sse==0.4.3
46
+ huggingface_hub==1.9.0
47
+ idna==3.11
48
+ importlib_metadata==8.7.1
49
+ importlib_resources==6.5.2
50
+ Jinja2==3.1.6
51
+ jiter==0.13.0
52
+ jsonpatch==1.33
53
+ jsonpointer==3.1.1
54
+ jsonschema==4.26.0
55
+ jsonschema-specifications==2025.9.1
56
+ kubernetes==35.0.0
57
+ langchain==1.2.15
58
+ langchain-classic==1.0.3
59
+ langchain-community==0.4.1
60
+ langchain-core==1.2.25
61
+ langchain-google-genai==4.2.1
62
+ langchain-openai==1.1.12
63
+ langchain-text-splitters==1.1.1
64
+ langgraph==1.1.6
65
+ langgraph-checkpoint==4.0.1
66
+ langgraph-prebuilt==1.0.9
67
+ langgraph-sdk==0.3.12
68
+ langsmith==0.7.25
69
+ markdown-it-py==4.0.0
70
+ MarkupSafe==3.0.3
71
+ marshmallow==3.26.2
72
+ mdurl==0.1.2
73
+ mmh3==5.2.1
74
+ mpmath==1.3.0
75
+ multidict==6.7.1
76
+ mypy_extensions==1.1.0
77
+ numpy==2.4.4
78
+ oauthlib==3.3.1
79
+ onnxruntime==1.24.4
80
+ openai==2.30.0
81
+ opentelemetry-api==1.40.0
82
+ opentelemetry-exporter-otlp-proto-common==1.40.0
83
+ opentelemetry-exporter-otlp-proto-grpc==1.40.0
84
+ opentelemetry-proto==1.40.0
85
+ opentelemetry-sdk==1.40.0
86
+ opentelemetry-semantic-conventions==0.61b0
87
+ orjson==3.11.8
88
+ ormsgpack==1.12.2
89
+ overrides==7.7.0
90
+ packaging==26.0
91
+ pandas==3.0.2
92
+ pillow==12.2.0
93
+ propcache==0.4.1
94
+ protobuf==6.33.6
95
+ pyasn1==0.6.3
96
+ pyasn1_modules==0.4.2
97
+ pybase64==1.4.3
98
+ pycparser==3.0
99
+ pydantic==2.12.5
100
+ pydantic-settings==2.13.1
101
+ pydantic_core==2.41.5
102
+ pydub==0.25.1
103
+ Pygments==2.20.0
104
+ pypdf==6.9.2
105
+ PyPika==0.51.1
106
+ pyproject_hooks==1.2.0
107
+ python-dateutil==2.9.0.post0
108
+ python-dotenv==1.2.2
109
+ python-multipart==0.0.22
110
+ pytz==2026.1.post1
111
+ PyYAML==6.0.3
112
+ referencing==0.37.0
113
+ regex==2026.3.32
114
+ requests==2.33.1
115
+ requests-oauthlib==2.0.0
116
+ requests-toolbelt==1.0.0
117
+ rich==14.3.3
118
+ rpds-py==0.30.0
119
+ safehttpx==0.1.7
120
+ semantic-version==2.10.0
121
+ shellingham==1.5.4
122
+ six==1.17.0
123
+ sniffio==1.3.1
124
+ soupsieve==2.8.3
125
+ SQLAlchemy==2.0.49
126
+ starlette==1.0.0
127
+ sympy==1.14.0
128
+ tenacity==9.1.4
129
+ tiktoken==0.12.0
130
+ tokenizers==0.22.2
131
+ tomlkit==0.13.3
132
+ tqdm==4.67.3
133
+ typer==0.24.1
134
+ typing-inspect==0.9.0
135
+ typing-inspection==0.4.2
136
+ typing_extensions==4.15.0
137
+ urllib3==2.6.3
138
+ uuid_utils==0.14.1
139
+ uvicorn==0.43.0
140
+ uvloop==0.22.1
141
+ watchfiles==1.1.1
142
+ websocket-client==1.9.0
143
+ websockets==16.0
144
+ xxhash==3.6.0
145
+ yarl==1.23.0
146
+ zipp==3.23.0
147
+ zstandard==0.25.0