Spaces:
Sleeping
Sleeping
Commit
Β·
6c7823c
1
Parent(s):
9daa604
init
Browse files- .gitignore +30 -0
- Dockerfile +33 -0
- README.md +29 -8
- data_ingetion/__init__.py +0 -0
- data_ingetion/data_api.py +45 -0
- data_ingetion/firms_report/Investment Strategy & Risk Management Guide.pdf +0 -0
- data_ingetion/firms_report/Quarterly Research Report - Global Equity Markets.pdf +0 -0
- data_ingetion/market_data.py +84 -0
- data_ingetion/portfolios/IND.csv +16 -0
- data_ingetion/portfolios/US.csv +16 -0
- data_ingetion/portfolios/portfolio_change.csv +16 -0
- data_ingetion/vectroDB.py +83 -0
- diagram.jpeg +0 -0
- main_api.py +8 -0
- orchestrator/__init__.py +0 -0
- orchestrator/orchestrator.py +77 -0
- orchestrator/orchestrator_api.py +30 -0
- orchestrator/prompts.py +55 -0
- requirements.txt +156 -0
- streamlit_app/app.py +136 -0
.gitignore
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Ignore ChromaDB storage folder
|
2 |
+
**/chroma_storage/*
|
3 |
+
|
4 |
+
# Python artifacts
|
5 |
+
__pycache__/
|
6 |
+
*.py[cod]
|
7 |
+
*.pkl
|
8 |
+
*.db
|
9 |
+
*.sqlite3
|
10 |
+
|
11 |
+
# Environment files
|
12 |
+
.env
|
13 |
+
*.env
|
14 |
+
|
15 |
+
# OS-specific
|
16 |
+
.DS_Store
|
17 |
+
Thumbs.db
|
18 |
+
|
19 |
+
# Jupyter/IPython
|
20 |
+
.ipynb_checkpoints/
|
21 |
+
|
22 |
+
# Virtual environments
|
23 |
+
.venv/
|
24 |
+
.env/
|
25 |
+
|
26 |
+
# Logs and cache
|
27 |
+
*.log
|
28 |
+
*.cache/
|
29 |
+
.cache/
|
30 |
+
|
Dockerfile
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Base Python image
|
2 |
+
FROM python:3.10-slim
|
3 |
+
|
4 |
+
# Set environment variables
|
5 |
+
ENV PYTHONDONTWRITEBYTECODE=1 \
|
6 |
+
PYTHONUNBUFFERED=1
|
7 |
+
|
8 |
+
# Create a user to avoid root-level issues with ChromaDB
|
9 |
+
RUN adduser --disabled-password --gecos "" appuser
|
10 |
+
USER appuser
|
11 |
+
|
12 |
+
# Create working directory
|
13 |
+
WORKDIR /home/appuser/app
|
14 |
+
|
15 |
+
# Copy project files
|
16 |
+
COPY --chown=appuser:appuser . .
|
17 |
+
|
18 |
+
# Install Python dependencies
|
19 |
+
RUN pip install --upgrade pip \
|
20 |
+
&& pip install -r requirement.txt
|
21 |
+
|
22 |
+
# If using .env file, install python-dotenv and make sure app reads it
|
23 |
+
RUN pip install python-dotenv
|
24 |
+
|
25 |
+
# Expose FastAPI default port
|
26 |
+
EXPOSE 8000
|
27 |
+
# Expose Streamlit default port
|
28 |
+
EXPOSE 8501
|
29 |
+
|
30 |
+
# Start FastAPI app in the background, then Streamlit
|
31 |
+
CMD uvicorn streamlit_app.main_api:app --host 0.0.0.0 --port 8000 & \
|
32 |
+
streamlit run streamlit_app/app.py --server.port 8501
|
33 |
+
|
README.md
CHANGED
@@ -1,10 +1,31 @@
|
|
|
|
|
|
|
|
1 |
---
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
|
10 |
-
|
|
|
|
1 |
+
## Market Brief Agent
|
2 |
+
|
3 |
+
### Workflow Diagram
|
4 |
---
|
5 |
+

|
6 |
+
|
7 |
+
#### Overview
|
8 |
+
* An acyclic workflow where the user interacts through a Streamlit App.
|
9 |
+
* User's query is first parsed by the Orchestrator API endpoint that returns the result for what tools to use along with the result of those tools' execution.
|
10 |
+
* The original user query and the generated supporting context are then passed to the final response synthesizer.
|
11 |
+
* Final response is streamed back to the Streamlit app again via API communication.
|
12 |
+
* User can further instruct to listen to the generated response using Deepgram's voice models.
|
13 |
+
|
14 |
+
### Tools
|
15 |
+
|
16 |
+
**All tools are accessible through an API interface**
|
17 |
+
* `/data/get_historical_data` : This tool brings historical changes in a particular given stock. Must provide a YFinance ticker as a parameter.
|
18 |
+
* `/data/get_earning_metrics` : This tool generates the stock earnings summary over the past 3β4 years using YFinance earning metrics.
|
19 |
+
* `/data/get_portfolio_data` : This tool brings a current portfolio snapshot/updates. *Currently only supports IND portfolio*.
|
20 |
+
* `/data/get_portfolio_data` : This is a ***RAG*** based tool. It uses a company's prior documents as a knowledge base and uses semantic similarity to provide context on company-related user queries.
|
21 |
+
* `/data/get_portfolio_data` : Tool to make orchestration decisionsβi.e., which tool to call with what parameters.
|
22 |
+
* `/data/get_portfolio_data` : Tool to generate the final user-friendly response with **guardrails** to avoid giving aggressive financial advice.
|
23 |
+
|
24 |
+
### Deployment
|
25 |
+
|
26 |
+
Fully functional **Docker**-based deployment for maintainability and scalability.
|
27 |
+
|
28 |
+
```DOCKER FILE CODE```
|
29 |
|
30 |
+
#### FYIs
|
31 |
+
* Voice I/O is slow because of Streamlit voice processing and Deepgram API latency.
|
data_ingetion/__init__.py
ADDED
File without changes
|
data_ingetion/data_api.py
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter
|
2 |
+
from pydantic import BaseModel
|
3 |
+
from .market_data import price_change, earning_summary, portfolio_data
|
4 |
+
from .vectroDB import get_relevant_chunks
|
5 |
+
|
6 |
+
app = APIRouter()
|
7 |
+
|
8 |
+
|
9 |
+
class HistoricalData(BaseModel):
|
10 |
+
symbol: str
|
11 |
+
period: int
|
12 |
+
|
13 |
+
|
14 |
+
class EarningReq(BaseModel):
|
15 |
+
symbol: str
|
16 |
+
|
17 |
+
|
18 |
+
class PortfolioReq(BaseModel):
|
19 |
+
region: str
|
20 |
+
|
21 |
+
|
22 |
+
class KnowledgeReq(BaseModel):
|
23 |
+
query: str
|
24 |
+
|
25 |
+
|
26 |
+
@app.post("/get_historical_data")
|
27 |
+
def get_historical_data(req: HistoricalData):
|
28 |
+
symbol = req.symbol
|
29 |
+
period = req.period
|
30 |
+
return {"response": price_change(symbol, period)}
|
31 |
+
|
32 |
+
|
33 |
+
@app.post("/get_earning_metrics")
|
34 |
+
def get_eraning_metrics(req: EarningReq):
|
35 |
+
return {"response": earning_summary(req.symbol)}
|
36 |
+
|
37 |
+
|
38 |
+
@app.post("/get_portfolio_data")
|
39 |
+
def get_portfolio_data(req: PortfolioReq):
|
40 |
+
return {"response": portfolio_data(req.region)}
|
41 |
+
|
42 |
+
|
43 |
+
@app.post("/get_knowledge")
|
44 |
+
def get_knowledge(req: KnowledgeReq):
|
45 |
+
return {"response": get_relevant_chunks(req.query)}
|
data_ingetion/firms_report/Investment Strategy & Risk Management Guide.pdf
ADDED
Binary file (82.1 kB). View file
|
|
data_ingetion/firms_report/Quarterly Research Report - Global Equity Markets.pdf
ADDED
Binary file (71.5 kB). View file
|
|
data_ingetion/market_data.py
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import yfinance as yf
|
2 |
+
import datetime
|
3 |
+
import pandas as pd
|
4 |
+
import os
|
5 |
+
|
6 |
+
|
7 |
+
def portfolio_data(region: str = "IND"):
|
8 |
+
if region == "IND":
|
9 |
+
portfolio = "/data_ingetion/portfolios/IND.csv"
|
10 |
+
else:
|
11 |
+
portfolio = "/data_ingetion/portfolios/US.csv"
|
12 |
+
|
13 |
+
pc_file = os.getcwd() + "/data_ingetion/portfolios/portfolio_change.csv"
|
14 |
+
|
15 |
+
today = datetime.datetime.today().strftime("%Y-%m-%d")
|
16 |
+
|
17 |
+
portfolio_change = pd.read_csv(pc_file)
|
18 |
+
df = pd.read_csv(os.getcwd() + portfolio)
|
19 |
+
|
20 |
+
if today not in portfolio_change.columns:
|
21 |
+
pc = []
|
22 |
+
|
23 |
+
for ticker in df["Ticker Symbol"]:
|
24 |
+
pc.append(price_change(ticker, 7, True))
|
25 |
+
|
26 |
+
portfolio_change[today] = pc
|
27 |
+
|
28 |
+
portfolio_change.to_csv(pc_file, index=False)
|
29 |
+
|
30 |
+
df["Price Change%"] = portfolio_change[today]
|
31 |
+
df.drop("Date of Investment", axis=1, inplace=True)
|
32 |
+
|
33 |
+
return f"Portfolio and change in prices in part 7 days : \n {str(df)}"
|
34 |
+
|
35 |
+
|
36 |
+
def price_change(symbol, days: int, raw: bool = False):
|
37 |
+
stock = yf.Ticker(ticker=symbol)
|
38 |
+
|
39 |
+
today_price = stock.history(period="1d")
|
40 |
+
|
41 |
+
# Target date: 1 year and 7 days ago
|
42 |
+
target_date = datetime.datetime.today() - datetime.timedelta(days=days)
|
43 |
+
start_date = (target_date - datetime.timedelta(days=5)).strftime("%Y-%m-%d")
|
44 |
+
end_date = (target_date + datetime.timedelta(days=1)).strftime("%Y-%m-%d")
|
45 |
+
|
46 |
+
# Fetch range around the target date
|
47 |
+
history = stock.history(start=start_date, end=end_date)
|
48 |
+
|
49 |
+
# Get the latest available price before or on the target date
|
50 |
+
past_price = history[history.index <= target_date.strftime("%Y-%m-%d")].iloc[-1]
|
51 |
+
|
52 |
+
percentage_difference = (
|
53 |
+
(today_price["Close"] - past_price["Close"]) / past_price["Close"]
|
54 |
+
).values[0] * 100
|
55 |
+
|
56 |
+
response = (
|
57 |
+
f"Price change for {symbol} in past {days} days is {percentage_difference:.2f}%"
|
58 |
+
)
|
59 |
+
if raw:
|
60 |
+
return percentage_difference
|
61 |
+
return response
|
62 |
+
|
63 |
+
|
64 |
+
def earning_summary(symbol):
|
65 |
+
stock = yf.Ticker(ticker=symbol)
|
66 |
+
metrics = [
|
67 |
+
"EBITDA",
|
68 |
+
"Total Expenses",
|
69 |
+
"Basic EPS",
|
70 |
+
"Net Income",
|
71 |
+
"Gross Profit",
|
72 |
+
"Total Revenue",
|
73 |
+
]
|
74 |
+
currency = stock.fast_info.currency
|
75 |
+
income_metrics = stock.income_stmt
|
76 |
+
|
77 |
+
scaler = 1e7 if currency == "INR" else 1e6
|
78 |
+
units = "carore" if currency == "INR" else "millions"
|
79 |
+
|
80 |
+
selected_metric = income_metrics.loc[metrics] / scaler
|
81 |
+
|
82 |
+
response = f"Earning metrics for {symbol} are following in {currency} currency in {units}: \n {selected_metric}"
|
83 |
+
|
84 |
+
return response
|
data_ingetion/portfolios/IND.csv
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Stock Name,Ticker Symbol,Sector,Investment Percentage,Date of Investment
|
2 |
+
Reliance Industries Ltd,RELIANCE.NS,Energy,12.5,2023-03-15
|
3 |
+
Tata Consultancy Services,TCS.NS,IT,10.8,2023-01-20
|
4 |
+
HDFC Bank Ltd,HDFCBANK.NS,Bank,9.2,2022-11-10
|
5 |
+
Infosys Ltd,INFY.NS,IT,8.7,2023-02-28
|
6 |
+
ICICI Bank Ltd,ICICIBANK.NS,Bank,7.9,2023-04-12
|
7 |
+
Hindustan Unilever Ltd,HINDUNILVR.NS,FMCG,6.8,2022-12-05
|
8 |
+
State Bank of India,SBIN.NS,Bank,6.5,2023-05-18
|
9 |
+
Bharti Airtel Ltd,BHARTIARTL.NS,Telecom,5.9,2023-03-22
|
10 |
+
ITC Ltd,ITC.NS,FMCG,5.4,2022-10-30
|
11 |
+
Larsen & Toubro Ltd,LT.NS,Infrastructure,5.2,2023-01-08
|
12 |
+
Wipro Ltd,WIPRO.NS,IT,4.8,2023-02-14
|
13 |
+
Mahindra & Mahindra,M&M.NS,Auto,4.3,2023-04-05
|
14 |
+
Dr Reddy's Laboratories,DRREDDY.NS,Pharma,4.1,2022-09-25
|
15 |
+
Asian Paints Ltd,ASIANPAINT.NS,Paints,3.9,2023-06-02
|
16 |
+
Bajaj Finance Ltd,BAJFINANCE.NS,NBFC,4.0,2023-03-08
|
data_ingetion/portfolios/US.csv
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Stock Name,Ticker Symbol,Sector,Investment Percentage,Date of Investment
|
2 |
+
Apple Inc,AAPL,Technology,11.8,2023-02-15
|
3 |
+
Microsoft Corporation,MSFT,Technology,10.5,2023-01-12
|
4 |
+
Amazon.com Inc,AMZN,Consumer Discretionary,9.3,2022-12-08
|
5 |
+
Alphabet Inc Class A,GOOGL,Technology,8.7,2023-03-22
|
6 |
+
Tesla Inc,TSLA,Auto,7.9,2023-04-18
|
7 |
+
JPMorgan Chase & Co,JPM,Bank,7.2,2022-11-25
|
8 |
+
Johnson & Johnson,JNJ,Healthcare,6.8,2023-01-30
|
9 |
+
Berkshire Hathaway Inc Class B,BRK-B,Financial Services,6.5,2022-10-15
|
10 |
+
UnitedHealth Group Inc,UNH,Healthcare,5.9,2023-05-10
|
11 |
+
Procter & Gamble Co,PG,Consumer Staples,5.4,2023-02-28
|
12 |
+
Visa Inc Class A,V,Financial Services,5.1,2023-03-15
|
13 |
+
Coca-Cola Company,KO,Consumer Staples,4.8,2022-09-20
|
14 |
+
Home Depot Inc,HD,Consumer Discretionary,4.2,2023-04-05
|
15 |
+
Mastercard Inc Class A,MA,Financial Services,3.9,2023-06-12
|
16 |
+
Walt Disney Company,DIS,Entertainment,2.0,2023-01-25
|
data_ingetion/portfolios/portfolio_change.csv
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Stock Name,2025-05-27,2025-05-28
|
2 |
+
Reliance Industries Ltd,-0.175487859247769,-1.1404930950667433
|
3 |
+
Tata Consultancy Services,0.0114313334524853,-0.7969850038883989
|
4 |
+
HDFC Bank Ltd,0.595332616257644,-0.17127095414659563
|
5 |
+
Infosys Ltd,0.608740237640537,0.2103952074035703
|
6 |
+
ICICI Bank Ltd,0.570162252856838,0.6298953390888131
|
7 |
+
Hindustan Unilever Ltd,1.67014872857291,-0.08037152257869686
|
8 |
+
State Bank of India,1.02462770226125,1.2006114216934662
|
9 |
+
Bharti Airtel Ltd,1.92935065194481,1.843620059585048
|
10 |
+
ITC Ltd,-0.229937919989491,-1.1680789821939643
|
11 |
+
Larsen & Toubro Ltd,2.02365463825782,1.5965463591007245
|
12 |
+
Wipro Ltd,-0.584680899688634,-1.3908837552335025
|
13 |
+
Mahindra & Mahindra,-0.773623344928187,-3.0057884560591495
|
14 |
+
Dr Reddy's Laboratories,1.55203404327962,1.461584480095272
|
15 |
+
Asian Paints Ltd,1.3939710157998,-0.3978529476043621
|
16 |
+
Bajaj Finance Ltd,1.01332745897125,0.7610763794509378
|
data_ingetion/vectroDB.py
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
2 |
+
import chromadb
|
3 |
+
from openai import OpenAI
|
4 |
+
import pypdf
|
5 |
+
import uuid
|
6 |
+
import os
|
7 |
+
|
8 |
+
VECTOR_NAME = "database"
|
9 |
+
EMBEDDING_MODEL = "togethercomputer/m2-bert-80M-2k-retrieval"
|
10 |
+
|
11 |
+
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
12 |
+
CHROMA_PATH = os.path.join(BASE_DIR, "chroma_storage")
|
13 |
+
|
14 |
+
|
15 |
+
api_key = os.getenv("TOGETHER_API")
|
16 |
+
ai_client = OpenAI(api_key=api_key, base_url="https://api.together.xyz/v1")
|
17 |
+
|
18 |
+
|
19 |
+
def extract_pdf(pdf_path: str) -> str:
|
20 |
+
|
21 |
+
text = ""
|
22 |
+
with open(pdf_path, "rb") as file:
|
23 |
+
reader = pypdf.PdfReader(file)
|
24 |
+
for page_num in range(len(reader.pages)):
|
25 |
+
page = reader.pages[page_num]
|
26 |
+
text += page.extract_text()
|
27 |
+
text += "\n--PAGE BREAK--\n"
|
28 |
+
|
29 |
+
return text
|
30 |
+
|
31 |
+
|
32 |
+
def create_vectorDB():
|
33 |
+
docs_paths = os.listdir(os.getcwd() + "/data_ingetion/firms_report/")
|
34 |
+
|
35 |
+
complete_text = ""
|
36 |
+
|
37 |
+
for doc_path in docs_paths:
|
38 |
+
complete_text += extract_pdf(
|
39 |
+
os.getcwd() + "/data_ingetion/firms_report/" + doc_path
|
40 |
+
)
|
41 |
+
complete_text += "\n\n"
|
42 |
+
|
43 |
+
splitter = RecursiveCharacterTextSplitter(
|
44 |
+
chunk_size=512,
|
45 |
+
chunk_overlap=84,
|
46 |
+
length_function=len,
|
47 |
+
is_separator_regex=False,
|
48 |
+
)
|
49 |
+
|
50 |
+
processed_docs = splitter.split_text(complete_text)
|
51 |
+
db_client = chromadb.PersistentClient(path=CHROMA_PATH)
|
52 |
+
collection = db_client.create_collection(VECTOR_NAME)
|
53 |
+
|
54 |
+
response = ai_client.embeddings.create(input=processed_docs, model=EMBEDDING_MODEL)
|
55 |
+
embeddings = [item.embedding for item in response.data]
|
56 |
+
unique_ids = [str(uuid.uuid4()) for _ in range(len(embeddings))]
|
57 |
+
collection.add(documents=processed_docs, embeddings=embeddings, ids=unique_ids)
|
58 |
+
|
59 |
+
return collection.name
|
60 |
+
|
61 |
+
|
62 |
+
def get_relevant_chunks(query: str):
|
63 |
+
|
64 |
+
db_client = chromadb.PersistentClient(path=CHROMA_PATH)
|
65 |
+
found = VECTOR_NAME in [c.name for c in db_client.list_collections()]
|
66 |
+
|
67 |
+
if found:
|
68 |
+
collection = db_client.get_collection(VECTOR_NAME)
|
69 |
+
else:
|
70 |
+
collection = db_client.get_collection(create_vectorDB())
|
71 |
+
|
72 |
+
response = ai_client.embeddings.create(input=query, model=EMBEDDING_MODEL)
|
73 |
+
QE = response.data[0].embedding
|
74 |
+
|
75 |
+
relevant_chunks = collection.query(query_embeddings=QE, n_results=4)
|
76 |
+
|
77 |
+
processed = ""
|
78 |
+
|
79 |
+
for idx, doc in enumerate(relevant_chunks["documents"][0], start=1):
|
80 |
+
processed += f"Chunks number {idx}\n\n"
|
81 |
+
processed += doc + "\n\n"
|
82 |
+
|
83 |
+
return processed
|
diagram.jpeg
ADDED
![]() |
main_api.py
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI
|
2 |
+
from data_ingetion.data_api import app as data_app
|
3 |
+
from orchestrator.orchestrator_api import app as or_app
|
4 |
+
|
5 |
+
app = FastAPI()
|
6 |
+
|
7 |
+
app.include_router(data_app, prefix="/data")
|
8 |
+
app.include_router(or_app, prefix="/orchestrator")
|
orchestrator/__init__.py
ADDED
File without changes
|
orchestrator/orchestrator.py
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from openai import OpenAI
|
2 |
+
from .prompts import ORCHESTRATOR_SYS_PROMPT, FINAL_SYS_PROMPT
|
3 |
+
import requests
|
4 |
+
import json
|
5 |
+
import os
|
6 |
+
|
7 |
+
api_key = os.getenv("TOGETHER_API")
|
8 |
+
client = OpenAI(api_key=api_key, base_url="https://api.together.xyz/v1")
|
9 |
+
|
10 |
+
|
11 |
+
def get_orchertration_resposne(query, history):
|
12 |
+
|
13 |
+
history = history[::-1][:5]
|
14 |
+
response = client.chat.completions.create(
|
15 |
+
model="meta-llama/Llama-3.3-70B-Instruct-Turbo-Free",
|
16 |
+
messages=[
|
17 |
+
{
|
18 |
+
"role": "system",
|
19 |
+
"content": ORCHESTRATOR_SYS_PROMPT,
|
20 |
+
},
|
21 |
+
*history,
|
22 |
+
{"role": "user", "content": "Query: " + query},
|
23 |
+
],
|
24 |
+
)
|
25 |
+
r = response.choices[0].message.content
|
26 |
+
data = json.loads(str(r))
|
27 |
+
|
28 |
+
if data["tool"] == "get_change":
|
29 |
+
result = requests.post(
|
30 |
+
url="http://127.0.0.1:8000/data/get_historical_data",
|
31 |
+
json=data["parameters"],
|
32 |
+
).json()
|
33 |
+
|
34 |
+
elif data["tool"] == "get_earning":
|
35 |
+
result = requests.post(
|
36 |
+
url="http://127.0.0.1:8000/data/get_earning_metrics",
|
37 |
+
json=data["parameters"],
|
38 |
+
).json()
|
39 |
+
|
40 |
+
elif data["tool"] == "get_portfolio_status":
|
41 |
+
result = requests.post(
|
42 |
+
url="http://127.0.0.1:8000/data/get_portfolio_data", json=data["parameters"]
|
43 |
+
).json()
|
44 |
+
|
45 |
+
elif data["tool"] == "get_knowledge":
|
46 |
+
result = requests.post(
|
47 |
+
url="http://127.0.0.1:8000/data/get_knowledge", json=data["parameters"]
|
48 |
+
).json()
|
49 |
+
|
50 |
+
elif data["tool"] == None:
|
51 |
+
return data["parameters"]
|
52 |
+
|
53 |
+
else:
|
54 |
+
result = {
|
55 |
+
"response": "An error occured internally please communicate this to user firmly"
|
56 |
+
}
|
57 |
+
return result
|
58 |
+
|
59 |
+
|
60 |
+
def final_response(query, context, history):
|
61 |
+
|
62 |
+
history = history[::-1][:5]
|
63 |
+
response = client.chat.completions.create(
|
64 |
+
model="meta-llama/Llama-3.3-70B-Instruct-Turbo-Free",
|
65 |
+
messages=[
|
66 |
+
{
|
67 |
+
"role": "system",
|
68 |
+
"content": FINAL_SYS_PROMPT,
|
69 |
+
},
|
70 |
+
*history,
|
71 |
+
{"role": "user", "content": f"Query : {query} \n\n Context: {context}"},
|
72 |
+
],
|
73 |
+
stream=True,
|
74 |
+
)
|
75 |
+
|
76 |
+
for chunk in response:
|
77 |
+
yield chunk.choices[0].delta.content or ""
|
orchestrator/orchestrator_api.py
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter
|
2 |
+
from fastapi.responses import StreamingResponse
|
3 |
+
from pydantic import BaseModel
|
4 |
+
|
5 |
+
from .orchestrator import get_orchertration_resposne, final_response
|
6 |
+
|
7 |
+
app = APIRouter()
|
8 |
+
|
9 |
+
|
10 |
+
class ODReq(BaseModel):
|
11 |
+
query: str
|
12 |
+
history: list = []
|
13 |
+
|
14 |
+
|
15 |
+
class FinalReq(BaseModel):
|
16 |
+
query: str
|
17 |
+
history: list = []
|
18 |
+
context: str = ""
|
19 |
+
|
20 |
+
|
21 |
+
@app.post("/orchestrator_decision")
|
22 |
+
def get_OD(req: ODReq):
|
23 |
+
return get_orchertration_resposne(req.query, req.history)
|
24 |
+
|
25 |
+
|
26 |
+
@app.post("/final_response")
|
27 |
+
def get_final(req: FinalReq):
|
28 |
+
return StreamingResponse(
|
29 |
+
final_response(req.query, req.context, req.history), media_type="text/plain"
|
30 |
+
)
|
orchestrator/prompts.py
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
ORCHESTRATOR_SYS_PROMPT = """
|
2 |
+
You are in role of orchestrator and your task is to orchestrate a LLM based AI agent workflow working with tools to achive the goal of being a helpful Finance Assistant.
|
3 |
+
|
4 |
+
You are provided with multiple tools and your task is to call these tool based on user request and use the returned response from tools to answer the user query.
|
5 |
+
|
6 |
+
Tools available:
|
7 |
+
|
8 |
+
To get information on stocks:
|
9 |
+
|
10 |
+
get_change() -> use this tool to get infomration on change in stock price in given timeframe ie. Price[today]-price[today-period] percentage change.
|
11 |
+
parameters -> symbol: str the symbol of that ticker/stock YFinance style (use .NS for indian stocks), period: int number of days to get change for.
|
12 |
+
|
13 |
+
get_earning() -> use this tool to get infomration on year on year earning reports on the stock.
|
14 |
+
parameters -> symbol: str the symbol of the ticker/stock YFinance style (use .NS for indian stocks) for which we need earning metrics.
|
15 |
+
|
16 |
+
get_portfolio_status() -> use this tool to get information on current portfolio structure and price changes to make informed decisions.
|
17 |
+
parameters -> region:str["IND","US"] Region for which we are fetching portfolio result. Currently only IND (india) and US(USA) supported
|
18 |
+
|
19 |
+
get_knowledge -> use this tool to get prior knowledge as context about the firm, its a RAG based tool ie. you will give the augmented user query for better retrival results.
|
20 |
+
parameters -> query:str User query augmented/expanded by you for better retrival results
|
21 |
+
|
22 |
+
|
23 |
+
|
24 |
+
You have to respond in structured json format, mentioning tool name and prameter json.
|
25 |
+
If the query is general and can be answered without any tool put "tool"=null (JSON null) and parameters just have one key value pair of "response":"your_response" to the query.
|
26 |
+
|
27 |
+
For example:
|
28 |
+
|
29 |
+
Query : What is the change in apple stock in past 1 month.
|
30 |
+
|
31 |
+
response :
|
32 |
+
{
|
33 |
+
"tool":"get_change",
|
34 |
+
"parameters" : {
|
35 |
+
"symbol" : "AAPL",
|
36 |
+
"period" : 31
|
37 |
+
}
|
38 |
+
}
|
39 |
+
|
40 |
+
Query : Hi, how are you ?
|
41 |
+
|
42 |
+
response :
|
43 |
+
{
|
44 |
+
"tool":null,
|
45 |
+
"parameters" : {
|
46 |
+
"response" : "Hey! I am fine. How can i help you today?"
|
47 |
+
}
|
48 |
+
}
|
49 |
+
|
50 |
+
|
51 |
+
Dont add any comments around json, you should only respond in valid json format only.
|
52 |
+
"""
|
53 |
+
FINAL_SYS_PROMPT = """
|
54 |
+
You task to to generate final response of a long workflow/reseach. You will be provided with Query that is the original query and some context that is derived from different tools your task is to create a condensed output to effectively answer the user query. Try to be consise and to the point. Also add some disclamers if there is any uncertanity.
|
55 |
+
"""
|
requirements.txt
ADDED
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
aenum==3.1.16
|
2 |
+
aiofiles==24.1.0
|
3 |
+
aiohappyeyeballs==2.6.1
|
4 |
+
aiohttp==3.12.2
|
5 |
+
aiosignal==1.3.2
|
6 |
+
altair==5.5.0
|
7 |
+
annotated-types==0.7.0
|
8 |
+
anyio==4.9.0
|
9 |
+
asgiref==3.8.1
|
10 |
+
async-timeout==5.0.1
|
11 |
+
asyncio==3.4.3
|
12 |
+
attrs==25.3.0
|
13 |
+
backoff==2.2.1
|
14 |
+
bcrypt==4.3.0
|
15 |
+
beautifulsoup4==4.13.4
|
16 |
+
blinker==1.9.0
|
17 |
+
build==1.2.2.post1
|
18 |
+
cachetools==5.5.2
|
19 |
+
certifi==2025.4.26
|
20 |
+
cffi==1.17.1
|
21 |
+
charset-normalizer==3.4.2
|
22 |
+
chromadb==1.0.11
|
23 |
+
click==8.2.1
|
24 |
+
coloredlogs==15.0.1
|
25 |
+
curl_cffi==0.11.1
|
26 |
+
dataclasses-json==0.6.7
|
27 |
+
deepgram-sdk==4.0.0
|
28 |
+
Deprecated==1.2.18
|
29 |
+
deprecation==2.1.0
|
30 |
+
distro==1.9.0
|
31 |
+
dnspython==2.7.0
|
32 |
+
durationpy==0.10
|
33 |
+
email_validator==2.2.0
|
34 |
+
exceptiongroup==1.3.0
|
35 |
+
fastapi==0.115.9
|
36 |
+
fastapi-cli==0.0.7
|
37 |
+
filelock==3.18.0
|
38 |
+
flatbuffers==25.2.10
|
39 |
+
frozendict==2.4.6
|
40 |
+
frozenlist==1.6.0
|
41 |
+
fsspec==2025.5.1
|
42 |
+
gitdb==4.0.12
|
43 |
+
GitPython==3.1.44
|
44 |
+
google-auth==2.40.2
|
45 |
+
googleapis-common-protos==1.70.0
|
46 |
+
grpcio==1.71.0
|
47 |
+
h11==0.16.0
|
48 |
+
hf-xet==1.1.2
|
49 |
+
httpcore==1.0.9
|
50 |
+
httptools==0.6.4
|
51 |
+
httpx==0.28.1
|
52 |
+
huggingface-hub==0.32.2
|
53 |
+
humanfriendly==10.0
|
54 |
+
idna==3.10
|
55 |
+
importlib_metadata==8.6.1
|
56 |
+
importlib_resources==6.5.2
|
57 |
+
Jinja2==3.1.6
|
58 |
+
jiter==0.10.0
|
59 |
+
jsonpatch==1.33
|
60 |
+
jsonpointer==3.0.0
|
61 |
+
jsonschema==4.24.0
|
62 |
+
jsonschema-specifications==2025.4.1
|
63 |
+
kubernetes==32.0.1
|
64 |
+
langchain-core==0.3.62
|
65 |
+
langchain-text-splitters==0.3.8
|
66 |
+
langsmith==0.3.42
|
67 |
+
markdown-it-py==3.0.0
|
68 |
+
MarkupSafe==3.0.2
|
69 |
+
marshmallow==3.26.1
|
70 |
+
mdurl==0.1.2
|
71 |
+
mmh3==5.1.0
|
72 |
+
mpmath==1.3.0
|
73 |
+
multidict==6.4.4
|
74 |
+
multitasking==0.0.11
|
75 |
+
mypy_extensions==1.1.0
|
76 |
+
narwhals==1.41.0
|
77 |
+
numpy==2.2.6
|
78 |
+
oauthlib==3.2.2
|
79 |
+
onnxruntime==1.22.0
|
80 |
+
openai==1.82.0
|
81 |
+
opentelemetry-api==1.33.1
|
82 |
+
opentelemetry-exporter-otlp-proto-common==1.33.1
|
83 |
+
opentelemetry-exporter-otlp-proto-grpc==1.33.1
|
84 |
+
opentelemetry-instrumentation==0.54b1
|
85 |
+
opentelemetry-instrumentation-asgi==0.54b1
|
86 |
+
opentelemetry-instrumentation-fastapi==0.54b1
|
87 |
+
opentelemetry-proto==1.33.1
|
88 |
+
opentelemetry-sdk==1.33.1
|
89 |
+
opentelemetry-semantic-conventions==0.54b1
|
90 |
+
opentelemetry-util-http==0.54b1
|
91 |
+
orjson==3.10.18
|
92 |
+
overrides==7.7.0
|
93 |
+
packaging==24.2
|
94 |
+
pandas==2.2.3
|
95 |
+
peewee==3.18.1
|
96 |
+
pillow==11.2.1
|
97 |
+
platformdirs==4.3.8
|
98 |
+
posthog==4.2.0
|
99 |
+
propcache==0.3.1
|
100 |
+
protobuf==5.29.4
|
101 |
+
pyarrow==20.0.0
|
102 |
+
pyasn1==0.6.1
|
103 |
+
pyasn1_modules==0.4.2
|
104 |
+
pycparser==2.22
|
105 |
+
pydantic==2.11.5
|
106 |
+
pydantic_core==2.33.2
|
107 |
+
pydeck==0.9.1
|
108 |
+
Pygments==2.19.1
|
109 |
+
pypdf==5.5.0
|
110 |
+
PyPika==0.48.9
|
111 |
+
pyproject_hooks==1.2.0
|
112 |
+
python-dateutil==2.9.0.post0
|
113 |
+
python-dotenv==1.1.0
|
114 |
+
python-multipart==0.0.20
|
115 |
+
pytz==2025.2
|
116 |
+
PyYAML==6.0.2
|
117 |
+
referencing==0.36.2
|
118 |
+
requests==2.32.3
|
119 |
+
requests-oauthlib==2.0.0
|
120 |
+
requests-toolbelt==1.0.0
|
121 |
+
rich==14.0.0
|
122 |
+
rich-toolkit==0.14.6
|
123 |
+
rpds-py==0.25.1
|
124 |
+
rsa==4.9.1
|
125 |
+
shellingham==1.5.4
|
126 |
+
six==1.17.0
|
127 |
+
smmap==5.0.2
|
128 |
+
sniffio==1.3.1
|
129 |
+
soupsieve==2.7
|
130 |
+
starlette==0.45.3
|
131 |
+
streamlit==1.45.1
|
132 |
+
sympy==1.14.0
|
133 |
+
tenacity==9.1.2
|
134 |
+
tokenizers==0.21.1
|
135 |
+
toml==0.10.2
|
136 |
+
tomli==2.2.1
|
137 |
+
tornado==6.5.1
|
138 |
+
tqdm==4.67.1
|
139 |
+
typer==0.16.0
|
140 |
+
typing-inspect==0.9.0
|
141 |
+
typing-inspection==0.4.1
|
142 |
+
typing_extensions==4.13.2
|
143 |
+
tzdata==2025.2
|
144 |
+
urllib3==2.4.0
|
145 |
+
uvicorn==0.34.2
|
146 |
+
uvloop==0.21.0
|
147 |
+
verboselogs==1.7
|
148 |
+
watchdog==6.0.0
|
149 |
+
watchfiles==1.0.5
|
150 |
+
websocket-client==1.8.0
|
151 |
+
websockets==15.0.1
|
152 |
+
wrapt==1.17.2
|
153 |
+
yarl==1.20.0
|
154 |
+
yfinance==0.2.61
|
155 |
+
zipp==3.22.0
|
156 |
+
zstandard==0.23.0
|
streamlit_app/app.py
ADDED
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import requests
|
3 |
+
from deepgram import (
|
4 |
+
DeepgramClient,
|
5 |
+
DeepgramClientOptions,
|
6 |
+
PrerecordedOptions,
|
7 |
+
FileSource,
|
8 |
+
)
|
9 |
+
from io import BytesIO
|
10 |
+
import os
|
11 |
+
|
12 |
+
# Page configuration
|
13 |
+
st.set_page_config(page_title="Market Brief Chat", page_icon="π¬", layout="wide")
|
14 |
+
DG_API = os.getenv("DG_API")
|
15 |
+
|
16 |
+
# Initialize session state for chat history
|
17 |
+
if "messages" not in st.session_state:
|
18 |
+
st.session_state.messages = [
|
19 |
+
{"role": "assistant", "content": "How can I help you today?"}
|
20 |
+
]
|
21 |
+
if "um" not in st.session_state:
|
22 |
+
st.session_state.um = None
|
23 |
+
|
24 |
+
# Initialize other session state variables
|
25 |
+
if "is_recording" not in st.session_state:
|
26 |
+
st.session_state.is_recording = False
|
27 |
+
|
28 |
+
|
29 |
+
def STT(buffer):
|
30 |
+
|
31 |
+
config: DeepgramClientOptions = DeepgramClientOptions(api_key=DG_API)
|
32 |
+
deepgram: DeepgramClient = DeepgramClient("", config)
|
33 |
+
payload: FileSource = {
|
34 |
+
"buffer": buffer,
|
35 |
+
}
|
36 |
+
# STEP 2: Configure Deepgram options for audio analysis
|
37 |
+
options = PrerecordedOptions(
|
38 |
+
model="nova-3",
|
39 |
+
smart_format=True,
|
40 |
+
)
|
41 |
+
# STEP 3: Call the transcribe_file method with the text payload and options
|
42 |
+
response = deepgram.listen.rest.v("1").transcribe_file(payload, options)
|
43 |
+
|
44 |
+
data = response.to_json()
|
45 |
+
transcript = data["results"]["channels"][0]["alternatives"][0]["transcript"]
|
46 |
+
|
47 |
+
return transcript
|
48 |
+
|
49 |
+
|
50 |
+
def TTS(text):
|
51 |
+
DEEPGRAM_URL = "https://api.deepgram.com/v1/speak?model=aura-2-thalia-en"
|
52 |
+
DEEPGRAM_API_KEY = DG_API
|
53 |
+
|
54 |
+
payload = {"text": text}
|
55 |
+
|
56 |
+
headers = {
|
57 |
+
"Authorization": f"Token {DEEPGRAM_API_KEY}",
|
58 |
+
"Content-Type": "application/json",
|
59 |
+
}
|
60 |
+
|
61 |
+
# Create a BytesIO buffer to store audio
|
62 |
+
audio_buffer = BytesIO()
|
63 |
+
|
64 |
+
response = requests.post(DEEPGRAM_URL, headers=headers, json=payload)
|
65 |
+
|
66 |
+
audio_buffer.write(response.content)
|
67 |
+
|
68 |
+
# Move cursor to the beginning of the buffer
|
69 |
+
audio_buffer.seek(0)
|
70 |
+
return audio_buffer
|
71 |
+
|
72 |
+
|
73 |
+
# App title
|
74 |
+
st.markdown("<h1>π¬ Market Brief Chat</h1>", unsafe_allow_html=True)
|
75 |
+
st.markdown("---")
|
76 |
+
|
77 |
+
|
78 |
+
# Display chat history
|
79 |
+
chat_col, audio_col = st.columns([0.85, 0.15])
|
80 |
+
with chat_col:
|
81 |
+
c = st.container(height=400, border=True)
|
82 |
+
with c:
|
83 |
+
for message in st.session_state.messages:
|
84 |
+
with st.chat_message(message["role"]):
|
85 |
+
st.write(message["content"])
|
86 |
+
|
87 |
+
with audio_col:
|
88 |
+
# Voice input buttonif
|
89 |
+
data = st.audio_input(label="π€ Record")
|
90 |
+
if data:
|
91 |
+
st.session_state.um = STT(data)
|
92 |
+
if st.button("π listen"):
|
93 |
+
text = st.session_state.messages[-1]["content"]
|
94 |
+
buffer = TTS(text)
|
95 |
+
st.audio(data=buffer)
|
96 |
+
st.success("Playing")
|
97 |
+
|
98 |
+
# Voice input button (beside text input conceptually)
|
99 |
+
# Handle text input
|
100 |
+
user_input = st.chat_input("Ask me about market trends...")
|
101 |
+
st.session_state.um = user_input
|
102 |
+
if st.session_state.um:
|
103 |
+
with c:
|
104 |
+
with st.chat_message("user"):
|
105 |
+
st.markdown(user_input)
|
106 |
+
st.session_state.messages.append(
|
107 |
+
{"role": "user", "content": st.session_state.um}
|
108 |
+
)
|
109 |
+
|
110 |
+
data = {"query": user_input, "history": []}
|
111 |
+
or_response = requests.post(
|
112 |
+
url="http://127.0.0.1:8000/orchestrator/orchestrator_decision", json=data
|
113 |
+
).json()
|
114 |
+
|
115 |
+
print(or_response)
|
116 |
+
|
117 |
+
agent_response = ""
|
118 |
+
data = {"query": user_input, "context": or_response["response"]}
|
119 |
+
full_response = requests.post(
|
120 |
+
url="http://127.0.0.1:8000/orchestrator/final_response",
|
121 |
+
json=data,
|
122 |
+
stream=True,
|
123 |
+
)
|
124 |
+
with st.chat_message("assistant"):
|
125 |
+
placeholder = st.empty()
|
126 |
+
for chunk in full_response.iter_content(
|
127 |
+
decode_unicode=True, chunk_size=None
|
128 |
+
):
|
129 |
+
agent_response += chunk
|
130 |
+
placeholder.markdown(agent_response + "β")
|
131 |
+
|
132 |
+
st.session_state.messages.append(
|
133 |
+
{"role": "assistant", "content": agent_response}
|
134 |
+
)
|
135 |
+
st.session_state.um = None
|
136 |
+
st.rerun()
|