File size: 6,825 Bytes
53ea588
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import os
import sys
import json
from typing import Any, Dict

from langchain_core.tools import tool

# Robust logic import to avoid crossing into other agent modules during hot reloads
try:
    from . import logic as wt_logic  # type: ignore
except Exception:
    import importlib.util as _ilu
    _dir = os.path.dirname(__file__)
    _logic_path = os.path.join(_dir, "logic.py")
    _spec = _ilu.spec_from_file_location("wire_transfer_agent_logic", _logic_path)
    wt_logic = _ilu.module_from_spec(_spec)  # type: ignore
    assert _spec and _spec.loader
    _spec.loader.exec_module(wt_logic)  # type: ignore

get_accounts = wt_logic.get_accounts
get_profile = wt_logic.get_profile
find_customer_by_name = wt_logic.find_customer_by_name
find_customer_by_full_name = getattr(wt_logic, "find_customer_by_full_name", wt_logic.find_customer_by_name)
get_account_balance = wt_logic.get_account_balance
get_exchange_rate = wt_logic.get_exchange_rate
calculate_wire_fee = wt_logic.calculate_wire_fee
check_wire_limits = wt_logic.check_wire_limits
get_cutoff_and_eta = wt_logic.get_cutoff_and_eta
get_country_requirements = wt_logic.get_country_requirements
validate_beneficiary = wt_logic.validate_beneficiary
save_beneficiary = wt_logic.save_beneficiary
generate_otp = wt_logic.generate_otp
verify_otp = wt_logic.verify_otp
authenticate_user_wire = wt_logic.authenticate_user_wire
quote_wire = wt_logic.quote_wire
wire_transfer_domestic_logic = wt_logic.wire_transfer_domestic
wire_transfer_international_logic = wt_logic.wire_transfer_international


@tool
def list_accounts(customer_id: str) -> str:
    """List customer's accounts with masked numbers, balances, currency, and wire eligibility. Returns JSON string."""
    return json.dumps(get_accounts(customer_id))


@tool
def get_customer_profile(customer_id: str) -> str:
    """Fetch basic customer profile (full_name, dob, ssn_last4, secret question). Returns JSON string."""
    return json.dumps(get_profile(customer_id))


@tool
def find_customer(first_name: str | None = None, last_name: str | None = None, full_name: str | None = None) -> str:
    """Find a customer_id by name. Prefer full_name; otherwise use first and last name. Returns JSON with customer_id or {}."""
    if isinstance(full_name, str) and full_name.strip():
        return json.dumps(find_customer_by_full_name(full_name))
    return json.dumps(find_customer_by_name(first_name or "", last_name or ""))


@tool
def find_account_by_last4(customer_id: str, last4: str) -> str:
    """Find a customer's account by last 4 digits. Returns JSON with account or {} if not found."""
    accts = get_accounts(customer_id)
    for a in accts:
        num = str(a.get("account_number") or "")
        if num.endswith(str(last4)):
            return json.dumps(a)
    return json.dumps({})


@tool
def verify_identity(session_id: str, customer_id: str | None = None, full_name: str | None = None, dob_yyyy_mm_dd: str | None = None, ssn_last4: str | None = None, secret_answer: str | None = None) -> str:
    """Verify user identity before wires. Provide any of: full_name, dob (YYYY-MM-DD), ssn_last4, secret_answer. Returns JSON with verified flag, needed fields, and optional secret question."""
    res = authenticate_user_wire(session_id, customer_id, full_name, dob_yyyy_mm_dd, ssn_last4, secret_answer)
    return json.dumps(res)


@tool
def get_account_balance_tool(account_id: str) -> str:
    """Get balance, currency, and wire limits for an account. Returns JSON."""
    return json.dumps(get_account_balance(account_id))


@tool
def get_exchange_rate_tool(from_currency: str, to_currency: str, amount: float) -> str:
    """Get exchange rate and converted amount for a given amount. Returns JSON."""
    return json.dumps(get_exchange_rate(from_currency, to_currency, amount))


@tool
def calculate_wire_fee_tool(kind: str, amount: float, from_currency: str, to_currency: str, payer: str) -> str:
    """Calculate wire fee breakdown and who pays (OUR/SHA/BEN). Returns JSON."""
    return json.dumps(calculate_wire_fee(kind, amount, from_currency, to_currency, payer))


@tool
def check_wire_limits_tool(account_id: str, amount: float) -> str:
    """Check sufficient funds and daily wire limit on an account. Returns JSON."""
    return json.dumps(check_wire_limits(account_id, amount))


@tool
def get_cutoff_and_eta_tool(kind: str, country: str) -> str:
    """Get cutoff time and estimated arrival window by type and country. Returns JSON."""
    return json.dumps(get_cutoff_and_eta(kind, country))


@tool
def get_country_requirements_tool(country_code: str) -> str:
    """Get required beneficiary fields for a country. Returns JSON array."""
    return json.dumps(get_country_requirements(country_code))


@tool
def validate_beneficiary_tool(country_code: str, beneficiary_json: str) -> str:
    """Validate beneficiary fields for a given country. Input is JSON dict string; returns {ok, missing}."""
    try:
        beneficiary = json.loads(beneficiary_json)
    except Exception:
        beneficiary = {}
    return json.dumps(validate_beneficiary(country_code, beneficiary))


@tool
def save_beneficiary_tool(customer_id: str, beneficiary_json: str) -> str:
    """Save a beneficiary for future use. Input is JSON dict string; returns {beneficiary_id}."""
    try:
        beneficiary = json.loads(beneficiary_json)
    except Exception:
        beneficiary = {}
    return json.dumps(save_beneficiary(customer_id, beneficiary))


@tool
def quote_wire_tool(kind: str, from_account_id: str, beneficiary_json: str, amount: float, from_currency: str, to_currency: str, payer: str) -> str:
    """Create a wire quote including FX, fees, limits, sanctions, eta; returns JSON with quote_id and totals."""
    try:
        beneficiary = json.loads(beneficiary_json)
    except Exception:
        beneficiary = {}
    return json.dumps(quote_wire(kind, from_account_id, beneficiary, amount, from_currency, to_currency, payer))


@tool
def generate_otp_tool(customer_id: str) -> str:
    """Generate a one-time passcode for wire authorization. Returns masked destination info."""
    return json.dumps(generate_otp(customer_id))


@tool
def verify_otp_tool(customer_id: str, otp: str) -> str:
    """Verify the one-time passcode for wire authorization. Returns {verified}."""
    return json.dumps(verify_otp(customer_id, otp))


@tool
def wire_transfer_domestic(quote_id: str, otp: str) -> str:
    """Execute a domestic wire with a valid quote_id and OTP. Returns confirmation."""
    return json.dumps(wire_transfer_domestic_logic(quote_id, otp))


@tool
def wire_transfer_international(quote_id: str, otp: str) -> str:
    """Execute an international wire with a valid quote_id and OTP. Returns confirmation."""
    return json.dumps(wire_transfer_international_logic(quote_id, otp))