mirror of
https://github.com/stripe/ai.git
synced 2026-09-14 18:39:59 +08:00
first commit
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
name: Bug report
|
||||
description: Create a report to help us improve
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: A clear and concise description of what the bug is.
|
||||
placeholder: Tell us what you see!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: repro-steps
|
||||
attributes:
|
||||
label: To Reproduce
|
||||
description: Steps to reproduce the behavior
|
||||
placeholder: |
|
||||
1. Fetch a '...'
|
||||
2. Update the '....'
|
||||
3. See error
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: A clear and concise description of what you expected to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: code-snippets
|
||||
attributes:
|
||||
label: Code snippets
|
||||
description: If applicable, add code snippets to help explain your problem.
|
||||
render: Python
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
id: os
|
||||
attributes:
|
||||
label: OS
|
||||
placeholder: macOS
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: language-version
|
||||
attributes:
|
||||
label: Language version
|
||||
placeholder: Python 3.10.4
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: lib-version
|
||||
attributes:
|
||||
label: Library version
|
||||
placeholder: stripe-python v2.73.0
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: api-version
|
||||
attributes:
|
||||
label: API version
|
||||
description: See [Versioning](https://stripe.com/docs/api/versioning) in the API Reference to find which version you're using
|
||||
placeholder: "2020-08-27"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any other context about the problem here.
|
||||
validations:
|
||||
required: false
|
||||
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Stripe support
|
||||
url: https://support.stripe.com/
|
||||
about: |
|
||||
Please only file issues here that you believe represent actual bugs or feature requests for the Stripe Agent Tools library.
|
||||
|
||||
If you're having general trouble with your Stripe integration, please reach out to support.
|
||||
@@ -0,0 +1,28 @@
|
||||
name: Feature request
|
||||
description: Suggest an idea for this library
|
||||
labels: ["feature-request"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this feature request!
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: Is your feature request related to a problem? Please describe.
|
||||
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: Describe the solution you'd like
|
||||
description: A clear and concise description of what you want to happen.
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Describe alternatives you've considered
|
||||
description: A clear and concise description of any alternative solutions or features you've considered.
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any other context about the feature request here.
|
||||
@@ -0,0 +1,84 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
typescript-build:
|
||||
name: Build - TypeScript
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./typescript
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.11.0
|
||||
|
||||
- name: Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "18"
|
||||
|
||||
- name: Install
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: pnpm run build
|
||||
|
||||
- name: Clean
|
||||
run: pnpm run clean
|
||||
|
||||
- name: Lint
|
||||
run: pnpm run lint
|
||||
|
||||
- name: Prettier
|
||||
run: pnpm run prettier-check
|
||||
|
||||
- name: Test
|
||||
run: pnpm run test
|
||||
|
||||
python-build:
|
||||
name: Build - Python
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./python
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Install
|
||||
run: make venv
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
set -x
|
||||
source venv/bin/activate
|
||||
rm -rf build dist *.egg-info
|
||||
make build
|
||||
python -m twine check dist/*
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
make venv
|
||||
make test
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"EditorConfig.editorconfig", // default
|
||||
"ms-python.python", // intellisense
|
||||
"ms-python.flake8", // linting
|
||||
"charliermarsh.ruff" // formatting
|
||||
]
|
||||
}
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"python.defaultInterpreterPath": "./venv/bin/python",
|
||||
"[python]": {
|
||||
"editor.defaultFormatter": "charliermarsh.ruff",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": "never"
|
||||
}
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"ruff.lint.enable": false
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to make participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all project spaces, and it also applies when
|
||||
an individual is representing the project or its community in public spaces.
|
||||
Examples of representing a project or community include using an official
|
||||
project e-mail address, posting via an official social media account, or acting
|
||||
as an appointed representative at an online or offline event. Representation of
|
||||
a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at conduct@stripe.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
@@ -0,0 +1,23 @@
|
||||
# Contributing
|
||||
|
||||
Contributions of any kind are welcome! If you've found a bug or have a feature request, please feel free to [open an issue](/issues).
|
||||
|
||||
<!-- We will try and respond to your issue or pull request within a week. -->
|
||||
|
||||
To make changes yourself, follow these steps:
|
||||
|
||||
1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository and [clone](https://help.github.com/articles/cloning-a-repository/) it locally.
|
||||
<!-- 1. TODO add install step(s), e.g. "Run `npm install`" -->
|
||||
<!-- 1. TODO add build step(s), e.g. "Build the library using `npm run build`" -->
|
||||
2. Make your changes
|
||||
<!-- 1. TODO add test step(s), e.g. "Test your changes with `npm test`" -->
|
||||
3. Submit a [pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)
|
||||
|
||||
## Contributor License Agreement ([CLA](https://en.wikipedia.org/wiki/Contributor_License_Agreement))
|
||||
|
||||
Once you have submitted a pull request, sign the CLA by clicking on the badge in the comment from [@CLAassistant](https://github.com/CLAassistant).
|
||||
|
||||
<img width="910" alt="image" src="https://user-images.githubusercontent.com/62121649/198740836-70aeb322-5755-49fc-af55-93c8e8a39058.png">
|
||||
|
||||
<br />
|
||||
Thanks for contributing to Stripe! :sparkles:
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2024 Stripe
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,156 @@
|
||||
# Stripe Agent Toolkit
|
||||
|
||||
[](https://github.com/stripe/agent-toolkit/actions/workflows/main.yml)
|
||||
|
||||
The Stripe Agent Toolkit enables popular agent frameworks including LangChain,
|
||||
CrewAI, and Vercel's AI SDK, to integrate with Stripe APIs through function calling. The
|
||||
library is not exhaustive of the entire Stripe API. It includes support for both Python and TypeScript and is built directly on top of the Stripe [Python][python-sdk] and [Node][node-sdk] SDKs.
|
||||
|
||||
Included below are basic instructions, but refer to the [Python](/python) and [TypeScript](/typescript) packages for more information.
|
||||
|
||||
## Python
|
||||
|
||||
### Installation
|
||||
|
||||
You don't need this source code unless you want to modify the package. If you just
|
||||
want to use the package run:
|
||||
|
||||
```sh
|
||||
pip install stripe-agent-toolkit
|
||||
```
|
||||
|
||||
#### Requirements
|
||||
|
||||
- Python 3.11+
|
||||
|
||||
### Usage
|
||||
|
||||
The library needs to be configured with your account's secret key which is
|
||||
available in your [Stripe Dashboard][api-keys].
|
||||
|
||||
```python
|
||||
from stripe_agent_toolkit.crewai.toolkit import StripeAgentToolkit
|
||||
|
||||
stripe_agent_toolkit = StripeAgentToolkit(
|
||||
secret_key="sk_test_...",
|
||||
configuration={
|
||||
"actions": {
|
||||
"payment_links": {
|
||||
"create": True,
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
The toolkit works with LangChain and CrewAI and can be passed as a list of tools. For example:
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
|
||||
stripe_agent = Agent(
|
||||
role="Stripe Agent",
|
||||
goal="Integrate with Stripe",
|
||||
backstory="You are an expert at integrating with Stripe",
|
||||
tools=[*stripe_agent_toolkit.get_tools()]
|
||||
)
|
||||
```
|
||||
|
||||
Examples for LangChain and CrewAI are included in [/examples](/python/examples).
|
||||
|
||||
## TypeScript
|
||||
|
||||
### Installation
|
||||
|
||||
You don't need this source code unless you want to modify the package. If you just
|
||||
want to use the package run:
|
||||
|
||||
```
|
||||
npm install @stripe/agent-toolkit
|
||||
```
|
||||
|
||||
#### Requirements
|
||||
|
||||
- Node 18+
|
||||
|
||||
### Usage
|
||||
|
||||
The library needs to be configured with your account's secret key which is available in your [Stripe Dashboard][api-keys]. Additionally, `configuration` enables you to specify the types of actions that can be taken using the toolkit.
|
||||
|
||||
```typescript
|
||||
import {StripeAgentToolkit} from "@stripe/agent-toolkit/langchain";
|
||||
|
||||
const stripeAgentToolkit = new StripeAgentToolkit({
|
||||
secretKey: process.env.STRIPE_SECRET_KEY!,
|
||||
configuration: {
|
||||
actions: {
|
||||
paymentLinks: {
|
||||
create: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### Tools
|
||||
|
||||
The toolkit works with LangChain and Vercel's AI SDK and can be passed as a list of tools. For example:
|
||||
|
||||
```typescript
|
||||
import {AgentExecutor, createStructuredChatAgent} from 'langchain/agents';
|
||||
|
||||
const tools = stripeAgentToolkit.getTools();
|
||||
|
||||
const agent = await createStructuredChatAgent({
|
||||
llm,
|
||||
tools,
|
||||
prompt,
|
||||
});
|
||||
|
||||
const agentExecutor = new AgentExecutor({
|
||||
agent,
|
||||
tools,
|
||||
});
|
||||
```
|
||||
|
||||
#### Metered billing
|
||||
|
||||
For Vercel's AI SDK, you can use middleware to submit billing events for usage. All that is required is the customer ID and the input/output meters to bill.
|
||||
|
||||
```typescript
|
||||
import {StripeAgentToolkit} from '@stripe/agent-toolkit/ai-sdk';
|
||||
import {openai} from '@ai-sdk/openai';
|
||||
import {
|
||||
generateText,
|
||||
experimental_wrapLanguageModel as wrapLanguageModel,
|
||||
} from 'ai';
|
||||
|
||||
const stripeAgentToolkit = new StripeAgentToolkit({
|
||||
secretKey: process.env.STRIPE_SECRET_KEY!,
|
||||
configuration: {
|
||||
actions: {
|
||||
paymentLinks: {
|
||||
create: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const model = wrapLanguageModel({
|
||||
model: openai('gpt-4o'),
|
||||
middleware: stripeAgentToolkit.middleware({
|
||||
billing: {
|
||||
customer: 'cus_123',
|
||||
meters: {
|
||||
input: 'input_tokens',
|
||||
output: 'output_tokens',
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
[python-sdk]: https://github.com/stripe/stripe-python
|
||||
[node-sdk]: https://github.com/stripe/stripe-node
|
||||
[api-keys]: https://dashboard.stripe.com/account/apikeys
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# Security Policy
|
||||
|
||||
### Reporting a vulnerability
|
||||
|
||||
Please do not open GitHub issues or pull requests - this makes the problem immediately visible to everyone, including malicious actors.
|
||||
|
||||
Security issues in this open-source project can be safely reported to Stripe's [Vulnerability Disclosure and Reward Program](https://stripe.com/docs/security/stripe#disclosure-and-reward-program).
|
||||
Stripe's security team will triage your report and respond according to its impact on Stripe users and systems.
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "stripe-toolkit",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
; https://editorconfig.org/
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
|
||||
[*.{cfg,ini,json,toml,yml}]
|
||||
indent_size = 2
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
@@ -0,0 +1,12 @@
|
||||
[flake8]
|
||||
# E501 is the "Line too long" error. We disable it because we use Black for
|
||||
# code formatting. Black makes a best effort to keep lines under the max
|
||||
# length, but can go over in some cases.
|
||||
# W503 goes against PEP8 rules. It's disabled by default, but must be disabled
|
||||
# explicitly when using `ignore`.
|
||||
# E704 is disabled in the default configuration, but by specifying `ignore`, we wipe that out.
|
||||
# ruff formatting creates code that violates it, so we have to disable it manually
|
||||
ignore = E501, W503, E704
|
||||
per-file-ignores =
|
||||
# setup.py is required for tooling
|
||||
setup.py: IMP102
|
||||
@@ -0,0 +1,19 @@
|
||||
VENV_NAME?=venv
|
||||
PIP?=pip
|
||||
PYTHON?=python3.11
|
||||
DEFAULT_TEST_ENV?=py311
|
||||
|
||||
venv: $(VENV_NAME)/bin/activate
|
||||
|
||||
$(VENV_NAME)/bin/activate: requirements.txt
|
||||
@test -d $(VENV_NAME) || $(PYTHON) -m venv --clear $(VENV_NAME)
|
||||
${VENV_NAME}/bin/python -m pip install -r requirements.txt
|
||||
@touch $(VENV_NAME)/bin/activate
|
||||
|
||||
test: venv
|
||||
${VENV_NAME}/bin/python -m unittest discover tests
|
||||
|
||||
build: venv
|
||||
cp ../LICENSE LICENSE
|
||||
${VENV_NAME}/bin/python -m build
|
||||
rm LICENSE
|
||||
@@ -0,0 +1,64 @@
|
||||
# Stripe Agent Toolkit - Python
|
||||
|
||||
The Stripe Agent Toolkit library enables popular agent frameworks including LangChain and CrewAI to integrate with Stripe APIs through function calling. The
|
||||
library is not exhaustive of the entire Stripe API. It is built directly on top
|
||||
of the [Stripe Python SDK][python-sdk].
|
||||
|
||||
## Installation
|
||||
|
||||
You don't need this source code unless you want to modify the package. If you just
|
||||
want to use the package, just run:
|
||||
|
||||
```sh
|
||||
pip install stripe-agent-toolkit
|
||||
```
|
||||
|
||||
### Requirements
|
||||
|
||||
- Python 3.11+
|
||||
|
||||
## Usage
|
||||
|
||||
The library needs to be configured with your account's secret key which is
|
||||
available in your [Stripe Dashboard][api-keys].
|
||||
|
||||
```python
|
||||
from stripe_agent_toolkit.crewai.toolkit import StripeAgentToolkit
|
||||
|
||||
stripe_agent_toolkit = StripeAgentToolkit(
|
||||
secret_key="sk_test_...",
|
||||
configuration={
|
||||
"actions": {
|
||||
"payment_links": {
|
||||
"create": True,
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
The toolkit works with LangChain and CrewAI and can be passed as a list of tools. For example:
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
|
||||
stripe_agent = Agent(
|
||||
role="Stripe Agent",
|
||||
goal="Integrate with Stripe",
|
||||
backstory="You are an expert at integrating with Stripe",
|
||||
tools=[*stripe_toolkit.get_tools()]
|
||||
)
|
||||
```
|
||||
|
||||
Examples for LangChain and CrewAI are included in `/examples`.
|
||||
|
||||
[python-sdk]: https://github.com/stripe/stripe-python
|
||||
[api-keys]: https://dashboard.stripe.com/account/apikeys
|
||||
|
||||
## Development
|
||||
|
||||
```
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
@@ -0,0 +1,4 @@
|
||||
STRIPE_SECRET_KEY=""
|
||||
OPENAI_API_BASE=""
|
||||
OPENAI_MODEL_NAME="gpt-4o"
|
||||
OPENAI_API_KEY=""
|
||||
@@ -0,0 +1,15 @@
|
||||
# CrewAI Example
|
||||
|
||||
## Setup
|
||||
|
||||
Copy the `.env.template` and populate with your values.
|
||||
|
||||
```
|
||||
cp .env.template .env
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
python main.py
|
||||
```
|
||||
@@ -0,0 +1,63 @@
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from crewai import Agent, Task, Crew
|
||||
from stripe_agent_toolkit.crewai.toolkit import StripeAgentToolkit
|
||||
|
||||
load_dotenv()
|
||||
|
||||
stripe_agent_toolkit = StripeAgentToolkit(
|
||||
secret_key=os.getenv("STRIPE_SECRET_KEY"),
|
||||
configuration={
|
||||
"actions": {
|
||||
"payment_links": {
|
||||
"create": True,
|
||||
},
|
||||
"products": {
|
||||
"create": True,
|
||||
},
|
||||
"prices": {
|
||||
"create": True,
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
stripe_agent = Agent(
|
||||
role="Stripe Agent",
|
||||
goal="Integrate with Stripe effectively to support our business.",
|
||||
backstory="You have been using stripe forever.",
|
||||
tools=[*stripe_agent_toolkit.get_tools()],
|
||||
allow_delegation=False,
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
haiku_writer = Agent(
|
||||
role="Haiku writer",
|
||||
goal="Write a haiku",
|
||||
backstory="You are really good at writing haikus.",
|
||||
allow_delegation=False,
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
create_payment_link = Task(
|
||||
description="Create a payment link for a new product called 'test' "
|
||||
"with a price of $100. The description should be a haiku",
|
||||
expected_output="url",
|
||||
agent=stripe_agent,
|
||||
)
|
||||
|
||||
write_haiku = Task(
|
||||
description="Write a haiku about buy bots.",
|
||||
expected_output="haiku",
|
||||
agent=haiku_writer,
|
||||
)
|
||||
|
||||
crew = Crew(
|
||||
agents=[stripe_agent, haiku_writer],
|
||||
tasks=[create_payment_link, write_haiku],
|
||||
verbose=True,
|
||||
planning=True,
|
||||
)
|
||||
|
||||
crew.kickoff()
|
||||
@@ -0,0 +1,5 @@
|
||||
LANGSMITH_API_KEY=""
|
||||
STRIPE_SECRET_KEY=""
|
||||
OPENAI_API_BASE=""
|
||||
OPENAI_MODEL_NAME="gpt-4o"
|
||||
OPENAI_API_KEY=""
|
||||
@@ -0,0 +1,15 @@
|
||||
# LangChain Example
|
||||
|
||||
## Setup
|
||||
|
||||
Copy the `.env.template` and populate with your values.
|
||||
|
||||
```
|
||||
cp .env.template .env
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
python main.py
|
||||
```
|
||||
@@ -0,0 +1,51 @@
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from langchain import hub
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langchain.agents import AgentExecutor, create_structured_chat_agent
|
||||
|
||||
from stripe_agent_toolkit.langchain.toolkit import StripeAgentToolkit
|
||||
|
||||
load_dotenv()
|
||||
|
||||
llm = ChatOpenAI(
|
||||
model="gpt-4o",
|
||||
)
|
||||
|
||||
stripe_agent_toolkit = StripeAgentToolkit(
|
||||
secret_key=os.getenv("STRIPE_SECRET_KEY"),
|
||||
configuration={
|
||||
"actions": {
|
||||
"payment_links": {
|
||||
"create": True,
|
||||
},
|
||||
"products": {
|
||||
"create": True,
|
||||
},
|
||||
"prices": {
|
||||
"create": True,
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
tools = []
|
||||
tools.extend(stripe_agent_toolkit.get_tools())
|
||||
|
||||
prompt = hub.pull("hwchase17/structured-chat-agent")
|
||||
agent = create_structured_chat_agent(llm, tools, prompt)
|
||||
|
||||
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
|
||||
|
||||
response = agent_executor.invoke(
|
||||
{
|
||||
"input": """
|
||||
Create a payment link for a new product called 'test' with a price
|
||||
of $100. Come up with a funny description about buy bots,
|
||||
maybe a haiku.
|
||||
""",
|
||||
}
|
||||
)
|
||||
|
||||
print(response["output"])
|
||||
@@ -0,0 +1,40 @@
|
||||
[project]
|
||||
name = "stripe-agent-toolkit"
|
||||
version = "0.1.16"
|
||||
description = "Stripe Agent Toolkit"
|
||||
readme = "README.md"
|
||||
license = {file = "LICENSE"}
|
||||
authors = [
|
||||
{name = "Stripe", email = "support@stripe.com"}
|
||||
]
|
||||
keywords = ["stripe", "api", "payments"]
|
||||
|
||||
[project.urls]
|
||||
"Bug Tracker" = "https://github.com/stripe/agent-toolkit/issues"
|
||||
"Source Code" = "https://github.com/stripe/agent-toolkit"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
include = ["stripe_agent_toolkit*"]
|
||||
exclude = ["tests*", "examples*"]
|
||||
|
||||
[tool.ruff]
|
||||
# same as our black config
|
||||
line-length = 79
|
||||
extend-exclude = ["build"]
|
||||
|
||||
[tool.ruff.format]
|
||||
# currently the default value, but opt-out in the future
|
||||
docstring-code-format = false
|
||||
|
||||
[tool.pyright]
|
||||
include = [
|
||||
"*",
|
||||
]
|
||||
exclude = ["build", "**/__pycache__"]
|
||||
reportMissingTypeArgument = true
|
||||
reportUnnecessaryCast = true
|
||||
reportUnnecessaryComparison = true
|
||||
reportUnnecessaryContains = true
|
||||
reportUnnecessaryIsInstance = true
|
||||
reportPrivateImportUsage = true
|
||||
reportUnnecessaryTypeIgnoreComment = true
|
||||
@@ -0,0 +1,12 @@
|
||||
twine
|
||||
crewai==0.76.2
|
||||
crewai-tools===0.13.2
|
||||
flake8
|
||||
langchain==0.3.4
|
||||
langchain-openai==0.2.2
|
||||
mypy==1.7.0
|
||||
pydantic==2.9.2
|
||||
pyright==1.1.350
|
||||
python-dotenv==1.0.1
|
||||
ruff==0.4.4
|
||||
stripe==11.0.0
|
||||
@@ -0,0 +1,60 @@
|
||||
"""Util that calls Stripe."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import stripe
|
||||
from pydantic import BaseModel
|
||||
|
||||
from .functions import (
|
||||
create_customer,
|
||||
list_customers,
|
||||
create_product,
|
||||
list_products,
|
||||
create_price,
|
||||
list_prices,
|
||||
create_payment_link,
|
||||
create_invoice,
|
||||
create_invoice_item,
|
||||
finalize_invoice,
|
||||
retrieve_balance,
|
||||
)
|
||||
|
||||
|
||||
class StripeAPI(BaseModel):
|
||||
""" "Wrapper for Stripe API"""
|
||||
|
||||
def __init__(self, secret_key: str):
|
||||
super().__init__()
|
||||
stripe.api_key = secret_key
|
||||
stripe.set_app_info(
|
||||
"stripe-agent-toolkit-python",
|
||||
version="0.1.16",
|
||||
url="https://github.com/stripe/agent-toolkit",
|
||||
)
|
||||
|
||||
def run(self, method: str, *args, **kwargs) -> str:
|
||||
if method == "create_customer":
|
||||
return json.dumps(create_customer(*args, **kwargs))
|
||||
elif method == "list_customers":
|
||||
return json.dumps(list_customers(*args, **kwargs))
|
||||
elif method == "create_product":
|
||||
return json.dumps(create_product(*args, **kwargs))
|
||||
elif method == "list_products":
|
||||
return json.dumps(list_products(*args, **kwargs))
|
||||
elif method == "create_price":
|
||||
return json.dumps(create_price(*args, **kwargs))
|
||||
elif method == "list_prices":
|
||||
return json.dumps(list_prices(*args, **kwargs))
|
||||
elif method == "create_payment_link":
|
||||
return json.dumps(create_payment_link(*args, **kwargs))
|
||||
elif method == "create_invoice":
|
||||
return json.dumps(create_invoice(*args, **kwargs))
|
||||
elif method == "create_invoice_item":
|
||||
return json.dumps(create_invoice_item(*args, **kwargs))
|
||||
elif method == "finalize_invoice":
|
||||
return json.dumps(finalize_invoice(*args, **kwargs))
|
||||
elif method == "retrieve_balance":
|
||||
return json.dumps(retrieve_balance(*args, **kwargs))
|
||||
else:
|
||||
raise ValueError("Invalid method " + method)
|
||||
@@ -0,0 +1,54 @@
|
||||
from typing import TypedDict, Literal, Optional
|
||||
|
||||
# Define Object type
|
||||
Object = Literal[
|
||||
"customers",
|
||||
"invoices",
|
||||
"invoiceItems",
|
||||
"paymentLinks",
|
||||
"products",
|
||||
"prices",
|
||||
"balance",
|
||||
]
|
||||
|
||||
|
||||
# Define Permission type
|
||||
class Permission(TypedDict, total=False):
|
||||
create: Optional[bool]
|
||||
update: Optional[bool]
|
||||
read: Optional[bool]
|
||||
|
||||
|
||||
# Define BalancePermission type
|
||||
class BalancePermission(TypedDict, total=False):
|
||||
read: Optional[bool]
|
||||
|
||||
|
||||
# Define Actions type
|
||||
class Actions(TypedDict, total=False):
|
||||
customers: Optional[Permission]
|
||||
invoices: Optional[Permission]
|
||||
invoice_items: Optional[Permission]
|
||||
payment_links: Optional[Permission]
|
||||
products: Optional[Permission]
|
||||
prices: Optional[Permission]
|
||||
balance: Optional[BalancePermission]
|
||||
|
||||
|
||||
# Define Configuration type
|
||||
class Configuration(TypedDict, total=False):
|
||||
actions: Optional[Actions]
|
||||
|
||||
|
||||
def is_tool_allowed(tool, configuration):
|
||||
for resource, permissions in tool.get("actions").items():
|
||||
if resource not in configuration.get("actions", {}):
|
||||
return False
|
||||
for permission in permissions:
|
||||
if (
|
||||
not configuration["actions"]
|
||||
.get(resource, {})
|
||||
.get(permission, False)
|
||||
):
|
||||
return False
|
||||
return True
|
||||
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
This tool allows agents to interact with the Stripe API.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Optional, Type
|
||||
from pydantic import BaseModel
|
||||
|
||||
from crewai_tools import BaseTool
|
||||
|
||||
from ..api import StripeAPI
|
||||
|
||||
|
||||
class StripeTool(BaseTool):
|
||||
"""Tool for interacting with the Stripe API."""
|
||||
|
||||
stripe_api: StripeAPI
|
||||
method: str
|
||||
name: str = ""
|
||||
description: str = ""
|
||||
args_schema: Optional[Type[BaseModel]] = None
|
||||
|
||||
def _run(
|
||||
self,
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> str:
|
||||
"""Use the Stripe API to run an operation."""
|
||||
return self.stripe_api.run(self.method, *args, **kwargs)
|
||||
@@ -0,0 +1,37 @@
|
||||
"""Stripe Agent Toolkit."""
|
||||
|
||||
from typing import List
|
||||
from pydantic import PrivateAttr
|
||||
|
||||
from ..api import StripeAPI
|
||||
from ..tools import tools
|
||||
from ..configuration import Configuration, is_tool_allowed
|
||||
from .tool import StripeTool
|
||||
|
||||
|
||||
class StripeAgentToolkit:
|
||||
_tools: List = PrivateAttr(default=[])
|
||||
|
||||
def __init__(self, secret_key: str, configuration: Configuration = None):
|
||||
super().__init__()
|
||||
|
||||
stripe_api = StripeAPI(secret_key=secret_key)
|
||||
|
||||
filtered_tools = [
|
||||
tool for tool in tools if is_tool_allowed(tool, configuration)
|
||||
]
|
||||
|
||||
self._tools = [
|
||||
StripeTool(
|
||||
name=tool["name"],
|
||||
description=tool["description"],
|
||||
method=tool["method"],
|
||||
stripe_api=stripe_api,
|
||||
args_schema=tool.get("args_schema", None),
|
||||
)
|
||||
for tool in filtered_tools
|
||||
]
|
||||
|
||||
def get_tools(self) -> List:
|
||||
"""Get the tools in the toolkit."""
|
||||
return self._tools
|
||||
@@ -0,0 +1,181 @@
|
||||
import stripe
|
||||
|
||||
|
||||
def create_customer(name: str, email: str = None):
|
||||
"""
|
||||
Create a customer.
|
||||
|
||||
Parameters:
|
||||
name (str): The name of the customer.
|
||||
email (str, optional): The email address of the customer.
|
||||
|
||||
Returns:
|
||||
stripe.Customer: The created customer.
|
||||
"""
|
||||
customer = stripe.Customer.create(name=name, email=email)
|
||||
return {"id": customer.id}
|
||||
|
||||
|
||||
def list_customers(email: str = None, limit: int = None):
|
||||
"""
|
||||
List Customers.
|
||||
|
||||
Parameters:
|
||||
email (str, optional): The email address of the customer.
|
||||
limit (int, optional): The number of customers to return.
|
||||
|
||||
Returns:
|
||||
stripe.ListObject: A list of customers.
|
||||
"""
|
||||
customers = stripe.Customer.list(email=email, limit=limit)
|
||||
return [{"id": customer.id} for customer in customers.data]
|
||||
|
||||
|
||||
def create_product(name: str, description: str = None):
|
||||
"""
|
||||
Create a product.
|
||||
|
||||
Parameters:
|
||||
name (str): The name of the product.
|
||||
description (str, optional): The description of the product.
|
||||
|
||||
Returns:
|
||||
stripe.Product: The created product.
|
||||
"""
|
||||
return stripe.Product.create(name=name, description=description)
|
||||
|
||||
|
||||
def list_products(limit: int = None):
|
||||
"""
|
||||
List Products.
|
||||
Parameters:
|
||||
limit (int, optional): The number of products to return.
|
||||
|
||||
Returns:
|
||||
stripe.ListObject: A list of products.
|
||||
"""
|
||||
return stripe.Product.list(limit=limit).data
|
||||
|
||||
|
||||
def create_price(product: str, currency: str, unit_amount: int):
|
||||
"""
|
||||
Create a price.
|
||||
|
||||
Parameters:
|
||||
product (str): The ID of the product.
|
||||
currency (str): The currency of the price.
|
||||
unit_amount (int): The unit amount of the price.
|
||||
|
||||
Returns:
|
||||
stripe.Price: The created price.
|
||||
"""
|
||||
return stripe.Price.create(
|
||||
product=product, currency=currency, unit_amount=unit_amount
|
||||
)
|
||||
|
||||
|
||||
def list_prices(product: str = None, limit: int = None):
|
||||
"""
|
||||
List Prices.
|
||||
|
||||
Parameters:
|
||||
product (str, optional): The ID of the product to list prices for.
|
||||
limit (int, optional): The number of prices to return.
|
||||
|
||||
Returns:
|
||||
stripe.ListObject: A list of prices.
|
||||
"""
|
||||
return stripe.Price.list(product=product, limit=limit).data
|
||||
|
||||
|
||||
def create_payment_link(price: str, quantity: int):
|
||||
"""
|
||||
Create a payment link.
|
||||
|
||||
Parameters:
|
||||
price (str): The ID of the price.
|
||||
quantity (int): The quantity of the product.
|
||||
|
||||
Returns:
|
||||
stripe.PaymentLink: The created payment link.
|
||||
"""
|
||||
payment_link = stripe.PaymentLink.create(
|
||||
line_items=[{"price": price, "quantity": quantity}]
|
||||
)
|
||||
return {"id": payment_link.id, "url": payment_link.url}
|
||||
|
||||
|
||||
def create_invoice(customer: str, days_until_due: int = 30):
|
||||
"""
|
||||
Create an invoice.
|
||||
|
||||
Parameters:
|
||||
customer (str): The ID of the customer.
|
||||
days_until_due (int, optional): The number of days until the
|
||||
invoice is due.
|
||||
|
||||
Returns:
|
||||
stripe.Invoice: The created invoice.
|
||||
"""
|
||||
invoice = stripe.Invoice.create(
|
||||
customer=customer,
|
||||
collection_method="send_invoice",
|
||||
days_until_due=days_until_due,
|
||||
)
|
||||
|
||||
return {
|
||||
"id": invoice.id,
|
||||
"hosted_invoice_url": invoice.hosted_invoice_url,
|
||||
"customer": invoice.customer,
|
||||
"status": invoice.status,
|
||||
}
|
||||
|
||||
|
||||
def create_invoice_item(customer: str, price: str, invoice: str):
|
||||
"""
|
||||
Create an invoice item.
|
||||
|
||||
Parameters:
|
||||
customer (str): The ID of the customer.
|
||||
price (str): The ID of the price.
|
||||
invoice (str): The ID of the invoice.
|
||||
|
||||
Returns:
|
||||
stripe.InvoiceItem: The created invoice item.
|
||||
"""
|
||||
invoice_item = stripe.InvoiceItem.create(
|
||||
customer=customer,
|
||||
price=price,
|
||||
invoice=invoice,
|
||||
)
|
||||
return {"id": invoice_item.id, "invoice": invoice_item.invoice}
|
||||
|
||||
|
||||
def finalize_invoice(invoice: str):
|
||||
"""
|
||||
Finalize an invoice.
|
||||
|
||||
Parameters:
|
||||
invoice (str): The ID of the invoice.
|
||||
|
||||
Returns:
|
||||
stripe.Invoice: The finalized invoice.
|
||||
"""
|
||||
invoice = stripe.Invoice.finalize_invoice(invoice=invoice)
|
||||
|
||||
return {
|
||||
"id": invoice.id,
|
||||
"hosted_invoice_url": invoice.hosted_invoice_url,
|
||||
"customer": invoice.customer,
|
||||
"status": invoice.status,
|
||||
}
|
||||
|
||||
|
||||
def retrieve_balance():
|
||||
"""
|
||||
Retrieve the balance.
|
||||
|
||||
Returns:
|
||||
stripe.Balance: The balance.
|
||||
"""
|
||||
return stripe.Balance.retrieve()
|
||||
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
This tool allows agents to interact with the Stripe API.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Optional, Type
|
||||
from pydantic import BaseModel
|
||||
|
||||
from langchain.tools import BaseTool
|
||||
|
||||
from ..api import StripeAPI
|
||||
|
||||
|
||||
class StripeTool(BaseTool):
|
||||
"""Tool for interacting with the Stripe API."""
|
||||
|
||||
stripe_api: StripeAPI
|
||||
method: str
|
||||
name: str = ""
|
||||
description: str = ""
|
||||
args_schema: Optional[Type[BaseModel]] = None
|
||||
|
||||
def _run(
|
||||
self,
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> str:
|
||||
"""Use the Stripe API to run an operation."""
|
||||
return self.stripe_api.run(self.method, *args, **kwargs)
|
||||
@@ -0,0 +1,37 @@
|
||||
"""Stripe Agent Toolkit."""
|
||||
|
||||
from typing import List
|
||||
from pydantic import PrivateAttr
|
||||
|
||||
from ..api import StripeAPI
|
||||
from ..tools import tools
|
||||
from ..configuration import Configuration, is_tool_allowed
|
||||
from .tool import StripeTool
|
||||
|
||||
|
||||
class StripeAgentToolkit:
|
||||
_tools: List = PrivateAttr(default=[])
|
||||
|
||||
def __init__(self, secret_key: str, configuration: Configuration = None):
|
||||
super().__init__()
|
||||
|
||||
stripe_api = StripeAPI(secret_key=secret_key)
|
||||
|
||||
filtered_tools = [
|
||||
tool for tool in tools if is_tool_allowed(tool, configuration)
|
||||
]
|
||||
|
||||
self._tools = [
|
||||
StripeTool(
|
||||
name=tool["name"],
|
||||
description=tool["description"],
|
||||
method=tool["method"],
|
||||
stripe_api=stripe_api,
|
||||
args_schema=tool.get("args_schema", None),
|
||||
)
|
||||
for tool in filtered_tools
|
||||
]
|
||||
|
||||
def get_tools(self) -> List:
|
||||
"""Get the tools in the toolkit."""
|
||||
return self._tools
|
||||
@@ -0,0 +1,80 @@
|
||||
CREATE_CUSTOMER_PROMPT = """
|
||||
This tool will create a customer in Stripe.
|
||||
|
||||
It takes two arguments:
|
||||
- name (str): The name of the customer.
|
||||
- email (str, optional): The email of the customer.
|
||||
"""
|
||||
|
||||
LIST_CUSTOMERS_PROMPT = """
|
||||
This tool will fetch a list of Customers from Stripe.
|
||||
|
||||
It takes no input.
|
||||
"""
|
||||
|
||||
CREATE_PRODUCT_PROMPT = """
|
||||
This tool will create a product in Stripe.
|
||||
|
||||
It takes two arguments:
|
||||
- name (str): The name of the product.
|
||||
- description (str, optional): The description of the product.
|
||||
"""
|
||||
|
||||
LIST_PRODUCTS_PROMPT = """
|
||||
This tool will fetch a list of Products from Stripe.
|
||||
|
||||
It takes one optional argument:
|
||||
- limit (int, optional): The number of products to return.
|
||||
"""
|
||||
|
||||
CREATE_PRICE_PROMPT = """
|
||||
This tool will create a price in Stripe. If a product has not already been
|
||||
specified, a product should be created first.
|
||||
|
||||
It takes three arguments:
|
||||
- product (str): The ID of the product to create the price for.
|
||||
- unit_amount (int): The unit amount of the price in cents.
|
||||
- currency (str): The currency of the price.
|
||||
"""
|
||||
|
||||
LIST_PRICES_PROMPT = """
|
||||
This tool will fetch a list of Prices from Stripe.
|
||||
|
||||
It takes two arguments.
|
||||
- product (str, optional): The ID of the product to list prices for.
|
||||
- limit (int, optional): The number of prices to return.
|
||||
"""
|
||||
|
||||
CREATE_PAYMENT_LINK_PROMPT = """
|
||||
This tool will create a payment link in Stripe.
|
||||
|
||||
It takes two arguments:
|
||||
- price (str): The ID of the price to create the payment link for.
|
||||
- quantity (int): The quantity of the product to include in the payment link.
|
||||
"""
|
||||
|
||||
CREATE_INVOICE_PROMPT = """
|
||||
This tool will create an invoice in Stripe.
|
||||
|
||||
It takes one argument:
|
||||
- customer (str): The ID of the customer to create the invoice for.
|
||||
"""
|
||||
|
||||
CREATE_INVOICE_ITEM_PROMPT = """
|
||||
This tool will create an invoice item in Stripe.
|
||||
|
||||
It takes two arguments:
|
||||
- customer (str): The ID of the customer to create the invoice item for.
|
||||
- price (str): The ID of the price to create the invoice item for.
|
||||
"""
|
||||
|
||||
FINALIZE_INVOICE_PROMPT = """
|
||||
This tool will finalize an invoice in Stripe.
|
||||
|
||||
It takes one argument:
|
||||
- invoice (str): The ID of the invoice to finalize.
|
||||
"""
|
||||
|
||||
RETRIEVE_BALANCE_PROMPT = """
|
||||
This tool will retrieve the balance from Stripe. It takes no input.
|
||||
"""
|
||||
@@ -0,0 +1,151 @@
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class CreateCustomer(BaseModel):
|
||||
"""Schema for the ``create_customer`` operation."""
|
||||
|
||||
name: str = Field(
|
||||
...,
|
||||
description="The name of the customer.",
|
||||
)
|
||||
|
||||
email: Optional[str] = Field(
|
||||
None,
|
||||
description="The email of the customer.",
|
||||
)
|
||||
|
||||
|
||||
class ListCustomers(BaseModel):
|
||||
"""Schema for the ``list_customers`` operation."""
|
||||
|
||||
limit: Optional[int] = Field(
|
||||
None,
|
||||
description=(
|
||||
"A limit on the number of objects to be returned."
|
||||
" Limit can range between 1 and 100, and the default is 10."
|
||||
),
|
||||
)
|
||||
|
||||
email: Optional[str] = Field(
|
||||
None,
|
||||
description=(
|
||||
"A case-sensitive filter on the list based on"
|
||||
" the customer's email field. The value must be a string."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class CreateProduct(BaseModel):
|
||||
"""Schema for the ``create_product`` operation."""
|
||||
|
||||
name: str = Field(
|
||||
...,
|
||||
description="The name of the product.",
|
||||
)
|
||||
description: Optional[str] = Field(
|
||||
None,
|
||||
description="The description of the product.",
|
||||
)
|
||||
|
||||
|
||||
class ListProducts(BaseModel):
|
||||
"""Schema for the ``list_products`` operation."""
|
||||
|
||||
limit: Optional[int] = Field(
|
||||
None,
|
||||
description=(
|
||||
"A limit on the number of objects to be returned."
|
||||
" Limit can range between 1 and 100, and the default is 10."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class CreatePrice(BaseModel):
|
||||
"""Schema for the ``create_price`` operation."""
|
||||
|
||||
product: str = Field(
|
||||
..., description="The ID of the product to create the price for."
|
||||
)
|
||||
unit_amount: int = Field(
|
||||
...,
|
||||
description="The unit amount of the price in cents.",
|
||||
)
|
||||
currency: str = Field(
|
||||
...,
|
||||
description="The currency of the price.",
|
||||
)
|
||||
|
||||
|
||||
class ListPrices(BaseModel):
|
||||
"""Schema for the ``list_prices`` operation."""
|
||||
|
||||
product: Optional[str] = Field(
|
||||
None,
|
||||
description="The ID of the product to list prices for.",
|
||||
)
|
||||
limit: Optional[int] = Field(
|
||||
None,
|
||||
description=(
|
||||
"A limit on the number of objects to be returned."
|
||||
" Limit can range between 1 and 100, and the default is 10."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class CreatePaymentLink(BaseModel):
|
||||
"""Schema for the ``create_payment_link`` operation."""
|
||||
|
||||
price: str = Field(
|
||||
...,
|
||||
description="The ID of the price to create the payment link for.",
|
||||
)
|
||||
quantity: int = Field(
|
||||
...,
|
||||
description="The quantity of the product to include.",
|
||||
)
|
||||
|
||||
|
||||
class CreateInvoice(BaseModel):
|
||||
"""Schema for the ``create_invoice`` operation."""
|
||||
|
||||
customer: str = Field(
|
||||
..., description="The ID of the customer to create the invoice for."
|
||||
)
|
||||
|
||||
days_until_due: Optional[int] = Field(
|
||||
None,
|
||||
description="The number of days until the invoice is due.",
|
||||
)
|
||||
|
||||
|
||||
class CreateInvoiceItem(BaseModel):
|
||||
"""Schema for the ``create_invoice_item`` operation."""
|
||||
|
||||
customer: str = Field(
|
||||
...,
|
||||
description="The ID of the customer to create the invoice item for.",
|
||||
)
|
||||
price: str = Field(
|
||||
...,
|
||||
description="The ID of the price for the item.",
|
||||
)
|
||||
invoice: str = Field(
|
||||
...,
|
||||
description="The ID of the invoice to create the item for.",
|
||||
)
|
||||
|
||||
|
||||
class FinalizeInvoice(BaseModel):
|
||||
"""Schema for the ``finalize_invoice`` operation."""
|
||||
|
||||
invoice: str = Field(
|
||||
...,
|
||||
description="The ID of the invoice to finalize.",
|
||||
)
|
||||
|
||||
|
||||
class RetrieveBalance(BaseModel):
|
||||
"""Schema for the ``retrieve_balance`` operation."""
|
||||
|
||||
pass
|
||||
@@ -0,0 +1,153 @@
|
||||
from typing import Dict, List
|
||||
|
||||
from .prompts import (
|
||||
CREATE_CUSTOMER_PROMPT,
|
||||
LIST_CUSTOMERS_PROMPT,
|
||||
CREATE_PRODUCT_PROMPT,
|
||||
LIST_PRODUCTS_PROMPT,
|
||||
CREATE_PRICE_PROMPT,
|
||||
LIST_PRICES_PROMPT,
|
||||
CREATE_PAYMENT_LINK_PROMPT,
|
||||
CREATE_INVOICE_PROMPT,
|
||||
CREATE_INVOICE_ITEM_PROMPT,
|
||||
FINALIZE_INVOICE_PROMPT,
|
||||
RETRIEVE_BALANCE_PROMPT,
|
||||
)
|
||||
|
||||
from .schema import (
|
||||
CreateCustomer,
|
||||
ListCustomers,
|
||||
CreateProduct,
|
||||
ListProducts,
|
||||
CreatePrice,
|
||||
ListPrices,
|
||||
CreatePaymentLink,
|
||||
CreateInvoice,
|
||||
CreateInvoiceItem,
|
||||
FinalizeInvoice,
|
||||
RetrieveBalance,
|
||||
)
|
||||
|
||||
tools: List[Dict] = [
|
||||
{
|
||||
"method": "create_customer",
|
||||
"name": "Create Customer",
|
||||
"description": CREATE_CUSTOMER_PROMPT,
|
||||
"args_schema": CreateCustomer,
|
||||
"actions": {
|
||||
"customers": {
|
||||
"create": True,
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "list_customers",
|
||||
"name": "List Customers",
|
||||
"description": LIST_CUSTOMERS_PROMPT,
|
||||
"args_schema": ListCustomers,
|
||||
"actions": {
|
||||
"customers": {
|
||||
"read": True,
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "create_product",
|
||||
"name": "Create Product",
|
||||
"description": CREATE_PRODUCT_PROMPT,
|
||||
"args_schema": CreateProduct,
|
||||
"actions": {
|
||||
"products": {
|
||||
"create": True,
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "list_products",
|
||||
"name": "List Products",
|
||||
"description": LIST_PRODUCTS_PROMPT,
|
||||
"args_schema": ListProducts,
|
||||
"actions": {
|
||||
"products": {
|
||||
"read": True,
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "create_price",
|
||||
"name": "Create Price",
|
||||
"description": CREATE_PRICE_PROMPT,
|
||||
"args_schema": CreatePrice,
|
||||
"actions": {
|
||||
"prices": {
|
||||
"create": True,
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "list_prices",
|
||||
"name": "List Prices",
|
||||
"description": LIST_PRICES_PROMPT,
|
||||
"args_schema": ListPrices,
|
||||
"actions": {
|
||||
"prices": {
|
||||
"read": True,
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "create_payment_link",
|
||||
"name": "Create Payment Link",
|
||||
"description": CREATE_PAYMENT_LINK_PROMPT,
|
||||
"args_schema": CreatePaymentLink,
|
||||
"actions": {
|
||||
"payment_links": {
|
||||
"create": True,
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "create_invoice",
|
||||
"name": "Create Invoice",
|
||||
"description": CREATE_INVOICE_PROMPT,
|
||||
"args_schema": CreateInvoice,
|
||||
"actions": {
|
||||
"invoices": {
|
||||
"create": True,
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "create_invoice_item",
|
||||
"name": "Create Invoice Item",
|
||||
"description": CREATE_INVOICE_ITEM_PROMPT,
|
||||
"args_schema": CreateInvoiceItem,
|
||||
"actions": {
|
||||
"invoice_items": {
|
||||
"create": True,
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "finalize_invoice",
|
||||
"name": "Finalize Invoice",
|
||||
"description": FINALIZE_INVOICE_PROMPT,
|
||||
"args_schema": FinalizeInvoice,
|
||||
"actions": {
|
||||
"invoices": {
|
||||
"update": True,
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "retrieve_balance",
|
||||
"name": "Retrieve Balance",
|
||||
"description": RETRIEVE_BALANCE_PROMPT,
|
||||
"args_schema": RetrieveBalance,
|
||||
"actions": {
|
||||
"balance": {
|
||||
"read": True,
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,58 @@
|
||||
import unittest
|
||||
from stripe_agent_toolkit.configuration import is_tool_allowed
|
||||
|
||||
|
||||
class TestConfigurations(unittest.TestCase):
|
||||
def test_allowed(self):
|
||||
tool = {
|
||||
"actions": {
|
||||
"customers": {"create": True, "read": True},
|
||||
"invoices": {"create": True, "read": True},
|
||||
}
|
||||
}
|
||||
|
||||
configuration = {
|
||||
"actions": {
|
||||
"customers": {"create": True, "read": True},
|
||||
"invoices": {"create": True, "read": True},
|
||||
}
|
||||
}
|
||||
|
||||
self.assertTrue(is_tool_allowed(tool, configuration))
|
||||
|
||||
def test_partial_allowed(self):
|
||||
tool = {
|
||||
"actions": {
|
||||
"customers": {"create": True, "read": True},
|
||||
"invoices": {"create": True, "read": True},
|
||||
}
|
||||
}
|
||||
|
||||
configuration = {
|
||||
"actions": {
|
||||
"customers": {"create": True, "read": True},
|
||||
"invoices": {"create": True, "read": False},
|
||||
}
|
||||
}
|
||||
|
||||
self.assertFalse(is_tool_allowed(tool, configuration))
|
||||
|
||||
def test_not_allowed(self):
|
||||
tool = {
|
||||
"actions": {
|
||||
"payment_links": {"create": True},
|
||||
}
|
||||
}
|
||||
|
||||
configuration = {
|
||||
"actions": {
|
||||
"customers": {"create": True, "read": True},
|
||||
"invoices": {"create": True, "read": True},
|
||||
}
|
||||
}
|
||||
|
||||
self.assertFalse(is_tool_allowed(tool, configuration))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,256 @@
|
||||
import unittest
|
||||
import stripe
|
||||
from unittest import mock
|
||||
from stripe_agent_toolkit.functions import (
|
||||
create_customer,
|
||||
list_customers,
|
||||
create_product,
|
||||
list_products,
|
||||
create_price,
|
||||
list_prices,
|
||||
create_payment_link,
|
||||
create_invoice,
|
||||
create_invoice_item,
|
||||
finalize_invoice,
|
||||
retrieve_balance,
|
||||
)
|
||||
|
||||
|
||||
class TestStripeFunctions(unittest.TestCase):
|
||||
def test_create_customer(self):
|
||||
with mock.patch("stripe.Customer.create") as mock_function:
|
||||
mock_customer = {"id": "cus_123"}
|
||||
mock_function.return_value = stripe.Customer.construct_from(
|
||||
mock_customer, "sk_test_123"
|
||||
)
|
||||
|
||||
result = create_customer(
|
||||
name="Test User", email="test@example.com"
|
||||
)
|
||||
|
||||
self.assertEqual(result, {"id": mock_customer["id"]})
|
||||
|
||||
def test_list_customers(self):
|
||||
with mock.patch("stripe.Customer.list") as mock_function:
|
||||
mock_customers = [{"id": "cus_123"}, {"id": "cus_456"}]
|
||||
|
||||
mock_function.return_value = stripe.ListObject.construct_from(
|
||||
{
|
||||
"object": "list",
|
||||
"data": [
|
||||
stripe.Customer.construct_from(
|
||||
{
|
||||
"id": "cus_123",
|
||||
"email": "customer1@example.com",
|
||||
"name": "Customer One",
|
||||
},
|
||||
"sk_test_123",
|
||||
),
|
||||
stripe.Customer.construct_from(
|
||||
{
|
||||
"id": "cus_456",
|
||||
"email": "customer2@example.com",
|
||||
"name": "Customer Two",
|
||||
},
|
||||
"sk_test_123",
|
||||
),
|
||||
],
|
||||
"has_more": False,
|
||||
"url": "/v1/customers",
|
||||
},
|
||||
"sk_test_123",
|
||||
)
|
||||
|
||||
result = list_customers()
|
||||
self.assertEqual(result, mock_customers)
|
||||
|
||||
def test_create_product(self):
|
||||
with mock.patch("stripe.Product.create") as mock_function:
|
||||
mock_product = {"id": "prod_123"}
|
||||
mock_function.return_value = stripe.Product.construct_from(
|
||||
mock_product, "sk_test_123"
|
||||
)
|
||||
|
||||
result = create_product(name="Test Product")
|
||||
|
||||
self.assertEqual(result, {"id": mock_product["id"]})
|
||||
|
||||
def test_list_products(self):
|
||||
with mock.patch("stripe.Product.list") as mock_function:
|
||||
mock_products = [
|
||||
{"id": "prod_123", "name": "Product One"},
|
||||
{"id": "prod_456", "name": "Product Two"},
|
||||
]
|
||||
|
||||
mock_function.return_value = stripe.ListObject.construct_from(
|
||||
{
|
||||
"object": "list",
|
||||
"data": [
|
||||
stripe.Product.construct_from(
|
||||
{
|
||||
"id": "prod_123",
|
||||
"name": "Product One",
|
||||
},
|
||||
"sk_test_123",
|
||||
),
|
||||
stripe.Product.construct_from(
|
||||
{
|
||||
"id": "prod_456",
|
||||
"name": "Product Two",
|
||||
},
|
||||
"sk_test_123",
|
||||
),
|
||||
],
|
||||
"has_more": False,
|
||||
"url": "/v1/products",
|
||||
},
|
||||
"sk_test_123",
|
||||
)
|
||||
|
||||
result = list_products()
|
||||
self.assertEqual(result, mock_products)
|
||||
|
||||
def test_create_price(self):
|
||||
with mock.patch("stripe.Price.create") as mock_function:
|
||||
mock_price = {"id": "price_123"}
|
||||
mock_function.return_value = stripe.Price.construct_from(
|
||||
mock_price, "sk_test_123"
|
||||
)
|
||||
|
||||
result = create_price(
|
||||
product="prod_123", currency="usd", unit_amount=1000
|
||||
)
|
||||
|
||||
self.assertEqual(result, {"id": mock_price["id"]})
|
||||
|
||||
def test_list_prices(self):
|
||||
with mock.patch("stripe.Price.list") as mock_function:
|
||||
mock_prices = [
|
||||
{"id": "price_123", "product": "prod_123"},
|
||||
{"id": "price_456", "product": "prod_456"},
|
||||
]
|
||||
|
||||
mock_function.return_value = stripe.ListObject.construct_from(
|
||||
{
|
||||
"object": "list",
|
||||
"data": [
|
||||
stripe.Price.construct_from(
|
||||
{
|
||||
"id": "price_123",
|
||||
"product": "prod_123",
|
||||
},
|
||||
"sk_test_123",
|
||||
),
|
||||
stripe.Price.construct_from(
|
||||
{
|
||||
"id": "price_456",
|
||||
"product": "prod_456",
|
||||
},
|
||||
"sk_test_123",
|
||||
),
|
||||
],
|
||||
"has_more": False,
|
||||
"url": "/v1/prices",
|
||||
},
|
||||
"sk_test_123",
|
||||
)
|
||||
|
||||
result = list_prices()
|
||||
|
||||
self.assertEqual(result, mock_prices)
|
||||
|
||||
def test_create_payment_link(self):
|
||||
with mock.patch("stripe.PaymentLink.create") as mock_function:
|
||||
mock_payment_link = {"id": "pl_123", "url": "https://example.com"}
|
||||
mock_function.return_value = stripe.PaymentLink.construct_from(
|
||||
mock_payment_link, "sk_test_123"
|
||||
)
|
||||
|
||||
result = create_payment_link(price="price_123", quantity=1)
|
||||
|
||||
self.assertEqual(result, mock_payment_link)
|
||||
|
||||
def test_create_invoice(self):
|
||||
with mock.patch("stripe.Invoice.create") as mock_function:
|
||||
mock_invoice = {
|
||||
"id": "in_123",
|
||||
"hosted_invoice_url": "https://example.com",
|
||||
"customer": "cus_123",
|
||||
"status": "open",
|
||||
}
|
||||
|
||||
mock_function.return_value = stripe.Invoice.construct_from(
|
||||
mock_invoice, "sk_test_123"
|
||||
)
|
||||
|
||||
result = create_invoice(customer="cus_123")
|
||||
|
||||
self.assertEqual(
|
||||
result,
|
||||
{
|
||||
"id": mock_invoice["id"],
|
||||
"hosted_invoice_url": mock_invoice["hosted_invoice_url"],
|
||||
"customer": mock_invoice["customer"],
|
||||
"status": mock_invoice["status"],
|
||||
},
|
||||
)
|
||||
|
||||
def test_create_invoice_item(self):
|
||||
with mock.patch("stripe.InvoiceItem.create") as mock_function:
|
||||
mock_invoice_item = {"id": "ii_123", "invoice": "in_123"}
|
||||
mock_function.return_value = stripe.InvoiceItem.construct_from(
|
||||
mock_invoice_item, "sk_test_123"
|
||||
)
|
||||
|
||||
result = create_invoice_item(
|
||||
customer="cus_123", price="price_123", invoice="in_123"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
result,
|
||||
{
|
||||
"id": mock_invoice_item["id"],
|
||||
"invoice": mock_invoice_item["invoice"],
|
||||
},
|
||||
)
|
||||
|
||||
def test_finalize_invoice(self):
|
||||
with mock.patch("stripe.Invoice.finalize_invoice") as mock_function:
|
||||
mock_invoice = {
|
||||
"id": "in_123",
|
||||
"hosted_invoice_url": "https://example.com",
|
||||
"customer": "cus_123",
|
||||
"status": "open",
|
||||
}
|
||||
|
||||
mock_function.return_value = stripe.Invoice.construct_from(
|
||||
mock_invoice, "sk_test_123"
|
||||
)
|
||||
|
||||
result = finalize_invoice(invoice="in_123")
|
||||
|
||||
self.assertEqual(
|
||||
result,
|
||||
{
|
||||
"id": mock_invoice["id"],
|
||||
"hosted_invoice_url": mock_invoice["hosted_invoice_url"],
|
||||
"customer": mock_invoice["customer"],
|
||||
"status": mock_invoice["status"],
|
||||
},
|
||||
)
|
||||
|
||||
def test_retrieve_balance(self):
|
||||
with mock.patch("stripe.Balance.retrieve") as mock_function:
|
||||
mock_balance = {"available": [{"amount": 1000, "currency": "usd"}]}
|
||||
|
||||
mock_function.return_value = stripe.Balance.construct_from(
|
||||
mock_balance, "sk_test_123"
|
||||
)
|
||||
|
||||
result = retrieve_balance()
|
||||
|
||||
self.assertEqual(result, mock_balance)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,132 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
.turbo
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"bracketSpacing": false
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
# Stripe Agent Toolkit - TypeScript
|
||||
|
||||
The Stripe Agent Toolkit enables popular agent frameworks including LangChain and Vercel's AI SDK to integrate with Stripe APIs through function calling. It also provides tooling to quickly integrate metered billing for prompt and completion token usage.
|
||||
|
||||
## Installation
|
||||
|
||||
You don't need this source code unless you want to modify the package. If you just
|
||||
want to use the package run:
|
||||
|
||||
```
|
||||
npm install @stripe/agent-toolkit
|
||||
```
|
||||
|
||||
### Requirements
|
||||
|
||||
- Node 18+
|
||||
|
||||
## Usage
|
||||
|
||||
The library needs to be configured with your account's secret key which is available in your [Stripe Dashboard][api-keys]. Additionally, `configuration` enables you to specify the types of actions that can be taken using the toolkit.
|
||||
|
||||
```typescript
|
||||
import {StripeAgentToolkit} from '@stripe/agent-toolkit/langchain';
|
||||
|
||||
const stripeAgentToolkit = new StripeAgentToolkit({
|
||||
secretKey: process.env.STRIPE_SECRET_KEY!,
|
||||
configuration: {
|
||||
actions: {
|
||||
paymentLinks: {
|
||||
create: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Tools
|
||||
|
||||
The toolkit works with LangChain and Vercel's AI SDK and can be passed as a list of tools. For example:
|
||||
|
||||
```typescript
|
||||
import {AgentExecutor, createStructuredChatAgent} from 'langchain/agents';
|
||||
|
||||
const tools = stripeAgentToolkit.getTools();
|
||||
|
||||
const agent = await createStructuredChatAgent({
|
||||
llm,
|
||||
tools,
|
||||
prompt,
|
||||
});
|
||||
|
||||
const agentExecutor = new AgentExecutor({
|
||||
agent,
|
||||
tools,
|
||||
});
|
||||
```
|
||||
|
||||
### Metered billing
|
||||
|
||||
For Vercel's AI SDK, you can use middleware to submit billing events for usage. All that is required is the customer ID and the input/output meters to bill.
|
||||
|
||||
```typescript
|
||||
import {StripeAgentToolkit} from '@stripe/agent-toolkit/ai-sdk';
|
||||
import {openai} from '@ai-sdk/openai';
|
||||
import {
|
||||
generateText,
|
||||
experimental_wrapLanguageModel as wrapLanguageModel,
|
||||
} from 'ai';
|
||||
|
||||
const stripeAgentToolkit = new StripeAgentToolkit({
|
||||
secretKey: process.env.STRIPE_SECRET_KEY!,
|
||||
configuration: {
|
||||
actions: {
|
||||
paymentLinks: {
|
||||
create: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const model = wrapLanguageModel({
|
||||
model: openai('gpt-4o'),
|
||||
middleware: stripeAgentToolkit.middleware({
|
||||
billing: {
|
||||
customer: 'cus_123',
|
||||
meters: {
|
||||
input: 'input_tokens',
|
||||
output: 'output_tokens',
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
This works with both `generateText` and `generateStream` from the Vercel AI SDK.
|
||||
|
||||
[node-sdk]: https://github.com/stripe/stripe-node
|
||||
[api-keys]: https://dashboard.stripe.com/account/apikeys
|
||||
@@ -0,0 +1,311 @@
|
||||
import prettier from "eslint-plugin-prettier";
|
||||
import _import from "eslint-plugin-import";
|
||||
import { fixupPluginRules } from "@eslint/compat";
|
||||
import globals from "globals";
|
||||
import typescriptEslint from "@typescript-eslint/eslint-plugin";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import js from "@eslint/js";
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all
|
||||
});
|
||||
|
||||
export default [...compat.extends("plugin:prettier/recommended"), {
|
||||
plugins: {
|
||||
prettier,
|
||||
import: fixupPluginRules(_import),
|
||||
},
|
||||
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node,
|
||||
},
|
||||
|
||||
ecmaVersion: 2018,
|
||||
sourceType: "commonjs",
|
||||
},
|
||||
|
||||
rules: {
|
||||
"accessor-pairs": "error",
|
||||
"array-bracket-spacing": ["error", "never"],
|
||||
"array-callback-return": "off",
|
||||
"arrow-parens": "error",
|
||||
"arrow-spacing": "error",
|
||||
"block-scoped-var": "off",
|
||||
"block-spacing": "off",
|
||||
|
||||
"brace-style": ["error", "1tbs", {
|
||||
allowSingleLine: true,
|
||||
}],
|
||||
|
||||
"capitalized-comments": "off",
|
||||
"class-methods-use-this": "off",
|
||||
"comma-dangle": "off",
|
||||
"comma-spacing": "off",
|
||||
"comma-style": ["error", "last"],
|
||||
complexity: "error",
|
||||
"computed-property-spacing": ["error", "never"],
|
||||
"consistent-return": "off",
|
||||
"consistent-this": "off",
|
||||
curly: "error",
|
||||
"default-case": "off",
|
||||
"dot-location": ["error", "property"],
|
||||
"dot-notation": "error",
|
||||
"eol-last": "error",
|
||||
eqeqeq: "off",
|
||||
"func-call-spacing": "error",
|
||||
"func-name-matching": "error",
|
||||
"func-names": "off",
|
||||
|
||||
"func-style": ["error", "declaration", {
|
||||
allowArrowFunctions: true,
|
||||
}],
|
||||
|
||||
"generator-star-spacing": "error",
|
||||
"global-require": "off",
|
||||
"guard-for-in": "error",
|
||||
"handle-callback-err": "off",
|
||||
"id-blacklist": "error",
|
||||
"id-length": "off",
|
||||
"id-match": "error",
|
||||
"import/extensions": "off",
|
||||
"init-declarations": "off",
|
||||
"jsx-quotes": "error",
|
||||
"key-spacing": "error",
|
||||
|
||||
"keyword-spacing": ["error", {
|
||||
after: true,
|
||||
before: true,
|
||||
}],
|
||||
|
||||
"line-comment-position": "off",
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"lines-around-directive": "error",
|
||||
"max-depth": "error",
|
||||
"max-len": "off",
|
||||
"max-lines": "off",
|
||||
"max-nested-callbacks": "error",
|
||||
"max-params": "off",
|
||||
"max-statements": "off",
|
||||
"max-statements-per-line": "off",
|
||||
"multiline-ternary": "off",
|
||||
"new-cap": "off",
|
||||
"new-parens": "error",
|
||||
"newline-after-var": "off",
|
||||
"newline-before-return": "off",
|
||||
"newline-per-chained-call": "off",
|
||||
"no-alert": "error",
|
||||
"no-array-constructor": "error",
|
||||
"no-await-in-loop": "error",
|
||||
"no-bitwise": "off",
|
||||
"no-caller": "error",
|
||||
"no-catch-shadow": "off",
|
||||
"no-compare-neg-zero": "error",
|
||||
"no-confusing-arrow": "error",
|
||||
"no-continue": "off",
|
||||
"no-div-regex": "error",
|
||||
"no-duplicate-imports": "off",
|
||||
"no-else-return": "off",
|
||||
"no-empty-function": "off",
|
||||
"no-eq-null": "off",
|
||||
"no-eval": "error",
|
||||
"no-extend-native": "error",
|
||||
"no-extra-bind": "error",
|
||||
"no-extra-label": "error",
|
||||
"no-extra-parens": "off",
|
||||
"no-floating-decimal": "error",
|
||||
"no-implicit-globals": "error",
|
||||
"no-implied-eval": "error",
|
||||
"no-inline-comments": "off",
|
||||
"no-inner-declarations": ["error", "functions"],
|
||||
"no-invalid-this": "off",
|
||||
"no-iterator": "error",
|
||||
"no-label-var": "error",
|
||||
"no-labels": "error",
|
||||
"no-lone-blocks": "error",
|
||||
"no-lonely-if": "error",
|
||||
"no-loop-func": "error",
|
||||
"no-magic-numbers": "off",
|
||||
"no-mixed-requires": "error",
|
||||
"no-multi-assign": "off",
|
||||
"no-multi-spaces": "error",
|
||||
"no-multi-str": "error",
|
||||
"no-multiple-empty-lines": "error",
|
||||
"no-native-reassign": "error",
|
||||
"no-negated-condition": "off",
|
||||
"no-negated-in-lhs": "error",
|
||||
"no-nested-ternary": "error",
|
||||
"no-new": "error",
|
||||
"no-new-func": "error",
|
||||
"no-new-object": "error",
|
||||
"no-new-require": "error",
|
||||
"no-new-wrappers": "error",
|
||||
"no-octal-escape": "error",
|
||||
"no-param-reassign": "off",
|
||||
"no-path-concat": "error",
|
||||
|
||||
"no-plusplus": ["error", {
|
||||
allowForLoopAfterthoughts: true,
|
||||
}],
|
||||
|
||||
"no-process-env": "off",
|
||||
"no-process-exit": "error",
|
||||
"no-proto": "error",
|
||||
"no-prototype-builtins": "off",
|
||||
"no-restricted-globals": "error",
|
||||
"no-restricted-imports": "error",
|
||||
"no-restricted-modules": "error",
|
||||
"no-restricted-properties": "error",
|
||||
"no-restricted-syntax": "error",
|
||||
"no-return-assign": "error",
|
||||
"no-return-await": "error",
|
||||
"no-script-url": "error",
|
||||
"no-self-compare": "error",
|
||||
"no-sequences": "error",
|
||||
"no-shadow": "off",
|
||||
"no-shadow-restricted-names": "error",
|
||||
"no-spaced-func": "error",
|
||||
"no-sync": "error",
|
||||
"no-tabs": "error",
|
||||
"no-template-curly-in-string": "error",
|
||||
"no-ternary": "off",
|
||||
"no-throw-literal": "error",
|
||||
"no-trailing-spaces": "error",
|
||||
"no-undef-init": "error",
|
||||
"no-undefined": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"no-unmodified-loop-condition": "error",
|
||||
"no-unneeded-ternary": "error",
|
||||
"no-unused-expressions": "error",
|
||||
|
||||
"no-unused-vars": ["error", {
|
||||
args: "none",
|
||||
}],
|
||||
|
||||
"no-use-before-define": "off",
|
||||
"no-useless-call": "error",
|
||||
"no-useless-computed-key": "error",
|
||||
"no-useless-concat": "error",
|
||||
"no-useless-constructor": "error",
|
||||
"no-useless-escape": "off",
|
||||
"no-useless-rename": "error",
|
||||
"no-useless-return": "error",
|
||||
"no-var": "off",
|
||||
"no-void": "error",
|
||||
"no-warning-comments": "error",
|
||||
"no-whitespace-before-property": "error",
|
||||
"no-with": "error",
|
||||
"nonblock-statement-body-position": "error",
|
||||
"object-curly-newline": "off",
|
||||
"object-curly-spacing": ["error", "never"],
|
||||
"object-property-newline": "off",
|
||||
"object-shorthand": "off",
|
||||
"one-var": "off",
|
||||
"one-var-declaration-per-line": "error",
|
||||
"operator-assignment": ["error", "always"],
|
||||
"operator-linebreak": "off",
|
||||
"padded-blocks": "off",
|
||||
"prefer-arrow-callback": "off",
|
||||
"prefer-const": "error",
|
||||
|
||||
"prefer-destructuring": ["error", {
|
||||
array: false,
|
||||
object: false,
|
||||
}],
|
||||
|
||||
"prefer-numeric-literals": "error",
|
||||
"prefer-promise-reject-errors": "error",
|
||||
"prefer-reflect": "off",
|
||||
"prefer-rest-params": "off",
|
||||
"prefer-spread": "off",
|
||||
"prefer-template": "off",
|
||||
"quote-props": "off",
|
||||
|
||||
quotes: ["error", "single", {
|
||||
avoidEscape: true,
|
||||
}],
|
||||
|
||||
radix: "error",
|
||||
"require-await": "error",
|
||||
"require-jsdoc": "off",
|
||||
"rest-spread-spacing": "error",
|
||||
semi: "off",
|
||||
|
||||
"semi-spacing": ["error", {
|
||||
after: true,
|
||||
before: false,
|
||||
}],
|
||||
|
||||
"sort-imports": "off",
|
||||
"sort-keys": "off",
|
||||
"sort-vars": "error",
|
||||
"space-before-blocks": "error",
|
||||
"space-before-function-paren": "off",
|
||||
"space-in-parens": ["error", "never"],
|
||||
"space-infix-ops": "error",
|
||||
"space-unary-ops": "error",
|
||||
"spaced-comment": ["error", "always"],
|
||||
strict: "off",
|
||||
"symbol-description": "error",
|
||||
"template-curly-spacing": "error",
|
||||
"template-tag-spacing": "error",
|
||||
"unicode-bom": ["error", "never"],
|
||||
"valid-jsdoc": "off",
|
||||
"vars-on-top": "off",
|
||||
"wrap-regex": "off",
|
||||
"yield-star-spacing": "error",
|
||||
yoda: ["error", "never"],
|
||||
},
|
||||
}, ...compat.extends(
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
).map(config => ({
|
||||
...config,
|
||||
files: ["**/*.ts"],
|
||||
})), {
|
||||
files: ["**/*.ts"],
|
||||
|
||||
plugins: {
|
||||
"@typescript-eslint": typescriptEslint,
|
||||
prettier,
|
||||
},
|
||||
|
||||
rules: {
|
||||
"@typescript-eslint/no-use-before-define": 0,
|
||||
"@typescript-eslint/no-empty-interface": 0,
|
||||
"@typescript-eslint/no-unused-vars": 0,
|
||||
"@typescript-eslint/triple-slash-reference": 0,
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-empty-function": 0,
|
||||
"@typescript-eslint/no-require-imports": 0,
|
||||
|
||||
"@typescript-eslint/naming-convention": ["error", {
|
||||
selector: "default",
|
||||
format: ["camelCase", "UPPER_CASE", "PascalCase"],
|
||||
leadingUnderscore: "allow",
|
||||
}, {
|
||||
selector: "property",
|
||||
format: null,
|
||||
}],
|
||||
|
||||
"@typescript-eslint/no-explicit-any": 0,
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/no-this-alias": "off",
|
||||
"@typescript-eslint/no-var-requires": 0,
|
||||
"prefer-rest-params": "off",
|
||||
},
|
||||
}, {
|
||||
files: ["test/**/*.ts"],
|
||||
|
||||
rules: {
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
},
|
||||
}];
|
||||
@@ -0,0 +1,4 @@
|
||||
STRIPE_SECRET_KEY=""
|
||||
STRIPE_CUSTOMER_ID=""
|
||||
STRIPE_METER_INPUT=""
|
||||
STRIPE_METER_OUTPUT=""
|
||||
@@ -0,0 +1,15 @@
|
||||
# AI SDK Example
|
||||
|
||||
## Setup
|
||||
|
||||
Copy the `.env.template` and populate with your values.
|
||||
|
||||
```
|
||||
cp .env.template .env
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
npx ts-node index.ts --env
|
||||
```
|
||||
@@ -0,0 +1,52 @@
|
||||
import {StripeAgentToolkit} from '@stripe/agent-toolkit/ai-sdk';
|
||||
import {openai} from '@ai-sdk/openai';
|
||||
import {
|
||||
generateText,
|
||||
experimental_wrapLanguageModel as wrapLanguageModel,
|
||||
} from 'ai';
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
const stripeAgentToolkit = new StripeAgentToolkit({
|
||||
secretKey: process.env.STRIPE_SECRET_KEY!,
|
||||
configuration: {
|
||||
actions: {
|
||||
paymentLinks: {
|
||||
create: true,
|
||||
},
|
||||
products: {
|
||||
create: true,
|
||||
},
|
||||
prices: {
|
||||
create: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const model = wrapLanguageModel({
|
||||
model: openai('gpt-4o'),
|
||||
middleware: stripeAgentToolkit.middleware({
|
||||
billing: {
|
||||
customer: process.env.STRIPE_CUSTOMER_ID!,
|
||||
meters: {
|
||||
input: process.env.STRIPE_METER_INPUT!,
|
||||
output: process.env.STRIPE_METER_OUTPUT!,
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
(async () => {
|
||||
const result = await generateText({
|
||||
model: model,
|
||||
tools: {
|
||||
...stripeAgentToolkit.getTools(),
|
||||
},
|
||||
maxSteps: 5,
|
||||
prompt:
|
||||
'Create a payment link for a new product called "test" with a price of $100. Come up with a funny description about buy bots, maybe a haiku.',
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
})();
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "stripe-agent-toolkit-examples-ai-sdk",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ai-sdk/openai": "^0.0.63",
|
||||
"@stripe/agent-toolkit": "workspace:*",
|
||||
"ai": "^3.4.7",
|
||||
"dotenv": "^16.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.7.4"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["index.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
LANGSMITH_API_KEY=""
|
||||
STRIPE_SECRET_KEY=""
|
||||
@@ -0,0 +1,15 @@
|
||||
# LangChain Example
|
||||
|
||||
## Setup
|
||||
|
||||
Copy the `.env.template` and populate with your values.
|
||||
|
||||
```
|
||||
cp .env.template .env
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
npx ts-node index.ts --env
|
||||
```
|
||||
@@ -0,0 +1,57 @@
|
||||
import {StripeAgentToolkit} from '@stripe/agent-toolkit/langchain';
|
||||
import {ChatOpenAI} from '@langchain/openai';
|
||||
import type {ChatPromptTemplate} from '@langchain/core/prompts';
|
||||
import {pull} from 'langchain/hub';
|
||||
import {AgentExecutor, createStructuredChatAgent} from 'langchain/agents';
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
const llm = new ChatOpenAI({
|
||||
model: 'gpt-4o',
|
||||
});
|
||||
|
||||
const stripeAgentToolkit = new StripeAgentToolkit({
|
||||
secretKey: process.env.STRIPE_SECRET_KEY!,
|
||||
configuration: {
|
||||
actions: {
|
||||
paymentLinks: {
|
||||
create: true,
|
||||
},
|
||||
products: {
|
||||
create: true,
|
||||
},
|
||||
prices: {
|
||||
create: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
(async (): Promise<void> => {
|
||||
const prompt = await pull<ChatPromptTemplate>(
|
||||
'hwchase17/structured-chat-agent'
|
||||
);
|
||||
|
||||
const tools = stripeAgentToolkit.getTools();
|
||||
|
||||
const agent = await createStructuredChatAgent({
|
||||
llm,
|
||||
tools,
|
||||
prompt,
|
||||
});
|
||||
|
||||
const agentExecutor = new AgentExecutor({
|
||||
agent,
|
||||
tools,
|
||||
});
|
||||
|
||||
const response = await agentExecutor.invoke({
|
||||
input: `
|
||||
Create a payment link for a new product called 'test' with a price
|
||||
of $100. Come up with a funny description about buy bots,
|
||||
maybe a haiku.
|
||||
`,
|
||||
});
|
||||
|
||||
console.log(response);
|
||||
})();
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "stripe-agent-toolkit-examples-langchain",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@langchain/core": "^0.3.6",
|
||||
"@langchain/openai": "^0.3.5",
|
||||
"@stripe/agent-toolkit": "workspace:*",
|
||||
"dotenv": "^16.4.5",
|
||||
"langchain": "^0.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.7.4"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["index.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import type {Config} from 'jest';
|
||||
|
||||
const config: Config = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
roots: ['<rootDir>/src'],
|
||||
testMatch: ['**/test/**/*.ts?(x)'],
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"name": "@stripe/agent-toolkit",
|
||||
"version": "0.1.16",
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"clean": "rm -rf langchain ai-sdk",
|
||||
"lint": "eslint \"./**/*.ts*\"",
|
||||
"prettier": "prettier './**/*.{js,ts,md,html,css}' --write",
|
||||
"prettier-check": "prettier './**/*.{js,ts,md,html,css}' --check",
|
||||
"test": "jest"
|
||||
},
|
||||
"exports": {
|
||||
"./langchain": {
|
||||
"types": "./dist/langchain.d.ts",
|
||||
"require": "./dist/langchain.js",
|
||||
"import": "./dist/langchain.mjs"
|
||||
},
|
||||
"./ai-sdk": {
|
||||
"types": "./dist/ai-sdk.d.ts",
|
||||
"require": "./dist/ai-sdk.js",
|
||||
"import": "./dist/ai-sdk.mjs"
|
||||
}
|
||||
},
|
||||
"packageManager": "pnpm@9.11.0",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"author": "Stripe <support@stripe.com> (https://stripe.com/)",
|
||||
"contributors": [
|
||||
"Steve Kaliski <stevekaliski@stripe.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^1.1.1",
|
||||
"@types/jest": "^29.5.13",
|
||||
"@types/node": "^22.7.4",
|
||||
"@typescript-eslint/eslint-plugin": "^8.8.0",
|
||||
"eslint": "^9.11.1",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.30.0",
|
||||
"eslint-plugin-jest": "^28.8.3",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"globals": "^15.10.0",
|
||||
"jest": "^29.7.0",
|
||||
"prettier": "^3.3.3",
|
||||
"ts-jest": "^29.2.5",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsup": "^8.3.0",
|
||||
"typescript": "^5.6.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@langchain/core": "^0.3.6",
|
||||
"ai": "^3.4.7",
|
||||
"stripe": "^17.0.0",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"workspaces": [
|
||||
".",
|
||||
"examples/*"
|
||||
],
|
||||
"files": [
|
||||
"ai-sdk/**/*",
|
||||
"langchain/**/*",
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"VERSION",
|
||||
"package.json"
|
||||
]
|
||||
}
|
||||
Generated
+6300
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
packages:
|
||||
- '.'
|
||||
- 'examples/*'
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
RELEASE_TYPE=${1:-}
|
||||
|
||||
echo_help() {
|
||||
cat << EOF
|
||||
USAGE:
|
||||
./scripts/publish <release_type>
|
||||
|
||||
ARGS:
|
||||
<release_type>
|
||||
A Semantic Versioning release type used to bump the version number. Either "patch", "minor", or "major".
|
||||
EOF
|
||||
}
|
||||
|
||||
# Show help if no arguments passed
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "Error! Missing release type argument"
|
||||
echo ""
|
||||
echo_help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Show help message if -h, --help, or help passed
|
||||
case $1 in
|
||||
-h | --help | help)
|
||||
echo_help
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Validate passed release type
|
||||
case $RELEASE_TYPE in
|
||||
patch | minor | major)
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Error! Invalid release type supplied"
|
||||
echo ""
|
||||
echo_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Make sure our working dir is the typescript directory
|
||||
cd "$(git rev-parse --show-toplevel)/typescript"
|
||||
|
||||
echo "Fetching git remotes"
|
||||
git fetch
|
||||
|
||||
GIT_STATUS=$(git status)
|
||||
|
||||
if ! grep -q 'On branch main' <<< "$GIT_STATUS"; then
|
||||
echo "Error! Must be on main branch to publish"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -q "Your branch is up to date with 'origin/main'." <<< "$GIT_STATUS"; then
|
||||
echo "Error! Must be up to date with origin/main to publish"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -q 'working tree clean' <<< "$GIT_STATUS"; then
|
||||
echo "Error! Cannot publish with dirty working tree"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Installing dependencies according to lockfile"
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
echo "Running tests"
|
||||
pnpm run test
|
||||
|
||||
echo "Building package"
|
||||
pnpm run build
|
||||
|
||||
echo "Publishing release"
|
||||
npm --ignore-scripts publish --non-interactive --access=restricted
|
||||
|
||||
echo "Pushing git commit and tag"
|
||||
git push
|
||||
|
||||
echo "Clean"
|
||||
pnpm run clean
|
||||
|
||||
echo "Publish successful!"
|
||||
echo ""
|
||||
@@ -0,0 +1,2 @@
|
||||
import StripeAgentToolkit from './toolkit';
|
||||
export {StripeAgentToolkit};
|
||||
@@ -0,0 +1,19 @@
|
||||
import type {CoreTool} from 'ai';
|
||||
import {tool} from 'ai';
|
||||
import {z} from 'zod';
|
||||
import StripeAPI from '../shared/api';
|
||||
|
||||
export default function StripeTool(
|
||||
stripeAPI: StripeAPI,
|
||||
method: string,
|
||||
description: string,
|
||||
schema: z.ZodObject<any, any, any, any, {[x: string]: any}>
|
||||
): CoreTool {
|
||||
return tool({
|
||||
description: description,
|
||||
parameters: schema,
|
||||
execute: (arg: z.output<typeof schema>) => {
|
||||
return stripeAPI.run(method, arg);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
import StripeAPI from '../shared/api';
|
||||
import tools from '../shared/tools';
|
||||
import {isToolAllowed, type Configuration} from '../shared/configuration';
|
||||
import type {
|
||||
CoreTool,
|
||||
LanguageModelV1StreamPart,
|
||||
Experimental_LanguageModelV1Middleware as LanguageModelV1Middleware,
|
||||
} from 'ai';
|
||||
import StripeTool from './tool';
|
||||
|
||||
type StripeMiddlewareConfig = {
|
||||
billing?: {
|
||||
type?: 'token';
|
||||
customer: string;
|
||||
meters: {
|
||||
input?: string;
|
||||
output?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
class StripeAgentToolkit {
|
||||
private _stripe: StripeAPI;
|
||||
|
||||
tools: {[key: string]: CoreTool};
|
||||
|
||||
constructor({
|
||||
secretKey,
|
||||
configuration,
|
||||
}: {
|
||||
secretKey: string;
|
||||
configuration: Configuration;
|
||||
}) {
|
||||
this._stripe = new StripeAPI(secretKey);
|
||||
this.tools = {};
|
||||
|
||||
const filteredTools = tools.filter((tool) =>
|
||||
isToolAllowed(tool, configuration)
|
||||
);
|
||||
|
||||
filteredTools.forEach((tool) => {
|
||||
// @ts-ignore
|
||||
this.tools[tool.method] = StripeTool(
|
||||
this._stripe,
|
||||
tool.method,
|
||||
tool.description,
|
||||
tool.parameters
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
middleware(config: StripeMiddlewareConfig): LanguageModelV1Middleware {
|
||||
const bill = async ({
|
||||
promptTokens,
|
||||
completionTokens,
|
||||
}: {
|
||||
promptTokens: number;
|
||||
completionTokens: number;
|
||||
}) => {
|
||||
if (config.billing) {
|
||||
if (config.billing.meters.input) {
|
||||
await this._stripe.createMeterEvent({
|
||||
event: config.billing.meters.input,
|
||||
customer: config.billing.customer,
|
||||
value: promptTokens.toString(),
|
||||
});
|
||||
}
|
||||
if (config.billing.meters.output) {
|
||||
await this._stripe.createMeterEvent({
|
||||
event: config.billing.meters.output,
|
||||
customer: config.billing.customer,
|
||||
value: completionTokens.toString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
wrapGenerate: async ({doGenerate}) => {
|
||||
const result = await doGenerate();
|
||||
|
||||
if (config.billing) {
|
||||
await bill(result.usage);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
wrapStream: async ({doStream}) => {
|
||||
const {stream, ...rest} = await doStream();
|
||||
|
||||
const transformStream = new TransformStream<
|
||||
LanguageModelV1StreamPart,
|
||||
LanguageModelV1StreamPart
|
||||
>({
|
||||
async transform(chunk, controller) {
|
||||
if (chunk.type === 'finish') {
|
||||
if (config.billing) {
|
||||
await bill(chunk.usage);
|
||||
}
|
||||
}
|
||||
|
||||
controller.enqueue(chunk);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
stream: stream.pipeThrough(transformStream),
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
getTools(): {[key: string]: CoreTool} {
|
||||
return this.tools;
|
||||
}
|
||||
}
|
||||
|
||||
export default StripeAgentToolkit;
|
||||
@@ -0,0 +1,2 @@
|
||||
import StripeAgentToolkit from './toolkit';
|
||||
export {StripeAgentToolkit};
|
||||
@@ -0,0 +1,43 @@
|
||||
import {z} from 'zod';
|
||||
import {StructuredTool} from '@langchain/core/tools';
|
||||
import {CallbackManagerForToolRun} from '@langchain/core/callbacks/manager';
|
||||
import {RunnableConfig} from '@langchain/core/runnables';
|
||||
import StripeAPI from '../shared/api';
|
||||
|
||||
class StripeTool extends StructuredTool {
|
||||
stripeAPI: StripeAPI;
|
||||
|
||||
method: string;
|
||||
|
||||
name: string;
|
||||
|
||||
description: string;
|
||||
|
||||
schema: z.ZodObject<any, any, any, any>;
|
||||
|
||||
constructor(
|
||||
StripeAPI: StripeAPI,
|
||||
method: string,
|
||||
name: string,
|
||||
description: string,
|
||||
schema: z.ZodObject<any, any, any, any, {[x: string]: any}>
|
||||
) {
|
||||
super();
|
||||
|
||||
this.stripeAPI = StripeAPI;
|
||||
this.method = method;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.schema = schema;
|
||||
}
|
||||
|
||||
_call(
|
||||
arg: z.output<typeof this.schema>,
|
||||
_runManager?: CallbackManagerForToolRun,
|
||||
_parentConfig?: RunnableConfig
|
||||
): Promise<any> {
|
||||
return this.stripeAPI.run(this.method, arg);
|
||||
}
|
||||
}
|
||||
|
||||
export default StripeTool;
|
||||
@@ -0,0 +1,42 @@
|
||||
import {BaseToolkit} from '@langchain/core/tools';
|
||||
import StripeTool from './tool';
|
||||
import StripeAPI from '../shared/api';
|
||||
import tools from '../shared/tools';
|
||||
import {isToolAllowed, type Configuration} from '../shared/configuration';
|
||||
|
||||
class StripeAgentToolkit implements BaseToolkit {
|
||||
private _stripe: StripeAPI;
|
||||
|
||||
tools: StripeTool[];
|
||||
|
||||
constructor({
|
||||
secretKey,
|
||||
configuration,
|
||||
}: {
|
||||
secretKey: string;
|
||||
configuration: Configuration;
|
||||
}) {
|
||||
this._stripe = new StripeAPI(secretKey);
|
||||
|
||||
const filteredTools = tools.filter((tool) =>
|
||||
isToolAllowed(tool, configuration)
|
||||
);
|
||||
|
||||
this.tools = filteredTools.map(
|
||||
(tool) =>
|
||||
new StripeTool(
|
||||
this._stripe,
|
||||
tool.method,
|
||||
tool.name,
|
||||
tool.description,
|
||||
tool.parameters
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
getTools(): StripeTool[] {
|
||||
return this.tools;
|
||||
}
|
||||
}
|
||||
|
||||
export default StripeAgentToolkit;
|
||||
@@ -0,0 +1,88 @@
|
||||
import Stripe from 'stripe';
|
||||
import {
|
||||
createCustomer,
|
||||
listCustomers,
|
||||
createProduct,
|
||||
listProducts,
|
||||
createPrice,
|
||||
listPrices,
|
||||
createPaymentLink,
|
||||
createInvoice,
|
||||
createInvoiceItem,
|
||||
finalizeInvoice,
|
||||
retrieveBalance,
|
||||
} from './functions';
|
||||
|
||||
class StripeAPI {
|
||||
stripe: Stripe;
|
||||
|
||||
constructor(secretKey: string) {
|
||||
const stripeClient = new Stripe(secretKey, {
|
||||
appInfo: {
|
||||
name: 'stripe-agent-toolkit-typescript',
|
||||
version: '0.1.16',
|
||||
url: 'https://github.com/stripe/agent-toolkit',
|
||||
},
|
||||
});
|
||||
this.stripe = stripeClient;
|
||||
}
|
||||
|
||||
async createMeterEvent({
|
||||
event,
|
||||
customer,
|
||||
value,
|
||||
}: {
|
||||
event: string;
|
||||
customer: string;
|
||||
value: string;
|
||||
}) {
|
||||
await this.stripe.billing.meterEvents.create({
|
||||
event_name: event,
|
||||
payload: {
|
||||
stripe_customer_id: customer,
|
||||
value: value,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async run(method: string, arg: any) {
|
||||
if (method === 'createCustomer') {
|
||||
const output = JSON.stringify(await createCustomer(this.stripe, arg));
|
||||
return output;
|
||||
} else if (method === 'listCustomers') {
|
||||
const output = JSON.stringify(await listCustomers(this.stripe, arg));
|
||||
return output;
|
||||
} else if (method === 'createProduct') {
|
||||
const output = JSON.stringify(await createProduct(this.stripe, arg));
|
||||
return output;
|
||||
} else if (method === 'listProducts') {
|
||||
const output = JSON.stringify(await listProducts(this.stripe, arg));
|
||||
return output;
|
||||
} else if (method === 'createPrice') {
|
||||
const output = JSON.stringify(await createPrice(this.stripe, arg));
|
||||
return output;
|
||||
} else if (method === 'listPrices') {
|
||||
const output = JSON.stringify(await listPrices(this.stripe, arg));
|
||||
return output;
|
||||
} else if (method === 'createPaymentLink') {
|
||||
const output = JSON.stringify(await createPaymentLink(this.stripe, arg));
|
||||
return output;
|
||||
} else if (method === 'createInvoice') {
|
||||
const output = JSON.stringify(await createInvoice(this.stripe, arg));
|
||||
return output;
|
||||
} else if (method === 'createInvoiceItem') {
|
||||
const output = JSON.stringify(await createInvoiceItem(this.stripe, arg));
|
||||
return output;
|
||||
} else if (method === 'finalizeInvoice') {
|
||||
const output = JSON.stringify(await finalizeInvoice(this.stripe, arg));
|
||||
return output;
|
||||
} else if (method === 'retrieveBalance') {
|
||||
const output = JSON.stringify(await retrieveBalance(this.stripe, arg));
|
||||
return output;
|
||||
} else {
|
||||
throw new Error('Invalid method ' + method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default StripeAPI;
|
||||
@@ -0,0 +1,47 @@
|
||||
import type {Tool} from './tools';
|
||||
|
||||
// Actions restrict the subset of API calls that can be made. They should
|
||||
// be used in conjunction with Restricted API Keys. Setting a permission to false
|
||||
// prevents the related "tool" from being considered.
|
||||
export type Object =
|
||||
| 'customers'
|
||||
| 'invoices'
|
||||
| 'invoiceItems'
|
||||
| 'paymentLinks'
|
||||
| 'products'
|
||||
| 'prices'
|
||||
| 'balance';
|
||||
|
||||
export type Permission = 'create' | 'update' | 'read';
|
||||
|
||||
export type Actions = {
|
||||
[K in Object]?: {
|
||||
[K in Permission]?: boolean;
|
||||
};
|
||||
} & {
|
||||
balance?: {
|
||||
read?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
// Configuration provides various settings and options for the integration
|
||||
// to tune and manage how it behaves.
|
||||
export type Configuration = {
|
||||
actions?: Actions;
|
||||
};
|
||||
|
||||
export const isToolAllowed = (
|
||||
tool: Tool,
|
||||
configuration: Configuration
|
||||
): boolean => {
|
||||
return Object.keys(tool.actions).every((resource) => {
|
||||
// For each resource.permission pair, check the configuration.
|
||||
// @ts-ignore
|
||||
const permissions = tool.actions[resource];
|
||||
|
||||
return Object.keys(permissions).every((permission) => {
|
||||
// @ts-ignore
|
||||
return configuration.actions[resource]?.[permission] === true;
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,162 @@
|
||||
import Stripe from 'stripe';
|
||||
import {z} from 'zod';
|
||||
import {
|
||||
createCustomerParameters,
|
||||
listCustomersParameters,
|
||||
createProductParameters,
|
||||
listProductsParameters,
|
||||
createPriceParameters,
|
||||
listPricesParameters,
|
||||
createPaymentLinkParameters,
|
||||
createInvoiceParameters,
|
||||
createInvoiceItemParameters,
|
||||
finalizeInvoiceParameters,
|
||||
retrieveBalanceParameters,
|
||||
} from './parameters';
|
||||
|
||||
export const createCustomer = async (
|
||||
stripe: Stripe,
|
||||
params: z.infer<typeof createCustomerParameters>
|
||||
) => {
|
||||
try {
|
||||
const customer = await stripe.customers.create(params);
|
||||
return {id: customer.id};
|
||||
} catch (error) {
|
||||
return 'Failed to create customer';
|
||||
}
|
||||
};
|
||||
|
||||
export const listCustomers = async (
|
||||
stripe: Stripe,
|
||||
params: z.infer<typeof listCustomersParameters>
|
||||
) => {
|
||||
try {
|
||||
const customers = await stripe.customers.list(params);
|
||||
return customers.data.map((customer) => ({id: customer.id}));
|
||||
} catch (error) {
|
||||
return 'Failed to list customers';
|
||||
}
|
||||
};
|
||||
|
||||
export const createProduct = async (
|
||||
stripe: Stripe,
|
||||
params: z.infer<typeof createProductParameters>
|
||||
) => {
|
||||
try {
|
||||
const product = await stripe.products.create(params);
|
||||
return product;
|
||||
} catch (error) {
|
||||
return 'Failed to create product';
|
||||
}
|
||||
};
|
||||
|
||||
export const listProducts = async (
|
||||
stripe: Stripe,
|
||||
params: z.infer<typeof listProductsParameters>
|
||||
) => {
|
||||
try {
|
||||
const products = await stripe.products.list(params);
|
||||
return products.data;
|
||||
} catch (error) {
|
||||
return 'Failed to list products';
|
||||
}
|
||||
};
|
||||
|
||||
export const createPrice = async (
|
||||
stripe: Stripe,
|
||||
params: z.infer<typeof createPriceParameters>
|
||||
) => {
|
||||
try {
|
||||
const price = await stripe.prices.create(params);
|
||||
return price;
|
||||
} catch (error) {
|
||||
return 'Failed to create price';
|
||||
}
|
||||
};
|
||||
|
||||
export const listPrices = async (
|
||||
stripe: Stripe,
|
||||
params: z.infer<typeof listPricesParameters>
|
||||
) => {
|
||||
try {
|
||||
const prices = await stripe.prices.list(params);
|
||||
return prices.data;
|
||||
} catch (error) {
|
||||
return 'Failed to list prices';
|
||||
}
|
||||
};
|
||||
|
||||
export const createPaymentLink = async (
|
||||
stripe: Stripe,
|
||||
params: z.infer<typeof createPaymentLinkParameters>
|
||||
) => {
|
||||
try {
|
||||
const paymentLink = await stripe.paymentLinks.create({
|
||||
line_items: [params],
|
||||
});
|
||||
return {id: paymentLink.id, url: paymentLink.url};
|
||||
} catch (error) {
|
||||
return 'Failed to create payment link';
|
||||
}
|
||||
};
|
||||
|
||||
export const createInvoice = async (
|
||||
stripe: Stripe,
|
||||
params: z.infer<typeof createInvoiceParameters>
|
||||
) => {
|
||||
try {
|
||||
const invoice = await stripe.invoices.create(params);
|
||||
return {
|
||||
id: invoice.id,
|
||||
url: invoice.hosted_invoice_url,
|
||||
customer: invoice.customer,
|
||||
status: invoice.status,
|
||||
};
|
||||
} catch (error) {
|
||||
return 'Failed to create invoice';
|
||||
}
|
||||
};
|
||||
|
||||
export const createInvoiceItem = async (
|
||||
stripe: Stripe,
|
||||
params: z.infer<typeof createInvoiceItemParameters>
|
||||
) => {
|
||||
try {
|
||||
const invoiceItem = await stripe.invoiceItems.create(params);
|
||||
return {
|
||||
id: invoiceItem.id,
|
||||
invoice: invoiceItem.invoice,
|
||||
};
|
||||
} catch (error) {
|
||||
return 'Failed to create invoice item';
|
||||
}
|
||||
};
|
||||
|
||||
export const finalizeInvoice = async (
|
||||
stripe: Stripe,
|
||||
params: z.infer<typeof finalizeInvoiceParameters>
|
||||
) => {
|
||||
try {
|
||||
const invoice = await stripe.invoices.finalizeInvoice(params.invoice);
|
||||
return {
|
||||
id: invoice.id,
|
||||
url: invoice.hosted_invoice_url,
|
||||
customer: invoice.customer,
|
||||
status: invoice.status,
|
||||
};
|
||||
} catch (error) {
|
||||
return 'Failed to finalize invoice';
|
||||
}
|
||||
};
|
||||
|
||||
export const retrieveBalance = async (
|
||||
stripe: Stripe,
|
||||
params: z.infer<typeof retrieveBalanceParameters>
|
||||
) => {
|
||||
try {
|
||||
const balance = await stripe.balance.retrieve(params);
|
||||
return balance;
|
||||
} catch (error) {
|
||||
return 'Failed to retrieve balance';
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,106 @@
|
||||
import {z} from 'zod';
|
||||
|
||||
export const createCustomerParameters = z.object({
|
||||
name: z.string().describe('The name of the customer'),
|
||||
email: z.string().email().optional().describe('The email of the customer'),
|
||||
});
|
||||
|
||||
export const listCustomersParameters = z.object({
|
||||
limit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(100)
|
||||
.optional()
|
||||
.describe(
|
||||
'A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.'
|
||||
),
|
||||
email: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
"A case-sensitive filter on the list based on the customer's email field. The value must be a string."
|
||||
),
|
||||
});
|
||||
|
||||
export const createProductParameters = z.object({
|
||||
name: z.string().describe('The name of the product.'),
|
||||
description: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe('The description of the product.'),
|
||||
});
|
||||
|
||||
export const listProductsParameters = z.object({
|
||||
limit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(100)
|
||||
.optional()
|
||||
.describe(
|
||||
'A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.'
|
||||
),
|
||||
});
|
||||
|
||||
export const createPriceParameters = z.object({
|
||||
product: z
|
||||
.string()
|
||||
.describe('The ID of the product to create the price for.'),
|
||||
unit_amount: z
|
||||
.number()
|
||||
.int()
|
||||
.describe('The unit amount of the price in cents.'),
|
||||
currency: z.string().describe('The currency of the price.'),
|
||||
});
|
||||
|
||||
export const listPricesParameters = z.object({
|
||||
product: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe('The ID of the product to list prices for.'),
|
||||
limit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(100)
|
||||
.optional()
|
||||
.describe(
|
||||
'A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.'
|
||||
),
|
||||
});
|
||||
|
||||
export const createPaymentLinkParameters = z.object({
|
||||
price: z
|
||||
.string()
|
||||
.describe('The ID of the price to create the payment link for.'),
|
||||
quantity: z
|
||||
.number()
|
||||
.int()
|
||||
.describe('The quantity of the product to include.'),
|
||||
});
|
||||
|
||||
export const createInvoiceParameters = z.object({
|
||||
customer: z
|
||||
.string()
|
||||
.describe('The ID of the customer to create the invoice for.'),
|
||||
days_until_due: z
|
||||
.number()
|
||||
.int()
|
||||
.optional()
|
||||
.describe('The number of days until the invoice is due.'),
|
||||
});
|
||||
|
||||
export const createInvoiceItemParameters = z.object({
|
||||
customer: z
|
||||
.string()
|
||||
.describe('The ID of the customer to create the invoice item for.'),
|
||||
price: z.string().describe('The ID of the price for the item.'),
|
||||
invoice: z.string().describe('The ID of the invoice to create the item for.'),
|
||||
});
|
||||
|
||||
export const finalizeInvoiceParameters = z.object({
|
||||
invoice: z.string().describe('The ID of the invoice to finalize.'),
|
||||
});
|
||||
|
||||
export const retrieveBalanceParameters = z.object({});
|
||||
@@ -0,0 +1,79 @@
|
||||
export const createCustomerPrompt = `
|
||||
This tool will create a customer in Stripe.
|
||||
|
||||
It takes two arguments:
|
||||
- name (str): The name of the customer.
|
||||
- email (str, optional): The email of the customer.
|
||||
`;
|
||||
|
||||
export const listCustomersPrompt = `
|
||||
This tool will fetch a list of Customers from Stripe.
|
||||
|
||||
It takes no input.
|
||||
`;
|
||||
|
||||
export const createProductPrompt = `
|
||||
This tool will create a product in Stripe.
|
||||
|
||||
It takes two arguments:
|
||||
- name (str): The name of the product.
|
||||
- description (str, optional): The description of the product.
|
||||
`;
|
||||
|
||||
export const listProductsPrompt = `
|
||||
This tool will fetch a list of Products from Stripe.
|
||||
|
||||
It takes one optional argument:
|
||||
- limit (int, optional): The number of products to return.
|
||||
`;
|
||||
|
||||
export const createPricePrompt = `
|
||||
This tool will create a price in Stripe. If a product has not already been specified, a product should be created first.
|
||||
|
||||
It takes three arguments:
|
||||
- product (str): The ID of the product to create the price for.
|
||||
- unit_amount (int): The unit amount of the price in cents.
|
||||
- currency (str): The currency of the price.
|
||||
`;
|
||||
|
||||
export const listPricesPrompt = `
|
||||
This tool will fetch a list of Prices from Stripe.
|
||||
|
||||
It takes two arguments.
|
||||
- product (str, optional): The ID of the product to list prices for.
|
||||
- limit (int, optional): The number of prices to return.
|
||||
`;
|
||||
|
||||
export const createPaymentLinkPrompt = `
|
||||
This tool will create a payment link in Stripe.
|
||||
|
||||
It takes two arguments:
|
||||
- price (str): The ID of the price to create the payment link for.
|
||||
- quantity (int): The quantity of the product to include in the payment link.
|
||||
`;
|
||||
|
||||
export const createInvoicePrompt = `
|
||||
This tool will create an invoice in Stripe.
|
||||
|
||||
It takes one argument:
|
||||
- customer (str): The ID of the customer to create the invoice for.
|
||||
`;
|
||||
|
||||
export const createInvoiceItemPrompt = `
|
||||
This tool will create an invoice item in Stripe.
|
||||
|
||||
It takes two arguments:
|
||||
- customer (str): The ID of the customer to create the invoice item for.
|
||||
- price (str): The ID of the price to create the invoice item for.
|
||||
`;
|
||||
|
||||
export const finalizeInvoicePrompt = `
|
||||
This tool will finalize an invoice in Stripe.
|
||||
|
||||
It takes one argument:
|
||||
- invoice (str): The ID of the invoice to finalize.
|
||||
`;
|
||||
|
||||
export const retrieveBalancePrompt = `
|
||||
This tool will retrieve the balance from Stripe. It takes no input.
|
||||
`;
|
||||
@@ -0,0 +1,165 @@
|
||||
import {
|
||||
createCustomerPrompt,
|
||||
listCustomersPrompt,
|
||||
createProductPrompt,
|
||||
listProductsPrompt,
|
||||
createPricePrompt,
|
||||
listPricesPrompt,
|
||||
createPaymentLinkPrompt,
|
||||
createInvoicePrompt,
|
||||
createInvoiceItemPrompt,
|
||||
finalizeInvoicePrompt,
|
||||
retrieveBalancePrompt,
|
||||
} from './prompts';
|
||||
|
||||
import {
|
||||
createCustomerParameters,
|
||||
listCustomersParameters,
|
||||
createProductParameters,
|
||||
listProductsParameters,
|
||||
createPriceParameters,
|
||||
listPricesParameters,
|
||||
createPaymentLinkParameters,
|
||||
createInvoiceParameters,
|
||||
createInvoiceItemParameters,
|
||||
finalizeInvoiceParameters,
|
||||
retrieveBalanceParameters,
|
||||
} from './parameters';
|
||||
|
||||
export type Tool = {
|
||||
method: string;
|
||||
name: string;
|
||||
description: string;
|
||||
parameters: any;
|
||||
actions: {
|
||||
[key: string]: {
|
||||
[action: string]: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const tools: Tool[] = [
|
||||
{
|
||||
method: 'createCustomer',
|
||||
name: 'Create Customer',
|
||||
description: createCustomerPrompt,
|
||||
parameters: createCustomerParameters,
|
||||
actions: {
|
||||
customers: {
|
||||
create: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'listCustomers',
|
||||
name: 'List Customers',
|
||||
description: listCustomersPrompt,
|
||||
parameters: listCustomersParameters,
|
||||
actions: {
|
||||
customers: {
|
||||
read: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'createProduct',
|
||||
name: 'Create Product',
|
||||
description: createProductPrompt,
|
||||
parameters: createProductParameters,
|
||||
actions: {
|
||||
products: {
|
||||
create: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'listProducts',
|
||||
name: 'List Products',
|
||||
description: listProductsPrompt,
|
||||
parameters: listProductsParameters,
|
||||
actions: {
|
||||
products: {
|
||||
read: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'createPrice',
|
||||
name: 'Create Price',
|
||||
description: createPricePrompt,
|
||||
parameters: createPriceParameters,
|
||||
actions: {
|
||||
prices: {
|
||||
create: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'listPrices',
|
||||
name: 'List Prices',
|
||||
description: listPricesPrompt,
|
||||
parameters: listPricesParameters,
|
||||
actions: {
|
||||
prices: {
|
||||
read: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'createPaymentLink',
|
||||
name: 'Create Payment Link',
|
||||
description: createPaymentLinkPrompt,
|
||||
parameters: createPaymentLinkParameters,
|
||||
actions: {
|
||||
paymentLinks: {
|
||||
create: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'createInvoice',
|
||||
name: 'Create Invoice',
|
||||
description: createInvoicePrompt,
|
||||
parameters: createInvoiceParameters,
|
||||
actions: {
|
||||
invoices: {
|
||||
create: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'createInvoiceItem',
|
||||
name: 'Create Invoice Item',
|
||||
description: createInvoiceItemPrompt,
|
||||
parameters: createInvoiceItemParameters,
|
||||
actions: {
|
||||
invoiceItems: {
|
||||
create: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'finalizeInvoice',
|
||||
name: 'Finalize Invoice',
|
||||
description: finalizeInvoicePrompt,
|
||||
parameters: finalizeInvoiceParameters,
|
||||
actions: {
|
||||
invoices: {
|
||||
update: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'retrieveBalance',
|
||||
name: 'Retrieve Balance',
|
||||
description: retrieveBalancePrompt,
|
||||
parameters: retrieveBalanceParameters,
|
||||
actions: {
|
||||
balance: {
|
||||
read: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default tools;
|
||||
@@ -0,0 +1,100 @@
|
||||
import {isToolAllowed} from '../../shared/configuration';
|
||||
|
||||
describe('isToolAllowed', () => {
|
||||
it('should return true if all permissions are allowed', () => {
|
||||
const tool = {
|
||||
method: 'test',
|
||||
name: 'Test',
|
||||
description: 'Test',
|
||||
parameters: {},
|
||||
actions: {
|
||||
customers: {
|
||||
create: true,
|
||||
read: true,
|
||||
},
|
||||
invoices: {
|
||||
create: true,
|
||||
read: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const configuration = {
|
||||
actions: {
|
||||
customers: {
|
||||
create: true,
|
||||
read: true,
|
||||
},
|
||||
invoices: {
|
||||
create: true,
|
||||
read: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(isToolAllowed(tool, configuration)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if any permission is denied', () => {
|
||||
const tool = {
|
||||
method: 'test',
|
||||
name: 'Test',
|
||||
description: 'Test',
|
||||
parameters: {},
|
||||
actions: {
|
||||
customers: {
|
||||
create: true,
|
||||
read: true,
|
||||
},
|
||||
invoices: {
|
||||
create: true,
|
||||
read: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const configuration = {
|
||||
actions: {
|
||||
customers: {
|
||||
create: true,
|
||||
read: true,
|
||||
},
|
||||
invoices: {
|
||||
create: true,
|
||||
read: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(isToolAllowed(tool, configuration)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false if any resource is not allowed', () => {
|
||||
const tool = {
|
||||
method: 'test',
|
||||
name: 'Test',
|
||||
description: 'Test',
|
||||
parameters: {},
|
||||
actions: {
|
||||
paymentLinks: {
|
||||
create: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const configuration = {
|
||||
actions: {
|
||||
customers: {
|
||||
create: true,
|
||||
read: true,
|
||||
},
|
||||
invoices: {
|
||||
create: true,
|
||||
read: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(isToolAllowed(tool, configuration)).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,224 @@
|
||||
import {
|
||||
createCustomer,
|
||||
listCustomers,
|
||||
createProduct,
|
||||
listProducts,
|
||||
createPrice,
|
||||
listPrices,
|
||||
createPaymentLink,
|
||||
createInvoice,
|
||||
createInvoiceItem,
|
||||
finalizeInvoice,
|
||||
retrieveBalance,
|
||||
} from '../../shared/functions';
|
||||
|
||||
const Stripe = jest.fn().mockImplementation(() => ({
|
||||
customers: {
|
||||
create: jest.fn(),
|
||||
list: jest.fn(),
|
||||
},
|
||||
products: {
|
||||
create: jest.fn(),
|
||||
list: jest.fn(),
|
||||
},
|
||||
prices: {
|
||||
create: jest.fn(),
|
||||
list: jest.fn(),
|
||||
},
|
||||
paymentLinks: {
|
||||
create: jest.fn(),
|
||||
},
|
||||
invoices: {
|
||||
create: jest.fn(),
|
||||
finalizeInvoice: jest.fn(),
|
||||
retrieve: jest.fn(),
|
||||
},
|
||||
invoiceItems: {
|
||||
create: jest.fn(),
|
||||
},
|
||||
balance: {
|
||||
retrieve: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
let stripe: ReturnType<typeof Stripe>;
|
||||
|
||||
beforeEach(() => {
|
||||
stripe = new Stripe('fake-api-key');
|
||||
});
|
||||
|
||||
describe('createCustomer', () => {
|
||||
it('should create a customer and return the id', async () => {
|
||||
const params = {
|
||||
email: 'test@example.com',
|
||||
name: 'Test User',
|
||||
};
|
||||
|
||||
const mockCustomer = {id: 'cus_123456', email: 'test@example.com'};
|
||||
stripe.customers.create.mockResolvedValue(mockCustomer);
|
||||
|
||||
const result = await createCustomer(stripe, params);
|
||||
|
||||
expect(stripe.customers.create).toHaveBeenCalledWith(params);
|
||||
expect(result).toEqual({id: mockCustomer.id});
|
||||
});
|
||||
});
|
||||
|
||||
describe('listCustomers', () => {
|
||||
it('should list customers and return their ids', async () => {
|
||||
const mockCustomers = [
|
||||
{id: 'cus_123456', email: 'test1@example.com'},
|
||||
{id: 'cus_789012', email: 'test2@example.com'},
|
||||
];
|
||||
|
||||
stripe.customers.list.mockResolvedValue({data: mockCustomers});
|
||||
const result = await listCustomers(stripe, {});
|
||||
|
||||
expect(stripe.customers.list).toHaveBeenCalledWith({});
|
||||
expect(result).toEqual(mockCustomers.map(({id}) => ({id})));
|
||||
});
|
||||
});
|
||||
|
||||
describe('createProduct', () => {
|
||||
it('should create a product and return it', async () => {
|
||||
const params = {
|
||||
name: 'Test Product',
|
||||
};
|
||||
|
||||
const mockProduct = {id: 'prod_123456', name: 'Test Product'};
|
||||
stripe.products.create.mockResolvedValue(mockProduct);
|
||||
|
||||
const result = await createProduct(stripe, params);
|
||||
|
||||
expect(stripe.products.create).toHaveBeenCalledWith(params);
|
||||
expect(result).toEqual(mockProduct);
|
||||
});
|
||||
});
|
||||
|
||||
describe('listProducts', () => {
|
||||
it('should list products and return them', async () => {
|
||||
const mockProducts = [
|
||||
{id: 'prod_123456', name: 'Test Product 1'},
|
||||
{id: 'prod_789012', name: 'Test Product 2'},
|
||||
];
|
||||
|
||||
stripe.products.list.mockResolvedValue({data: mockProducts});
|
||||
const result = await listProducts(stripe, {});
|
||||
|
||||
expect(stripe.products.list).toHaveBeenCalledWith({});
|
||||
expect(result).toEqual(mockProducts);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createPrice', () => {
|
||||
it('should create a price and return it', async () => {
|
||||
const params = {
|
||||
unit_amount: 1000,
|
||||
currency: 'usd',
|
||||
product: 'prod_123456',
|
||||
};
|
||||
|
||||
const mockPrice = {id: 'price_123456', unit_amount: 1000, currency: 'usd'};
|
||||
stripe.prices.create.mockResolvedValue(mockPrice);
|
||||
|
||||
const result = await createPrice(stripe, params);
|
||||
|
||||
expect(stripe.prices.create).toHaveBeenCalledWith(params);
|
||||
expect(result).toEqual(mockPrice);
|
||||
});
|
||||
});
|
||||
|
||||
describe('listPrices', () => {
|
||||
it('should list prices and return them', async () => {
|
||||
const mockPrices = [
|
||||
{id: 'price_123456', unit_amount: 1000, currency: 'usd'},
|
||||
{id: 'price_789012', unit_amount: 2000, currency: 'usd'},
|
||||
];
|
||||
|
||||
stripe.prices.list.mockResolvedValue({data: mockPrices});
|
||||
const result = await listPrices(stripe, {});
|
||||
|
||||
expect(stripe.prices.list).toHaveBeenCalledWith({});
|
||||
expect(result).toEqual(mockPrices);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createPaymentLink', () => {
|
||||
it('should create a payment link and return it', async () => {
|
||||
const params = {
|
||||
line_items: [
|
||||
{
|
||||
price: 'price_123456',
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const mockPaymentLink = {
|
||||
id: 'pl_123456',
|
||||
url: 'https://example.com',
|
||||
};
|
||||
|
||||
stripe.paymentLinks.create.mockResolvedValue(mockPaymentLink);
|
||||
|
||||
const result = await createPaymentLink(stripe, {
|
||||
price: 'price_123456',
|
||||
quantity: 1,
|
||||
});
|
||||
|
||||
expect(stripe.paymentLinks.create).toHaveBeenCalledWith(params);
|
||||
expect(result).toEqual(mockPaymentLink);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createInvoice', () => {
|
||||
it('should create an invoice and return it', async () => {
|
||||
const params = {
|
||||
customer: 'cus_123456',
|
||||
items: [{price: 'price_123456', quantity: 1}],
|
||||
};
|
||||
|
||||
const mockInvoice = {id: 'in_123456', customer: 'cus_123456'};
|
||||
stripe.invoices.create.mockResolvedValue(mockInvoice);
|
||||
const result = await createInvoice(stripe, params);
|
||||
expect(stripe.invoices.create).toHaveBeenCalledWith(params);
|
||||
expect(result).toEqual(mockInvoice);
|
||||
});
|
||||
});
|
||||
|
||||
describe('finalizeInvoice', () => {
|
||||
it('should finalize an invoice and return it', async () => {
|
||||
const invoiceId = 'in_123456';
|
||||
const mockInvoice = {id: invoiceId, customer: 'cus_123456'};
|
||||
stripe.invoices.finalizeInvoice.mockResolvedValue(mockInvoice);
|
||||
const result = await finalizeInvoice(stripe, {invoice: invoiceId});
|
||||
expect(stripe.invoices.finalizeInvoice).toHaveBeenCalledWith(invoiceId);
|
||||
expect(result).toEqual(mockInvoice);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createInvoiceItem', () => {
|
||||
it('should create an invoice item and return it', async () => {
|
||||
const params = {
|
||||
customer: 'cus_123456',
|
||||
price: 'price_123456',
|
||||
invoice: 'in_123456',
|
||||
};
|
||||
|
||||
const mockInvoiceItem = {id: 'ii_123456', invoice: 'in_123456'};
|
||||
stripe.invoiceItems.create.mockResolvedValue(mockInvoiceItem);
|
||||
const result = await createInvoiceItem(stripe, params);
|
||||
expect(stripe.invoiceItems.create).toHaveBeenCalledWith(params);
|
||||
expect(result).toEqual(mockInvoiceItem);
|
||||
});
|
||||
});
|
||||
|
||||
describe('retrieveBalance', () => {
|
||||
it('should retrieve the balance and return it', async () => {
|
||||
const mockBalance = {available: [{amount: 1000, currency: 'usd'}]};
|
||||
stripe.balance.retrieve.mockResolvedValue(mockBalance);
|
||||
const result = await retrieveBalance(stripe, {});
|
||||
expect(stripe.balance.retrieve).toHaveBeenCalled();
|
||||
expect(result).toEqual(mockBalance);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"display": "Default",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"target": "es2022",
|
||||
"moduleDetection": "force",
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"module": "NodeNext"
|
||||
},
|
||||
"include": ["**/*.ts"],
|
||||
"exclude": ["node_modules", "examples"]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import {defineConfig} from 'tsup';
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
entry: ['src/langchain/index.ts'],
|
||||
outDir: 'langchain',
|
||||
format: ['cjs', 'esm'],
|
||||
dts: true,
|
||||
sourcemap: true,
|
||||
},
|
||||
{
|
||||
entry: ['src/ai-sdk/index.ts'],
|
||||
outDir: 'ai-sdk',
|
||||
format: ['cjs', 'esm'],
|
||||
dts: true,
|
||||
sourcemap: true,
|
||||
},
|
||||
]);
|
||||
Reference in New Issue
Block a user