@nevermined-io/payments 1.1.4 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/x402/langchain/decorator.d.ts +146 -0
- package/dist/x402/langchain/decorator.d.ts.map +1 -0
- package/dist/x402/langchain/decorator.js +190 -0
- package/dist/x402/langchain/decorator.js.map +1 -0
- package/dist/x402/langchain/index.d.ts +5 -0
- package/dist/x402/langchain/index.d.ts.map +1 -0
- package/dist/x402/langchain/index.js +5 -0
- package/dist/x402/langchain/index.js.map +1 -0
- package/package.json +10 -2
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LangChain tool wrapper for Nevermined payment protection using the x402 protocol.
|
|
3
|
+
*
|
|
4
|
+
* Wraps a LangChain.js tool implementation function to:
|
|
5
|
+
*
|
|
6
|
+
* 1. Extract the x402 payment token from `config.configurable.payment_token`
|
|
7
|
+
* 2. Verify the subscriber has sufficient credits
|
|
8
|
+
* 3. Execute the wrapped tool function
|
|
9
|
+
* 4. Settle (burn) credits after successful execution
|
|
10
|
+
*
|
|
11
|
+
* Payment errors throw `PaymentRequiredError` so LangChain agents can catch and
|
|
12
|
+
* surface them to the user. The error carries the full `X402PaymentRequired`
|
|
13
|
+
* object for programmatic token acquisition.
|
|
14
|
+
*
|
|
15
|
+
* The `credits` option accepts two forms:
|
|
16
|
+
* - **Static number**: `credits: 1` — always charges 1 credit
|
|
17
|
+
* - **Function**: `credits: (ctx) => Math.max(1, ctx.result.length / 100)` — dynamic
|
|
18
|
+
*
|
|
19
|
+
* When `credits` is a function, it receives `{ args, result }` after tool execution.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* import { tool } from '@langchain/core/tools'
|
|
24
|
+
* import { z } from 'zod'
|
|
25
|
+
* import { Payments } from '@nevermined-io/payments'
|
|
26
|
+
* import { requiresPayment } from '@nevermined-io/payments/langchain'
|
|
27
|
+
*
|
|
28
|
+
* const payments = Payments.getInstance({ nvmApiKey: '...', environment: 'testing' })
|
|
29
|
+
*
|
|
30
|
+
* const searchData = tool(
|
|
31
|
+
* requiresPayment(
|
|
32
|
+
* (args) => `Results for ${args.query}`,
|
|
33
|
+
* { payments, planId: PLAN_ID, credits: 1 }
|
|
34
|
+
* ),
|
|
35
|
+
* { name: 'search_data', description: 'Search for data', schema: z.object({ query: z.string() }) }
|
|
36
|
+
* )
|
|
37
|
+
*
|
|
38
|
+
* // Invoke with payment token
|
|
39
|
+
* const result = await searchData.invoke(
|
|
40
|
+
* { query: 'AI trends' },
|
|
41
|
+
* { configurable: { payment_token: accessToken } }
|
|
42
|
+
* )
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
import type { Payments } from '../../payments.js';
|
|
46
|
+
import { type X402PaymentRequired } from '../facilitator-api.js';
|
|
47
|
+
/**
|
|
48
|
+
* Context passed to a dynamic credits function after tool execution.
|
|
49
|
+
*/
|
|
50
|
+
export interface CreditsContext {
|
|
51
|
+
/** The tool's input arguments */
|
|
52
|
+
args: Record<string, unknown>;
|
|
53
|
+
/** The tool's return value */
|
|
54
|
+
result: unknown;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Credits can be a static number or a function that receives
|
|
58
|
+
* `{ args, result }` and returns the number of credits to charge.
|
|
59
|
+
*/
|
|
60
|
+
export type CreditsCallable = (ctx: CreditsContext) => number;
|
|
61
|
+
/**
|
|
62
|
+
* Options for the `requiresPayment` wrapper.
|
|
63
|
+
*/
|
|
64
|
+
export interface RequiresPaymentOptions {
|
|
65
|
+
/** The Payments instance (with payments.facilitator) */
|
|
66
|
+
payments: Payments;
|
|
67
|
+
/** Single plan ID to accept */
|
|
68
|
+
planId: string;
|
|
69
|
+
/** Number of credits to charge, or a function for dynamic pricing (default: 1) */
|
|
70
|
+
credits?: number | CreditsCallable;
|
|
71
|
+
/** Optional agent identifier */
|
|
72
|
+
agentId?: string;
|
|
73
|
+
/** Blockchain network in CAIP-2 format (default: 'eip155:84532' for Base Sepolia) */
|
|
74
|
+
network?: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Thrown when payment verification fails or no token is provided.
|
|
78
|
+
*
|
|
79
|
+
* Carries the `X402PaymentRequired` object so callers can inspect
|
|
80
|
+
* accepted plans and acquire the correct payment token.
|
|
81
|
+
*/
|
|
82
|
+
export declare class PaymentRequiredError extends Error {
|
|
83
|
+
/** The x402 PaymentRequired object for programmatic token acquisition */
|
|
84
|
+
paymentRequired: X402PaymentRequired | undefined;
|
|
85
|
+
constructor(message: string, paymentRequired?: X402PaymentRequired);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Payment context stored in `config.configurable.payment_context` after verification.
|
|
89
|
+
*/
|
|
90
|
+
export interface PaymentContext {
|
|
91
|
+
/** The x402 access token */
|
|
92
|
+
token: string;
|
|
93
|
+
/** The payment required object */
|
|
94
|
+
paymentRequired: X402PaymentRequired;
|
|
95
|
+
/** Number of credits to settle */
|
|
96
|
+
creditsToSettle: number;
|
|
97
|
+
/** Whether verification was successful */
|
|
98
|
+
verified: boolean;
|
|
99
|
+
/** Agent request ID for observability tracking */
|
|
100
|
+
agentRequestId?: string;
|
|
101
|
+
/** Agent request context for observability */
|
|
102
|
+
agentRequest?: unknown;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Wraps a LangChain.js tool implementation with x402 payment verification and settlement.
|
|
106
|
+
*
|
|
107
|
+
* This is a higher-order function that takes the tool's implementation function and
|
|
108
|
+
* payment options, returning a new function with the same signature that:
|
|
109
|
+
*
|
|
110
|
+
* 1. Extracts the payment token from `config.configurable.payment_token`
|
|
111
|
+
* 2. Verifies the subscriber has sufficient credits
|
|
112
|
+
* 3. Calls the original tool function
|
|
113
|
+
* 4. Settles (burns) credits
|
|
114
|
+
* 5. Stores `payment_context` and `payment_settlement` in `config.configurable`
|
|
115
|
+
*
|
|
116
|
+
* @param fn - The tool implementation function: `(args, config?) => result`
|
|
117
|
+
* @param options - Payment configuration
|
|
118
|
+
* @returns Wrapped function with the same signature
|
|
119
|
+
*
|
|
120
|
+
* @example Static credits
|
|
121
|
+
* ```typescript
|
|
122
|
+
* const searchData = tool(
|
|
123
|
+
* requiresPayment(
|
|
124
|
+
* (args) => `Results for ${args.query}`,
|
|
125
|
+
* { payments, planId: PLAN_ID, credits: 1 }
|
|
126
|
+
* ),
|
|
127
|
+
* { name: 'search_data', description: 'Search', schema: z.object({ query: z.string() }) }
|
|
128
|
+
* )
|
|
129
|
+
* ```
|
|
130
|
+
*
|
|
131
|
+
* @example Dynamic credits
|
|
132
|
+
* ```typescript
|
|
133
|
+
* const summarize = tool(
|
|
134
|
+
* requiresPayment(
|
|
135
|
+
* (args) => `Summary of ${args.text}`,
|
|
136
|
+
* {
|
|
137
|
+
* payments, planId: PLAN_ID,
|
|
138
|
+
* credits: (ctx) => Math.max(1, Math.floor(String(ctx.result).length / 100)),
|
|
139
|
+
* }
|
|
140
|
+
* ),
|
|
141
|
+
* { name: 'summarize', description: 'Summarize text', schema: z.object({ text: z.string() }) }
|
|
142
|
+
* )
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
export declare function requiresPayment<TArgs extends Record<string, unknown>, TResult>(fn: (args: TArgs, config?: unknown) => TResult | Promise<TResult>, options: RequiresPaymentOptions): (args: TArgs, config?: unknown) => Promise<TResult>;
|
|
146
|
+
//# sourceMappingURL=decorator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorator.d.ts","sourceRoot":"","sources":["../../../src/x402/langchain/decorator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,uBAAuB,CAAA;AAE9B;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,8BAA8B;IAC9B,MAAM,EAAE,OAAO,CAAA;CAChB;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,cAAc,KAAK,MAAM,CAAA;AAE7D;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,wDAAwD;IACxD,QAAQ,EAAE,QAAQ,CAAA;IAClB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,GAAG,eAAe,CAAA;IAClC,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;GAKG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,yEAAyE;IACzE,eAAe,EAAE,mBAAmB,GAAG,SAAS,CAAA;gBAEpC,OAAO,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,mBAAmB;CAKnE;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,kCAAkC;IAClC,eAAe,EAAE,mBAAmB,CAAA;IACpC,kCAAkC;IAClC,eAAe,EAAE,MAAM,CAAA;IACvB,0CAA0C;IAC1C,QAAQ,EAAE,OAAO,CAAA;IACjB,kDAAkD;IAClD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,8CAA8C;IAC9C,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AA8BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,eAAe,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAC5E,EAAE,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,EACjE,OAAO,EAAE,sBAAsB,GAC9B,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAiFrD"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LangChain tool wrapper for Nevermined payment protection using the x402 protocol.
|
|
3
|
+
*
|
|
4
|
+
* Wraps a LangChain.js tool implementation function to:
|
|
5
|
+
*
|
|
6
|
+
* 1. Extract the x402 payment token from `config.configurable.payment_token`
|
|
7
|
+
* 2. Verify the subscriber has sufficient credits
|
|
8
|
+
* 3. Execute the wrapped tool function
|
|
9
|
+
* 4. Settle (burn) credits after successful execution
|
|
10
|
+
*
|
|
11
|
+
* Payment errors throw `PaymentRequiredError` so LangChain agents can catch and
|
|
12
|
+
* surface them to the user. The error carries the full `X402PaymentRequired`
|
|
13
|
+
* object for programmatic token acquisition.
|
|
14
|
+
*
|
|
15
|
+
* The `credits` option accepts two forms:
|
|
16
|
+
* - **Static number**: `credits: 1` — always charges 1 credit
|
|
17
|
+
* - **Function**: `credits: (ctx) => Math.max(1, ctx.result.length / 100)` — dynamic
|
|
18
|
+
*
|
|
19
|
+
* When `credits` is a function, it receives `{ args, result }` after tool execution.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* import { tool } from '@langchain/core/tools'
|
|
24
|
+
* import { z } from 'zod'
|
|
25
|
+
* import { Payments } from '@nevermined-io/payments'
|
|
26
|
+
* import { requiresPayment } from '@nevermined-io/payments/langchain'
|
|
27
|
+
*
|
|
28
|
+
* const payments = Payments.getInstance({ nvmApiKey: '...', environment: 'testing' })
|
|
29
|
+
*
|
|
30
|
+
* const searchData = tool(
|
|
31
|
+
* requiresPayment(
|
|
32
|
+
* (args) => `Results for ${args.query}`,
|
|
33
|
+
* { payments, planId: PLAN_ID, credits: 1 }
|
|
34
|
+
* ),
|
|
35
|
+
* { name: 'search_data', description: 'Search for data', schema: z.object({ query: z.string() }) }
|
|
36
|
+
* )
|
|
37
|
+
*
|
|
38
|
+
* // Invoke with payment token
|
|
39
|
+
* const result = await searchData.invoke(
|
|
40
|
+
* { query: 'AI trends' },
|
|
41
|
+
* { configurable: { payment_token: accessToken } }
|
|
42
|
+
* )
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
import { buildPaymentRequired, } from '../facilitator-api.js';
|
|
46
|
+
/**
|
|
47
|
+
* Thrown when payment verification fails or no token is provided.
|
|
48
|
+
*
|
|
49
|
+
* Carries the `X402PaymentRequired` object so callers can inspect
|
|
50
|
+
* accepted plans and acquire the correct payment token.
|
|
51
|
+
*/
|
|
52
|
+
export class PaymentRequiredError extends Error {
|
|
53
|
+
constructor(message, paymentRequired) {
|
|
54
|
+
super(message);
|
|
55
|
+
this.name = 'PaymentRequiredError';
|
|
56
|
+
this.paymentRequired = paymentRequired;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Extract the payment token from a LangChain RunnableConfig.
|
|
61
|
+
*
|
|
62
|
+
* In LangChain.js, the config is the optional second parameter to tool functions:
|
|
63
|
+
* `(args, config?) => ...` where config is a `RunnableConfig`.
|
|
64
|
+
*/
|
|
65
|
+
function extractPaymentToken(config) {
|
|
66
|
+
if (config == null || typeof config !== 'object')
|
|
67
|
+
return null;
|
|
68
|
+
const configurable = config.configurable;
|
|
69
|
+
if (configurable == null || typeof configurable !== 'object')
|
|
70
|
+
return null;
|
|
71
|
+
const token = configurable.payment_token;
|
|
72
|
+
return typeof token === 'string' ? token : null;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Store a value in config.configurable if available.
|
|
76
|
+
*/
|
|
77
|
+
function storeInConfigurable(config, key, value) {
|
|
78
|
+
if (config == null || typeof config !== 'object')
|
|
79
|
+
return;
|
|
80
|
+
const configurable = config.configurable;
|
|
81
|
+
if (configurable == null || typeof configurable !== 'object')
|
|
82
|
+
return;
|
|
83
|
+
configurable[key] = value;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Wraps a LangChain.js tool implementation with x402 payment verification and settlement.
|
|
87
|
+
*
|
|
88
|
+
* This is a higher-order function that takes the tool's implementation function and
|
|
89
|
+
* payment options, returning a new function with the same signature that:
|
|
90
|
+
*
|
|
91
|
+
* 1. Extracts the payment token from `config.configurable.payment_token`
|
|
92
|
+
* 2. Verifies the subscriber has sufficient credits
|
|
93
|
+
* 3. Calls the original tool function
|
|
94
|
+
* 4. Settles (burns) credits
|
|
95
|
+
* 5. Stores `payment_context` and `payment_settlement` in `config.configurable`
|
|
96
|
+
*
|
|
97
|
+
* @param fn - The tool implementation function: `(args, config?) => result`
|
|
98
|
+
* @param options - Payment configuration
|
|
99
|
+
* @returns Wrapped function with the same signature
|
|
100
|
+
*
|
|
101
|
+
* @example Static credits
|
|
102
|
+
* ```typescript
|
|
103
|
+
* const searchData = tool(
|
|
104
|
+
* requiresPayment(
|
|
105
|
+
* (args) => `Results for ${args.query}`,
|
|
106
|
+
* { payments, planId: PLAN_ID, credits: 1 }
|
|
107
|
+
* ),
|
|
108
|
+
* { name: 'search_data', description: 'Search', schema: z.object({ query: z.string() }) }
|
|
109
|
+
* )
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* @example Dynamic credits
|
|
113
|
+
* ```typescript
|
|
114
|
+
* const summarize = tool(
|
|
115
|
+
* requiresPayment(
|
|
116
|
+
* (args) => `Summary of ${args.text}`,
|
|
117
|
+
* {
|
|
118
|
+
* payments, planId: PLAN_ID,
|
|
119
|
+
* credits: (ctx) => Math.max(1, Math.floor(String(ctx.result).length / 100)),
|
|
120
|
+
* }
|
|
121
|
+
* ),
|
|
122
|
+
* { name: 'summarize', description: 'Summarize text', schema: z.object({ text: z.string() }) }
|
|
123
|
+
* )
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
export function requiresPayment(fn, options) {
|
|
127
|
+
const { payments, planId, credits = 1, agentId, network } = options;
|
|
128
|
+
return async (args, config) => {
|
|
129
|
+
// Build payment required object
|
|
130
|
+
const paymentRequired = buildPaymentRequired(planId, {
|
|
131
|
+
endpoint: fn.name || 'tool',
|
|
132
|
+
agentId,
|
|
133
|
+
network,
|
|
134
|
+
});
|
|
135
|
+
// Extract token from config.configurable.payment_token
|
|
136
|
+
const token = extractPaymentToken(config);
|
|
137
|
+
if (!token) {
|
|
138
|
+
throw new PaymentRequiredError("Payment required: missing payment_token in config.configurable", paymentRequired);
|
|
139
|
+
}
|
|
140
|
+
// Resolve pre-execution credits (static only; callable deferred to post-execution)
|
|
141
|
+
const creditsToVerify = typeof credits === 'number' ? credits : 1;
|
|
142
|
+
// Verify permissions
|
|
143
|
+
let verification;
|
|
144
|
+
try {
|
|
145
|
+
verification = await payments.facilitator.verifyPermissions({
|
|
146
|
+
paymentRequired,
|
|
147
|
+
x402AccessToken: token,
|
|
148
|
+
maxAmount: BigInt(creditsToVerify),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
throw new PaymentRequiredError(`Payment verification failed: ${error instanceof Error ? error.message : String(error)}`, paymentRequired);
|
|
153
|
+
}
|
|
154
|
+
if (!verification.isValid) {
|
|
155
|
+
throw new PaymentRequiredError(`Payment verification failed: ${verification.invalidReason || 'Insufficient credits or invalid token'}`, paymentRequired);
|
|
156
|
+
}
|
|
157
|
+
// Store payment context
|
|
158
|
+
const paymentContext = {
|
|
159
|
+
token,
|
|
160
|
+
paymentRequired,
|
|
161
|
+
creditsToSettle: creditsToVerify,
|
|
162
|
+
verified: true,
|
|
163
|
+
agentRequestId: verification.agentRequest?.agentRequestId || verification.agentRequestId,
|
|
164
|
+
agentRequest: verification.agentRequest,
|
|
165
|
+
};
|
|
166
|
+
storeInConfigurable(config, 'payment_context', paymentContext);
|
|
167
|
+
// Execute the tool function
|
|
168
|
+
const result = await fn(args, config);
|
|
169
|
+
// Resolve final credits (may be dynamic based on result)
|
|
170
|
+
const finalCredits = typeof credits === 'function'
|
|
171
|
+
? credits({ args: args, result })
|
|
172
|
+
: credits;
|
|
173
|
+
// Settle credits
|
|
174
|
+
try {
|
|
175
|
+
const settlement = await payments.facilitator.settlePermissions({
|
|
176
|
+
paymentRequired,
|
|
177
|
+
x402AccessToken: token,
|
|
178
|
+
maxAmount: BigInt(finalCredits),
|
|
179
|
+
agentRequestId: paymentContext.agentRequestId,
|
|
180
|
+
});
|
|
181
|
+
storeInConfigurable(config, 'payment_settlement', settlement);
|
|
182
|
+
}
|
|
183
|
+
catch (settleError) {
|
|
184
|
+
console.error('Payment settlement failed:', settleError);
|
|
185
|
+
// Still return result even if settlement fails
|
|
186
|
+
}
|
|
187
|
+
return result;
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=decorator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorator.js","sourceRoot":"","sources":["../../../src/x402/langchain/decorator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * LangChain tool wrapper for Nevermined payment protection using the x402 protocol.\n *\n * Wraps a LangChain.js tool implementation function to:\n *\n * 1. Extract the x402 payment token from `config.configurable.payment_token`\n * 2. Verify the subscriber has sufficient credits\n * 3. Execute the wrapped tool function\n * 4. Settle (burn) credits after successful execution\n *\n * Payment errors throw `PaymentRequiredError` so LangChain agents can catch and\n * surface them to the user. The error carries the full `X402PaymentRequired`\n * object for programmatic token acquisition.\n *\n * The `credits` option accepts two forms:\n * - **Static number**: `credits: 1` — always charges 1 credit\n * - **Function**: `credits: (ctx) => Math.max(1, ctx.result.length / 100)` — dynamic\n *\n * When `credits` is a function, it receives `{ args, result }` after tool execution.\n *\n * @example\n * ```typescript\n * import { tool } from '@langchain/core/tools'\n * import { z } from 'zod'\n * import { Payments } from '@nevermined-io/payments'\n * import { requiresPayment } from '@nevermined-io/payments/langchain'\n *\n * const payments = Payments.getInstance({ nvmApiKey: '...', environment: 'testing' })\n *\n * const searchData = tool(\n * requiresPayment(\n * (args) => `Results for ${args.query}`,\n * { payments, planId: PLAN_ID, credits: 1 }\n * ),\n * { name: 'search_data', description: 'Search for data', schema: z.object({ query: z.string() }) }\n * )\n *\n * // Invoke with payment token\n * const result = await searchData.invoke(\n * { query: 'AI trends' },\n * { configurable: { payment_token: accessToken } }\n * )\n * ```\n */\n\nimport type { Payments } from '../../payments.js'\nimport {\n buildPaymentRequired,\n type X402PaymentRequired,\n type VerifyPermissionsResult,\n} from '../facilitator-api.js'\n\n/**\n * Context passed to a dynamic credits function after tool execution.\n */\nexport interface CreditsContext {\n /** The tool's input arguments */\n args: Record<string, unknown>\n /** The tool's return value */\n result: unknown\n}\n\n/**\n * Credits can be a static number or a function that receives\n * `{ args, result }` and returns the number of credits to charge.\n */\nexport type CreditsCallable = (ctx: CreditsContext) => number\n\n/**\n * Options for the `requiresPayment` wrapper.\n */\nexport interface RequiresPaymentOptions {\n /** The Payments instance (with payments.facilitator) */\n payments: Payments\n /** Single plan ID to accept */\n planId: string\n /** Number of credits to charge, or a function for dynamic pricing (default: 1) */\n credits?: number | CreditsCallable\n /** Optional agent identifier */\n agentId?: string\n /** Blockchain network in CAIP-2 format (default: 'eip155:84532' for Base Sepolia) */\n network?: string\n}\n\n/**\n * Thrown when payment verification fails or no token is provided.\n *\n * Carries the `X402PaymentRequired` object so callers can inspect\n * accepted plans and acquire the correct payment token.\n */\nexport class PaymentRequiredError extends Error {\n /** The x402 PaymentRequired object for programmatic token acquisition */\n paymentRequired: X402PaymentRequired | undefined\n\n constructor(message: string, paymentRequired?: X402PaymentRequired) {\n super(message)\n this.name = 'PaymentRequiredError'\n this.paymentRequired = paymentRequired\n }\n}\n\n/**\n * Payment context stored in `config.configurable.payment_context` after verification.\n */\nexport interface PaymentContext {\n /** The x402 access token */\n token: string\n /** The payment required object */\n paymentRequired: X402PaymentRequired\n /** Number of credits to settle */\n creditsToSettle: number\n /** Whether verification was successful */\n verified: boolean\n /** Agent request ID for observability tracking */\n agentRequestId?: string\n /** Agent request context for observability */\n agentRequest?: unknown\n}\n\n/**\n * Extract the payment token from a LangChain RunnableConfig.\n *\n * In LangChain.js, the config is the optional second parameter to tool functions:\n * `(args, config?) => ...` where config is a `RunnableConfig`.\n */\nfunction extractPaymentToken(config: unknown): string | null {\n if (config == null || typeof config !== 'object') return null\n\n const configurable = (config as Record<string, unknown>).configurable\n if (configurable == null || typeof configurable !== 'object') return null\n\n const token = (configurable as Record<string, unknown>).payment_token\n return typeof token === 'string' ? token : null\n}\n\n/**\n * Store a value in config.configurable if available.\n */\nfunction storeInConfigurable(config: unknown, key: string, value: unknown): void {\n if (config == null || typeof config !== 'object') return\n\n const configurable = (config as Record<string, unknown>).configurable\n if (configurable == null || typeof configurable !== 'object') return\n\n ;(configurable as Record<string, unknown>)[key] = value\n}\n\n/**\n * Wraps a LangChain.js tool implementation with x402 payment verification and settlement.\n *\n * This is a higher-order function that takes the tool's implementation function and\n * payment options, returning a new function with the same signature that:\n *\n * 1. Extracts the payment token from `config.configurable.payment_token`\n * 2. Verifies the subscriber has sufficient credits\n * 3. Calls the original tool function\n * 4. Settles (burns) credits\n * 5. Stores `payment_context` and `payment_settlement` in `config.configurable`\n *\n * @param fn - The tool implementation function: `(args, config?) => result`\n * @param options - Payment configuration\n * @returns Wrapped function with the same signature\n *\n * @example Static credits\n * ```typescript\n * const searchData = tool(\n * requiresPayment(\n * (args) => `Results for ${args.query}`,\n * { payments, planId: PLAN_ID, credits: 1 }\n * ),\n * { name: 'search_data', description: 'Search', schema: z.object({ query: z.string() }) }\n * )\n * ```\n *\n * @example Dynamic credits\n * ```typescript\n * const summarize = tool(\n * requiresPayment(\n * (args) => `Summary of ${args.text}`,\n * {\n * payments, planId: PLAN_ID,\n * credits: (ctx) => Math.max(1, Math.floor(String(ctx.result).length / 100)),\n * }\n * ),\n * { name: 'summarize', description: 'Summarize text', schema: z.object({ text: z.string() }) }\n * )\n * ```\n */\nexport function requiresPayment<TArgs extends Record<string, unknown>, TResult>(\n fn: (args: TArgs, config?: unknown) => TResult | Promise<TResult>,\n options: RequiresPaymentOptions,\n): (args: TArgs, config?: unknown) => Promise<TResult> {\n const { payments, planId, credits = 1, agentId, network } = options\n\n return async (args: TArgs, config?: unknown): Promise<TResult> => {\n // Build payment required object\n const paymentRequired = buildPaymentRequired(planId, {\n endpoint: fn.name || 'tool',\n agentId,\n network,\n })\n\n // Extract token from config.configurable.payment_token\n const token = extractPaymentToken(config)\n if (!token) {\n throw new PaymentRequiredError(\n \"Payment required: missing payment_token in config.configurable\",\n paymentRequired,\n )\n }\n\n // Resolve pre-execution credits (static only; callable deferred to post-execution)\n const creditsToVerify = typeof credits === 'number' ? credits : 1\n\n // Verify permissions\n let verification: VerifyPermissionsResult\n try {\n verification = await payments.facilitator.verifyPermissions({\n paymentRequired,\n x402AccessToken: token,\n maxAmount: BigInt(creditsToVerify),\n })\n } catch (error) {\n throw new PaymentRequiredError(\n `Payment verification failed: ${error instanceof Error ? error.message : String(error)}`,\n paymentRequired,\n )\n }\n\n if (!verification.isValid) {\n throw new PaymentRequiredError(\n `Payment verification failed: ${verification.invalidReason || 'Insufficient credits or invalid token'}`,\n paymentRequired,\n )\n }\n\n // Store payment context\n const paymentContext: PaymentContext = {\n token,\n paymentRequired,\n creditsToSettle: creditsToVerify,\n verified: true,\n agentRequestId: verification.agentRequest?.agentRequestId || verification.agentRequestId,\n agentRequest: verification.agentRequest,\n }\n storeInConfigurable(config, 'payment_context', paymentContext)\n\n // Execute the tool function\n const result = await fn(args, config)\n\n // Resolve final credits (may be dynamic based on result)\n const finalCredits =\n typeof credits === 'function'\n ? credits({ args: args as Record<string, unknown>, result })\n : credits\n\n // Settle credits\n try {\n const settlement = await payments.facilitator.settlePermissions({\n paymentRequired,\n x402AccessToken: token,\n maxAmount: BigInt(finalCredits),\n agentRequestId: paymentContext.agentRequestId,\n })\n storeInConfigurable(config, 'payment_settlement', settlement)\n } catch (settleError) {\n console.error('Payment settlement failed:', settleError)\n // Still return result even if settlement fails\n }\n\n return result\n }\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LangChain integration for Nevermined payment protection using the x402 protocol.
|
|
3
|
+
*/
|
|
4
|
+
export { requiresPayment, PaymentRequiredError, type RequiresPaymentOptions, type CreditsCallable, type CreditsContext, type PaymentContext, } from './decorator.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/x402/langchain/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/x402/langchain/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,oBAAoB,GAKrB,MAAM,gBAAgB,CAAA","sourcesContent":["/**\n * LangChain integration for Nevermined payment protection using the x402 protocol.\n */\n\nexport {\n requiresPayment,\n PaymentRequiredError,\n type RequiresPaymentOptions,\n type CreditsCallable,\n type CreditsContext,\n type PaymentContext,\n} from './decorator.js'\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nevermined-io/payments",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"description": "Typescript SDK to interact with the Nevermined Payments Protocol",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -31,6 +31,10 @@
|
|
|
31
31
|
"./express": {
|
|
32
32
|
"types": "./dist/x402/express/index.d.ts",
|
|
33
33
|
"import": "./dist/x402/express/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./langchain": {
|
|
36
|
+
"types": "./dist/x402/langchain/index.d.ts",
|
|
37
|
+
"import": "./dist/x402/langchain/index.js"
|
|
34
38
|
}
|
|
35
39
|
},
|
|
36
40
|
"scripts": {
|
|
@@ -89,7 +93,8 @@
|
|
|
89
93
|
},
|
|
90
94
|
"peerDependencies": {
|
|
91
95
|
"@modelcontextprotocol/sdk": ">=1.25.0",
|
|
92
|
-
"express": ">=4.0.0"
|
|
96
|
+
"express": ">=4.0.0",
|
|
97
|
+
"@langchain/core": ">=0.3.0"
|
|
93
98
|
},
|
|
94
99
|
"peerDependenciesMeta": {
|
|
95
100
|
"@modelcontextprotocol/sdk": {
|
|
@@ -97,6 +102,9 @@
|
|
|
97
102
|
},
|
|
98
103
|
"express": {
|
|
99
104
|
"optional": true
|
|
105
|
+
},
|
|
106
|
+
"@langchain/core": {
|
|
107
|
+
"optional": true
|
|
100
108
|
}
|
|
101
109
|
},
|
|
102
110
|
"dependencies": {
|