First working implementation of the program
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
#LANGCHAIN_TRACING_V2=true
|
||||||
|
#LANGCHAIN_PROJECT=capstone_msagi_VR
|
||||||
|
#LANGCHAIN_ENDPOINT=https://apac.api.smith.langchain.com
|
||||||
|
|
||||||
|
LANGSMITH_TRACING_V2=true
|
||||||
|
LANGSMITH_ENDPOINT=https://apac.api.smith.langchain.com
|
||||||
|
LANGSMITH_PROJECT="capstone_msagi_VR"
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
# Ignore all __pycache__ directories
|
||||||
|
**/__pycache__/
|
||||||
|
|
||||||
|
# Ignore compiled Python files
|
||||||
|
*.py[cod]
|
||||||
|
|
||||||
|
venv/
|
||||||
|
log/
|
||||||
|
output/
|
||||||
|
|
||||||
|
|
||||||
|
bin/__pycache__/
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"python-envs.defaultEnvManager": "ms-python.python:venv"
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -1,3 +1,35 @@
|
|||||||
|
# Go through the following steps to run the application:
|
||||||
|
|
||||||
|
1) Create Python Virtual Env from your root directory
|
||||||
|
python -m venv venv
|
||||||
|
|
||||||
|
2) Activate Virtual Env
|
||||||
|
a) Windows: venv\Scripts\activate.bat
|
||||||
|
b) Unix: source venv/bin/activate
|
||||||
|
|
||||||
|
3) Installing Dependencies
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
4)
|
||||||
|
Install GTK-for-Windows (needed to generate the PDF)
|
||||||
|
1.Download the installer and install: Go to the GTK-for-Windows runtime project on GitHub (https://github.com/tschoonj/GTK-for-Windows-Runtime-Environment-Installer/releases)
|
||||||
|
2.Ensure you check the box during installation that says "Add to the PATH environment variable".
|
||||||
|
|
||||||
|
5)
|
||||||
|
Disable Smart App Control (SAC) on Windows
|
||||||
|
1.Open the Start menu, type Windows Security, and press Enter.
|
||||||
|
2.Click on App & browser control from the left navigation panel or home menu.
|
||||||
|
3.Click the Smart App Control settings link.
|
||||||
|
4.Select Off to disable the feature
|
||||||
|
|
||||||
|
6) Setup OPENAI_API_KEY from terminal
|
||||||
|
set LANGCHAIN_API_KEY=<your-langsmith-key>
|
||||||
|
set OPENAI_API_KEY=<your-api-key>
|
||||||
|
|
||||||
|
7) Running the Streamlit Application
|
||||||
|
python -m streamlit run app.py
|
||||||
|
|
||||||
|
|
||||||
# capstone_msagi
|
# capstone_msagi
|
||||||
|
|
||||||
Course-end Project: Product Strategy Simulation
|
Course-end Project: Product Strategy Simulation
|
||||||
|
|||||||
@@ -0,0 +1,201 @@
|
|||||||
|
import os
|
||||||
|
from pydantic import BaseModel, Field as PydanticField # Alias it to avoid conflicts
|
||||||
|
from crewai.tools import tool
|
||||||
|
from typing import List, Dict, Any
|
||||||
|
from shared_config import verbose, logger
|
||||||
|
from crewai import Agent, Task, Crew, Process, LLM, TaskOutput, TaskOutput
|
||||||
|
from crewai.tools import tool
|
||||||
|
from langchain_community.tools import DuckDuckGoSearchRun
|
||||||
|
from datetime import datetime
|
||||||
|
import os
|
||||||
|
|
||||||
|
###########################################################################################
|
||||||
|
#Define a callback function to log CrewAI step task
|
||||||
|
###########################################################################################
|
||||||
|
def log_task_to_file(output:TaskOutput):
|
||||||
|
"""Callback function to append task details into the log file."""
|
||||||
|
|
||||||
|
formatted_str = f"""
|
||||||
|
{'\n'}CrewAI Task Info
|
||||||
|
{"=" * 50}
|
||||||
|
Timestamp: {datetime.now()}
|
||||||
|
Task Description: {output.description}
|
||||||
|
Agent Assigned: {output.agent}
|
||||||
|
{"-" * 30}
|
||||||
|
Raw Output:{'\n'}
|
||||||
|
{output.raw}{'\n'}
|
||||||
|
{"=" * 50}
|
||||||
|
"""
|
||||||
|
logger.info(formatted_str)
|
||||||
|
|
||||||
|
def create_crew(config):
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# 1. DEFINE CUSTOM TOOLS & OUTPUT SCHEMAS
|
||||||
|
# ==========================================
|
||||||
|
|
||||||
|
# Simple search tool for the Research Agent
|
||||||
|
@tool("web_search")
|
||||||
|
def web_search_tool(query: str) -> str:
|
||||||
|
"""Search the web for current events, news, or factual information."""
|
||||||
|
ddg = DuckDuckGoSearchRun()
|
||||||
|
return ddg.invoke(query)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ResearchEvidence(BaseModel):
|
||||||
|
source_url: str = PydanticField(..., description="The source URL of the evidence.")
|
||||||
|
claim: str = PydanticField(..., description="The factual finding or data point.")
|
||||||
|
context: str = PydanticField(..., description="The context or time frame of this data point.")
|
||||||
|
|
||||||
|
class ResearchOutputSchema(BaseModel):
|
||||||
|
competitors: List[Dict[str, Any]] = PydanticField(..., description="List of competitors with pricing, features, and target audience.")
|
||||||
|
market_trends: List[str] = PydanticField(..., description="Key macro trends observed.")
|
||||||
|
evidence_citations: List[ResearchEvidence] = PydanticField(..., description="Valid source citations for facts collected.")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
#Create llm instance
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
if config.provider == "OpenAI":
|
||||||
|
llm = LLM(
|
||||||
|
model=config.model,
|
||||||
|
api_key=os.getenv("OPENAI_API_KEY"),
|
||||||
|
temperature=config.temperature,
|
||||||
|
max_completion_tokens=config.max_tokens,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
llm = LLM(
|
||||||
|
model=f"ollama/{config.model}",
|
||||||
|
api_key="ollama",
|
||||||
|
base_url="http://localhost:11434",
|
||||||
|
temperature=config.temperature
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"""LLM Model: {config.model}, Provider: {config.provider}, Temperature: {config.temperature}""")
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 1. Agent Definitions
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
|
||||||
|
# 1. Orchestrator & Documenter (Manager)
|
||||||
|
head_planner = Agent(
|
||||||
|
role='Head Planner and Orchestrator',
|
||||||
|
goal="Synthesize intermediary outputs and assemble the final master GTM document.",
|
||||||
|
backstory="A precise compiler and documentarian...",
|
||||||
|
#goal='Orchestrate the GTM workflow, delegate tasks to specialized agents, and synthesize all insights into a flawless final GTM document.',
|
||||||
|
#backstory='You are a Principal Operations Strategy Director. You excel at managing cross-functional teams, ensuring strict alignment, and formatting messy raw data into executive-ready corporate documentation.',
|
||||||
|
allow_delegation=False,
|
||||||
|
verbose=verbose,
|
||||||
|
llm=llm
|
||||||
|
)
|
||||||
|
|
||||||
|
# 2. researcher
|
||||||
|
researcher_agent = Agent(
|
||||||
|
role="Market Research Specialist",
|
||||||
|
goal="Gather raw competitive intelligence, target audience signals, and pricing tiers with structural citations.",
|
||||||
|
backstory="An elite OSINT researcher who tracks digital signals. You ignore marketing fluff and extract verifiable numbers, product capabilities, and structural URLs.",
|
||||||
|
#role='Research Agent',
|
||||||
|
#goal='Gather comprehensive market data, identify industry trends, and map out the competitive landscape.',
|
||||||
|
#backstory='You are an elite Market Research Analyst. You are an expert at mining deep web data, extracting customer pain points, and finding precise statistical facts from modern industry reports.',
|
||||||
|
verbose=verbose,
|
||||||
|
tools=[web_search_tool],
|
||||||
|
use_system_prompt=False,
|
||||||
|
llm=llm
|
||||||
|
)
|
||||||
|
|
||||||
|
# 3. analyst
|
||||||
|
analyst_agent = Agent(
|
||||||
|
role="Strategic Business Analyst",
|
||||||
|
goal="Transform unstructured research data into rigorous market models, SWOT matrixes, and 4P/7P frameworks.",
|
||||||
|
backstory="A former MBB consultant who specializes in corporate strategy. You find patterns in chaotic data, flag market gaps, and synthesize pricing elasticities.",
|
||||||
|
#role='Analyst Agent',
|
||||||
|
#goal='Synthesize raw market data into structured frameworks like SWOT, 4P, 7P, and target user personas.',
|
||||||
|
#backstory='You are a data-driven Strategic Analyst. You look past surface-level facts to find underlying market gaps, evaluate competitive positioning, and model risks.',
|
||||||
|
verbose=verbose,
|
||||||
|
llm=llm
|
||||||
|
)
|
||||||
|
|
||||||
|
# 4. strategist
|
||||||
|
strategist_agent = Agent(
|
||||||
|
role="Go-To-Market (GTM) Strategist",
|
||||||
|
goal="Draft the final actionable GTM blueprint including ICP maps, messaging layers, and launch milestone phases.",
|
||||||
|
backstory="A legendary growth marketing executive. You turn raw analytical data into high-converting messaging matrixes, channel frameworks, and scalable product launch schedules.",
|
||||||
|
#role='Strategy Agent',
|
||||||
|
#goal='Formulate the actionable Go-To-Market blueprint, positioning, pricing strategies, and launch timelines.',
|
||||||
|
#backstory='You are a veteran GTM Growth Strategist. You specialize in crafting commercial launch playbooks, positioning products uniquely against rivals, and designing user-acquisition loops.',
|
||||||
|
verbose=verbose,
|
||||||
|
llm=llm
|
||||||
|
)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 2. Task Definitions
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
|
||||||
|
#Fetch User query from config
|
||||||
|
query = config.user_query
|
||||||
|
|
||||||
|
# Task 1: Research Agent gathers raw intelligence
|
||||||
|
research_task = Task(
|
||||||
|
description=(
|
||||||
|
"Search the web for the top 3-5 players in the target market: '{query}'. "
|
||||||
|
"Extract pricing tiers, core feature modules, and target customer profiles. "
|
||||||
|
"Every single metric or claim MUST have a URL source tracked inside the JSON structure. "
|
||||||
|
"Do not fabricate any data and do not make assumptions. Only include verifiable facts using working URLs."
|
||||||
|
"If you cannot find a specific metric, leave it blank and do not fabricate data."
|
||||||
|
),
|
||||||
|
expected_output="A structured JSON file matching the schema with validated competitive rows and strict citation tracking.",
|
||||||
|
output_json=ResearchOutputSchema,
|
||||||
|
agent=researcher_agent
|
||||||
|
)
|
||||||
|
|
||||||
|
# Task 2: Analyst Agent creates comparative structures
|
||||||
|
analysis_task = Task(
|
||||||
|
description=(
|
||||||
|
"Review the structured JSON output provided by the Research Agent. "
|
||||||
|
"Construct a detailed markdown competitive landscape table and a pricing matrix. "
|
||||||
|
"Perform a comprehensive SWOT, 4P, and 7P analysis based strictly on the evidence compiled."
|
||||||
|
),
|
||||||
|
expected_output="A deep-dive analytical report featuring clean markdown tables, a SWOT quadrant, and an exhaustive 4P/7P matrix analysis.",
|
||||||
|
agent=analyst_agent
|
||||||
|
)
|
||||||
|
|
||||||
|
# Task 3: Strategy Agent plans market entry
|
||||||
|
strategy_task = Task(
|
||||||
|
description=(
|
||||||
|
"Take the analytical matrices and SWOT outputs to build a comprehensive GTM document. "
|
||||||
|
"Define 2 distinct Ideal Customer Profiles (ICPs). Outline the core value proposition. "
|
||||||
|
"Create a messaging framework (Hook, Problem, Solution) per ICP. "
|
||||||
|
"Identify high-ROI acquisition channels and detail a 30-60-90 day milestone launch plan."
|
||||||
|
),
|
||||||
|
expected_output="An actionable, comprehensive market entry strategy blueprint divided into logical execution phases.",
|
||||||
|
agent=strategist_agent
|
||||||
|
)
|
||||||
|
|
||||||
|
# Task 4: Head Planner synthesizes, formats, and exports
|
||||||
|
compilation_task = Task(
|
||||||
|
description=(
|
||||||
|
"Synthesize the outputs from the Research, Analyst, and Strategy agents into a seamless, high-caliber Master GTM Report. "
|
||||||
|
"Include all research data tables, citations, SWOT/7P strategic matrices, and execution timelines. "
|
||||||
|
"Include a title that cleary identifies the target market and a subtitle that highlights the core value proposition. "
|
||||||
|
),
|
||||||
|
expected_output="Final master GTM document.",
|
||||||
|
context=[research_task, analysis_task, strategy_task],
|
||||||
|
agent=head_planner
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 3. Crew Setup
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
|
||||||
|
crew= Crew(
|
||||||
|
agents=[researcher_agent, analyst_agent, strategist_agent, head_planner],
|
||||||
|
tasks=[research_task, analysis_task, strategy_task, compilation_task],
|
||||||
|
process=Process.sequential, # Tasks execute in exact sequence
|
||||||
|
#manager_agent=head_planner
|
||||||
|
task_callback=log_task_to_file,
|
||||||
|
verbose=verbose
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"Crew setup complete with {len(crew.agents)} agents and {len(crew.tasks)} tasks.")
|
||||||
|
return crew
|
||||||
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
from langsmith import traceable
|
||||||
|
from agent_workflows.crew_setup import create_crew
|
||||||
|
from shared_config import AgenticAIConfig, logger
|
||||||
|
from langsmith.integrations.otel import OtelSpanProcessor
|
||||||
|
from opentelemetry import trace
|
||||||
|
from opentelemetry.sdk.trace import TracerProvider
|
||||||
|
from opentelemetry.instrumentation.crewai import CrewAIInstrumentor
|
||||||
|
from opentelemetry.instrumentation.openai import OpenAIInstrumentor
|
||||||
|
|
||||||
|
# Initialize the trace provider
|
||||||
|
tracer_provider = TracerProvider()
|
||||||
|
#trace.set_tracer_provider(tracer_provider)
|
||||||
|
|
||||||
|
# Add the LangSmith OTel processor
|
||||||
|
tracer_provider.add_span_processor(OtelSpanProcessor())
|
||||||
|
|
||||||
|
# Instrument both CrewAI and OpenAI layers
|
||||||
|
CrewAIInstrumentor().instrument(tracer_provider=tracer_provider)
|
||||||
|
OpenAIInstrumentor().instrument(tracer_provider=tracer_provider)
|
||||||
|
|
||||||
|
@traceable(name="Capstone_GTM Strategy Simulation", run_type="chain")
|
||||||
|
def runAgenticWorkflow(config: AgenticAIConfig):
|
||||||
|
"""
|
||||||
|
Main Entry Point of Agentic AI Workflow
|
||||||
|
"""
|
||||||
|
logger.info("Agentic Workflow Started...")
|
||||||
|
|
||||||
|
crew = create_crew(config)
|
||||||
|
|
||||||
|
result = crew.kickoff(
|
||||||
|
inputs={
|
||||||
|
"query": config.user_query
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
config.response = str(result)
|
||||||
|
return config
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from shared_config import AgenticAIConfig, create_logger, logger
|
||||||
|
import streamlit as st
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from agent_workflows.orchestration import runAgenticWorkflow
|
||||||
|
from utilities.pdf_tools import export_to_pdf
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
#2- Loading Env Variables
|
||||||
|
load_dotenv() # It will load all the Env Variables
|
||||||
|
|
||||||
|
#3- Set Page Config
|
||||||
|
st.set_page_config(
|
||||||
|
page_title="Simplilearn Capstone Project", #Show it in the Tab
|
||||||
|
page_icon="🤖",
|
||||||
|
layout="wide"
|
||||||
|
)
|
||||||
|
|
||||||
|
#4- Initialize Config
|
||||||
|
if "config" not in st.session_state:
|
||||||
|
st.session_state.config = AgenticAIConfig()
|
||||||
|
config = st.session_state.config
|
||||||
|
|
||||||
|
#5- Initialize Streamlit- Conversation History
|
||||||
|
if "chat_history" not in st.session_state:
|
||||||
|
st.session_state.chat_history=[]
|
||||||
|
|
||||||
|
#6- Setting dict for Available Models
|
||||||
|
#llm_model = "gpt-5-nano" #Output~ 0.4 US$ / 1M tokens
|
||||||
|
#llm_model = "gpt-4o-mini" #Output~ 0.6 US$ / 1M tokens
|
||||||
|
#llm_model = "gpt-5.4-nano" #Output~ 1.25 US$ / 1M tokens
|
||||||
|
#llm_model = "gpt-5-mini" #Output~ 2 US$ / 1M tokens
|
||||||
|
#llm_model = "gpt-5.4-mini" #Output~ 4.50 US$ / 1M tokens
|
||||||
|
#llm_model = "gpt-4o" #Output~ 10 US$ / 1M tokens
|
||||||
|
#llm_model = "gpt-5.5" #Output~ 30 US$ / 1M tokens
|
||||||
|
MODEL_REGISTRY={
|
||||||
|
"OpenAI":["gpt-4o-mini","gpt-5.4-nano","gpt-5-mini","gpt-4.1","gpt-4o","gpt-3.5-turbo"],
|
||||||
|
"Ollama":["llama3.1","gpt-oss:20b","gemma4:e4b","qwen2.5-coder:14b","mistral-small3.2"],
|
||||||
|
}
|
||||||
|
|
||||||
|
#7- Creating Sidebar
|
||||||
|
with st.sidebar:
|
||||||
|
st.title("LLM Configuration")
|
||||||
|
config.provider = st.selectbox(
|
||||||
|
"LLM Provider",
|
||||||
|
list(MODEL_REGISTRY.keys())
|
||||||
|
)
|
||||||
|
|
||||||
|
#Dynamic Model Selection Logic
|
||||||
|
available_models = MODEL_REGISTRY[config.provider]
|
||||||
|
|
||||||
|
config.model = st.selectbox(
|
||||||
|
"Model",
|
||||||
|
available_models
|
||||||
|
)
|
||||||
|
|
||||||
|
config.temperature= st.slider(
|
||||||
|
"Temperature",
|
||||||
|
min_value=0.0,
|
||||||
|
max_value=2.0,
|
||||||
|
value=config.temperature,
|
||||||
|
step=0.1
|
||||||
|
)
|
||||||
|
|
||||||
|
config.max_tokens=st.slider(
|
||||||
|
"Max Tokens",
|
||||||
|
min_value=100,
|
||||||
|
max_value=4000,
|
||||||
|
value=config.max_tokens,
|
||||||
|
step=100
|
||||||
|
)
|
||||||
|
|
||||||
|
st.divider()
|
||||||
|
|
||||||
|
# Display Current Configurations Separately
|
||||||
|
st.subheader("Current Configuration:")
|
||||||
|
|
||||||
|
st.write(f"**Provider:** {config.provider}")
|
||||||
|
st.write(f"**Model:** {config.model}")
|
||||||
|
st.write(f"**Temperature:** {config.temperature}")
|
||||||
|
st.write(f"**Max Token:** {config.max_tokens}")
|
||||||
|
|
||||||
|
|
||||||
|
#8- Setting Page Title
|
||||||
|
st.title("🤖 Simplilearn Agentic AI Capstone: Research and GTM Planning")
|
||||||
|
|
||||||
|
#9- Creating User Input Section
|
||||||
|
config.user_query = st.text_area(
|
||||||
|
"Enter the product or service you want to research and plan a GTM strategy: ",
|
||||||
|
value = config.user_query,
|
||||||
|
height=200,
|
||||||
|
placeholder = "Example: Electrical bicycle, Burger shop in Tokyo, New AI-powered project management tool targeting small businesses, ..."
|
||||||
|
)
|
||||||
|
# Create Response Button
|
||||||
|
if st.button("Generate Response", type="primary"):
|
||||||
|
if not config.user_query.strip():
|
||||||
|
st.warning("Please enter a prompt before clicking the button")
|
||||||
|
else:
|
||||||
|
#Create logger
|
||||||
|
if logger is not None:
|
||||||
|
if logger.hasHandlers():
|
||||||
|
logger.removeHandler(logger.handlers[0]) # Remove existing handlers
|
||||||
|
create_logger()
|
||||||
|
#Store the User Query for displaying in conversation History on Streamlit
|
||||||
|
st.session_state.chat_history.append(
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"message": config.user_query
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info("Product or service to research and plan a GTM strategy: "+config.user_query)
|
||||||
|
|
||||||
|
# Create a Spinner until response is generated
|
||||||
|
with st.spinner("AI Agent is Thinking..."):
|
||||||
|
# Call CrewAI FLow
|
||||||
|
runAgenticWorkflow(config)
|
||||||
|
|
||||||
|
#Export the final GTM document to PDF
|
||||||
|
logger.info("Creating PDF...")
|
||||||
|
pdf_fiename_prefix = "Agentic_AI_Generated_GTM_Document"
|
||||||
|
script_dir = Path(__file__).parent
|
||||||
|
target_dir = script_dir / "output"
|
||||||
|
target_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
now = datetime.now()
|
||||||
|
formatted_DateTime = now.strftime("%Y-%m-%d_%H%M%S")
|
||||||
|
pdfpath = f"""output/{pdf_fiename_prefix}_{formatted_DateTime}.pdf"""
|
||||||
|
export_to_pdf(
|
||||||
|
inputText=config.response,
|
||||||
|
filename=pdfpath)
|
||||||
|
|
||||||
|
logger.info("PDF Exported Successfully at: "+pdfpath)
|
||||||
|
|
||||||
|
|
||||||
|
#Store the response for display in conversation history
|
||||||
|
st.session_state.chat_history.append(
|
||||||
|
{
|
||||||
|
"role": "assistant",
|
||||||
|
"message": config.response
|
||||||
|
}
|
||||||
|
)
|
||||||
|
st.session_state.config= config
|
||||||
|
|
||||||
|
|
||||||
|
#10- Creating Response Section
|
||||||
|
st.subheader("Conversation History")
|
||||||
|
|
||||||
|
for chat in st.session_state.chat_history:
|
||||||
|
if chat["role"]=="user":
|
||||||
|
with st.chat_message("user"):
|
||||||
|
st.write(chat["message"])
|
||||||
|
else:
|
||||||
|
with st.chat_message("assistant"):
|
||||||
|
st.write(chat["message"])
|
||||||
|
|
||||||
|
#11- Creating the button for Users to clear all the chats from the UI
|
||||||
|
if st.sidebar.button("Clear Chat"):
|
||||||
|
st.session_state.chat_history=[]
|
||||||
|
st.rerun()
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Useful Python Librarires
|
||||||
|
python-dotenv
|
||||||
|
|
||||||
|
# Agentic AI Libraries
|
||||||
|
langchain
|
||||||
|
langchain-openai
|
||||||
|
langchain-ollama
|
||||||
|
langchain-classic
|
||||||
|
langchain-community
|
||||||
|
langsmith
|
||||||
|
crewai
|
||||||
|
opentelemetry-instrumentation-crewai
|
||||||
|
opentelemetry-instrumentation-openai
|
||||||
|
#fastmcp
|
||||||
|
|
||||||
|
#web search
|
||||||
|
ddgs
|
||||||
|
|
||||||
|
# UI
|
||||||
|
streamlit
|
||||||
|
|
||||||
|
#PDF Generation
|
||||||
|
markdown
|
||||||
|
weasyprint
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import List
|
||||||
|
from utilities.logger import simple_logger
|
||||||
|
|
||||||
|
|
||||||
|
###########################################################################################
|
||||||
|
verbose = True #Set to True to see CrewAI process information in the console
|
||||||
|
###########################################################################################
|
||||||
|
|
||||||
|
###########################################################################################
|
||||||
|
printLogToConsole = False #Set to True to save and print log information to console, else set to False to only save log information to file
|
||||||
|
###########################################################################################
|
||||||
|
|
||||||
|
###########################################################################################
|
||||||
|
#Define logger parameter
|
||||||
|
###########################################################################################
|
||||||
|
logger = logging.getLogger()
|
||||||
|
def create_logger():
|
||||||
|
log_fiename_prefix = "log"
|
||||||
|
script_dir = Path(__file__).parent
|
||||||
|
target_dir = script_dir / "log"
|
||||||
|
target_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
now = datetime.now()
|
||||||
|
formatted_DateTime = now.strftime("%Y-%m-%d_%H%M%S")
|
||||||
|
log_path = f"""log/{log_fiename_prefix}_{formatted_DateTime}.log"""
|
||||||
|
global logger
|
||||||
|
logger = simple_logger(log_path, printLogToConsole)
|
||||||
|
|
||||||
|
###########################################################################################
|
||||||
|
#Create a 'dataclass' for all configs that can be used anywhere in this Project
|
||||||
|
###########################################################################################
|
||||||
|
@dataclass
|
||||||
|
class AgenticAIConfig:
|
||||||
|
# UI Selections from users
|
||||||
|
provider:str = "OpenAI"
|
||||||
|
model: str = "gpt-4o"
|
||||||
|
temperature: float = 0.4
|
||||||
|
max_tokens:int = 3000
|
||||||
|
|
||||||
|
# User Input
|
||||||
|
user_query:str=""
|
||||||
|
|
||||||
|
# Agent Settings (CrewAI Specifics)
|
||||||
|
#agent_name:str = "default"
|
||||||
|
#tools: List[str] = field(default_factory=list)
|
||||||
|
|
||||||
|
# Runtime
|
||||||
|
response:str=""
|
||||||
|
chat_history:List[dict] = field(default_factory=list)
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import logging
|
||||||
|
from xmlrpc.client import boolean
|
||||||
|
|
||||||
|
|
||||||
|
##############################################################################################################################
|
||||||
|
#Function to create a simple logger that logs messages to a file and optionally prints them to the console.
|
||||||
|
#If logger_name = "", then the root logger will be used. If you want to create a custom logger, provide a name for it.
|
||||||
|
##############################################################################################################################
|
||||||
|
def simple_logger(log_relative_path = str, printToConsole = boolean, logger_name = ""):
|
||||||
|
logger = logging.getLogger(logger_name)
|
||||||
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
formatter = logging.Formatter(fmt="%(asctime)s [%(levelname)s] %(message)s",
|
||||||
|
datefmt="%Y-%m-%d %H:%M:%S")
|
||||||
|
file_handler = logging.FileHandler(log_relative_path)
|
||||||
|
file_handler.setFormatter(formatter)
|
||||||
|
file_handler.setLevel(logging.INFO)
|
||||||
|
logger.addHandler(file_handler)
|
||||||
|
|
||||||
|
if printToConsole:
|
||||||
|
console_handler = logging.StreamHandler()
|
||||||
|
console_handler.setFormatter(formatter)
|
||||||
|
console_handler.setLevel(logging.INFO)
|
||||||
|
logger.addHandler(console_handler)
|
||||||
|
|
||||||
|
return logger
|
||||||
|
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
html_template = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<style>
|
||||||
|
/* Existing styles... */
|
||||||
|
|
||||||
|
/* Styles for Links */
|
||||||
|
a {{
|
||||||
|
color: #0000ff; /* Sets the link color to blue */
|
||||||
|
text-decoration: underline; /* Ensures the underline is visible */
|
||||||
|
}}
|
||||||
|
a:visited {{
|
||||||
|
color: #0000ff; /* Keeps the link blue even after it is clicked */
|
||||||
|
}}
|
||||||
|
|
||||||
|
/* Styles for clean Table Lines */
|
||||||
|
table {{
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse; /* Merges adjacent cell borders into a single line */
|
||||||
|
margin: 20px 0;
|
||||||
|
font-size: 11pt;
|
||||||
|
}}
|
||||||
|
th, td {{
|
||||||
|
border: 1px solid #bdc3c7; /* Draws the actual grid lines */
|
||||||
|
padding: 10px;
|
||||||
|
text-align: left;
|
||||||
|
}}
|
||||||
|
th {{
|
||||||
|
background-color: #f2f4f4; /* Optional: adds a neat background header color */
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2c3e50;
|
||||||
|
}}
|
||||||
|
tr:nth-child(even) {{
|
||||||
|
background-color: #f9f9f9; /* Optional: adds zebra striping to rows */
|
||||||
|
}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{content}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
import markdown
|
||||||
|
from weasyprint import HTML
|
||||||
|
|
||||||
|
def export_to_pdf(inputText, filename):
|
||||||
|
|
||||||
|
# Step 1: Convert Markdown components to HTML chunks.
|
||||||
|
# The 'tables' and 'fenced_code' extensions keep standard LLM formatting neat.
|
||||||
|
html_content = markdown.markdown(inputText, extensions=['tables', 'fenced_code'])
|
||||||
|
|
||||||
|
# Step 2: Inject the content into our CSS-styled HTML boilerplate template
|
||||||
|
final_html = html_template.format(content=html_content)
|
||||||
|
|
||||||
|
# Step 3: Render directly to a high-fidelity PDF file
|
||||||
|
HTML(string=final_html).write_pdf(filename)
|
||||||
|
#print(f"Successfully generated styled PDF at: {output_pdf_path}")
|
||||||
Reference in New Issue
Block a user