Skip to content

Commit 7e936dc

Browse files
committed
feat: Add MCP integration, DeepSeek, Groq, and Playwright tests; update RAG agents and prompts
1 parent 25dca5c commit 7e936dc

31 files changed

Lines changed: 5145 additions & 68 deletions

agents/agile_factory/state/agile_state.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
- Control fields (status, errors, thread_id, current_node)
1010
"""
1111

12-
try:d
12+
try:
1313
from typing_extensions import TypedDict
1414
except ImportError:
1515
from typing import TypedDict # Python 3.12+

agents/rag/query_analyst_agent.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,14 +217,30 @@ async def _analyze_query_with_llm(self, query: str) -> Dict[str, Any]:
217217
logger.warning("LLM response not valid JSON, using fallback")
218218
return self._fallback_analysis(query)
219219

220+
# Safely parse complexity
221+
complexity_val = analysis_data.get('complexity', 0.5)
222+
try:
223+
if isinstance(complexity_val, (int, float)):
224+
complexity = float(complexity_val)
225+
elif isinstance(complexity_val, str):
226+
if complexity_val.lower() == 'low': complexity = 0.3
227+
elif complexity_val.lower() == 'medium': complexity = 0.5
228+
elif complexity_val.lower() == 'high': complexity = 0.8
229+
else:
230+
complexity = float(complexity_val)
231+
else:
232+
complexity = 0.5
233+
except (ValueError, TypeError):
234+
complexity = 0.5
235+
220236
# Ensure all fields are present
221237
analysis = {
222238
'original_query': query,
223239
'intent': analysis_data.get('intent', 'factual'),
224240
'rewritten_queries': analysis_data.get('rewritten_queries', [query]),
225241
'key_concepts': analysis_data.get('key_concepts', []),
226242
'search_strategy': analysis_data.get('search_strategy', 'focused'),
227-
'complexity': float(analysis_data.get('complexity', 0.5)),
243+
'complexity': complexity,
228244
'reasoning': analysis_data.get('reasoning', '')
229245
}
230246

agents/rag/rag_swarm_coordinator.py

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,7 @@ def _extract_query_from_state(self, state: MessagesState) -> Optional[str]:
675675
logger.warning(f" First message: {str(messages[0])[:100]}")
676676
return None
677677

678-
def _query_analyst_node(self, state: MessagesState):
678+
async def _query_analyst_node(self, state: MessagesState):
679679
"""
680680
Query Analysis Node - Sophisticated query understanding.
681681
@@ -719,9 +719,8 @@ def _query_analyst_node(self, state: MessagesState):
719719
logger.info(f"🔍 QueryAnalystAgent analyzing: '{query[:60]}...'")
720720

721721
# Call QueryAnalystAgent
722-
import asyncio
723722
task = {"query": query}
724-
analysis_result = asyncio.run(self.query_analyst.execute(task))
723+
analysis_result = await self.query_analyst.execute(task)
725724

726725
# Format analysis as system message
727726
analysis_text = analysis_result.get("output_data", {})
@@ -736,7 +735,7 @@ def _query_analyst_node(self, state: MessagesState):
736735
logger.info(f"✅ Query analysis complete: {analysis_text.get('intent', 'unknown')} intent")
737736
return {"messages": [analysis_msg]}
738737

739-
def _retrieval_specialist_node(self, state: MessagesState):
738+
async def _retrieval_specialist_node(self, state: MessagesState):
740739
"""
741740
Retrieval Specialist Node - Multi-source retrieval orchestration.
742741
@@ -756,9 +755,8 @@ def _retrieval_specialist_node(self, state: MessagesState):
756755

757756
# Call QueryAnalystAgent first to get proper query_analysis format
758757
# (RetrievalSpecialistAgent expects query_analysis dict)
759-
import asyncio
760758
query_task = {"query": query}
761-
query_analysis_result = asyncio.run(self.query_analyst.execute(query_task))
759+
query_analysis_result = await self.query_analyst.execute(query_task)
762760

763761
# Extract query_analysis from QueryAnalystAgent result
764762
# QueryAnalystAgent returns: {'status': 'success', 'analysis': {...}, ...}
@@ -800,7 +798,7 @@ def _retrieval_specialist_node(self, state: MessagesState):
800798
task["document_filters"] = state["document_filters"]
801799
logger.info(f"🎯 Applying document filters from state: {state['document_filters']}")
802800

803-
retrieval_result = asyncio.run(self.retrieval_specialist.execute(task))
801+
retrieval_result = await self.retrieval_specialist.execute(task)
804802

805803
# Extract documents from search_results (correct field name)
806804
retrieved_docs = retrieval_result.get("search_results", [])
@@ -829,7 +827,7 @@ def _retrieval_specialist_node(self, state: MessagesState):
829827
logger.info(f"✅ Retrieved {len(tool_msgs)} documents from {len(set(d.name for d in tool_msgs))} sources")
830828
return {"messages": tool_msgs}
831829

832-
def _document_grader_node(self, state: MessagesState):
830+
async def _document_grader_node(self, state: MessagesState):
833831
"""
834832
Document Grader Node - Grade documents for relevance BEFORE re-ranking.
835833
@@ -848,7 +846,7 @@ def _document_grader_node(self, state: MessagesState):
848846

849847
# Get retrieval results (tool messages)
850848
tool_results = []
851-
for msg in messages:
849+
for msg in state["messages"]: # Use state["messages"] as messages var wasn't defined in original code snippet but extracted above
852850
if isinstance(msg, ToolMessage):
853851
tool_results.append({
854852
'content': msg.content,
@@ -881,7 +879,7 @@ def _document_grader_node(self, state: MessagesState):
881879
Give a binary score 'yes' or 'no' to indicate whether the document is relevant to the question.
882880
Be lenient - if the document contains ANY relevant information, mark it as 'yes'."""
883881

884-
grade = llm.with_structured_output(GradeDocuments).invoke([
882+
grade = await llm.with_structured_output(GradeDocuments).ainvoke([
885883
HumanMessage(content=grade_prompt)
886884
])
887885

@@ -917,7 +915,7 @@ def _document_grader_node(self, state: MessagesState):
917915
logger.info(f"✅ Grading complete: {len(graded_tool_msgs)}/{len(tool_results)} documents relevant")
918916
return {"messages": [grading_summary] + graded_tool_msgs}
919917

920-
def _re_ranker_node(self, state: MessagesState):
918+
async def _re_ranker_node(self, state: MessagesState):
921919
"""
922920
Re-Ranker Node - Relevance scoring and filtering.
923921
@@ -948,12 +946,11 @@ def _re_ranker_node(self, state: MessagesState):
948946
logger.info(f"🎯 ReRankerAgent re-ranking {len(tool_results)} documents...")
949947

950948
# Call ReRankerAgent
951-
import asyncio
952949
task = {
953950
"query": query,
954951
"documents": tool_results
955952
}
956-
rerank_result = asyncio.run(self.re_ranker.execute(task))
953+
rerank_result = await self.re_ranker.execute(task)
957954

958955
# Extract re-ranking info
959956
reranked_data = rerank_result.get("output_data", {})
@@ -972,7 +969,7 @@ def _re_ranker_node(self, state: MessagesState):
972969
logger.info(f"✅ Re-ranking complete: avg={avg_score:.1%}, quality_ok={quality_ok}")
973970
return {"messages": [rerank_msg]}
974971

975-
def _context_enrichment_node(self, state: MessagesState):
972+
async def _context_enrichment_node(self, state: MessagesState):
976973
"""
977974
Context Enrichment Node - Enrich context after re-ranking.
978975
@@ -992,7 +989,7 @@ def _context_enrichment_node(self, state: MessagesState):
992989

993990
# Get re-ranked context (tool messages from grader/re-ranker)
994991
context_parts = []
995-
for msg in messages:
992+
for msg in state["messages"]: # Use state["messages"]
996993
if isinstance(msg, ToolMessage):
997994
context_parts.append(msg.content)
998995

@@ -1028,7 +1025,7 @@ def _context_enrichment_node(self, state: MessagesState):
10281025
- Recommendation: "sufficient" if context is complete, "needs_more" if critical gaps exist"""
10291026

10301027
try:
1031-
enrichment_response = llm.invoke([HumanMessage(content=enrichment_prompt)])
1028+
enrichment_response = await llm.ainvoke([HumanMessage(content=enrichment_prompt)])
10321029
enriched_analysis = enrichment_response.content
10331030

10341031
# Check if enrichment recommends more retrieval
@@ -1054,7 +1051,7 @@ def _context_enrichment_node(self, state: MessagesState):
10541051
logger.error(f"❌ Context enrichment failed: {e}")
10551052
return {"messages": [SystemMessage(content=f"Context enrichment error: {e}")]}
10561053

1057-
def _quality_assurance_node(self, state: MessagesState):
1054+
async def _quality_assurance_node(self, state: MessagesState):
10581055
"""
10591056
Quality Assurance Node - Comprehensive quality checks.
10601057
@@ -1080,12 +1077,11 @@ def _quality_assurance_node(self, state: MessagesState):
10801077
logger.info(f"✅ QualityAssuranceAgent checking quality...")
10811078

10821079
# Call QualityAssuranceAgent
1083-
import asyncio
10841080
task = {
10851081
"query": query,
10861082
"context": context
10871083
}
1088-
qa_result = asyncio.run(self.quality_assurance.execute(task))
1084+
qa_result = await self.quality_assurance.execute(task)
10891085

10901086
# Extract quality metrics
10911087
qa_data = qa_result.get("output_data", {})
@@ -1170,7 +1166,7 @@ async def _writer_node(self, state: MessagesState):
11701166
logger.info(f"✅ Answer generated: {len(answer)} chars")
11711167
return {"messages": [AIMessage(content=answer)]}
11721168

1173-
def _citation_verification_node(self, state: MessagesState):
1169+
async def _citation_verification_node(self, state: MessagesState):
11741170
"""
11751171
Citation Verification Node - Verify citations are accurate and relevant.
11761172
@@ -1231,7 +1227,7 @@ def _citation_verification_node(self, state: MessagesState):
12311227
- Recommendations for improvement"""
12321228

12331229
try:
1234-
verification_response = llm.invoke([HumanMessage(content=verification_prompt)])
1230+
verification_response = await llm.ainvoke([HumanMessage(content=verification_prompt)])
12351231
verification_analysis = verification_response.content
12361232

12371233
# Store verification results as system message
@@ -2055,7 +2051,7 @@ def _route_after_tools(self, state: MessagesState) -> Literal["generate_answer",
20552051
logger.info("⚠️ FALLBACK: Routing to generate_answer (grading failed + human_in_loop=False)")
20562052
return "generate_answer"
20572053

2058-
def _rewrite_question(self, state: MessagesState):
2054+
async def _rewrite_question(self, state: MessagesState):
20592055
"""
20602056
Rewrite the original question for better retrieval.
20612057
@@ -2086,7 +2082,7 @@ def _rewrite_question(self, state: MessagesState):
20862082

20872083
# Format prompt with original question
20882084
prompt = f"{rewrite_prompt_template}\n\nOriginal question: {question}\n\nFormulate an improved question:"
2089-
response = self.llm.invoke([HumanMessage(content=prompt)])
2085+
response = await self.llm.ainvoke([HumanMessage(content=prompt)])
20902086

20912087
logger.info(f"📝 Rewrote question: '{question}' → '{response.content}'")
20922088

apps/rag_management_app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2409,7 +2409,7 @@ def system_settings_page():
24092409
if langsmith_enabled:
24102410
st.success("✅ LangSmith Tracing Enabled")
24112411
st.info(f"**Project:** {os.environ.get('LANGCHAIN_PROJECT', 'default')}")
2412-
st.markdown("🔗 View traces at: [https://smith.langchain.com/](https://smith.langchain.com/)")
2412+
st.markdown("🔗 View traces at: =[https://smith.langchain.com/](https://smith.langchain.com/)")
24132413
else:
24142414
st.warning("⚠️ LangSmith Tracing Disabled")
24152415
st.info("To enable tracing, add to `.streamlit/secrets.toml`:")

context/context_engine.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,15 @@ def _initialize_semantic_search(self) -> None:
175175

176176
# Initialize Qdrant client (local, embedded - no API key needed)
177177
# Initialize this even if embeddings failed, so we can still manage collections
178-
qdrant_path = Path(self.config.vector_db_path) / "qdrant_storage"
179-
qdrant_path.mkdir(parents=True, exist_ok=True)
180-
181-
self.qdrant_client = QdrantClient(path=str(qdrant_path))
182-
self.logger.info(f"✅ Qdrant initialized (local storage: {qdrant_path})")
178+
if self.config.vector_db_path == ":memory:":
179+
self.qdrant_client = QdrantClient(location=":memory:")
180+
self.logger.info("✅ Qdrant initialized (in-memory)")
181+
else:
182+
qdrant_path = Path(self.config.vector_db_path) / "qdrant_storage"
183+
qdrant_path.mkdir(parents=True, exist_ok=True)
184+
185+
self.qdrant_client = QdrantClient(path=str(qdrant_path))
186+
self.logger.info(f"✅ Qdrant initialized (local storage: {qdrant_path})")
183187

184188
if self.embeddings is None:
185189
self.logger.warning("⚠️ No embedding provider available - semantic search disabled")

langgraph.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
],
55
"graphs": {
66
"agile_factory": "agents/agile_factory/workflow.py:graph",
7-
"code_generator_test": "agents/agile_factory/test_code_generator_studio.py:graph"
7+
"rag_agent": "agents/rag/rag_swarm_coordinator.py:graph",
8+
"research_assistant": "agents/research_assistant/research_assistant.py:graph"
89
},
910
"env": {
1011
"GOOGLE_API_KEY": "${GOOGLE_API_KEY}",
@@ -13,7 +14,7 @@
1314
"LANGCHAIN_TRACING_V2": "true",
1415
"LANGCHAIN_PROJECT": "ai-dev-agent",
1516
"PYTHONPATH": ".",
16-
"DATABASE_URI": "sqlite://:memory:",
17+
"DATABASE_URI": "sqlite:///:memory:",
1718
"REDIS_URI": "redis://localhost:6379"
1819
},
1920
"python_version": "3.11",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
prompt=PromptTemplate(input_variables=[], input_types={}, partial_variables={}, template='You are an intelligent AI assistant with advanced conversation memory, document retrieval, and quality analysis capabilities.\n\nCONVERSATION AWARENESS (CRITICAL):\n- ALWAYS review the full conversation history before making decisions\n- Track what information you\'ve already provided in this conversation\n- Build upon previous answers instead of repeating the same information\n- Reference earlier messages when relevant\n- If asked for "something new" or "more", provide information NOT mentioned before\n- Acknowledge follow-up questions that reference previous context\n- Remember what topics have been discussed and what questions have been asked\n\nINTELLIGENT WORKFLOW:\n1. Review conversation history to understand full context\n2. Call retrieve_project_docs to search for relevant information\n3. Grade retrieved documents for relevance using your document grading capability\n4. If documents are highly relevant: Proceed to answer generation\n5. If documents are not relevant: Rewrite the query for better results and retry\n6. Generate a comprehensive answer using the best retrieved context\n\nDOCUMENT GRADING:\n- Evaluate each retrieved document for relevance to the user\'s question\n- Consider both direct relevance and contextual relevance based on conversation history\n- Use grading results to determine whether to answer or refine the query\n\nQUERY REWRITING:\n- If documents are not relevant, reformulate the query for better retrieval\n- Consider conversation context when rewriting queries\n- Make queries more specific based on what the user is actually asking\n\nRESPONSE GENERATION:\n- Use the retrieved context and conversation history to generate answers\n- Be concise and well-structured\n- Cite sources when appropriate\n- If you\'ve already answered a similar question, acknowledge it and provide NEW details\n- Never repeat the exact same information - always add value\n\nMEMORY UTILIZATION:\n- Remember previous questions: "Your last question was about X"\n- Remember your previous responses: "I previously explained X, now let me add Y"\n- Track conversation flow: "Building on what we discussed about X..."\n- Avoid repetition: Always check if you\'ve already provided this information\n\nIMPORTANT: You have access to the full conversation history and intelligent grading/rewriting capabilities. Use all these tools to provide context-aware, high-quality, non-repetitive responses.\n') additional_kwargs={}

0 commit comments

Comments
 (0)