first commit

This commit is contained in:
Steve Kaliski
2024-11-13 14:37:40 -05:00
commit 8eec7f80f9
75 changed files with 10569 additions and 0 deletions
+78
View File
@@ -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
+8
View File
@@ -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.
+84
View File
@@ -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
View File
@@ -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/
+8
View File
@@ -0,0 +1,8 @@
{
"recommendations": [
"EditorConfig.editorconfig", // default
"ms-python.python", // intellisense
"ms-python.flake8", // linting
"charliermarsh.ruff" // formatting
]
}
+17
View File
@@ -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
}
+76
View File
@@ -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
+23
View File
@@ -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:
+21
View File
@@ -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.
+156
View File
@@ -0,0 +1,156 @@
# Stripe Agent Toolkit
[![Build Status](https://github.com/stripe/agent-toolkit/actions/workflows/main.yml/badge.svg)](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
+8
View File
@@ -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.
+6
View File
@@ -0,0 +1,6 @@
{
"name": "stripe-toolkit",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}
+17
View File
@@ -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
+12
View File
@@ -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
+19
View File
@@ -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
+64
View File
@@ -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
```
+4
View File
@@ -0,0 +1,4 @@
STRIPE_SECRET_KEY=""
OPENAI_API_BASE=""
OPENAI_MODEL_NAME="gpt-4o"
OPENAI_API_KEY=""
+15
View File
@@ -0,0 +1,15 @@
# CrewAI Example
## Setup
Copy the `.env.template` and populate with your values.
```
cp .env.template .env
```
## Usage
```
python main.py
```
+63
View File
@@ -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()
+5
View File
@@ -0,0 +1,5 @@
LANGSMITH_API_KEY=""
STRIPE_SECRET_KEY=""
OPENAI_API_BASE=""
OPENAI_MODEL_NAME="gpt-4o"
OPENAI_API_KEY=""
+15
View File
@@ -0,0 +1,15 @@
# LangChain Example
## Setup
Copy the `.env.template` and populate with your values.
```
cp .env.template .env
```
## Usage
```
python main.py
```
+51
View File
@@ -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"])
+40
View File
@@ -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
+12
View File
@@ -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
+60
View File
@@ -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
+181
View File
@@ -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
+80
View File
@@ -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.
"""
+151
View File
@@ -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
+153
View File
@@ -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,
}
}
},
]
View File
+58
View File
@@ -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()
+256
View File
@@ -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()
+132
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
{
"singleQuote": true,
"trailingComma": "es5",
"bracketSpacing": false
}
+98
View File
@@ -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
+311
View File
@@ -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",
},
}];
+4
View File
@@ -0,0 +1,4 @@
STRIPE_SECRET_KEY=""
STRIPE_CUSTOMER_ID=""
STRIPE_METER_INPUT=""
STRIPE_METER_OUTPUT=""
+15
View File
@@ -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
```
+52
View File
@@ -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);
})();
+19
View File
@@ -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"
}
}
+8
View File
@@ -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=""
+15
View File
@@ -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
```
+57
View File
@@ -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"]
}
+11
View File
@@ -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;
+69
View File
@@ -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"
]
}
+6300
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
packages:
- '.'
- 'examples/*'
+90
View File
@@ -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 ""
+2
View File
@@ -0,0 +1,2 @@
import StripeAgentToolkit from './toolkit';
export {StripeAgentToolkit};
+19
View File
@@ -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);
},
});
}
+120
View File
@@ -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;
+2
View File
@@ -0,0 +1,2 @@
import StripeAgentToolkit from './toolkit';
export {StripeAgentToolkit};
+43
View File
@@ -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;
+42
View File
@@ -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;
+88
View File
@@ -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;
+47
View File
@@ -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;
});
});
};
+162
View File
@@ -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';
}
};
+106
View File
@@ -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({});
+79
View File
@@ -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.
`;
+165
View File
@@ -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);
});
});
+15
View File
@@ -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"]
}
+18
View File
@@ -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,
},
]);