fin-infra 0.1.62__tar.gz → 0.1.63__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. {fin_infra-0.1.62 → fin_infra-0.1.63}/PKG-INFO +1 -1
  2. {fin_infra-0.1.62 → fin_infra-0.1.63}/pyproject.toml +1 -1
  3. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/banking/__init__.py +2 -2
  4. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/categorization/llm_layer.py +2 -2
  5. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/compliance/__init__.py +4 -3
  6. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/credit/add.py +3 -2
  7. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/credit/experian/auth.py +3 -2
  8. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/credit/experian/client.py +2 -2
  9. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/credit/experian/provider.py +2 -2
  10. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/goals/add.py +2 -2
  11. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/goals/management.py +3 -3
  12. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/goals/milestones.py +2 -2
  13. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/investments/providers/plaid.py +2 -2
  14. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/investments/providers/snaptrade.py +2 -2
  15. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/normalization/providers/exchangerate.py +3 -3
  16. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/banking/plaid_client.py +4 -3
  17. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/banking/teller_client.py +6 -6
  18. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/brokerage/alpaca.py +5 -5
  19. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/market/ccxt_crypto.py +5 -3
  20. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/recurring/detectors_llm.py +2 -2
  21. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/recurring/insights.py +2 -2
  22. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/recurring/normalizer.py +2 -1
  23. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/recurring/normalizers.py +2 -2
  24. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/security/encryption.py +2 -2
  25. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/security/pii_patterns.py +1 -1
  26. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/utils/http.py +3 -2
  27. {fin_infra-0.1.62 → fin_infra-0.1.63}/LICENSE +0 -0
  28. {fin_infra-0.1.62 → fin_infra-0.1.63}/README.md +0 -0
  29. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/__init__.py +0 -0
  30. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/__main__.py +0 -0
  31. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/analytics/__init__.py +0 -0
  32. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/analytics/add.py +0 -0
  33. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/analytics/cash_flow.py +0 -0
  34. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/analytics/ease.py +0 -0
  35. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/analytics/models.py +0 -0
  36. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/analytics/portfolio.py +0 -0
  37. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/analytics/projections.py +0 -0
  38. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/analytics/rebalancing.py +0 -0
  39. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/analytics/savings.py +0 -0
  40. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/analytics/scenarios.py +0 -0
  41. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/analytics/spending.py +0 -0
  42. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/banking/history.py +0 -0
  43. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/banking/utils.py +0 -0
  44. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/brokerage/__init__.py +0 -0
  45. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/budgets/__init__.py +0 -0
  46. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/budgets/add.py +0 -0
  47. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/budgets/alerts.py +0 -0
  48. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/budgets/ease.py +0 -0
  49. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/budgets/models.py +0 -0
  50. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/budgets/scaffold_templates/README.md +0 -0
  51. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/budgets/scaffold_templates/__init__.py +0 -0
  52. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/budgets/scaffold_templates/models.py.tmpl +0 -0
  53. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/budgets/scaffold_templates/repository.py.tmpl +0 -0
  54. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/budgets/scaffold_templates/schemas.py.tmpl +0 -0
  55. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/budgets/templates.py +0 -0
  56. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/budgets/tracker.py +0 -0
  57. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/cashflows/__init__.py +0 -0
  58. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/cashflows/core.py +0 -0
  59. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/categorization/__init__.py +0 -0
  60. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/categorization/add.py +0 -0
  61. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/categorization/ease.py +0 -0
  62. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/categorization/engine.py +0 -0
  63. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/categorization/models.py +0 -0
  64. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/categorization/rules.py +0 -0
  65. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/categorization/taxonomy.py +0 -0
  66. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/chat/__init__.py +0 -0
  67. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/chat/ease.py +0 -0
  68. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/chat/planning.py +0 -0
  69. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/cli/__init__.py +0 -0
  70. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/cli/cmds/__init__.py +0 -0
  71. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/cli/cmds/scaffold_cmds.py +0 -0
  72. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/clients/__init__.py +0 -0
  73. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/clients/base.py +0 -0
  74. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/clients/plaid.py +0 -0
  75. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/credit/__init__.py +0 -0
  76. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/credit/experian/__init__.py +0 -0
  77. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/credit/experian/parser.py +0 -0
  78. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/credit/mock.py +0 -0
  79. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/crypto/__init__.py +0 -0
  80. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/crypto/insights.py +0 -0
  81. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/documents/__init__.py +0 -0
  82. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/documents/add.py +0 -0
  83. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/documents/analysis.py +0 -0
  84. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/documents/ease.py +0 -0
  85. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/documents/models.py +0 -0
  86. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/documents/ocr.py +0 -0
  87. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/documents/storage.py +0 -0
  88. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/exceptions.py +0 -0
  89. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/goals/__init__.py +0 -0
  90. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/goals/funding.py +0 -0
  91. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/goals/models.py +0 -0
  92. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/goals/scaffold_templates/README.md +0 -0
  93. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/goals/scaffold_templates/__init__.py +0 -0
  94. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/goals/scaffold_templates/models.py.tmpl +0 -0
  95. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/goals/scaffold_templates/repository.py.tmpl +0 -0
  96. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/goals/scaffold_templates/schemas.py.tmpl +0 -0
  97. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/insights/__init__.py +0 -0
  98. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/insights/aggregator.py +0 -0
  99. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/insights/models.py +0 -0
  100. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/investments/__init__.py +0 -0
  101. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/investments/add.py +0 -0
  102. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/investments/ease.py +0 -0
  103. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/investments/models.py +0 -0
  104. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/investments/providers/__init__.py +0 -0
  105. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/investments/providers/base.py +0 -0
  106. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/investments/scaffold_templates/README.md +0 -0
  107. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/investments/scaffold_templates/__init__.py +0 -0
  108. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/investments/scaffold_templates/models.py.tmpl +0 -0
  109. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/investments/scaffold_templates/repository.py.tmpl +0 -0
  110. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/investments/scaffold_templates/schemas.py.tmpl +0 -0
  111. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/markets/__init__.py +0 -0
  112. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/models/__init__.py +0 -0
  113. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/models/accounts.py +0 -0
  114. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/models/brokerage.py +0 -0
  115. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/models/candle.py +0 -0
  116. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/models/credit.py +0 -0
  117. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/models/money.py +0 -0
  118. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/models/quotes.py +0 -0
  119. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/models/tax.py +0 -0
  120. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/models/transactions.py +0 -0
  121. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/net_worth/__init__.py +0 -0
  122. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/net_worth/add.py +0 -0
  123. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/net_worth/aggregator.py +0 -0
  124. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/net_worth/calculator.py +0 -0
  125. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/net_worth/ease.py +0 -0
  126. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/net_worth/goals.py +0 -0
  127. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/net_worth/insights.py +0 -0
  128. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/net_worth/models.py +0 -0
  129. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/net_worth/scaffold_templates/README.md +0 -0
  130. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/net_worth/scaffold_templates/__init__.py +0 -0
  131. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/net_worth/scaffold_templates/models.py.tmpl +0 -0
  132. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/net_worth/scaffold_templates/repository.py.tmpl +0 -0
  133. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/net_worth/scaffold_templates/schemas.py.tmpl +0 -0
  134. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/normalization/__init__.py +0 -0
  135. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/normalization/currency_converter.py +0 -0
  136. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/normalization/models.py +0 -0
  137. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/normalization/providers/__init__.py +0 -0
  138. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/normalization/providers/static_mappings.py +0 -0
  139. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/normalization/symbol_resolver.py +0 -0
  140. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/obs/__init__.py +0 -0
  141. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/obs/classifier.py +0 -0
  142. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/__init__.py +0 -0
  143. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/banking/base.py +0 -0
  144. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/base.py +0 -0
  145. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/brokerage/base.py +0 -0
  146. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/credit/experian.py +0 -0
  147. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/identity/stripe_identity.py +0 -0
  148. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/market/alphavantage.py +0 -0
  149. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/market/base.py +0 -0
  150. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/market/coingecko.py +0 -0
  151. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/market/yahoo.py +0 -0
  152. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/registry.py +0 -0
  153. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/tax/__init__.py +0 -0
  154. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/tax/irs.py +0 -0
  155. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/tax/mock.py +0 -0
  156. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/providers/tax/taxbit.py +0 -0
  157. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/py.typed +0 -0
  158. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/recurring/__init__.py +0 -0
  159. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/recurring/add.py +0 -0
  160. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/recurring/detector.py +0 -0
  161. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/recurring/ease.py +0 -0
  162. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/recurring/models.py +0 -0
  163. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/recurring/summary.py +0 -0
  164. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/scaffold/__init__.py +0 -0
  165. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/scaffold/budgets.py +0 -0
  166. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/scaffold/goals.py +0 -0
  167. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/security/__init__.py +0 -0
  168. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/security/add.py +0 -0
  169. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/security/audit.py +0 -0
  170. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/security/models.py +0 -0
  171. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/security/pii_filter.py +0 -0
  172. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/security/token_store.py +0 -0
  173. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/settings.py +0 -0
  174. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/tax/__init__.py +0 -0
  175. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/tax/add.py +0 -0
  176. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/tax/tlh.py +0 -0
  177. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/utils/__init__.py +0 -0
  178. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/utils/retry.py +0 -0
  179. {fin_infra-0.1.62 → fin_infra-0.1.63}/src/fin_infra/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: fin-infra
3
- Version: 0.1.62
3
+ Version: 0.1.63
4
4
  Summary: Financial infrastructure toolkit: banking connections, market data, credit, cashflows, and brokerage integrations
5
5
  License: MIT
6
6
  Keywords: finance,banking,plaid,brokerage,markets,credit,tax,cashflow,fintech,infra
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "fin-infra"
3
- version = "0.1.62"
3
+ version = "0.1.63"
4
4
  description = "Financial infrastructure toolkit: banking connections, market data, credit, cashflows, and brokerage integrations"
5
5
  authors = ["Ali Khatami <aliikhatami94@gmail.com>"]
6
6
  license = "MIT"
@@ -45,7 +45,7 @@ from __future__ import annotations
45
45
 
46
46
  import os
47
47
  from datetime import date
48
- from typing import TYPE_CHECKING, Optional
48
+ from typing import TYPE_CHECKING, Optional, cast
49
49
 
50
50
  from pydantic import BaseModel, Field
51
51
 
@@ -199,7 +199,7 @@ def easy_banking(provider: str = "teller", **config) -> BankingProvider:
199
199
  }
200
200
 
201
201
  # Use provider registry to dynamically load and configure provider
202
- return resolve("banking", provider, **config)
202
+ return cast(BankingProvider, resolve("banking", provider, **config))
203
203
 
204
204
 
205
205
  def add_banking(
@@ -15,7 +15,7 @@ Expected performance:
15
15
 
16
16
  import hashlib
17
17
  import logging
18
- from typing import Optional, List, Tuple
18
+ from typing import Optional, List, Tuple, cast
19
19
  from pydantic import BaseModel, Field
20
20
 
21
21
  # ai-infra imports
@@ -245,7 +245,7 @@ class LLMCategorizer:
245
245
  f"Must be one of {len(valid_categories)} valid categories."
246
246
  )
247
247
 
248
- return response
248
+ return cast(CategoryPrediction, response)
249
249
 
250
250
  def _build_system_prompt(self) -> str:
251
251
  """Build system prompt with few-shot examples (reused across all requests)."""
@@ -21,7 +21,7 @@ from __future__ import annotations
21
21
 
22
22
  import logging
23
23
  from datetime import datetime
24
- from typing import Any, Callable, TYPE_CHECKING
24
+ from typing import Any, Callable, TYPE_CHECKING, cast
25
25
 
26
26
  if TYPE_CHECKING:
27
27
  from fastapi import FastAPI, Request, Response
@@ -118,7 +118,8 @@ def add_compliance_tracking(
118
118
 
119
119
  # Track only GET requests (data access)
120
120
  if method != "GET":
121
- return await call_next(request)
121
+ from starlette.responses import Response as StarletteResponse
122
+ return cast("Response", await call_next(request))
122
123
 
123
124
  # Determine if path is a compliance-tracked endpoint
124
125
  event = None
@@ -148,7 +149,7 @@ def add_compliance_tracking(
148
149
  if on_event:
149
150
  on_event(event, context)
150
151
 
151
- return response
152
+ return cast("Response", response)
152
153
 
153
154
  logger.info(
154
155
  "Compliance tracking enabled",
@@ -23,6 +23,7 @@ Example:
23
23
  """
24
24
 
25
25
  import logging
26
+ from typing import cast
26
27
 
27
28
  from fastapi import FastAPI, Depends, HTTPException, status
28
29
 
@@ -175,7 +176,7 @@ def add_credit(
175
176
  # Don't fail request if webhook publishing fails
176
177
  logger.warning(f"Failed to publish credit.score_changed webhook: {e}")
177
178
 
178
- return score
179
+ return cast(CreditScore, score)
179
180
 
180
181
  @router.post("/report", response_model=CreditReport)
181
182
  @credit_resource.cache_read(ttl=cache_ttl, suffix="report")
@@ -219,7 +220,7 @@ def add_credit(
219
220
  detail="Credit bureau service unavailable",
220
221
  )
221
222
 
222
- return report
223
+ return cast(CreditReport, report)
223
224
 
224
225
  # Mount router with dual routes (with/without trailing slash)
225
226
  app.include_router(router, include_in_schema=True)
@@ -24,6 +24,7 @@ Example:
24
24
  """
25
25
 
26
26
  import base64
27
+ from typing import cast
27
28
 
28
29
  import httpx
29
30
  from svc_infra.cache import cache_read
@@ -85,7 +86,7 @@ class ExperianAuthManager:
85
86
  >>> headers = {"Authorization": f"Bearer {token}"}
86
87
  """
87
88
  # Call the cached implementation with client_id for cache key
88
- return await self._get_token_cached(client_id=self.client_id)
89
+ return cast(str, await self._get_token_cached(client_id=self.client_id))
89
90
 
90
91
  @cache_read(
91
92
  key="oauth_token:experian:{client_id}", # Use client_id for uniqueness
@@ -140,7 +141,7 @@ class ExperianAuthManager:
140
141
 
141
142
  # Parse and return token
142
143
  data = response.json()
143
- return data["access_token"]
144
+ return cast(str, data["access_token"])
144
145
 
145
146
  async def invalidate(self) -> None:
146
147
  """Invalidate cached token for THIS client (force refresh on next get_token call).
@@ -14,7 +14,7 @@ Example:
14
14
  >>> data = await client.get_credit_score("user123")
15
15
  """
16
16
 
17
- from typing import Any
17
+ from typing import Any, cast
18
18
 
19
19
  import httpx
20
20
  from tenacity import (
@@ -155,7 +155,7 @@ class ExperianClient:
155
155
  **kwargs,
156
156
  )
157
157
  response.raise_for_status()
158
- return response.json()
158
+ return cast(dict[str, Any], response.json())
159
159
 
160
160
  except httpx.HTTPStatusError as e:
161
161
  # Parse error response
@@ -31,7 +31,7 @@ Example:
31
31
 
32
32
  import logging
33
33
  from datetime import datetime, timezone
34
- from typing import Literal
34
+ from typing import Literal, cast
35
35
 
36
36
  from fin_infra.credit.experian.auth import ExperianAuthManager
37
37
  from fin_infra.credit.experian.client import ExperianClient
@@ -360,4 +360,4 @@ class ExperianProvider(CreditProvider):
360
360
  signature_key=signature_key,
361
361
  )
362
362
 
363
- return data.get("subscriptionId", "unknown")
363
+ return cast(str, data.get("subscriptionId", "unknown"))
@@ -29,7 +29,7 @@ add_goals(app)
29
29
 
30
30
  import logging
31
31
  from datetime import datetime
32
- from typing import List, Optional
32
+ from typing import Any, List, Optional, cast
33
33
 
34
34
  from fastapi import FastAPI, HTTPException, status, Query, Body
35
35
  from pydantic import BaseModel, Field
@@ -469,7 +469,7 @@ def add_goals(
469
469
  # Get all milestones from the goal (check_milestones only returns newly reached ones)
470
470
  goal = get_goal(goal_id)
471
471
  milestones = goal.get("milestones", [])
472
- return milestones
472
+ return cast(list[dict[Any, Any]], milestones)
473
473
  except KeyError:
474
474
  raise HTTPException(
475
475
  status_code=status.HTTP_404_NOT_FOUND, detail=f"Goal {goal_id} not found"
@@ -41,7 +41,7 @@ Example:
41
41
  """
42
42
 
43
43
  from datetime import datetime
44
- from typing import Any
44
+ from typing import Any, cast
45
45
 
46
46
  from pydantic import BaseModel, Field
47
47
 
@@ -839,7 +839,7 @@ def get_goal(goal_id: str) -> dict[str, Any]:
839
839
  if goal_id not in _GOALS_STORE:
840
840
  raise KeyError(f"Goal not found: {goal_id}")
841
841
 
842
- return _GOALS_STORE[goal_id]
842
+ return cast(dict[str, Any], _GOALS_STORE[goal_id])
843
843
 
844
844
 
845
845
  def update_goal(
@@ -885,7 +885,7 @@ def update_goal(
885
885
 
886
886
  Goal(**goal) # Will raise ValidationError if invalid
887
887
 
888
- return goal
888
+ return cast(dict[str, Any], goal)
889
889
 
890
890
 
891
891
  def delete_goal(goal_id: str) -> None:
@@ -26,7 +26,7 @@ Example:
26
26
  """
27
27
 
28
28
  from datetime import datetime
29
- from typing import Any
29
+ from typing import Any, cast
30
30
 
31
31
  from fin_infra.goals.management import get_goal, update_goal
32
32
  from fin_infra.goals.models import Milestone
@@ -229,7 +229,7 @@ def get_next_milestone(goal_id: str) -> dict[str, Any] | None:
229
229
  # Find first unreached milestone (sorted by amount)
230
230
  for milestone in milestones:
231
231
  if not milestone.get("reached", False):
232
- return milestone
232
+ return cast(dict[str, Any], milestone)
233
233
 
234
234
  return None
235
235
 
@@ -10,7 +10,7 @@ from __future__ import annotations
10
10
 
11
11
  from datetime import date
12
12
  from decimal import Decimal
13
- from typing import Any, Dict, List, Optional
13
+ from typing import Any, Dict, List, Optional, cast
14
14
 
15
15
  from plaid.api import plaid_api
16
16
  from plaid.model.investments_holdings_get_request import InvestmentsHoldingsGetRequest
@@ -103,7 +103,7 @@ class PlaidInvestmentProvider(InvestmentProvider):
103
103
  "development": plaid.Environment.Sandbox, # Map development to sandbox
104
104
  "production": plaid.Environment.Production,
105
105
  }
106
- return hosts.get(environment.lower(), plaid.Environment.Sandbox)
106
+ return cast(str, hosts.get(environment.lower(), plaid.Environment.Sandbox))
107
107
 
108
108
  async def get_holdings(
109
109
  self, access_token: str, account_ids: Optional[List[str]] = None
@@ -11,7 +11,7 @@ from __future__ import annotations
11
11
 
12
12
  from datetime import date
13
13
  from decimal import Decimal
14
- from typing import Any, Dict, List, Optional
14
+ from typing import Any, Dict, List, Optional, cast
15
15
 
16
16
  import httpx
17
17
 
@@ -393,7 +393,7 @@ class SnapTradeInvestmentProvider(InvestmentProvider):
393
393
  url = f"{self.base_url}/connections"
394
394
  response = await self.client.get(url, headers=auth_headers)
395
395
  response.raise_for_status()
396
- return await response.json()
396
+ return cast(list[dict[str, Any]], await response.json())
397
397
 
398
398
  except httpx.HTTPStatusError as e:
399
399
  raise self._transform_error(e)
@@ -2,7 +2,7 @@
2
2
 
3
3
  import os
4
4
  from datetime import date as DateType
5
- from typing import Optional
5
+ from typing import Optional, cast
6
6
 
7
7
  import httpx
8
8
 
@@ -66,10 +66,10 @@ class ExchangeRateClient:
66
66
  raise ExchangeRateAPIError(
67
67
  f"API returned error: {data.get('error-type', 'unknown')}"
68
68
  )
69
- return data["conversion_rates"]
69
+ return cast(dict[str, float], data["conversion_rates"])
70
70
  else:
71
71
  # Free tier response format
72
- return data["rates"]
72
+ return cast(dict[str, float], data["rates"])
73
73
 
74
74
  except httpx.HTTPError as e:
75
75
  raise ExchangeRateAPIError(f"HTTP error fetching rates: {e}")
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from datetime import date, datetime, timedelta
4
+ from typing import Any, cast
4
5
 
5
6
  # Plaid SDK v25+ uses new API structure
6
7
  try:
@@ -96,7 +97,7 @@ class PlaidClient(BankingProvider):
96
97
  language="en",
97
98
  )
98
99
  response = self.client.link_token_create(request)
99
- return response["link_token"]
100
+ return cast(str, response["link_token"])
100
101
 
101
102
  def exchange_public_token(self, public_token: str) -> dict:
102
103
  request = ItemPublicTokenExchangeRequest(public_token=public_token)
@@ -146,8 +147,8 @@ class PlaidClient(BankingProvider):
146
147
  # Return all balances
147
148
  return {"balances": [acc.get("balances", {}) for acc in accounts]}
148
149
 
149
- def identity(self, access_token: str) -> dict:
150
+ def identity(self, access_token: str) -> dict[Any, Any]:
150
151
  """Fetch identity/account holder information."""
151
152
  request = IdentityGetRequest(access_token=access_token)
152
153
  response = self.client.identity_get(request)
153
- return response.to_dict()
154
+ return cast(dict[Any, Any], response.to_dict())
@@ -24,7 +24,7 @@ from __future__ import annotations
24
24
 
25
25
  import ssl
26
26
  import httpx
27
- from typing import Any
27
+ from typing import Any, cast
28
28
 
29
29
  from ..base import BankingProvider
30
30
 
@@ -139,7 +139,7 @@ class TellerClient(BankingProvider):
139
139
  "products": ["accounts", "transactions", "balances", "identity"],
140
140
  },
141
141
  )
142
- return response.get("enrollment_id", "")
142
+ return cast(str, response.get("enrollment_id", ""))
143
143
 
144
144
  def exchange_public_token(self, public_token: str) -> dict:
145
145
  """Exchange public token for access token.
@@ -186,7 +186,7 @@ class TellerClient(BankingProvider):
186
186
  auth=(access_token, ""),
187
187
  )
188
188
  response.raise_for_status()
189
- return response.json()
189
+ return cast(list[dict[Any, Any]], response.json())
190
190
 
191
191
  def transactions(
192
192
  self,
@@ -229,7 +229,7 @@ class TellerClient(BankingProvider):
229
229
  params=params,
230
230
  )
231
231
  response.raise_for_status()
232
- return response.json()
232
+ return cast(list[dict[Any, Any]], response.json())
233
233
 
234
234
  def balances(self, access_token: str, account_id: str | None = None) -> dict:
235
235
  """Fetch current balances.
@@ -261,7 +261,7 @@ class TellerClient(BankingProvider):
261
261
  )
262
262
 
263
263
  response.raise_for_status()
264
- return response.json()
264
+ return cast(dict[Any, Any], response.json())
265
265
 
266
266
  def identity(self, access_token: str) -> dict:
267
267
  """Fetch identity/account holder information.
@@ -285,7 +285,7 @@ class TellerClient(BankingProvider):
285
285
  auth=(access_token, ""),
286
286
  )
287
287
  response.raise_for_status()
288
- return response.json()
288
+ return cast(dict[Any, Any], response.json())
289
289
 
290
290
  def __del__(self) -> None:
291
291
  """Close HTTP client on cleanup."""
@@ -7,7 +7,7 @@ mode for development and testing. Live trading requires explicit opt-in.
7
7
  from __future__ import annotations
8
8
 
9
9
  import os
10
- from typing import Literal
10
+ from typing import Any, Literal, cast
11
11
 
12
12
  try:
13
13
  from alpaca_trade_api import REST
@@ -308,14 +308,14 @@ class AlpacaBrokerage(BrokerageProvider):
308
308
  return self._extract_raw(watchlist)
309
309
 
310
310
  @staticmethod
311
- def _extract_raw(obj) -> dict:
311
+ def _extract_raw(obj: Any) -> dict[Any, Any]:
312
312
  """Extract raw dict from Alpaca entity object.
313
313
 
314
314
  Alpaca entities have a _raw attribute with the API response data.
315
315
  """
316
316
  if hasattr(obj, "_raw"):
317
- return obj._raw
317
+ return cast(dict[Any, Any], obj._raw)
318
318
  elif hasattr(obj, "__dict__"):
319
- return obj.__dict__
319
+ return cast(dict[Any, Any], obj.__dict__)
320
320
  else:
321
- return obj
321
+ return cast(dict[Any, Any], obj)
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from typing import Any, cast
4
+
3
5
  import ccxt
4
6
 
5
7
  from ..base import CryptoDataProvider
@@ -15,14 +17,14 @@ class CCXTCryptoData(CryptoDataProvider):
15
17
  # Defer load_markets to first call to avoid network on construction
16
18
  self._markets_loaded = False
17
19
 
18
- def ticker(self, symbol_pair: str) -> dict:
20
+ def ticker(self, symbol_pair: str) -> dict[Any, Any]:
19
21
  if not self._markets_loaded:
20
22
  self.exchange.load_markets()
21
23
  self._markets_loaded = True
22
- return self.exchange.fetch_ticker(symbol_pair)
24
+ return cast(dict[Any, Any], self.exchange.fetch_ticker(symbol_pair))
23
25
 
24
26
  def ohlcv(self, symbol_pair: str, timeframe: str = "1d", limit: int = 100) -> list[list[float]]:
25
27
  if not self._markets_loaded:
26
28
  self.exchange.load_markets()
27
29
  self._markets_loaded = True
28
- return self.exchange.fetch_ohlcv(symbol_pair, timeframe=timeframe, limit=limit)
30
+ return cast(list[list[float]], self.exchange.fetch_ohlcv(symbol_pair, timeframe=timeframe, limit=limit))
@@ -14,7 +14,7 @@ Only called for ambiguous patterns (20-40% variance, ~10% of patterns).
14
14
  from __future__ import annotations
15
15
 
16
16
  import logging
17
- from typing import Any, Optional
17
+ from typing import Any, Optional, cast
18
18
 
19
19
  from pydantic import BaseModel, ConfigDict, Field
20
20
 
@@ -292,7 +292,7 @@ class VariableDetectorLLM:
292
292
 
293
293
  # Extract structured output
294
294
  if hasattr(response, "structured") and response.structured:
295
- return response.structured
295
+ return cast(VariableRecurringPattern, response.structured)
296
296
  else:
297
297
  raise ValueError(f"LLM returned no structured output for '{merchant_name}'")
298
298
 
@@ -15,7 +15,7 @@ from __future__ import annotations
15
15
 
16
16
  import hashlib
17
17
  import logging
18
- from typing import Any, Optional
18
+ from typing import Any, Optional, cast
19
19
 
20
20
  from pydantic import BaseModel, ConfigDict, Field
21
21
 
@@ -383,7 +383,7 @@ class SubscriptionInsightsGenerator:
383
383
 
384
384
  # Extract structured output
385
385
  if hasattr(response, "structured") and response.structured:
386
- return response.structured
386
+ return cast(SubscriptionInsights, response.structured)
387
387
  else:
388
388
  raise ValueError("LLM returned no structured output for insights")
389
389
 
@@ -11,6 +11,7 @@ from __future__ import annotations
11
11
 
12
12
  import re
13
13
  from functools import lru_cache
14
+ from typing import cast
14
15
 
15
16
  try:
16
17
  from rapidfuzz import fuzz, process
@@ -165,7 +166,7 @@ class FuzzyMatcher:
165
166
  norm2 = normalize_merchant(name2)
166
167
 
167
168
  similarity = fuzz.token_sort_ratio(norm1, norm2)
168
- return similarity >= self.similarity_threshold
169
+ return cast(bool, similarity >= self.similarity_threshold)
169
170
 
170
171
  def group_merchants(self, merchants: list[str]) -> dict[str, list[str]]:
171
172
  """
@@ -16,7 +16,7 @@ from __future__ import annotations
16
16
 
17
17
  import hashlib
18
18
  import logging
19
- from typing import Any, Optional
19
+ from typing import Any, Optional, cast
20
20
 
21
21
  from pydantic import BaseModel, ConfigDict, Field
22
22
 
@@ -354,7 +354,7 @@ class MerchantNormalizer:
354
354
 
355
355
  # Extract structured output
356
356
  if hasattr(response, "structured") and response.structured:
357
- return response.structured
357
+ return cast(MerchantNormalized, response.structured)
358
358
  else:
359
359
  raise ValueError(f"LLM returned no structured output for '{merchant_name}'")
360
360
 
@@ -7,7 +7,7 @@ Encrypt/decrypt financial provider API tokens at rest.
7
7
  import base64
8
8
  import json
9
9
  import os
10
- from typing import Any, Dict, Optional
10
+ from typing import Any, Dict, Optional, cast
11
11
 
12
12
  from cryptography.fernet import Fernet, InvalidToken
13
13
 
@@ -144,7 +144,7 @@ class ProviderTokenEncryption:
144
144
  "Token may have been tampered with or used for wrong user/provider."
145
145
  )
146
146
 
147
- return data["token"]
147
+ return cast(str, data["token"])
148
148
 
149
149
  except InvalidToken as e:
150
150
  raise ValueError(
@@ -67,7 +67,7 @@ def luhn_checksum(card_number: str) -> bool:
67
67
  True if valid, False otherwise
68
68
  """
69
69
 
70
- def digits_of(n):
70
+ def digits_of(n: int | str) -> list[int]:
71
71
  return [int(d) for d in str(n)]
72
72
 
73
73
  digits = digits_of(card_number)
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import httpx
4
+ from typing import Any, cast
4
5
  from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
5
6
 
6
7
  _DEFAULT_TIMEOUT = httpx.Timeout(20.0)
@@ -12,8 +13,8 @@ _DEFAULT_TIMEOUT = httpx.Timeout(20.0)
12
13
  retry=retry_if_exception_type(httpx.HTTPError),
13
14
  reraise=True,
14
15
  )
15
- async def aget_json(url: str, **kwargs) -> dict:
16
+ async def aget_json(url: str, **kwargs) -> dict[Any, Any]:
16
17
  async with httpx.AsyncClient(timeout=_DEFAULT_TIMEOUT) as client:
17
18
  r = await client.get(url, **kwargs)
18
19
  r.raise_for_status()
19
- return r.json()
20
+ return cast(dict[Any, Any], r.json())
File without changes
File without changes