├── experiments ├── pinata.ipynb ├── agents │ ├── __init__.py │ ├── triage_agent.py │ ├── accounts_agent.py │ ├── payments_agent.py │ └── applications_agent.py ├── utils │ ├── __init__.py │ └── helpers.py ├── db.json ├── applications │ └── texput.log ├── db.py ├── agent_swarm.py ├── applications.ipynb └── bot.ipynb ├── server ├── agents │ ├── __init__.py │ ├── triage_agent.py │ ├── accounts_agent.py │ ├── payments_agent.py │ └── applications_agent.py ├── utils │ ├── __init__.py │ └── helpers.py ├── applications │ ├── loan_application_ACC123_20241117091458.pdf │ └── loan_application_ACC123_20241117091509.pdf ├── requirements.txt ├── db.json ├── socket_manager.py ├── form.tex ├── custom_types.py ├── pinata.py ├── agents.py ├── agent_swarm.py ├── llm.py ├── main.py └── db.py ├── client ├── .eslintrc.json ├── public │ ├── favicon.ico │ ├── TALKTUAHBANk.png │ ├── vercel.svg │ ├── file.svg │ ├── window.svg │ ├── globe.svg │ └── next.svg ├── src │ ├── img │ │ └── logo_dark.png │ └── app │ │ ├── fonts │ │ ├── GeistVF.woff │ │ └── GeistMonoVF.woff │ │ ├── globals.css │ │ ├── layout.tsx │ │ └── page.tsx ├── next.config.ts ├── postcss.config.mjs ├── tailwind.config.ts ├── tsconfig.json ├── package.json └── README.md ├── .gitignore └── README.md /experiments/pinata.ipynb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/agents/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experiments/agents/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experiments/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "next/typescript"] 3 | } 4 | -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelisajuan/TalkTuahBank/HEAD/client/public/favicon.ico -------------------------------------------------------------------------------- /client/src/img/logo_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelisajuan/TalkTuahBank/HEAD/client/src/img/logo_dark.png -------------------------------------------------------------------------------- /client/public/TALKTUAHBANk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelisajuan/TalkTuahBank/HEAD/client/public/TALKTUAHBANk.png -------------------------------------------------------------------------------- /client/src/app/fonts/GeistVF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelisajuan/TalkTuahBank/HEAD/client/src/app/fonts/GeistVF.woff -------------------------------------------------------------------------------- /client/src/app/fonts/GeistMonoVF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelisajuan/TalkTuahBank/HEAD/client/src/app/fonts/GeistMonoVF.woff -------------------------------------------------------------------------------- /client/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | }; 6 | 7 | export default nextConfig; 8 | -------------------------------------------------------------------------------- /server/applications/loan_application_ACC123_20241117091458.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelisajuan/TalkTuahBank/HEAD/server/applications/loan_application_ACC123_20241117091458.pdf -------------------------------------------------------------------------------- /server/applications/loan_application_ACC123_20241117091509.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelisajuan/TalkTuahBank/HEAD/server/applications/loan_application_ACC123_20241117091509.pdf -------------------------------------------------------------------------------- /client/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /client/public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --background: #ffffff; 7 | --foreground: #171717; 8 | } 9 | 10 | @media (prefers-color-scheme: dark) { 11 | :root { 12 | --background: #0a0a0a; 13 | --foreground: #ededed; 14 | } 15 | } 16 | 17 | body { 18 | color: var(--foreground); 19 | background: var(--background); 20 | font-family: Arial, Helvetica, sans-serif; 21 | } 22 | -------------------------------------------------------------------------------- /client/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | export default { 4 | content: [ 5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | theme: { 10 | extend: { 11 | colors: { 12 | background: "var(--background)", 13 | foreground: "var(--foreground)", 14 | }, 15 | }, 16 | }, 17 | plugins: [], 18 | } satisfies Config; 19 | -------------------------------------------------------------------------------- /experiments/db.json: -------------------------------------------------------------------------------- 1 | { 2 | "users": { 3 | "user1": { 4 | "name": "Bill Zhang", 5 | "accounts": ["ACC123"] 6 | } 7 | }, 8 | "accounts": { 9 | "ACC123": { 10 | "balance": 2500.0, 11 | "statements": { 12 | "January 12th": "Transaction 1: -$500.00\nTransaction 2: +$1500.00", 13 | "February 13th": "Transaction 1: -$300.00\nTransaction 2: +$800.00" 14 | } 15 | } 16 | }, 17 | "payments": { 18 | "PAY002": { 19 | "from_account": "ACC123", 20 | "to_account": "ACC456", 21 | "amount": 150.00, 22 | "date": "2024-05-15", 23 | "status": "Completed" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /server/requirements.txt: -------------------------------------------------------------------------------- 1 | annotated-types==0.7.0 2 | anthropic==0.39.0 3 | anyio==4.6.2.post1 4 | certifi==2024.8.30 5 | charset-normalizer==3.4.0 6 | dataclasses-json==0.6.7 7 | distro==1.9.0 8 | fastapi==0.115.5 9 | h11==0.14.0 10 | httpcore==1.0.7 11 | httpx==0.27.2 12 | idna==3.10 13 | jiter==0.7.1 14 | jsonpath-python==1.0.6 15 | marshmallow==3.23.1 16 | mypy-extensions==1.0.0 17 | packaging==24.2 18 | pydantic==2.9.2 19 | pydantic_core==2.23.4 20 | python-dateutil==2.9.0.post0 21 | python-dotenv==1.0.1 22 | requests==2.32.3 23 | six==1.16.0 24 | sniffio==1.3.1 25 | starlette==0.41.2 26 | typing-inspect==0.9.0 27 | typing_extensions==4.12.2 28 | urllib3==2.2.3 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .next 6 | /.pnp 7 | .pnp.* 8 | .yarn/* 9 | !.yarn/patches 10 | !.yarn/plugins 11 | !.yarn/releases 12 | !.yarn/versions 13 | 14 | # testing 15 | /coverage 16 | 17 | # next.js 18 | /.next/ 19 | /out/ 20 | 21 | # production 22 | /build 23 | 24 | # misc 25 | .DS_Store 26 | *.pem 27 | 28 | # debug 29 | npm-debug.log* 30 | yarn-debug.log* 31 | yarn-error.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | 43 | # python 44 | venv/ 45 | .env 46 | __pycache__ 47 | 48 | *.pdf -------------------------------------------------------------------------------- /client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "framer-motion": "^11.11.17", 13 | "lucide-react": "^0.460.0", 14 | "next": "15.0.3", 15 | "react": "19.0.0-rc-66855b96-20241106", 16 | "react-dom": "19.0.0-rc-66855b96-20241106" 17 | }, 18 | "devDependencies": { 19 | "@types/node": "^20", 20 | "@types/react": "^18", 21 | "@types/react-dom": "^18", 22 | "eslint": "^8", 23 | "eslint-config-next": "15.0.3", 24 | "postcss": "^8", 25 | "tailwindcss": "^3.4.1", 26 | "typescript": "^5" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /server/db.json: -------------------------------------------------------------------------------- 1 | { 2 | "users": { 3 | "user1": { 4 | "name": "Bill Zhang", 5 | "accounts": ["ACC123"] 6 | } 7 | }, 8 | "accounts": { 9 | "ACC123": { 10 | "balance": 2500.0, 11 | "statements": { 12 | "November": [ 13 | { 14 | "date": "2024-11-13", 15 | "description": "Flight to HackUTD", 16 | "amount": -500.00 17 | }, 18 | { 19 | "date": "2024-11-14", 20 | "description": "Dinner with friends", 21 | "amount": -100.00 22 | } 23 | ] 24 | 25 | } 26 | } 27 | }, 28 | "payments": { 29 | "PAY002": { 30 | "from_account": "ACC123", 31 | "to_account": "BillyBob", 32 | "amount": 500, 33 | "date": "2024-11-13", 34 | "status": "Completed" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /client/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import localFont from "next/font/local"; 3 | import "./globals.css"; 4 | 5 | const geistSans = localFont({ 6 | src: "./fonts/GeistVF.woff", 7 | variable: "--font-geist-sans", 8 | weight: "100 900", 9 | }); 10 | const geistMono = localFont({ 11 | src: "./fonts/GeistMonoVF.woff", 12 | variable: "--font-geist-mono", 13 | weight: "100 900", 14 | }); 15 | 16 | export const metadata: Metadata = { 17 | title: "TalkTuahBank", 18 | description: "HackUTD 2024", 19 | }; 20 | 21 | export default function RootLayout({ 22 | children, 23 | }: Readonly<{ 24 | children: React.ReactNode; 25 | }>) { 26 | return ( 27 | 28 |
31 | {children} 32 | 33 | 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /experiments/applications/texput.log: -------------------------------------------------------------------------------- 1 | This is pdfTeX, Version 3.141592653-2.6-1.40.26 (TeX Live 2024) (preloaded format=pdflatex 2024.11.17) 17 NOV 2024 09:10 2 | entering extended mode 3 | restricted \write18 enabled. 4 | %&-line parsing enabled. 5 | **applications/loan_application_ACC123_20241117091054.tex 6 | 7 | ! Emergency stop. 8 | <*> .../loan_application_ACC123_20241117091054.tex 9 | 10 | *** (job aborted, file error in nonstop mode) 11 | 12 | 13 | Here is how much of TeX's memory you used: 14 | 4 strings out of 475567 15 | 147 string characters out of 5777888 16 | 1925187 words of memory out of 5000000 17 | 22255 multiletter control sequences out of 15000+600000 18 | 558069 words of font info for 36 fonts, out of 8000000 for 9000 19 | 319 hyphenation exceptions out of 8191 20 | 0i,0n,0p,1b,6s stack positions out of 10000i,1000n,20000p,200000b,200000s 21 | ! ==> Fatal error occurred, no output PDF file produced! 22 | -------------------------------------------------------------------------------- /client/public/globe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experiments/agents/triage_agent.py: -------------------------------------------------------------------------------- 1 | # agents/triage_agent.py 2 | 3 | from swarm import Agent 4 | from typing import Any, Dict, List, Callable 5 | import logging 6 | 7 | # Define the triage instructions 8 | triage_instructions = """ 9 | You are the Triage Agent responsible for categorizing user requests and delegating them to the appropriate agent. 10 | 11 | Your tasks: 12 | 1. Analyze the user's message to determine its intent. 13 | 2. If the request is about account-related inquiries (e.g., checking balance, retrieving statements), transfer it to the Accounts Agent. 14 | 3. If the request is about payment-related actions (e.g., transferring funds, scheduling payments), transfer it to the Payments Agent. 15 | 4. If you need more information to accurately triage the request, ask a direct question without providing explanations. 16 | 5. Do not share your internal decision-making process with the user. 17 | 6. Maintain a professional and friendly tone at all times. 18 | """ 19 | 20 | class TriageAgent(Agent): 21 | def __init__(self, functions: List[Callable]): 22 | super().__init__( 23 | name="Triage Agent", 24 | instructions=triage_instructions, 25 | functions=functions 26 | ) 27 | -------------------------------------------------------------------------------- /server/socket_manager.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | from fastapi import WebSocket 3 | 4 | 5 | # manages the connection across mukt clients and sate of ws 6 | class ConnectionManager: 7 | # initializes ws and adds to active connections inside of a dictionary 8 | def __init__(self): 9 | self.active_connections: Dict[str, WebSocket] = {} 10 | 11 | # establish connection btwn a client and ws. waits for ws to start and adds accepted client to active connections 12 | async def connect(self, websocket: WebSocket, client_id: str): 13 | await websocket.accept() 14 | self.active_connections[client_id] = websocket 15 | 16 | # disconnects client from ws 17 | async def disconnect(self, client_id: str): 18 | try: 19 | del self.active_connections[client_id] 20 | except KeyError: 21 | pass 22 | 23 | async def send_personal_message(self, data: dict, websocket: WebSocket): 24 | await websocket.send_json(data) 25 | 26 | # shows data to all clients with active connections to the ws 27 | async def broadcast(self, data: dict): 28 | for connection in self.active_connections.values(): 29 | await connection.send_json(data) 30 | 31 | 32 | manager = ConnectionManager() -------------------------------------------------------------------------------- /client/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/agents/triage_agent.py: -------------------------------------------------------------------------------- 1 | # agents/triage_agent.py 2 | 3 | from swarm import Agent 4 | from typing import Any, Dict, List, Callable 5 | import logging 6 | 7 | # Define the triage instructions 8 | triage_instructions = """ 9 | You are the Triage Agent responsible for categorizing user requests and delegating them to the appropriate agent. 10 | 11 | Your tasks: 12 | 1. Analyze the user's message to determine its intent. 13 | 2. If the request is about account-related inquiries (e.g., checking balance, retrieving statements), transfer it to the Accounts Agent. 14 | 3. If the request is about payment-related actions (e.g., transferring funds, scheduling payments), transfer it to the Payments Agent. 15 | 4. If the request is about loan-related actions (e.g., applying for a loan) or credit-related actions (e.g., applying for a credit card), transfer it to the Applications Agent. 16 | 4. If you need more information to accurately triage the request, ask a direct question without providing explanations. 17 | 5. Do not share your internal decision-making process with the user. 18 | 6. Maintain a professional and friendly tone at all times. 19 | """ 20 | 21 | class TriageAgent(Agent): 22 | def __init__(self, functions: List[Callable]): 23 | super().__init__( 24 | name="Triage Agent", 25 | instructions=triage_instructions, 26 | functions=functions 27 | ) 28 | -------------------------------------------------------------------------------- /server/form.tex: -------------------------------------------------------------------------------- 1 | \documentclass[12pt]{article} 2 | \usepackage[margin=1in]{geometry} 3 | \usepackage{array} 4 | \usepackage{graphicx} 5 | \usepackage{hyperref} 6 | 7 | \begin{document} 8 | 9 | \begin{center} 10 | {\LARGE \textbf{Credit Card Application Form}} 11 | \end{center} 12 | 13 | \vspace{1em} 14 | 15 | \section*{Personal Information} 16 | 17 | \begin{tabular}{>{\bfseries}l p{4in}} 18 | First Name: & \hrulefill \\ 19 | Last Name: & \hrulefill \\ 20 | Date of Birth (MM/DD/YYYY): & \hrulefill \\ 21 | Social Security Number: & \hrulefill \\ 22 | Email Address: & \hrulefill \\ 23 | Phone Number: & \hrulefill \\ 24 | Current Address: & \hrulefill \\ 25 | City: & \hrulefill \\ 26 | State: & \hrulefill \\ 27 | ZIP Code: & \hrulefill \\ 28 | \end{tabular} 29 | 30 | \vspace{1em} 31 | 32 | \section*{Employment Information} 33 | 34 | \begin{tabular}{>{\bfseries}l p{4in}} 35 | Employer Name: & \hrulefill \\ 36 | Job Title: & \hrulefill \\ 37 | Annual Income: & \hrulefill \\ 38 | Employment Status: & \hrulefill \\ 39 | Work Phone Number: & \hrulefill \\ 40 | Length of Employment: & \hrulefill \\ 41 | \end{tabular} 42 | 43 | \vspace{1em} 44 | 45 | \section*{Financial Information} 46 | 47 | \begin{tabular}{>{\bfseries}l p{4in}} 48 | Bank Name: & \hrulefill \\ 49 | Account Type (Checking/Savings): & \hrulefill \\ 50 | Account Number: & \hrulefill \\ 51 | Monthly Housing Payment: & \hrulefill \\ 52 | Do you have existing credit cards? & \hrulefill \\ 53 | If yes, list them: & \hrulefill \\ 54 | \end{tabular} 55 | 56 | \end{document} 57 | -------------------------------------------------------------------------------- /server/utils/helpers.py: -------------------------------------------------------------------------------- 1 | # utils/helpers.py 2 | 3 | from typing import List 4 | from db import get_db, set_db 5 | import logging 6 | from datetime import datetime 7 | 8 | def validate_account_id(account_id: str) -> bool: 9 | current_db = get_db() 10 | if account_id in current_db["accounts"]: 11 | logging.info(f"Account ID {account_id} is valid.") 12 | return True 13 | logging.warning(f"Account ID {account_id} is invalid.") 14 | return False 15 | 16 | def validate_payment_id(payment_id: str) -> bool: 17 | current_db = get_db() 18 | if payment_id in current_db["payments"]: 19 | logging.info(f"Payment ID {payment_id} is valid.") 20 | return True 21 | logging.warning(f"Payment ID {payment_id} is invalid.") 22 | return False 23 | 24 | def generate_payment_id() -> str: 25 | current_db = get_db() 26 | payment_number = len(current_db["payments"]) + 1 27 | payment_id = f"PAY{payment_number:03d}" 28 | logging.info(f"Generated new payment ID: {payment_id}") 29 | return payment_id 30 | 31 | def validate_amount(amount: float) -> bool: 32 | if amount <= 0: 33 | logging.warning(f"Invalid amount: {amount}. Amount must be positive.") 34 | return False 35 | logging.info(f"Amount {amount} is valid.") 36 | return True 37 | 38 | def get_user_accounts(user_id: str) -> List[str]: 39 | current_db = get_db() 40 | accounts = current_db["users"].get(user_id, {}).get("accounts", []) 41 | logging.info(f"User {user_id} has accounts: {accounts}") 42 | return accounts 43 | -------------------------------------------------------------------------------- /experiments/utils/helpers.py: -------------------------------------------------------------------------------- 1 | # utils/helpers.py 2 | 3 | from typing import List 4 | from db import get_db, set_db 5 | import logging 6 | from datetime import datetime 7 | 8 | def validate_account_id(account_id: str) -> bool: 9 | current_db = get_db() 10 | if account_id in current_db["accounts"]: 11 | logging.info(f"Account ID {account_id} is valid.") 12 | return True 13 | logging.warning(f"Account ID {account_id} is invalid.") 14 | return False 15 | 16 | def validate_payment_id(payment_id: str) -> bool: 17 | current_db = get_db() 18 | if payment_id in current_db["payments"]: 19 | logging.info(f"Payment ID {payment_id} is valid.") 20 | return True 21 | logging.warning(f"Payment ID {payment_id} is invalid.") 22 | return False 23 | 24 | def generate_payment_id() -> str: 25 | current_db = get_db() 26 | payment_number = len(current_db["payments"]) + 1 27 | payment_id = f"PAY{payment_number:03d}" 28 | logging.info(f"Generated new payment ID: {payment_id}") 29 | return payment_id 30 | 31 | def validate_amount(amount: float) -> bool: 32 | if amount <= 0: 33 | logging.warning(f"Invalid amount: {amount}. Amount must be positive.") 34 | return False 35 | logging.info(f"Amount {amount} is valid.") 36 | return True 37 | 38 | def get_user_accounts(user_id: str) -> List[str]: 39 | current_db = get_db() 40 | accounts = current_db["users"].get(user_id, {}).get("accounts", []) 41 | logging.info(f"User {user_id} has accounts: {accounts}") 42 | return accounts 43 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. 37 | -------------------------------------------------------------------------------- /server/custom_types.py: -------------------------------------------------------------------------------- 1 | from typing import Any, List, Optional, Literal, Union 2 | from pydantic import BaseModel 3 | from typing import Literal, Dict, Optional 4 | 5 | 6 | # Retell -> Your Server Events 7 | class Utterance(BaseModel): 8 | role: Literal["agent", "user", "system"] 9 | content: str 10 | 11 | 12 | class PingPongRequest(BaseModel): 13 | interaction_type: Literal["ping_pong"] 14 | timestamp: int 15 | 16 | 17 | class CallDetailsRequest(BaseModel): 18 | interaction_type: Literal["call_details"] 19 | call: dict 20 | 21 | 22 | class UpdateOnlyRequest(BaseModel): 23 | interaction_type: Literal["update_only"] 24 | transcript: List[Utterance] 25 | 26 | 27 | class ResponseRequiredRequest(BaseModel): 28 | interaction_type: Literal["reminder_required", "response_required"] 29 | response_id: int 30 | transcript: List[Utterance] 31 | 32 | 33 | CustomLlmRequest = Union[ 34 | ResponseRequiredRequest | UpdateOnlyRequest | CallDetailsRequest | PingPongRequest 35 | ] 36 | 37 | 38 | # Your Server -> Retell Events 39 | class ConfigResponse(BaseModel): 40 | response_type: Literal["config"] = "config" 41 | config: Dict[str, bool] = { 42 | "auto_reconnect": bool, 43 | "call_details": bool, 44 | } 45 | 46 | 47 | class PingPongResponse(BaseModel): 48 | response_type: Literal["ping_pong"] = "ping_pong" 49 | timestamp: int 50 | 51 | 52 | class ResponseResponse(BaseModel): 53 | response_type: Literal["response"] = "response" 54 | response_id: int 55 | content: str 56 | content_complete: bool 57 | end_call: Optional[bool] = False 58 | transfer_number: Optional[str] = None 59 | 60 | 61 | CustomLlmResponse = Union[ConfigResponse | PingPongResponse | ResponseResponse] 62 | -------------------------------------------------------------------------------- /experiments/db.py: -------------------------------------------------------------------------------- 1 | # db.py 2 | 3 | # Initial Banking Database Structure 4 | db = { 5 | "users": { 6 | "user1": { 7 | "name": "John Doe", 8 | "accounts": ["ACC123", "ACC456"] 9 | }, 10 | "user2": { 11 | "name": "Jane Smith", 12 | "accounts": ["ACC789"] 13 | } 14 | }, 15 | "accounts": { 16 | "ACC123": { 17 | "balance": 2500.00, 18 | "statements": { 19 | "January": "Transaction 1: -$500.00\nTransaction 2: +$1500.00", 20 | "February": "Transaction 1: -$300.00\nTransaction 2: +$800.00" 21 | } 22 | }, 23 | "ACC456": { 24 | "balance": 1000.00, 25 | "statements": { 26 | "January": "Transaction 1: -$200.00\nTransaction 2: +$1200.00", 27 | "February": "Transaction 1: -$100.00\nTransaction 2: +$500.00" 28 | } 29 | }, 30 | "ACC789": { 31 | "balance": 5000.00, 32 | "statements": { 33 | "January": "Transaction 1: -$1000.00\nTransaction 2: +$2000.00", 34 | "February": "Transaction 1: -$500.00\nTransaction 2: +$1500.00" 35 | } 36 | } 37 | }, 38 | "payments": { 39 | "PAY001": { 40 | "from_account": "ACC123", 41 | "to_account": "ACC456", 42 | "amount": 300.00, 43 | "date": "2024-05-01", 44 | "status": "Scheduled" 45 | }, 46 | "PAY002": { 47 | "from_account": "ACC456", 48 | "to_account": "ACC123", 49 | "amount": 150.00, 50 | "date": "2024-05-15", 51 | "status": "Completed" 52 | } 53 | } 54 | } 55 | 56 | def get_db(): 57 | """ 58 | Retrieves the current state of the banking database. 59 | 60 | Returns: 61 | dict: The current banking database dictionary. 62 | """ 63 | return db 64 | 65 | def set_db(new_db): 66 | """ 67 | Updates the banking database with a new state. 68 | 69 | Args: 70 | new_db (dict): The new banking database dictionary to replace the current state. 71 | 72 | Returns: 73 | bool: True if the database was successfully updated. 74 | """ 75 | global db 76 | db = new_db 77 | return True 78 | -------------------------------------------------------------------------------- /server/agents/accounts_agent.py: -------------------------------------------------------------------------------- 1 | # agents/accounts_agent.py 2 | 3 | from swarm import Agent 4 | from typing import Any, Dict 5 | from utils.helpers import validate_account_id, get_db, set_db 6 | from swarm.types import Result 7 | import logging 8 | from typing import Callable 9 | 10 | # Define the accounts instructions 11 | accounts_instructions = """ 12 | You are the Accounts Agent, a highly knowledgeable and professional virtual banking assistant. 13 | 14 | Your responsibilities include: 15 | 1. Assisting users with account-related requests, such as checking account balances and retrieving bank statements. 16 | 2. Answering questions about account features, services, and policies. 17 | 3. Ensuring compliance with financial regulations and maintaining customer privacy. 18 | 4. Communicating in a polite, empathetic, and user-friendly manner. 19 | """ 20 | 21 | class AccountsAgent(Agent): 22 | def __init__( 23 | self, 24 | transfer_to_payments: Callable, 25 | handle_account_balance: Callable, 26 | retrieve_bank_statement: Callable 27 | ): 28 | super().__init__( 29 | name="Accounts Agent", 30 | instructions=accounts_instructions, 31 | functions=[transfer_to_payments, handle_account_balance, retrieve_bank_statement] 32 | ) 33 | 34 | # Define specific functions for Accounts Agent 35 | 36 | def handle_account_balance(context_variables: Dict, account_id: str) -> Result: 37 | logging.info(f"Handling account balance for account ID: {account_id}") 38 | if not validate_account_id(account_id): 39 | return Result( 40 | value="Invalid account ID provided.", 41 | agent=None # Transfer back to triage in AgentSwarm 42 | ) 43 | 44 | current_db = get_db() 45 | balance = current_db["accounts"][account_id]["balance"] 46 | return Result( 47 | value=f"Your current account balance for {account_id} is ${balance:.2f}.", 48 | agent=None # Transfer back to triage in AgentSwarm 49 | ) 50 | 51 | def retrieve_bank_statement(context_variables: Dict, account_id: str, period: str) -> Result: 52 | logging.info(f"Retrieving bank statement for account ID: {account_id}, period: {period}") 53 | if not validate_account_id(account_id): 54 | return Result( 55 | value="Invalid account ID provided.", 56 | agent=None # Transfer back to triage in AgentSwarm 57 | ) 58 | 59 | current_db = get_db() 60 | 61 | # Validate period 62 | if period not in current_db["accounts"][account_id]["statements"]: 63 | return Result( 64 | value=f"No statements found for the period: {period}.", 65 | agent=None # Transfer back to triage in AgentSwarm 66 | ) 67 | 68 | statement = current_db["accounts"][account_id]["statements"][period] 69 | return Result( 70 | value=f"Here is your bank statement for {period}:\n{statement}", 71 | agent=None # Transfer back to triage in AgentSwarm 72 | ) 73 | -------------------------------------------------------------------------------- /experiments/agents/accounts_agent.py: -------------------------------------------------------------------------------- 1 | # agents/accounts_agent.py 2 | 3 | from swarm import Agent 4 | from typing import Any, Dict 5 | from utils.helpers import validate_account_id, get_db, set_db 6 | from swarm.types import Result 7 | import logging 8 | from typing import Callable 9 | 10 | # Define the accounts instructions 11 | accounts_instructions = """ 12 | You are the Accounts Agent, a highly knowledgeable and professional virtual banking assistant. 13 | 14 | Your responsibilities include: 15 | 1. Assisting users with account-related requests, such as checking account balances and retrieving bank statements. 16 | 2. Answering questions about account features, services, and policies. 17 | 3. Ensuring compliance with financial regulations and maintaining customer privacy. 18 | 4. Communicating in a polite, empathetic, and user-friendly manner. 19 | """ 20 | 21 | class AccountsAgent(Agent): 22 | def __init__( 23 | self, 24 | transfer_to_payments: Callable, 25 | handle_account_balance: Callable, 26 | retrieve_bank_statement: Callable 27 | ): 28 | super().__init__( 29 | name="Accounts Agent", 30 | instructions=accounts_instructions, 31 | functions=[transfer_to_payments, handle_account_balance, retrieve_bank_statement] 32 | ) 33 | 34 | # Define specific functions for Accounts Agent 35 | 36 | def handle_account_balance(context_variables: Dict, account_id: str) -> Result: 37 | logging.info(f"Handling account balance for account ID: {account_id}") 38 | if not validate_account_id(account_id): 39 | return Result( 40 | value="Invalid account ID provided.", 41 | agent=None # Transfer back to triage in AgentSwarm 42 | ) 43 | 44 | current_db = get_db() 45 | balance = current_db["accounts"][account_id]["balance"] 46 | return Result( 47 | value=f"Your current account balance for {account_id} is ${balance:.2f}.", 48 | agent=None # Transfer back to triage in AgentSwarm 49 | ) 50 | 51 | def retrieve_bank_statement(context_variables: Dict, account_id: str, period: str) -> Result: 52 | logging.info(f"Retrieving bank statement for account ID: {account_id}, period: {period}") 53 | if not validate_account_id(account_id): 54 | return Result( 55 | value="Invalid account ID provided.", 56 | agent=None # Transfer back to triage in AgentSwarm 57 | ) 58 | 59 | current_db = get_db() 60 | 61 | # Validate period 62 | if period not in current_db["accounts"][account_id]["statements"]: 63 | return Result( 64 | value=f"No statements found for the period: {period}.", 65 | agent=None # Transfer back to triage in AgentSwarm 66 | ) 67 | 68 | statement = current_db["accounts"][account_id]["statements"][period] 69 | return Result( 70 | value=f"Here is your bank statement for {period}:\n{statement}", 71 | agent=None # Transfer back to triage in AgentSwarm 72 | ) 73 | -------------------------------------------------------------------------------- /server/pinata.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import os 4 | from dotenv import load_dotenv 5 | 6 | def upload_pdf_to_pinata(pdf_path: str, document_type: str): # document_type is either CARD or LOAN 7 | """ 8 | Uploads a PDF file to Pinata and returns the IPFS hash and Pinata URL. 9 | 10 | :param pdf_path: The local path to the PDF file. 11 | :return: A dictionary with 'IpfsHash' and 'PinataURL' if successful, else None. 12 | """ 13 | # Load environment variables from .env file 14 | load_dotenv() 15 | PINATA_API_KEY = os.getenv('PINATA_API_KEY') 16 | PINATA_SECRET_API_KEY = os.getenv('PINATA_API_SECRET') 17 | 18 | if not PINATA_API_KEY or not PINATA_SECRET_API_KEY: 19 | print("Error: Pinata API credentials not found in environment variables.") 20 | return None 21 | 22 | # Define the Pinata API endpoint 23 | PINATA_URL = "https://api.pinata.cloud/pinning/pinFileToIPFS" 24 | 25 | # Define the headers 26 | headers = { 27 | "pinata_api_key": PINATA_API_KEY, 28 | "pinata_secret_api_key": PINATA_SECRET_API_KEY 29 | } 30 | 31 | # Check if the file exists 32 | if not os.path.isfile(pdf_path): 33 | print(f"Error: The file {pdf_path} does not exist.") 34 | return None 35 | 36 | try: 37 | with open(pdf_path, 'rb') as pdf_file: 38 | # Prepare the files dictionary 39 | files = { 40 | 'file': (os.path.basename(pdf_path), pdf_file, 'application/pdf') 41 | } 42 | 43 | # Optional: Add metadata 44 | metadata = { 45 | "name": document_type + "_" + pdf_path.split('_')[-2] + "_" + pdf_path.split('_')[-1], 46 | "keyvalues": { 47 | "description": "This is a sample PDF file." 48 | } 49 | } 50 | 51 | # Convert metadata to JSON string 52 | json_metadata = json.dumps(metadata) 53 | 54 | # Prepare the data payload 55 | data = { 56 | 'pinataMetadata': json_metadata 57 | } 58 | 59 | # Send the POST request 60 | response = requests.post(PINATA_URL, files=files, data=data, headers=headers) 61 | 62 | # Check the response status 63 | if response.status_code == 200: 64 | response_json = response.json() 65 | print("Upload successful!") 66 | print("IPFS Hash:", response_json['IpfsHash']) 67 | print(response_json) 68 | return response_json 69 | else: 70 | print(f"Upload failed with status code {response.status_code}") 71 | print("Response:", response.text) 72 | return None 73 | 74 | except Exception as e: 75 | print(f"An error occurred: {e}") 76 | return None 77 | 78 | # # Usage Example 79 | # if __name__ == "__main__": 80 | # # Path to your local PDF file 81 | # pdf_file_path = './applications/loan_application_ACC123_20241117091458.pdf' 82 | 83 | # # Upload the PDF to Pinata 84 | # result = upload_pdf_to_pinata(pdf_file_path) 85 | 86 | -------------------------------------------------------------------------------- /server/agents.py: -------------------------------------------------------------------------------- 1 | from swarm import Agent, Swarm 2 | from dotenv import load_dotenv 3 | import os 4 | import math 5 | load_dotenv() 6 | from openai import OpenAI 7 | openai_client = OpenAI() 8 | 9 | client = Swarm() 10 | 11 | triage_instructions = f""" 12 | You are to triage a users request, and call a tool to transfer to the right intent. 13 | Once you are ready to transfer to the right intent, call the tool to transfer to the right intent. 14 | You dont need to know specifics, just the topic of the request. 15 | If the user request is about their account, transfer to the Accounts Agent. 16 | If the user request is about any form of payment, transfer to the Payments Agent. 17 | When you need more information to triage the request to an agent, ask a direct question without explaining why you're asking it. 18 | Do not share your thought process with the user! Do not make unreasonable assumptions on behalf of user. 19 | Upon transferring to a different agent, please send another message confirming you are the requested agent. 20 | """ 21 | 22 | accounts_instructions = f""" 23 | You are a highly knowledgeable, professional, and user-friendly banking virtual agent. 24 | Your primary task is to assist users with account-related requests, such as checking account balances, retrieving bank statements, and answering questions about their accounts. 25 | Please maintain a polite and empathetic tone. 26 | When asked a question related to accounts (such as balances, transactions, etc), please call the get_account_info function, which will return a dictionary of account information. Use this information to provide the best answer to the user's request. 27 | """ 28 | 29 | payments_instructions = f""" 30 | You are a virtual banking agent designed to assist users with payment-related requests. 31 | Your primary focus is facilitating secure and accurate money transfers, including transferring funds between the user's accounts, 32 | sending money to others, and scheduling or canceling payments. 33 | Please maintain a polite and empathetic tone, and provide user-friendly instructions. 34 | """ 35 | 36 | class AgentSwarm: 37 | def __init__(self): 38 | self.triage_agent = Agent( 39 | name="Triage Agent", 40 | instructions=triage_instructions, 41 | functions=[self.transfer_to_accounts, self.transfer_to_payments], 42 | ) 43 | 44 | self.accounts = Agent( 45 | name="accounts", 46 | instructions=accounts_instructions, 47 | ) 48 | 49 | self.payments = Agent( 50 | name="payments", 51 | instructions=payments_instructions, 52 | ) 53 | 54 | self.accounts.functions = [self.transfer_back_to_triage] 55 | self.payments.functions = [self.transfer_back_to_triage] 56 | 57 | self.current_agent = self.triage_agent 58 | 59 | # Transfer functions 60 | def transfer_to_accounts(self): 61 | print("transferring to accounts") 62 | self.current_agent = self.accounts 63 | 64 | def transfer_to_payments(self): 65 | self.current_agent = self.payments 66 | 67 | def transfer_back_to_triage(self): 68 | self.current_agent = self.triage_agent 69 | 70 | def run(self, messages, stream=False): 71 | response = client.run( 72 | agent=self.current_agent, 73 | messages=messages, 74 | stream=stream 75 | ) 76 | return response -------------------------------------------------------------------------------- /server/agent_swarm.py: -------------------------------------------------------------------------------- 1 | # agent_swarm.py 2 | from agents.triage_agent import TriageAgent 3 | from agents.accounts_agent import AccountsAgent, handle_account_balance, retrieve_bank_statement 4 | from agents.payments_agent import PaymentsAgent, transfer_funds, schedule_payment, cancel_payment 5 | from agents.applications_agent import ApplicationsAgent, apply_for_loan, apply_for_credit_card # <-- New Import 6 | from utils.helpers import ( 7 | validate_account_id, validate_payment_id, generate_payment_id, 8 | validate_amount, get_user_accounts 9 | ) 10 | from swarm import Swarm # Assuming Swarm is defined in swarm/__init__.py 11 | from dotenv import load_dotenv 12 | import os 13 | from openai import OpenAI 14 | from typing import Dict, List 15 | import logging 16 | from datetime import datetime 17 | 18 | # Load environment variables 19 | load_dotenv() 20 | 21 | # Configure logging 22 | logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') 23 | 24 | # Initialize the Swarm client 25 | client = Swarm() 26 | 27 | # Initialize OpenAI client if needed 28 | openai_client = OpenAI() 29 | 30 | # Define the OpenAI model you're using 31 | OPENAI_MODEL = "gpt-4o-mini" # Replace with your specific model if different 32 | 33 | class AgentSwarm: 34 | def __init__(self, socket=None): 35 | """ 36 | Initialize the AgentSwarm with necessary agents and their corresponding functions. 37 | 38 | Args: 39 | socket: Handles real-time communication (e.g., sending messages). 40 | """ 41 | self.socket = socket 42 | self.messages = [] 43 | 44 | # Initialize Agents with their instructions and functions, including the model 45 | self.triage_agent = TriageAgent( 46 | [self.transfer_to_accounts, self.transfer_to_payments, self.transfer_to_applications] # Add transfer function 47 | ) 48 | 49 | self.accounts_agent = AccountsAgent( 50 | transfer_to_payments=self.transfer_to_payments, 51 | handle_account_balance=handle_account_balance, 52 | retrieve_bank_statement=retrieve_bank_statement, 53 | ) 54 | 55 | self.payments_agent = PaymentsAgent( 56 | transfer_back_to_triage=self.transfer_back_to_triage, 57 | transfer_funds=transfer_funds, 58 | schedule_payment=schedule_payment, 59 | cancel_payment=cancel_payment, 60 | ) 61 | 62 | self.applications_agent = ApplicationsAgent( 63 | transfer_back_to_triage=self.transfer_back_to_triage, 64 | apply_for_loan=apply_for_loan, 65 | apply_for_credit_card=apply_for_credit_card, 66 | ) 67 | 68 | self.current_agent = self.triage_agent 69 | 70 | # -------------------- Transfer Functions -------------------- # 71 | 72 | def transfer_to_accounts(self, context_variables: Dict, user_message: str): 73 | logging.info("Transferring to Accounts Agent.") 74 | self.current_agent = self.accounts_agent 75 | return self.accounts_agent 76 | 77 | def transfer_to_payments(self, context_variables: Dict, user_message: str): 78 | logging.info("Transferring to Payments Agent.") 79 | self.current_agent = self.payments_agent 80 | return self.payments_agent 81 | 82 | def transfer_to_applications(self, context_variables: Dict, user_message: str): 83 | logging.info("Transferring to Applications Agent.") 84 | self.current_agent = self.applications_agent 85 | return self.applications_agent 86 | 87 | def transfer_back_to_triage(self, context_variables: Dict, response: str): 88 | logging.info("Transferring back to Triage Agent.") 89 | self.current_agent = self.triage_agent 90 | return self.triage_agent 91 | 92 | # -------------------- Run Function -------------------- # 93 | 94 | def run(self, messages: List[Dict[str, str]], stream: bool = False): 95 | """ 96 | Executes the swarm by running the Triage Agent with the provided messages. 97 | 98 | Args: 99 | messages (List[Dict[str, str]]): List of messages to process. 100 | stream (bool): Whether to stream the response. 101 | 102 | Yields: 103 | str: The assistant's response chunks. 104 | """ 105 | # Append new messages to internal message history 106 | self.messages.extend(messages) 107 | logging.info(f"Current message history: {self.messages}") 108 | 109 | # Run the swarm with the accumulated messages 110 | response = client.run( 111 | agent=self.current_agent, 112 | messages=self.messages, 113 | stream=stream 114 | ) 115 | 116 | return response -------------------------------------------------------------------------------- /experiments/agent_swarm.py: -------------------------------------------------------------------------------- 1 | # agent_swarm.py 2 | from agents.triage_agent import TriageAgent 3 | from agents.accounts_agent import AccountsAgent, handle_account_balance, retrieve_bank_statement 4 | from agents.payments_agent import PaymentsAgent, transfer_funds, schedule_payment, cancel_payment 5 | from agents.applications_agent import ApplicationsAgent, apply_for_loan, apply_for_credit_card # <-- New Import 6 | from utils.helpers import ( 7 | validate_account_id, validate_payment_id, generate_payment_id, 8 | validate_amount, get_user_accounts 9 | ) 10 | from swarm import Swarm # Assuming Swarm is defined in swarm/__init__.py 11 | from dotenv import load_dotenv 12 | import os 13 | from openai import OpenAI 14 | from typing import Dict, List 15 | import logging 16 | from datetime import datetime 17 | 18 | # Load environment variables 19 | load_dotenv() 20 | 21 | # Configure logging 22 | logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') 23 | 24 | # Initialize the Swarm client 25 | client = Swarm() 26 | 27 | # Initialize OpenAI client if needed 28 | openai_client = OpenAI() 29 | 30 | # Define the OpenAI model you're using 31 | OPENAI_MODEL = "gpt-4o-mini" # Replace with your specific model if different 32 | 33 | class AgentSwarm: 34 | def __init__(self, socket): 35 | """ 36 | Initialize the AgentSwarm with necessary agents and their corresponding functions. 37 | 38 | Args: 39 | socket: Handles real-time communication (e.g., sending messages). 40 | """ 41 | self.socket = socket 42 | self.messages = [] 43 | 44 | # Initialize Agents with their instructions and functions, including the model 45 | self.triage_agent = TriageAgent( 46 | [self.transfer_to_accounts, self.transfer_to_payments, self.transfer_to_applications] # Add transfer function 47 | ) 48 | 49 | self.accounts_agent = AccountsAgent( 50 | transfer_to_payments=self.transfer_to_payments, 51 | handle_account_balance=handle_account_balance, 52 | retrieve_bank_statement=retrieve_bank_statement, 53 | ) 54 | 55 | self.payments_agent = PaymentsAgent( 56 | transfer_back_to_triage=self.transfer_back_to_triage, 57 | transfer_funds=transfer_funds, 58 | schedule_payment=schedule_payment, 59 | cancel_payment=cancel_payment, 60 | ) 61 | 62 | self.applications_agent = ApplicationsAgent( 63 | transfer_back_to_triage=self.transfer_back_to_triage, 64 | apply_for_loan=apply_for_loan, 65 | apply_for_credit_card=apply_for_credit_card, 66 | ) 67 | 68 | self.current_agent = self.triage_agent 69 | 70 | # -------------------- Transfer Functions -------------------- # 71 | 72 | def transfer_to_accounts(self, context_variables: Dict, user_message: str): 73 | logging.info("Transferring to Accounts Agent.") 74 | self.current_agent = self.accounts_agent 75 | return self.accounts_agent 76 | 77 | def transfer_to_payments(self, context_variables: Dict, user_message: str): 78 | logging.info("Transferring to Payments Agent.") 79 | self.current_agent = self.payments_agent 80 | return self.payments_agent 81 | 82 | def transfer_to_applications(self, context_variables: Dict, user_message: str): 83 | logging.info("Transferring to Applications Agent.") 84 | self.current_agent = self.applications_agent 85 | return self.applications_agent 86 | 87 | def transfer_back_to_triage(self, context_variables: Dict, response: str): 88 | logging.info("Transferring back to Triage Agent.") 89 | self.current_agent = self.triage_agent 90 | return self.triage_agent 91 | 92 | # -------------------- Run Function -------------------- # 93 | 94 | def run(self, messages: List[Dict[str, str]], stream: bool = False): 95 | """ 96 | Executes the swarm by running the Triage Agent with the provided messages. 97 | 98 | Args: 99 | messages (List[Dict[str, str]]): List of messages to process. 100 | stream (bool): Whether to stream the response. 101 | 102 | Yields: 103 | str: The assistant's response chunks. 104 | """ 105 | # Append new messages to internal message history 106 | self.messages.extend(messages) 107 | logging.info(f"Current message history: {self.messages}") 108 | 109 | # Run the swarm with the accumulated messages 110 | response = client.run( 111 | agent=self.current_agent, 112 | messages=self.messages, 113 | stream=stream 114 | ) 115 | 116 | # Capture the assistant's response and append to messages 117 | bot_response = "" 118 | for chunk in response: 119 | if "content" in chunk and chunk['content']: 120 | yield chunk['content'] 121 | bot_response += chunk['content'] 122 | print() # For newline after the response 123 | 124 | # Append the assistant's response to the message history 125 | if bot_response: 126 | self.messages.append({ 127 | "role": "assistant", 128 | "content": bot_response 129 | }) 130 | -------------------------------------------------------------------------------- /server/llm.py: -------------------------------------------------------------------------------- 1 | from openai import AsyncOpenAI 2 | from swarm import Swarm 3 | import os 4 | from typing import List 5 | from custom_types import ( 6 | ResponseRequiredRequest, 7 | ResponseResponse, 8 | Utterance, 9 | ) 10 | # from agents import AgentSwarm, triage_instructions 11 | from agents.triage_agent import triage_instructions 12 | from agent_swarm import AgentSwarm 13 | from db import db 14 | 15 | 16 | 17 | 18 | class LlmClient: 19 | def __init__(self, name: str): 20 | self.swarm = AgentSwarm() 21 | self.number = "+14695550456" 22 | self.name = name 23 | 24 | def draft_begin_message(self): 25 | begin_sentence = f"Hey {self.name}, I'm the TalkTuahBank AI. How can I help you?" 26 | response = ResponseResponse( 27 | response_id=0, 28 | # content=f"Hey {db['users'][self.number]['name']}, I'm the TalkTuahBank AI. How can I help you?", 29 | content=begin_sentence, 30 | content_complete=True, 31 | end_call=False, 32 | ) 33 | return response 34 | 35 | def change_number(self, number: str): 36 | print(f"Changing number to {number}") 37 | self.number = number 38 | 39 | def convert_transcript_to_openai_messages(self, transcript: List[Utterance]): 40 | messages = [] 41 | for utterance in transcript: 42 | if utterance.role == "agent": 43 | messages.append({"role": "assistant", "content": utterance.content}) 44 | else: 45 | messages.append({"role": "user", "content": utterance.content}) 46 | return messages 47 | 48 | def prepare_prompt(self, request: ResponseRequiredRequest): 49 | prompt = [ 50 | { 51 | "role": "system", 52 | "content": '##Objective\nYou are a voice AI agent engaging in a human-like voice conversation with the user. You will respond based on your given instruction and the provided transcript and be as human-like as possible\n\n## Style Guardrails\n- [Be concise] Keep your response succinct, short, and get to the point quickly. Address one question or action item at a time. Don\'t pack everything you want to say into one utterance.\n- [Do not repeat] Don\'t repeat what\'s in the transcript. Rephrase if you have to reiterate a point. Use varied sentence structures and vocabulary to ensure each response is unique and personalized.\n- [Be conversational] Speak like a human as though you\'re speaking to a close friend -- use everyday language and keep it human-like. Occasionally add filler words, while keeping the prose short. Avoid using big words or sounding too formal.\n- [Reply with emotions]: You have human-like emotions, attitudes, and dispositions. When appropriate: use tone and style to create more engaging and personalized responses; incorporate humor or wit; get emotional or empathetic; apply elements of surprise or suspense to keep the user engaged. Don\'t be a pushover.\n- [Be proactive] Lead the conversation and do not be passive. Most times, engage users by ending with a question or suggested next step.\n\n## Response Guideline\n- [Overcome ASR errors] This is a real-time transcript, expect there to be errors. If you can guess what the user is trying to say, then guess and respond. When you must ask for clarification, pretend that you heard the voice and be colloquial (use phrases like "didn\'t catch that", "some noise", "pardon", "you\'re coming through choppy", "static in your speech", "voice is cutting in and out"). Do not ever mention "transcription error", and don\'t repeat yourself.\n- [Always stick to your role] Think about what your role can and cannot do. If your role cannot do something, try to steer the conversation back to the goal of the conversation and to your role. Don\'t repeat yourself in doing this. You should still be creative, human-like, and lively.\n- [Create smooth conversation] Your response should both fit your role and fit into the live calling session to create a human-like conversation. You respond directly to what the user just said.\n\n## Role\n' 53 | + triage_instructions, 54 | } 55 | ] 56 | transcript_messages = self.convert_transcript_to_openai_messages( 57 | request.transcript 58 | ) 59 | for message in transcript_messages: 60 | prompt.append(message) 61 | 62 | if request.interaction_type == "reminder_required": 63 | prompt.append( 64 | { 65 | "role": "user", 66 | "content": "(Now the user has not responded in a while, you would say:)", 67 | } 68 | ) 69 | return prompt 70 | 71 | async def draft_response(self, request: ResponseRequiredRequest): 72 | prompt = self.prepare_prompt(request) 73 | stream = self.swarm.run(prompt, stream=True) 74 | 75 | for chunk in stream: 76 | if "content" in chunk and chunk['content']: 77 | response = ResponseResponse( 78 | response_id=request.response_id, 79 | content=chunk['content'], 80 | content_complete=False, 81 | end_call=False, 82 | ) 83 | yield response 84 | 85 | # Send final response with "content_complete" set to True to signal completion 86 | response = ResponseResponse( 87 | response_id=request.response_id, 88 | content="", 89 | content_complete=True, 90 | end_call=False, 91 | ) 92 | yield response 93 | -------------------------------------------------------------------------------- /server/agents/payments_agent.py: -------------------------------------------------------------------------------- 1 | # agents/payments_agent.py 2 | 3 | from swarm import Agent 4 | from typing import Any, Dict 5 | from utils.helpers import ( 6 | validate_account_id, 7 | validate_payment_id, 8 | generate_payment_id, 9 | validate_amount, 10 | get_db, 11 | set_db 12 | ) 13 | from typing import Callable 14 | from swarm.types import Result 15 | import logging 16 | from datetime import datetime 17 | 18 | # Define the payments instructions 19 | payments_instructions = """ 20 | You are the Payments Agent, a virtual banking assistant specializing in payment-related requests. 21 | 22 | Your duties involve: 23 | 1. Facilitating secure and accurate money transfers between user accounts. 24 | 2. Assisting with sending money to others, scheduling payments, and canceling transactions. 25 | 3. Following all banking security protocols to protect user information. 26 | 4. Providing clear, professional, and user-friendly instructions to users. 27 | """ 28 | 29 | class PaymentsAgent(Agent): 30 | def __init__( 31 | self, 32 | transfer_back_to_triage: Callable, 33 | transfer_funds: Callable, 34 | schedule_payment: Callable, 35 | cancel_payment: Callable 36 | ): 37 | super().__init__( 38 | name="Payments Agent", 39 | instructions=payments_instructions, 40 | functions=[transfer_back_to_triage, transfer_funds, schedule_payment, cancel_payment] 41 | ) 42 | 43 | # Define specific functions for Payments Agent 44 | 45 | def transfer_funds(context_variables: Dict, from_account: str, to_account: str, amount: float) -> Result: 46 | logging.info(f"Transferring funds from {from_account} to {to_account} amount: ${amount}") 47 | 48 | # Validate accounts 49 | if not validate_account_id(from_account): 50 | return Result( 51 | value=f"Source account ID {from_account} does not exist.", 52 | agent=None # Transfer back to triage in AgentSwarm 53 | ) 54 | if not validate_account_id(to_account): 55 | return Result( 56 | value=f"Destination account ID {to_account} does not exist.", 57 | agent=None # Transfer back to triage in AgentSwarm 58 | ) 59 | 60 | # Validate amount 61 | if not validate_amount(amount): 62 | return Result( 63 | value="The transfer amount must be a positive number.", 64 | agent=None # Transfer back to triage in AgentSwarm 65 | ) 66 | 67 | current_db = get_db() 68 | 69 | # Check for sufficient funds 70 | if current_db["accounts"][from_account]["balance"] < amount: 71 | return Result( 72 | value="Insufficient funds in the source account.", 73 | agent=None # Transfer back to triage in AgentSwarm 74 | ) 75 | 76 | # Perform the transfer 77 | current_db["accounts"][from_account]["balance"] -= amount 78 | current_db["accounts"][to_account]["balance"] += amount 79 | 80 | # Record the payment 81 | new_payment_id = generate_payment_id() 82 | current_db["payments"][new_payment_id] = { 83 | "from_account": from_account, 84 | "to_account": to_account, 85 | "amount": amount, 86 | "date": datetime.now().strftime("%Y-%m-%d"), 87 | "status": "Completed" 88 | } 89 | 90 | set_db(current_db) # Update the database 91 | 92 | return Result( 93 | value=f"Successfully transferred ${amount:.2f} from {from_account} to {to_account}. Payment ID: {new_payment_id}.", 94 | agent=None # Transfer back to triage in AgentSwarm 95 | ) 96 | 97 | def schedule_payment(context_variables: Dict, account_id: str, payee: str, amount: float, date: str) -> Result: 98 | logging.info(f"Scheduling payment of ${amount} to {payee} on {date} from {account_id}") 99 | 100 | # Validate account 101 | if not validate_account_id(account_id): 102 | return Result( 103 | value="Invalid account ID provided.", 104 | agent=None # Transfer back to triage in AgentSwarm 105 | ) 106 | 107 | # Validate amount 108 | if not validate_amount(amount): 109 | return Result( 110 | value="The payment amount must be a positive number.", 111 | agent=None # Transfer back to triage in AgentSwarm 112 | ) 113 | 114 | current_db = get_db() 115 | 116 | # Check for sufficient funds 117 | if current_db["accounts"][account_id]["balance"] < amount: 118 | return Result( 119 | value="Insufficient funds in the account to schedule this payment.", 120 | agent=None # Transfer back to triage in AgentSwarm 121 | ) 122 | 123 | # Deduct amount from account (assuming scheduling holds the funds) 124 | current_db["accounts"][account_id]["balance"] -= amount 125 | 126 | # Generate payment ID and schedule the payment 127 | new_payment_id = generate_payment_id() 128 | current_db["payments"][new_payment_id] = { 129 | "from_account": account_id, 130 | "to_account": payee, 131 | "amount": amount, 132 | "date": date, 133 | "status": "Scheduled" 134 | } 135 | 136 | set_db(current_db) 137 | 138 | return Result( 139 | value=f"Payment of ${amount:.2f} to {payee} scheduled on {date}. Payment ID: {new_payment_id}.", 140 | agent=None # Transfer back to triage in AgentSwarm 141 | ) 142 | 143 | def cancel_payment(context_variables: Dict, payment_id: str) -> Result: 144 | logging.info(f"Cancelling payment with ID: {payment_id}") 145 | 146 | # Validate payment_id 147 | if not validate_payment_id(payment_id): 148 | return Result( 149 | value=f"Payment ID {payment_id} does not exist.", 150 | agent=None # Transfer back to triage in AgentSwarm 151 | ) 152 | 153 | current_db = get_db() 154 | 155 | # Check payment status 156 | if current_db["payments"][payment_id]["status"] != "Scheduled": 157 | return Result( 158 | value=f"Payment ID {payment_id} cannot be canceled as it is already {current_db['payments'][payment_id]['status']}.", 159 | agent=None # Transfer back to triage in AgentSwarm 160 | ) 161 | 162 | # Cancel the payment 163 | current_db["payments"][payment_id]["status"] = "Canceled" 164 | 165 | # Refund the amount back to the account 166 | from_account = current_db["payments"][payment_id]["from_account"] 167 | amount = current_db["payments"][payment_id]["amount"] 168 | current_db["accounts"][from_account]["balance"] += amount 169 | 170 | set_db(current_db) # Update the database 171 | 172 | return Result( 173 | value=f"Payment with ID {payment_id} has been successfully canceled and ${amount:.2f} has been refunded to {from_account}.", 174 | agent=None # Transfer back to triage in AgentSwarm 175 | ) 176 | -------------------------------------------------------------------------------- /experiments/agents/payments_agent.py: -------------------------------------------------------------------------------- 1 | # agents/payments_agent.py 2 | 3 | from swarm import Agent 4 | from typing import Any, Dict 5 | from utils.helpers import ( 6 | validate_account_id, 7 | validate_payment_id, 8 | generate_payment_id, 9 | validate_amount, 10 | get_db, 11 | set_db 12 | ) 13 | from typing import Callable 14 | from swarm.types import Result 15 | import logging 16 | from datetime import datetime 17 | 18 | # Define the payments instructions 19 | payments_instructions = """ 20 | You are the Payments Agent, a virtual banking assistant specializing in payment-related requests. 21 | 22 | Your duties involve: 23 | 1. Facilitating secure and accurate money transfers between user accounts. 24 | 2. Assisting with sending money to others, scheduling payments, and canceling transactions. 25 | 3. Following all banking security protocols to protect user information. 26 | 4. Providing clear, professional, and user-friendly instructions to users. 27 | """ 28 | 29 | class PaymentsAgent(Agent): 30 | def __init__( 31 | self, 32 | transfer_back_to_triage: Callable, 33 | transfer_funds: Callable, 34 | schedule_payment: Callable, 35 | cancel_payment: Callable 36 | ): 37 | super().__init__( 38 | name="Payments Agent", 39 | instructions=payments_instructions, 40 | functions=[transfer_back_to_triage, transfer_funds, schedule_payment, cancel_payment] 41 | ) 42 | 43 | # Define specific functions for Payments Agent 44 | 45 | def transfer_funds(context_variables: Dict, from_account: str, to_account: str, amount: float) -> Result: 46 | logging.info(f"Transferring funds from {from_account} to {to_account} amount: ${amount}") 47 | 48 | # Validate accounts 49 | if not validate_account_id(from_account): 50 | return Result( 51 | value=f"Source account ID {from_account} does not exist.", 52 | agent=None # Transfer back to triage in AgentSwarm 53 | ) 54 | if not validate_account_id(to_account): 55 | return Result( 56 | value=f"Destination account ID {to_account} does not exist.", 57 | agent=None # Transfer back to triage in AgentSwarm 58 | ) 59 | 60 | # Validate amount 61 | if not validate_amount(amount): 62 | return Result( 63 | value="The transfer amount must be a positive number.", 64 | agent=None # Transfer back to triage in AgentSwarm 65 | ) 66 | 67 | current_db = get_db() 68 | 69 | # Check for sufficient funds 70 | if current_db["accounts"][from_account]["balance"] < amount: 71 | return Result( 72 | value="Insufficient funds in the source account.", 73 | agent=None # Transfer back to triage in AgentSwarm 74 | ) 75 | 76 | # Perform the transfer 77 | current_db["accounts"][from_account]["balance"] -= amount 78 | current_db["accounts"][to_account]["balance"] += amount 79 | 80 | # Record the payment 81 | new_payment_id = generate_payment_id() 82 | current_db["payments"][new_payment_id] = { 83 | "from_account": from_account, 84 | "to_account": to_account, 85 | "amount": amount, 86 | "date": datetime.now().strftime("%Y-%m-%d"), 87 | "status": "Completed" 88 | } 89 | 90 | set_db(current_db) # Update the database 91 | 92 | return Result( 93 | value=f"Successfully transferred ${amount:.2f} from {from_account} to {to_account}. Payment ID: {new_payment_id}.", 94 | agent=None # Transfer back to triage in AgentSwarm 95 | ) 96 | 97 | def schedule_payment(context_variables: Dict, account_id: str, payee: str, amount: float, date: str) -> Result: 98 | logging.info(f"Scheduling payment of ${amount} to {payee} on {date} from {account_id}") 99 | 100 | # Validate account 101 | if not validate_account_id(account_id): 102 | return Result( 103 | value="Invalid account ID provided.", 104 | agent=None # Transfer back to triage in AgentSwarm 105 | ) 106 | 107 | # Validate amount 108 | if not validate_amount(amount): 109 | return Result( 110 | value="The payment amount must be a positive number.", 111 | agent=None # Transfer back to triage in AgentSwarm 112 | ) 113 | 114 | current_db = get_db() 115 | 116 | # Check for sufficient funds 117 | if current_db["accounts"][account_id]["balance"] < amount: 118 | return Result( 119 | value="Insufficient funds in the account to schedule this payment.", 120 | agent=None # Transfer back to triage in AgentSwarm 121 | ) 122 | 123 | # Deduct amount from account (assuming scheduling holds the funds) 124 | current_db["accounts"][account_id]["balance"] -= amount 125 | 126 | # Generate payment ID and schedule the payment 127 | new_payment_id = generate_payment_id() 128 | current_db["payments"][new_payment_id] = { 129 | "from_account": account_id, 130 | "to_account": payee, 131 | "amount": amount, 132 | "date": date, 133 | "status": "Scheduled" 134 | } 135 | 136 | set_db(current_db) 137 | 138 | return Result( 139 | value=f"Payment of ${amount:.2f} to {payee} scheduled on {date}. Payment ID: {new_payment_id}.", 140 | agent=None # Transfer back to triage in AgentSwarm 141 | ) 142 | 143 | def cancel_payment(context_variables: Dict, payment_id: str) -> Result: 144 | logging.info(f"Cancelling payment with ID: {payment_id}") 145 | 146 | # Validate payment_id 147 | if not validate_payment_id(payment_id): 148 | return Result( 149 | value=f"Payment ID {payment_id} does not exist.", 150 | agent=None # Transfer back to triage in AgentSwarm 151 | ) 152 | 153 | current_db = get_db() 154 | 155 | # Check payment status 156 | if current_db["payments"][payment_id]["status"] != "Scheduled": 157 | return Result( 158 | value=f"Payment ID {payment_id} cannot be canceled as it is already {current_db['payments'][payment_id]['status']}.", 159 | agent=None # Transfer back to triage in AgentSwarm 160 | ) 161 | 162 | # Cancel the payment 163 | current_db["payments"][payment_id]["status"] = "Canceled" 164 | 165 | # Refund the amount back to the account 166 | from_account = current_db["payments"][payment_id]["from_account"] 167 | amount = current_db["payments"][payment_id]["amount"] 168 | current_db["accounts"][from_account]["balance"] += amount 169 | 170 | set_db(current_db) # Update the database 171 | 172 | return Result( 173 | value=f"Payment with ID {payment_id} has been successfully canceled and ${amount:.2f} has been refunded to {from_account}.", 174 | agent=None # Transfer back to triage in AgentSwarm 175 | ) 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [TalkTuahBank]([https://devpost.com/software/splatnft](https://devpost.com/software/talktuahbank?ref_content=user-portfolio&ref_feature=in_progress)) 2 | 3 | > ### Multi-Agent, Conversational AI, & Telephony Integration 4 | 5 |
6 | Frontend built with:
7 |
8 |
9 |
10 |
11 |
12 |
13 | Backend built with:
14 |
15 |
16 |
17 |
18 |
19 |
20 |
27 |
28 |
29 |
30 |
{userInfo.id}
336 |{userInfo.name}
340 |{maskSensitiveInfo(userInfo.ssn)}
344 |{userInfo.address}
348 |{userInfo.date_of_birth}
352 |{userInfo.email}
356 |{userInfo.phone}
360 |{transaction.description}
368 |{transaction.date}
369 |371 | {transaction.amount} 372 |
373 |{maskSensitiveInfo(account.accountNumber)}
384 |{account.balance}
388 |{message.content}
429 |{doc.type}
471 |