Deepri24 commited on
Commit
3e5e5b9
Β·
0 Parent(s):

reinitialized commit

Browse files
.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ venv/
2
+ leave/
3
+ .env
4
+ .DS_Store
README.md ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Blog Evaluator
2
+
3
+ ![alt text](image.png)
4
+ ![alt text](image-1.png)
5
+
6
+ # Blog Generation Workflow
7
+
8
+ ## Overview
9
+ This project implements an **Evaluator-Optimizer Workflow** using **LangGraph** and **LangChain** to generate and refine short blogs. The workflow follows an iterative process where an LLM generates a blog, evaluates it against predefined criteria, and either accepts it or provides feedback for revision. This ensures that the final output meets quality standards.
10
+
11
+ ## Why This Workflow Works
12
+ The **Evaluator-Optimizer Workflow** is effective because it automates content generation while maintaining **quality control** through an LLM-powered evaluation loop. If the initial blog meets the set criteria (**concise, engaging, structured with subtitles and a conclusion**), it is accepted. Otherwise, the LLM provides feedback, and the blog is regenerated with improvements.
13
+
14
+ ## Features
15
+ - **Automated Blog Generation**: Generates a blog based on a given topic.
16
+ - **Evaluation & Feedback**: Reviews the blog for conciseness, structure, and entertainment value.
17
+ - **Iterative Refinement**: If the blog needs revision, feedback is provided, and a revised version is generated.
18
+ - **LangSmith Studio Integration**: Visualizes and tests workflow execution.
19
+
20
+ ## Workflow Overview
21
+ ```mermaid
22
+ graph TD;
23
+ A[Start] --> B[Generate Blog];
24
+ B --> C[Evaluate Blog];
25
+ C -->|Needs Revision| B;
26
+ C -->|Accepted| D[End];
27
+ ```
28
+ - **Generates** an initial blog based on the provided topic.
29
+ - **Evaluates** the blog and determines if it meets quality standards.
30
+ - **Routing Decision**:
31
+ - If the blog is **good**, the workflow **ends**.
32
+ - If the blog **needs revision**, feedback is given, and a new version is generated.
33
+
34
+ ## Setup & Usage
35
+
36
+ ### Install dependencies:
37
+ ```bash
38
+ pip install langchain_groq langgraph pydantic python-dotenv
39
+ ```
40
+
41
+ ### Set environment variables in a `.env` file:
42
+ ```env
43
+ GROQ_API_KEY=your_api_key
44
+ LANGCHAIN_API_KEY=your_langchain_api_key
45
+ ```
46
+
47
+ ### Run the script in an IDE or Jupyter Notebook:
48
+ ```python
49
+ state = optimizer_workflow.invoke({"topic": "MCP from Anthropic"})
50
+ print(state["blog"])
51
+ ```
52
+
53
+ ## Testing in LangSmith Studio
54
+ - Deploy the workflow and **provide only the topic** as input.
55
+ - Monitor execution flow and **validate outputs** by logging into your LangSmith account (Adding @traceable to your function helps track it)
56
+ - You can also test via Langraph dev (ensure you have the langgraph.json file for this)
57
+
58
+
59
+ # Parallelized Code Review with LLMs
60
+ ![alt text](image-2.png)
61
+
62
+ ## Introduction
63
+ This project demonstrates a **parallelized workflow** for **automated code review** using **large language models (LLMs)**. Instead of running feedback checks sequentially, the system executes multiple review processes **in parallel**, making it an **efficient and scalable** solution for code assessment.
64
+
65
+ ### Why Parallelization?
66
+ - **Faster Execution:** Multiple feedback checks run **simultaneously**, reducing the overall processing time.
67
+ - **Improved Scalability:** New review criteria can be added without significant slowdowns.
68
+ - **Better Resource Utilization:** Leverages LLM calls efficiently by distributing tasks.
69
+
70
+ ## Features
71
+ - **Readability Analysis**: Evaluates the clarity and structure of the code.
72
+ - **Security Review**: Identifies potential vulnerabilities.
73
+ - **Best Practices Compliance**: Checks adherence to industry-standard coding best practices.
74
+ - **Feedback Aggregation**: Combines results into a single, structured response.
75
+
76
+ ## How It Works
77
+ 1. A **code snippet** is provided as input.
78
+ 2. Three independent LLM processes analyze the snippet for:
79
+ - Readability
80
+ - Security vulnerabilities
81
+ - Best practices adherence
82
+ 3. The results from these processes are aggregated into a final feedback report.
83
+
84
+ ## Technologies Used
85
+ - **Python**
86
+ - **LangChain** (LLM-based workflow automation)
87
+ - **LangGraph** (Parallel execution of LLM tasks)
88
+ - **Groq API** (LLM inference)
89
+ - **Pydantic & TypedDict** (Data validation)
90
+ - **Dotenv & OS** (Environment variable management)
91
+
92
+ ## Running the Code
93
+ 1. Clone this repository:
94
+
95
+ 2. Install dependencies:
96
+ ```sh
97
+ pip install -r requirements.txt
98
+ ```
99
+ 3. Set up your environment variables in .env file
100
+
101
+ 4. Run the script
102
+
103
+
104
+ ## Testing in LangSmith Studio
105
+ - Deploy the workflow and **provide only the topic** as input.
106
+ - Monitor execution flow and **validate outputs** by logging into your LangSmith account (Adding @traceable to your function helps track it)
107
+ - You can also test via Langraph dev (ensure you have the langgraph.json file for this)
108
+
109
+
110
+ # Learning Path Generator
111
+ ![alt text](image-3.png)
112
+ ![alt text](image-4.png)
113
+
114
+ ## Overview
115
+ This project implements an **Orchestrator-Synthesizer** workflow to dynamically generate a personalized **learning roadmap** based on a user's existing skills and learning goals. It uses **LangChain, LangGraph, and Groq AI models** to generate structured study plans and topic summaries.
116
+
117
+ ## Why Orchestrator-Synthesizer?
118
+ The **Orchestrator-Synthesizer** pattern is ideal for structured content generation workflows where tasks need to be dynamically assigned, processed independently, and then combined into a final output. It differs from traditional parallelization in the following ways:
119
+ - **Orchestration** dynamically determines what needs to be processed, ensuring relevant tasks are executed based on user input.
120
+ - **Workers** independently generate content summaries for each topic in the study plan.
121
+ - **Synthesis** intelligently merges topic summaries into a well-structured learning roadmap.
122
+
123
+ This ensures a **scalable, modular, and adaptable** approach to content generation, avoiding unnecessary processing while keeping results contextual.
124
+
125
+ ## Workflow Breakdown
126
+ The workflow consists of three key components:
127
+
128
+ ### 1️⃣ Orchestrator
129
+ - Creates a **study plan** based on the user's **skills and learning goals**.
130
+ - Uses an LLM with a structured output schema to generate a list of **learning topics**.
131
+
132
+ ### 2️⃣ Workers
133
+ - Each **worker** processes an individual **learning topic**.
134
+ - Generates a **markdown-formatted content summary** for the topic, including key concepts and learning resources.
135
+
136
+ ### 3️⃣ Synthesizer
137
+ - Collects all **topic summaries** and organizes them into a **cohesive learning roadmap**.
138
+ - Ensures smooth flow and structured representation of the learning journey.
139
+
140
+ ## Code Structure
141
+ - `orchestrator(state: State)`: Generates the study plan dynamically.
142
+ - `llm_call(state: WorkerState)`: Summarizes a single topic.
143
+ - `synthesizer(state: State)`: Merges all topic summaries into the final roadmap.
144
+ - `assign_workers(state: State)`: Dynamically assigns tasks based on generated topics.
145
+
146
+ ## Running the Workflow
147
+ To generate a personalized learning path, the workflow takes the following inputs:
148
+
149
+ ```python
150
+ user_skills = "Python programming, basic machine learning concepts"
151
+ user_goals = "Learn advanced AI, master prompt engineering, and build AI applications"
152
+ ```
153
+
154
+ It then executes the **Orchestrator β†’ Workers β†’ Synthesizer** pipeline, producing a structured learning roadmap.
155
+
156
+ ## Future Enhancements
157
+ - **Incorporate user feedback loops** to refine study plans over time.
158
+ - **Add multimodal learning resources** (e.g., videos, interactive exercises).
159
+ - **Expand to different learning domains** beyond AI and machine learning.
160
+ ---
161
+
162
+
app7.py ADDED
@@ -0,0 +1,429 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import os
3
+ from typing import Literal, List, Dict, TypedDict, Annotated
4
+ from langchain_groq import ChatGroq
5
+ from pydantic import BaseModel, Field
6
+ from langsmith import traceable
7
+ from langgraph.graph import StateGraph, START, END
8
+ from langchain_core.messages import SystemMessage, HumanMessage
9
+ from langgraph.constants import Send
10
+ import operator
11
+ from langchain_core.prompts import ChatPromptTemplate
12
+ from dotenv import load_dotenv
13
+
14
+ load_dotenv()
15
+
16
+ # --- Helper Functions ---
17
+
18
+ def markdown_converter(text):
19
+ return st.markdown(text)
20
+
21
+
22
+ # --- Blog Evaluator Workflow ---
23
+
24
+ class BlogState(TypedDict):
25
+ topic: str
26
+ blog: str
27
+ evaluation: str
28
+ feedback: str
29
+ accepted: bool
30
+
31
+
32
+ def generate_blog(state: BlogState, llm):
33
+ prompt = ChatPromptTemplate.from_messages([
34
+ ("system", "You are a helpful assistant that generates short blogs."),
35
+ ("human", "Generate a short blog about: {topic}")
36
+ ])
37
+ chain = prompt | llm
38
+ result = chain.invoke({"topic": state["topic"]}).content
39
+ return {"blog": result}
40
+
41
+
42
+ def evaluate_blog(state: BlogState, llm):
43
+ prompt = ChatPromptTemplate.from_messages([
44
+ ("system", "You are a strict blog evaluator."),
45
+ ("human",
46
+ "Evaluate this blog:\n{blog}\nIs it concise, engaging, structured with subtitles and a conclusion? Respond with 'yes' or 'no'."),
47
+ ("human", "If the answer is no. provide specific feedback on the needed improvements")
48
+ ])
49
+ chain = prompt | llm
50
+ result = chain.invoke({"blog": state["blog"]}).content
51
+
52
+ lines = result.split('\n')
53
+ evaluation_text = lines[0].strip().lower()
54
+ if 'no' in evaluation_text:
55
+ return {"evaluation": "Needs Revision", "feedback": "\n".join(lines[1:]), "accepted": False}
56
+ else:
57
+ return {"evaluation": "Accepted", "feedback": "", "accepted": True}
58
+
59
+
60
+ def provide_feedback(state: BlogState):
61
+ return {"feedback": state["feedback"]}
62
+
63
+
64
+ def conditional_check(state):
65
+ if not state["accepted"]:
66
+ return "revise"
67
+ else:
68
+ return "end"
69
+
70
+
71
+ def build_blog_graph(llm):
72
+ def generate_blog_llm(state):
73
+ return generate_blog(state, llm)
74
+
75
+ def evaluate_blog_llm(state):
76
+ return evaluate_blog(state, llm)
77
+
78
+ graph = StateGraph(BlogState)
79
+ graph.add_node("generate_blog", generate_blog_llm)
80
+ graph.add_node("evaluate_blog", evaluate_blog_llm)
81
+ graph.add_node("provide_feedback", provide_feedback)
82
+ graph.set_entry_point("generate_blog")
83
+ graph.add_conditional_edges(
84
+ "evaluate_blog",
85
+ conditional_check,
86
+ {
87
+ "revise": "generate_blog",
88
+ "end": END
89
+ }
90
+ )
91
+ graph.add_edge("generate_blog", "evaluate_blog")
92
+ graph.add_edge("provide_feedback", "generate_blog")
93
+
94
+ return graph
95
+
96
+
97
+ # --- Parallelized Code Review Workflow ---
98
+
99
+ class CodeReviewState(TypedDict):
100
+ code_snippet: str
101
+ readability_feedback: str
102
+ security_feedback: str
103
+ best_practices_feedback: str
104
+ feedback_aggregator: str
105
+
106
+
107
+ @traceable
108
+ def get_readability_feedback(state: CodeReviewState, llm):
109
+ """First LLM call to check code readability"""
110
+ st.session_state.progress_text = "Analyzing Readability..."
111
+ msg = llm.invoke([
112
+ HumanMessage(content=f"Provide readability feedback for the following code:\n\n {state['code_snippet']}")
113
+ ])
114
+ return {"readability_feedback": msg.content}
115
+
116
+
117
+ @traceable
118
+ def get_security_feedback(state: CodeReviewState, llm):
119
+ """Second LLM call to check for security vulnerabilities in code"""
120
+ st.session_state.progress_text = "Analyzing Security..."
121
+ msg = llm.invoke([
122
+ HumanMessage(
123
+ content=f"Check for potential security vulnerabilities in the following code and provide feedback:\n\n {state['code_snippet']}")
124
+ ])
125
+ return {"security_feedback": msg.content}
126
+
127
+
128
+ @traceable
129
+ def get_best_practices_feedback(state: CodeReviewState, llm):
130
+ """Third LLM call to check for adherence to coding best practices"""
131
+ st.session_state.progress_text = "Analyzing Best Practices..."
132
+ msg = llm.invoke([
133
+ HumanMessage(
134
+ content=f"Evaluate the adherence to coding best practices in the following code and provide feedback:\n\n {state['code_snippet']}")
135
+ ])
136
+ return {"best_practices_feedback": msg.content}
137
+
138
+
139
+ @traceable
140
+ def aggregate_feedback(state: CodeReviewState):
141
+ """Combine all the feedback from the three LLM calls into a single output"""
142
+ st.session_state.progress_text = "Aggregating Feedback..."
143
+ combined = f"Here's the overall feedback for the code:\n\n"
144
+ combined += f"READABILITY FEEDBACK:\n{state['readability_feedback']}\n\n"
145
+ combined += f"SECURITY FEEDBACK:\n{state['security_feedback']}\n\n"
146
+ combined += f"BEST PRACTICES FEEDBACK:\n{state['best_practices_feedback']}"
147
+ return {"feedback_aggregator": combined}
148
+
149
+
150
+ def build_code_review_graph(llm):
151
+ def get_readability_feedback_llm(state):
152
+ return get_readability_feedback(state, llm)
153
+
154
+ def get_security_feedback_llm(state):
155
+ return get_security_feedback(state, llm)
156
+
157
+ def get_best_practices_feedback_llm(state):
158
+ return get_best_practices_feedback(state, llm)
159
+
160
+ parallel_builder = StateGraph(CodeReviewState)
161
+
162
+ # Add nodes
163
+ parallel_builder.add_node("get_readability_feedback", get_readability_feedback_llm)
164
+ parallel_builder.add_node("get_security_feedback", get_security_feedback_llm)
165
+ parallel_builder.add_node("get_best_practices_feedback", get_best_practices_feedback_llm)
166
+ parallel_builder.add_node("aggregate_feedback", aggregate_feedback)
167
+
168
+ # Add edges
169
+ parallel_builder.add_edge(START, "get_readability_feedback")
170
+ parallel_builder.add_edge(START, "get_security_feedback")
171
+ parallel_builder.add_edge(START, "get_best_practices_feedback")
172
+ parallel_builder.add_edge("get_readability_feedback", "aggregate_feedback")
173
+ parallel_builder.add_edge("get_security_feedback", "aggregate_feedback")
174
+ parallel_builder.add_edge("get_best_practices_feedback", "aggregate_feedback")
175
+ parallel_builder.add_edge("aggregate_feedback", END)
176
+
177
+ return parallel_builder.compile()
178
+
179
+
180
+ # --- Learning Path Generator Workflow ---
181
+
182
+ class Topic(BaseModel):
183
+ name: str = Field(description="Name of the learning topic.")
184
+ description: str = Field(description="Brief overview of the topic.")
185
+
186
+
187
+ class Topics(BaseModel):
188
+ topics: List[Topic] = Field(description="List of topics to learn.")
189
+
190
+
191
+ class State(TypedDict):
192
+ user_skills: str
193
+ user_goals: str
194
+ topics: List[Topic]
195
+ completed_topics: Annotated[List[str], operator.add]
196
+ learning_roadmap: str
197
+
198
+
199
+ class WorkerState(TypedDict):
200
+ topic: Topic
201
+ completed_topics: List[str]
202
+
203
+
204
+ @traceable
205
+ def orchestrator(state: State, planner):
206
+ study_plan = planner.invoke([
207
+ SystemMessage(
208
+ content="Create a detailed study plan based on user skills and goals."
209
+ ),
210
+ HumanMessage(
211
+ content=f"User skills: {state['user_skills']}\nUser goals: {state['user_goals']}"
212
+ ),
213
+ ])
214
+ return {"topics": study_plan.topics}
215
+
216
+
217
+ @traceable
218
+ def llm_call(state: WorkerState, llm):
219
+ topic_summary = llm.invoke([
220
+ SystemMessage(
221
+ content="Generate a content summary for the provided topic."
222
+ ),
223
+ HumanMessage(
224
+ content=f"Topic: {state['topic'].name}\nDescription: {state['topic'].description}"
225
+ ),
226
+ ])
227
+
228
+ return {"completed_topics": [topic_summary.content]}
229
+
230
+
231
+ @traceable
232
+ def synthesizer(state: State):
233
+ topic_summaries = state["completed_topics"]
234
+ learning_roadmap = "\n\n---\n\n".join(topic_summaries)
235
+ return {"learning_roadmap": learning_roadmap}
236
+
237
+
238
+ def assign_workers(state: State):
239
+ return [Send("llm_call", {"topic": t}) for t in state["topics"]]
240
+
241
+
242
+ def build_learning_path_graph(llm, planner):
243
+ def orchestrator_planner(state):
244
+ return orchestrator(state, planner)
245
+
246
+ def llm_call_llm(state):
247
+ return llm_call(state, llm)
248
+
249
+ learning_path_builder = StateGraph(State)
250
+
251
+ learning_path_builder.add_node("orchestrator", orchestrator_planner)
252
+ learning_path_builder.add_node("llm_call", llm_call_llm)
253
+ learning_path_builder.add_node("synthesizer", synthesizer)
254
+
255
+ learning_path_builder.set_entry_point("orchestrator")
256
+ learning_path_builder.add_conditional_edges("orchestrator", assign_workers, {"llm_call": "llm_call"})
257
+ learning_path_builder.add_edge("llm_call", "synthesizer")
258
+ learning_path_builder.add_edge("synthesizer", END)
259
+
260
+ return learning_path_builder
261
+
262
+
263
+ # --- Streamlit App ---
264
+
265
+ st.set_page_config(page_title="LLM-Powered Workflows", layout="wide")
266
+
267
+ # Custom CSS for colors
268
+ st.markdown(
269
+ """
270
+ <style>
271
+ [data-testid="stSidebar"][aria-expanded="true"] > div:first-child {
272
+ background-color: #FF7F50; /* Coral */
273
+ }
274
+ [data-testid="stAppViewContainer"] {
275
+ background-color: #FF1493; /* Deep Pink */
276
+ }
277
+
278
+ /* Adjusting main content text color */
279
+ .block-container {
280
+ color: #9400D3; /* Dark Violet */
281
+ }
282
+ /* for all text */
283
+ body {
284
+ color: #9400D3 !important; /* Dark Violet */
285
+ }
286
+
287
+ </style>
288
+ """,
289
+ unsafe_allow_html=True,
290
+ )
291
+
292
+
293
+ st.title("Try out LLM-Powered Workflows")
294
+ st.markdown("""
295
+ <p style='color:#9400D3; font-size: 20px;'>
296
+ <b>1. Learning Path Generator</b> - Orchestrator-Synthesizer Workflow<br>
297
+ <b>2. Peer Code Review</b> - Parallelized Workflow<br>
298
+ <b>3. Blog Generation</b> - Evaluator-Optimizer Workflow
299
+ </p>
300
+ <p style='color:#9400D3;'><b>Enter your GROQ API key on the left to get started!</b></p>
301
+ """, unsafe_allow_html=True)
302
+
303
+ # Initialize session state
304
+ if "model_choice" not in st.session_state:
305
+ st.session_state.model_choice = "mixtral-8x7b-32768"
306
+ if "progress_text" not in st.session_state:
307
+ st.session_state.progress_text = ""
308
+ if "api_key_submitted" not in st.session_state:
309
+ st.session_state.api_key_submitted = False
310
+ # Sidebar for API key, model selection, and workflow selection
311
+ with st.sidebar:
312
+ st.header("Configuration")
313
+ groq_api_key_input = st.text_input("Enter your Groq API Key:", type="password", key="api_key_input")
314
+ api_key_submitted = st.button("Submit API Key")
315
+
316
+ available_models = ["mixtral-8x7b-32768", "deepseek-r1-distill-qwen-32b", "qwen-2.5-32b", "llama-3.1-8b-instant"]
317
+
318
+ llm = None
319
+ planner = None
320
+
321
+ if api_key_submitted:
322
+ st.session_state.api_key_submitted = True
323
+
324
+ if st.session_state.api_key_submitted:
325
+ if groq_api_key_input:
326
+ os.environ["GROQ_API_KEY"] = groq_api_key_input
327
+ elif os.environ.get("GROQ_API_KEY"):
328
+ groq_api_key_input = os.environ.get("GROQ_API_KEY")
329
+
330
+ if groq_api_key_input or os.environ.get("GROQ_API_KEY"):
331
+ try:
332
+ llm = ChatGroq(groq_api_key=groq_api_key_input, model_name=st.session_state.model_choice)
333
+ planner = llm.with_structured_output(Topics)
334
+ st.success(f"API key loaded successfully!")
335
+
336
+ st.session_state.model_choice = st.selectbox(
337
+ "Choose a Model",
338
+ available_models,
339
+ key="model_select_box",
340
+ index=available_models.index(st.session_state.model_choice) if st.session_state.model_choice in available_models else 0
341
+ )
342
+
343
+ llm = ChatGroq(groq_api_key=groq_api_key_input, model_name=st.session_state.model_choice)
344
+ planner = llm.with_structured_output(Topics)
345
+
346
+ st.success(f"model '{st.session_state.model_choice}' loaded successfully!")
347
+
348
+ except Exception as e:
349
+ st.error(f"Error initializing LLM: {e}")
350
+ llm = None
351
+ planner = None
352
+ else:
353
+ st.warning("Please enter your Groq API key to continue.")
354
+
355
+ if llm is not None:
356
+ # Emojis for workflow choices
357
+ workflow_emojis = {
358
+ "Learning Path Generator": "πŸ“š Learning Path", # Books
359
+ "Parallelized Code Review": "πŸ‘¨β€πŸ’» Code Review", # Man technologist
360
+ "Blog Evaluator": "πŸ“ Blog Evaluator", # Writing hand
361
+ }
362
+
363
+ # Correct order for selectbox:
364
+ workflow_order = ["Learning Path Generator", "Parallelized Code Review", "Blog Evaluator"]
365
+
366
+ workflow_choice = st.selectbox(
367
+ "Choose a Workflow",
368
+ workflow_order,
369
+ format_func=lambda x: f"{workflow_emojis[x]}",
370
+ key="workflow_choice"
371
+ )
372
+
373
+ # Main content area
374
+ if llm and planner:
375
+ # Emojis for workflow choices
376
+ workflow_emojis = {
377
+ "Learning Path Generator": "πŸ“š", # Books
378
+ "Parallelized Code Review": "πŸ‘¨β€πŸ’»", # Man technologist
379
+ "Blog Evaluator": "πŸ“", # Writing hand
380
+ }
381
+
382
+ if st.session_state.get("workflow_choice") == "Learning Path Generator":
383
+ st.header(f"{workflow_emojis['Learning Path Generator']} Learning Path Generator")
384
+ user_skills = st.text_area("Enter your current skills:")
385
+ user_goals = st.text_area("Enter your learning goals:")
386
+ if st.button("Generate Learning Path"):
387
+ if user_skills and user_goals:
388
+ learning_graph = build_learning_path_graph(llm, planner)
389
+ learning_app = learning_graph.compile()
390
+ result = learning_app.invoke({"user_skills": user_skills, "user_goals": user_goals})
391
+ st.subheader("Learning Roadmap:")
392
+ markdown_converter(result["learning_roadmap"])
393
+ else:
394
+ st.error("Please enter both your skills and goals")
395
+
396
+ elif st.session_state.get("workflow_choice") == "Parallelized Code Review":
397
+ st.header(f"{workflow_emojis['Parallelized Code Review']} Parallelized Code Review")
398
+ code_snippet = st.text_area("Enter code snippet:", height=300)
399
+ review_button = st.button("Review Code")
400
+
401
+ if review_button:
402
+ if code_snippet:
403
+ workflow = build_code_review_graph(llm)
404
+ progress_bar = st.progress(0)
405
+ progress_bar.progress(25, text="Starting...")
406
+ result = workflow.invoke({"code_snippet": code_snippet})
407
+ progress_bar.progress(100, text="Done!")
408
+ st.subheader("Code Review Feedback:")
409
+ st.markdown(result["feedback_aggregator"])
410
+ progress_bar.empty()
411
+ st.session_state.progress_text = ""
412
+ else:
413
+ st.error("Please enter a code snippet to review.")
414
+ else:
415
+ st.write(st.session_state.progress_text)
416
+
417
+ elif st.session_state.get("workflow_choice") == "Blog Evaluator":
418
+ st.header(f"{workflow_emojis['Blog Evaluator']} Blog Evaluator")
419
+ blog_topic = st.text_input("Enter blog topic:")
420
+ if st.button("Generate and Evaluate"):
421
+ if blog_topic:
422
+ blog_graph = build_blog_graph(llm)
423
+ blog_app = blog_graph.compile()
424
+ result = blog_app.invoke({"topic": blog_topic})
425
+ st.subheader("Blog:")
426
+ markdown_converter(result["blog"])
427
+ #only display blog content. No Evaluation or feedback.
428
+ else:
429
+ st.error("Please enter a blog topic")
blog_evaluater_optimizer.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+ from typing import Literal, List, Dict, TypedDict
4
+ from langchain_groq import ChatGroq
5
+ from pydantic import BaseModel, Field
6
+ from langsmith import traceable
7
+ from langgraph.graph import StateGraph, START, END
8
+ from IPython.display import Image, display
9
+
10
+ load_dotenv()
11
+
12
+ os.environ["GROQ_API_KEY"] = os.getenv("GROQ_API_KEY")
13
+ os.environ["LANGCHAIN_TRACING_V2"] = "true"
14
+ os.environ["LANGCHAIN_API_KEY"] = os.getenv("LANGCHAIN_API_KEY")
15
+
16
+ llm = ChatGroq(model="qwen-2.5-32b")
17
+
18
+ # Graph state
19
+ class State(TypedDict):
20
+ blog: str
21
+ topic: str
22
+ feedback: str
23
+ good_or_revise: str
24
+
25
+
26
+ class Feedback(BaseModel):
27
+ grade: Literal["good", "needs revision"] = Field(
28
+ description="Decide if the blog is entertaining, concise with maxiumum of 400 characters, with subtitles and a conclusion or needs revision.",
29
+ )
30
+ feedback: str = Field(
31
+ description="If the blog is not good, provide feedback on how to improve it.",
32
+ )
33
+
34
+
35
+ evaluator = llm.with_structured_output(Feedback)
36
+
37
+
38
+ # Nodes
39
+ @traceable
40
+ def llm_call_generator(state: State):
41
+ """LLM generates a blog"""
42
+ if state.get("feedback"):
43
+ msg = llm.invoke(
44
+ f"Write a blog about {state['topic']} but take into account the feedback: {state['feedback']}"
45
+ )
46
+ else:
47
+ msg = llm.invoke(f"Write a blog about {state['topic']}")
48
+
49
+ # Debugging print statement
50
+ print("Generated blog content:", msg.content)
51
+
52
+ return {"blog": msg.content} # Ensure this key is returned!
53
+
54
+
55
+ @traceable
56
+ def llm_call_evaluator(state: State):
57
+ """LLM evaluates the blog"""
58
+ grade = evaluator.invoke(f"Grade the blog {state['blog']}")
59
+ return {"good_or_revise": grade.grade, "feedback": grade.feedback}
60
+
61
+
62
+ @traceable
63
+ def route_blog(state: State):
64
+ """Route back to blog generator or end based upon feedback from evaluator"""
65
+ if state["good_or_revise"] == "good":
66
+ return "Accepted"
67
+ elif state["good_or_revise"] == "needs revision":
68
+ return "llm_call_generator"
69
+
70
+
71
+ # Build workflow
72
+ optimizer_builder = StateGraph(State)
73
+
74
+ # Add the nodes
75
+ optimizer_builder.add_node("llm_call_generator", llm_call_generator)
76
+ optimizer_builder.add_node("llm_call_evaluator", llm_call_evaluator)
77
+
78
+ # Add edges to connect nodes
79
+ optimizer_builder.add_edge(START, "llm_call_generator")
80
+ optimizer_builder.add_edge("llm_call_generator", "llm_call_evaluator")
81
+ optimizer_builder.add_conditional_edges(
82
+ "llm_call_evaluator",
83
+ route_blog,
84
+ {
85
+ "Accepted": END,
86
+ "llm_call_generator": "llm_call_generator",
87
+ },
88
+ )
89
+
90
+ # Compile the workflow
91
+ optimizer_workflow = optimizer_builder.compile()
92
+
93
+ # Show the workflow
94
+ display(Image(optimizer_workflow.get_graph().draw_mermaid_png()))
95
+
96
+ # Invoke
97
+ state = optimizer_workflow.invoke({"topic": "Vibe Coding"})
98
+ print(state["blog"])
code_peer_review_parallel.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+ from typing import TypedDict
4
+ from langchain_groq import ChatGroq
5
+ from pydantic import BaseModel, Field
6
+ from langsmith import traceable
7
+ from langgraph.graph import StateGraph, START, END
8
+ from IPython.display import Image, display
9
+
10
+ load_dotenv()
11
+
12
+ os.environ["GROQ_API_KEY"] = os.getenv("GROQ_API_KEY")
13
+ os.environ["LANGCHAIN_TRACING_V2"] = "true"
14
+ os.environ["LANGCHAIN_API_KEY"] = os.getenv("LANGCHAIN_API_KEY")
15
+
16
+ llm = ChatGroq(model="qwen-2.5-32b")
17
+
18
+ # Graph state
19
+ class State(TypedDict):
20
+ code_snippet: str # input
21
+ readability_feedback: str # intermediate
22
+ security_feedback: str # intermediate
23
+ best_practices_feedback: str # intermediate
24
+ feedback_aggregator: str # output
25
+
26
+
27
+ # Nodes
28
+ @traceable
29
+ def get_readability_feedback(state: State):
30
+ """First LLM call to check code readability"""
31
+ msg = llm.invoke(
32
+ f"Provide readability feedback for the following code:\n\n {state['code_snippet']}"
33
+ )
34
+ return {"readability_feedback": msg.content}
35
+
36
+
37
+ @traceable
38
+ def get_security_feedback(state: State):
39
+ """Second LLM call to check for security vulnerabilities in code"""
40
+ msg = llm.invoke(
41
+ f"Check for potential security vulnerabilities in the following code and provide feedback:\n\n {state['code_snippet']}"
42
+ )
43
+ return {"security_feedback": msg.content}
44
+
45
+
46
+ @traceable
47
+ def get_best_practices_feedback(state: State):
48
+ """Third LLM call to check for adherence to coding best practices"""
49
+ msg = llm.invoke(
50
+ f"Evaluate the adherence to coding best practices in the following code and provide feedback:\n\n {state['code_snippet']}"
51
+ )
52
+ return {"best_practices_feedback": msg.content}
53
+
54
+
55
+ @traceable
56
+ def aggregate_feedback(state: State):
57
+ """Combine all the feedback from the three LLM calls into a single output"""
58
+ combined = f"Here's the overall feedback for the code:\n\n"
59
+ combined += f"READABILITY FEEDBACK:\n{state['readability_feedback']}\n\n"
60
+ combined += f"SECURITY FEEDBACK:\n{state['security_feedback']}\n\n"
61
+ combined += f"BEST PRACTICES FEEDBACK:\n{state['best_practices_feedback']}"
62
+ return {"feedback_aggregator": combined}
63
+
64
+
65
+ # Build workflow
66
+ parallel_builder = StateGraph(State)
67
+
68
+ # Add nodes - Corrected node names
69
+ parallel_builder.add_node("get_readability_feedback", get_readability_feedback)
70
+ parallel_builder.add_node("get_security_feedback", get_security_feedback)
71
+ parallel_builder.add_node("get_best_practices_feedback", get_best_practices_feedback)
72
+ parallel_builder.add_node("aggregate_feedback", aggregate_feedback)
73
+
74
+ # Add edges to connect nodes
75
+ parallel_builder.add_edge(START, "get_readability_feedback")
76
+ parallel_builder.add_edge(START, "get_security_feedback")
77
+ parallel_builder.add_edge(START, "get_best_practices_feedback")
78
+ parallel_builder.add_edge("get_readability_feedback", "aggregate_feedback")
79
+ parallel_builder.add_edge("get_security_feedback", "aggregate_feedback")
80
+ parallel_builder.add_edge("get_best_practices_feedback", "aggregate_feedback")
81
+ parallel_builder.add_edge("aggregate_feedback", END)
82
+
83
+ parallel_workflow = parallel_builder.compile()
84
+
85
+ # Show workflow - Try and except to handle the timeout
86
+ try:
87
+ display(Image(parallel_workflow.get_graph().draw_mermaid_png()))
88
+ except Exception as e:
89
+ print(f"Error generating Mermaid diagram: {e}")
90
+
91
+ # Invoke
92
+ # Here is an example of a program, you can change it for any python code.
93
+ full_program = """
94
+ import os
95
+ from dotenv import load_dotenv
96
+
97
+ load_dotenv()
98
+
99
+ print(os.getenv("LANGCHAIN_API_KEY"))
100
+ """
101
+ state = parallel_workflow.invoke({"code_snippet": full_program})
102
+ print(state["feedback_aggregator"])
103
+
learning_path_orchestrator.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+ from typing import TypedDict, List, Annotated
4
+ from langchain_groq import ChatGroq
5
+ from pydantic import BaseModel, Field
6
+ from langsmith import traceable
7
+ from langgraph.graph import StateGraph, START, END
8
+ from IPython.display import Image, display, Markdown
9
+ from langchain_core.messages import SystemMessage, HumanMessage
10
+ from langgraph.constants import Send
11
+ import operator
12
+
13
+ # Load environment variables
14
+ load_dotenv()
15
+ os.environ["GROQ_API_KEY"] = os.getenv("GROQ_API_KEY")
16
+ os.environ["LANGCHAIN_TRACING_V2"] = "true"
17
+ os.environ["LANGCHAIN_API_KEY"] = os.getenv("LANGCHAIN_API_KEY")
18
+
19
+ # Initialize LLM model
20
+ llm = ChatGroq(model="qwen-2.5-32b")
21
+
22
+ # ----------------------------
23
+ # 1️⃣ Define Custom Data Structures
24
+ # ----------------------------
25
+
26
+ class Topic(BaseModel):
27
+ """Represents a learning topic with a name and description."""
28
+ name: str = Field(description="Name of the learning topic.")
29
+ description: str = Field(description="Brief overview of the topic.")
30
+
31
+ class Topics(BaseModel):
32
+ """Wrapper for a list of learning topics."""
33
+ topics: List[Topic] = Field(description="List of topics to learn.")
34
+
35
+ # Augment the LLM with structured schema
36
+ planner = llm.with_structured_output(Topics)
37
+
38
+ # Define the state that carries data throughout the workflow
39
+ class State(TypedDict):
40
+ user_skills: str
41
+ user_goals: str
42
+ topics: List[Topic]
43
+ completed_topics: Annotated[List[str], operator.add] # Merging completed topics
44
+ learning_roadmap: str
45
+
46
+ # Worker state for topic processing
47
+ class WorkerState(TypedDict):
48
+ topic: Topic
49
+ completed_topics: List[str]
50
+
51
+ # ----------------------------
52
+ # 2️⃣ Define Core Processing Functions
53
+ # ----------------------------
54
+
55
+ @traceable
56
+ def orchestrator(state: State):
57
+ """Creates a study plan based on user skills and goals."""
58
+
59
+ # LLM generates a structured study plan
60
+ study_plan = planner.invoke([
61
+ SystemMessage(
62
+ content="Create a detailed study plan based on user skills and goals."
63
+ ),
64
+ HumanMessage(
65
+ content=f"User skills: {state['user_skills']}\nUser goals: {state['user_goals']}"
66
+ ),
67
+ ])
68
+
69
+ print("Study Plan:", study_plan)
70
+
71
+ return {"topics": study_plan.topics} # Returns generated topics
72
+
73
+
74
+ @traceable
75
+ def llm_call(state: WorkerState):
76
+ """Generates a content summary for a specific topic."""
77
+
78
+ # LLM processes the topic and generates a summary
79
+ topic_summary = llm.invoke([
80
+ SystemMessage(
81
+ content="Generate a content summary for the provided topic."
82
+ ),
83
+ HumanMessage(
84
+ content=f"Topic: {state['topic'].name}\nDescription: {state['topic'].description}"
85
+ ),
86
+ ])
87
+
88
+ return {"completed_topics": [topic_summary.content]} # Returns generated summary
89
+
90
+
91
+ @traceable
92
+ def synthesizer(state: State):
93
+ """Compiles topic summaries into a structured learning roadmap."""
94
+
95
+ topic_summaries = state["completed_topics"]
96
+ learning_roadmap = "\n\n---\n\n".join(topic_summaries) # Formatting output
97
+
98
+ return {"learning_roadmap": learning_roadmap} # Returns final roadmap
99
+
100
+ # ----------------------------
101
+ # 3️⃣ Define Conditional Edge Function (Before Using It)
102
+ # ----------------------------
103
+
104
+ def assign_workers(state: State):
105
+ """Assigns a worker (llm_call) to each topic in the plan."""
106
+
107
+ return [Send("llm_call", {"topic": t}) for t in state["topics"]] # Creates worker tasks
108
+
109
+ # ----------------------------
110
+ # 4️⃣ Build Workflow
111
+ # ----------------------------
112
+
113
+ learning_path_builder = StateGraph(State)
114
+
115
+ # Add nodes
116
+ learning_path_builder.add_node("orchestrator", orchestrator)
117
+ learning_path_builder.add_node("llm_call", llm_call)
118
+ learning_path_builder.add_node("synthesizer", synthesizer)
119
+
120
+ # Define execution order using edges
121
+ learning_path_builder.add_edge(START, "orchestrator") # Start with orchestrator
122
+ learning_path_builder.add_conditional_edges("orchestrator", assign_workers, ["llm_call"]) # Assign workers
123
+ learning_path_builder.add_edge("llm_call", "synthesizer") # Process topics
124
+ learning_path_builder.add_edge("synthesizer", END) # End workflow
125
+
126
+ # Compile workflow
127
+ learning_path_workflow = learning_path_builder.compile()
128
+
129
+ # ----------------------------
130
+ # 5️⃣ Run the Workflow
131
+ # ----------------------------
132
+
133
+ user_skills = "Python programming, basic machine learning concepts"
134
+ user_goals = "Learn advanced AI, master prompt engineering, and build AI applications"
135
+
136
+ state = learning_path_workflow.invoke(
137
+ {"user_skills": user_skills, "user_goals": user_goals}
138
+ )
139
+
140
+ # Display the final learning roadmap
141
+ Markdown(state["learning_roadmap"])
orchestrator_data_flow.md ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Data Flow Breakdown in learning_path_orchestrator
2
+ We follow a structured data pipeline where each step modifies and passes data to the next stage.
3
+
4
+ 1️⃣ Define Custom Data Structures
5
+ - Topic (BaseModel) β†’ Represents a single topic with name and description.
6
+ - Topics (BaseModel) β†’ A wrapper around multiple Topic objects (essentially a list of topics).
7
+ - State (TypedDict) β†’ Holds global state, including user input, generated topics, and completed topics.
8
+ - WorkerState (TypedDict) β†’ Holds individual topic assignments for processing.
9
+
10
+ 2️⃣ Step-by-Step Data Flow
11
+
12
+ Step 1: Orchestrator Generates Topics
13
+ Input: user_skills and user_goals
14
+ Process: Calls planner.invoke(), which uses an LLM (Groq API) to generate topics.
15
+ Output: A structured Topics object (a list of Topic objects).
16
+ Storage: The topics list is saved inside State.
17
+ Returns: {"topics": study_plan.topics}
18
+ πŸ“Œ Key Detail:
19
+ The Orchestrator only generates topics and doesn’t process them. It assigns each topic to workers.
20
+
21
+ Step 2: Assign Workers to Each Topic
22
+ Function: assign_workers(state: State)
23
+ Process: Iterates over state["topics"] and assigns each topic to a worker (i.e., llm_call).
24
+ Returns: A list of dispatch instructions, sending each topic to the llm_call function.
25
+ Key Mechanism:
26
+ Uses Send("llm_call", {"topic": t}), which maps each topic to WorkerState.
27
+ πŸ“Œ Key Detail:
28
+ This step distributes work in parallel across multiple workers, each handling a single topic.
29
+
30
+ Step 3: LLM Call Generates Topic Summaries
31
+ Function: llm_call(state: WorkerState)
32
+ Input: A single topic object (from WorkerState).
33
+ Process:
34
+ Calls the LLM (llm.invoke) with the topic's name and description.
35
+ Generates a summary + resources in markdown format.
36
+ Output:
37
+ {"completed_topics": [topic_summary.content]}
38
+ Storage: The summaries are stored inside completed_topics in State.
39
+ πŸ“Œ Key Detail:
40
+ Each worker only receives one topic at a time. The WorkerState helps isolate one topic per call instead of processing everything at once.
41
+
42
+ Step 4: Synthesizer Combines Summaries into a Learning Roadmap
43
+ Function: synthesizer(state: State)
44
+ Input: completed_topics list (all processed topics).
45
+ Process: Joins all summaries together into a structured format.
46
+ Output: {"learning_roadmap": learning_roadmap}
47
+ Final Storage: The roadmap is stored inside State.
48
+ πŸ“Œ Key Detail:
49
+ This step aggregates all topic summaries into a final, structured learning plan.
50
+
51
+ 3️⃣ Where Does the Data Go?
52
+ Step Function Input Output Where the Data Goes
53
+ 1 orchestrator(state) User skills & goals topics list Stored in State["topics"]
54
+ 2 assign_workers(state) Topics list Send("llm_call", {"topic": t}) Sends each topic to llm_call
55
+ 3 llm_call(state) A single topic {"completed_topics": [summary]} Appends to State["completed_topics"]
56
+ 4 synthesizer(state) completed_topics list learning_roadmap Stores final roadmap in State["learning_roadmap"]
57
+
58
+ πŸ“ Key Takeaways
59
+ - Orchestrator generates the topics based on user_skills and user_goals.
60
+ - Workers process each topic separately (using llm_call).
61
+ - WorkerState ensures only one topic is processed per worker to avoid mixing topics.
62
+ - The synthesizer combines all results into a final structured roadmap.
63
+ - Data flows in a structured manner through State and WorkerState, ensuring modular and parallel execution.
requirements.txt ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ langchain
2
+ python-dotenv
3
+ langchain-openai
4
+ langchain-core
5
+ langchain-community
6
+ bs4
7
+ faiss-cpu
8
+ pypdf
9
+ arxiv
10
+ pymupdf
11
+ wikipedia
12
+ lxml
13
+ langchain_huggingface
14
+ langchain-groq
15
+ langgraph
16
+ langgraph-cli[inmem]
17
+ streamlit