| import sys |
| import os |
| sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
| from langchain.tools import tool |
| import pandas as pd |
| import json |
| import re |
| from copy import deepcopy |
| from langchain_pinecone import PineconeVectorStore |
| from dotenv import load_dotenv |
| load_dotenv() |
| from langchain_openai import OpenAIEmbeddings |
| from pydantic import BaseModel |
| from typing import Any, Optional |
|
|
| api_key = os.getenv('PINCEONE_API_KEY') |
|
|
| class JsonToTableInput(BaseModel): |
| json_data: Any |
|
|
| class RagToolInput(BaseModel): |
| query: str |
|
|
| |
| def json_to_table(input_data: JsonToTableInput): |
| """Convert JSON data to a markdown table. Use when user asks to visualise or tabulate structured data.""" |
| json_data = input_data.json_data |
| |
| if isinstance(json_data, str): |
| try: |
| json_data = json.loads(json_data) |
| except: |
| |
| pass |
| |
| |
| if isinstance(json_data, dict) and 'allocations' in json_data: |
| json_data = json_data['allocations'] |
| |
| |
| if not json_data: |
| json_data = [{"Note": "No allocation data available"}] |
| |
| df = pd.json_normalize(json_data) |
| markdown_table = df.to_markdown(index=False) |
| print(f"[DEBUG] json_to_table output:\n{markdown_table}") |
| |
| return markdown_table |
|
|
| def rag_tool(input_data: RagToolInput): |
| """Lets the agent use RAG system as a tool""" |
| query = input_data.query |
| |
| embedding_model = OpenAIEmbeddings( |
| model="text-embedding-3-small", |
| dimensions=384 |
| ) |
| kb = PineconeVectorStore( |
| pinecone_api_key=os.environ.get('PINCEONE_API_KEY'), |
| index_name='rag-rubic', |
| namespace='vectors_lightmodel' |
| ) |
| retriever = kb.as_retriever(search_kwargs={"k": 10}) |
| context = retriever.invoke(query) |
| return "\n".join([doc.page_content for doc in context]) |
|
|
| @tool |
| def goal_feasibility(goal_amount: float, timeline: float, current_savings: float, income : float) -> dict: |
| """Evaluate if a financial goal is feasible based on user income, timeline, and savings. Use when user asks about goal feasibility.""" |
| |
| if timeline <= 0: |
| return { |
| "feasible": False, |
| "status": "Invalid", |
| "monthly_required": 0, |
| "reason": "Timeline must be greater than 0 months." |
| } |
|
|
| |
| remaining_amount = goal_amount - current_savings |
| if remaining_amount <= 0: |
| return { |
| "feasible": True, |
| "status": "Already Achieved", |
| "monthly_required": 0, |
| "reason": "You have already met or exceeded your savings goal." |
| } |
|
|
| monthly_required = remaining_amount / timeline |
| income_ratio = monthly_required / income |
|
|
| |
| if income_ratio <= 0.3: |
| status = "Feasible" |
| feasible = True |
| reason = "The required savings per month is manageable for an average income." |
| elif income_ratio <= 0.7: |
| status = "Difficult" |
| feasible = False |
| reason = "The required monthly saving is high but may be possible with strict budgeting." |
| else: |
| status = "Infeasible" |
| feasible = False |
| reason = "The required monthly saving is unrealistic for an average income." |
|
|
| return { |
| "feasible": feasible, |
| "status": status, |
| "monthly_required": round(monthly_required, 2), |
| "reason": reason |
| } |
|
|
|
|
| @tool |
| def save_data(new_user_data:dict, new_alloc_data:dict): |
| "Saves the updated user_data and allocations data in a json file." |
| path = os.getenv("DATA_PATH", ".") |
| save_path = os.path.join(path, "updated_json") |
| os.makedirs(save_path, exist_ok=True) |
| with open(os.path.join(save_path, "updated_user_data.json"), "w") as f: |
| json.dump(new_user_data, f, indent=2) |
|
|
| with open(os.path.join(save_path, "updated_allocations.json"), "w") as f: |
| json.dump(new_alloc_data, f, indent=2) |
|
|
|
|