# LangGraph Integration

The `@shade402/langgraph` package provides payment nodes for LangGraph workflows, enabling agents to make X402 payments as part of their execution graph.

## Installation

```bash
pnpm add @shade402/langgraph @shade402/client @shade402/core @langchain/langgraph
```

## Quick Start

```typescript
import { StateGraph } from '@langchain/langgraph';
import { fetchWithPaymentNode } from '@shade402/langgraph';
import { Keypair } from '@solana/web3.js';
import { PaymentState } from '@shade402/langgraph';

const workflow = new StateGraph<PaymentState>({
  channels: {
    api_url: { reducer: (x, y) => y ?? x },
    api_response: { reducer: (x, y) => y ?? x },
    wallet_keypair: { reducer: (x, y) => y ?? x },
  },
});

workflow.addNode('fetch_api', fetchWithPaymentNode);
workflow.setEntryPoint('fetch_api');

const app = workflow.compile();

const result = await app.invoke({
  api_url: 'https://api.example.com/premium-data',
  wallet_keypair: Keypair.generate(),
});
```

## Payment Nodes

### `fetchWithPaymentNode`

Combined node that fetches API and handles payment automatically:

```typescript
import { fetchWithPaymentNode } from '@shade402/langgraph';

workflow.addNode('fetch_api', fetchWithPaymentNode);
```

This node:

1. Makes HTTP request to `state.api_url`
2. If 402 received, automatically creates payment
3. Retries request with payment
4. Returns API response in `state.api_response`

### `paymentNode`

Separate payment node for more control:

```typescript
import { paymentNode } from '@shade402/langgraph';

workflow.addNode('make_payment', paymentNode);
```

This node:

1. Expects `state.payment_required` to be true
2. Makes payment for `state.api_url`
3. Updates `state.api_response` and `state.payment_completed`

## Payment State

The `PaymentState` interface defines the state structure:

```typescript
interface PaymentState {
  wallet_keypair?: Keypair;        // Wallet for payments
  api_url?: string;                // URL to fetch
  api_response?: string;           // API response (JSON string)
  payment_completed?: boolean;     // Payment completion status
  payment_error?: string | null;   // Payment error message
  payment_required?: boolean;      // Whether payment is required
  max_payment_amount?: string;     // Maximum payment amount
  http_method?: string;            // HTTP method (default: 'GET')
  allow_local?: boolean;           // Allow localhost URLs
  rpc_url?: string;                // Solana RPC URL
  [key: string]: any;              // Additional state fields
}
```

## Workflow Patterns

### Simple Payment Flow

```typescript
import { StateGraph } from '@langchain/langgraph';
import { fetchWithPaymentNode } from '@shade402/langgraph';
import { PaymentState } from '@shade402/langgraph';

const workflow = new StateGraph<PaymentState>({
  channels: {
    api_url: { reducer: (x, y) => y ?? x },
    api_response: { reducer: (x, y) => y ?? x },
    wallet_keypair: { reducer: (x, y) => y ?? x },
    max_payment_amount: { reducer: (x, y) => y ?? x },
  },
});

workflow.addNode('fetch_api', fetchWithPaymentNode);
workflow.addNode('process', async (state: PaymentState) => {
  const data = JSON.parse(state.api_response || '{}');
  return { ...state, processed: data };
});

workflow.setEntryPoint('fetch_api');
workflow.addEdge('fetch_api', 'process');

const app = workflow.compile();
```

### Conditional Payment Flow

```typescript
import { StateGraph } from '@langchain/langgraph';
import { paymentNode, checkPaymentRequired, checkPaymentCompleted } from '@shade402/langgraph';
import { PaymentState } from '@shade402/langgraph';

const workflow = new StateGraph<PaymentState>({
  channels: {
    api_url: { reducer: (x, y) => y ?? x },
    api_response: { reducer: (x, y) => y ?? x },
    wallet_keypair: { reducer: (x, y) => y ?? x },
    payment_required: { reducer: (x, y) => y ?? x },
    payment_completed: { reducer: (x, y) => y ?? x },
  },
});

// Initial fetch node
workflow.addNode('fetch_api', async (state: PaymentState) => {
  // Make initial request
  const response = await fetch(state.api_url!);
  
  if (response.status === 402) {
    return { ...state, payment_required: true };
  }
  
  const data = await response.json();
  return { ...state, api_response: JSON.stringify(data) };
});

// Payment node
workflow.addNode('make_payment', paymentNode);

// Process node
workflow.addNode('process', async (state: PaymentState) => {
  const data = JSON.parse(state.api_response || '{}');
  return { ...state, processed: data };
});

// Error handler
workflow.addNode('error_handler', async (state: PaymentState) => {
  return { ...state, error: state.payment_error };
});

// Edges
workflow.setEntryPoint('fetch_api');

workflow.addConditionalEdges(
  'fetch_api',
  checkPaymentRequired,
  {
    payment_required: 'make_payment',
    success: 'process',
    error: 'error_handler',
  }
);

workflow.addConditionalEdges(
  'make_payment',
  checkPaymentCompleted,
  {
    completed: 'process',
    failed: 'error_handler',
  }
);

const app = workflow.compile();
```

### Multiple API Calls

```typescript
const workflow = new StateGraph<PaymentState>({
  channels: {
    api_urls: { reducer: (x, y) => y ?? x },
    api_responses: { reducer: (x, y) => y ?? x },
    wallet_keypair: { reducer: (x, y) => y ?? x },
  },
});

// Fetch multiple APIs
workflow.addNode('fetch_apis', async (state: PaymentState) => {
  const urls = state.api_urls || [];
  const responses: string[] = [];
  
  for (const url of urls) {
    const nodeState = {
      ...state,
      api_url: url,
    };
    
    const result = await fetchWithPaymentNode(nodeState);
    responses.push(result.api_response || '');
  }
  
  return { ...state, api_responses: responses };
});

workflow.setEntryPoint('fetch_apis');
```

## Conditional Functions

### `checkPaymentRequired`

Routes based on whether payment is required:

```typescript
import { checkPaymentRequired } from '@shade402/langgraph';

workflow.addConditionalEdges(
  'fetch_api',
  checkPaymentRequired,
  {
    payment_required: 'make_payment',
    success: 'process',
    error: 'error_handler',
  }
);
```

Returns:

* `'payment_required'`: If `state.payment_required` is true
* `'success'`: If `state.api_response` exists
* `'error'`: Otherwise

### `checkPaymentCompleted`

Routes based on payment completion:

```typescript
import { checkPaymentCompleted } from '@shade402/langgraph';

workflow.addConditionalEdges(
  'make_payment',
  checkPaymentCompleted,
  {
    completed: 'process',
    failed: 'error_handler',
  }
);
```

Returns:

* `'completed'`: If `state.payment_completed` is true
* `'failed'`: Otherwise

### Custom Conditional Functions

```typescript
function shouldRetryPayment(state: PaymentState): string {
  if (state.payment_error && state.payment_error.includes('INSUFFICIENT_FUNDS')) {
    return 'error'; // Don't retry insufficient funds
  }
  
  if (state.payment_error) {
    return 'retry'; // Retry other errors
  }
  
  return 'continue';
}

workflow.addConditionalEdges(
  'make_payment',
  shouldRetryPayment,
  {
    continue: 'process',
    retry: 'make_payment',
    error: 'error_handler',
  }
);
```

## Complete Example

```typescript
import { StateGraph } from '@langchain/langgraph';
import { fetchWithPaymentNode, PaymentState } from '@shade402/langgraph';
import { Keypair } from '@solana/web3.js';

interface MyState extends PaymentState {
  query: string;
  result?: any;
}

const workflow = new StateGraph<MyState>({
  channels: {
    query: { reducer: (x, y) => y ?? x },
    api_url: { reducer: (x, y) => y ?? x },
    api_response: { reducer: (x, y) => y ?? x },
    wallet_keypair: { reducer: (x, y) => y ?? x },
    max_payment_amount: { reducer: (x, y) => y ?? x },
    result: { reducer: (x, y) => y ?? x },
  },
});

// Fetch API
workflow.addNode('fetch_api', fetchWithPaymentNode);

// Process response
workflow.addNode('process', async (state: MyState) => {
  const data = JSON.parse(state.api_response || '{}');
  return {
    ...state,
    result: {
      query: state.query,
      data: data,
      paymentId: data.paymentId,
    },
  };
});

// Error handler
workflow.addNode('error_handler', async (state: MyState) => {
  return {
    ...state,
    result: {
      error: state.payment_error,
      query: state.query,
    },
  };
});

workflow.setEntryPoint('fetch_api');

workflow.addConditionalEdges(
  'fetch_api',
  (state: MyState) => {
    if (state.payment_error) return 'error';
    if (state.api_response) return 'success';
    return 'error';
  },
  {
    success: 'process',
    error: 'error_handler',
  }
);

const app = workflow.compile();

// Run workflow
const wallet = Keypair.generate();
const result = await app.invoke({
  query: 'Analyze this data',
  api_url: 'https://api.example.com/analyze',
  wallet_keypair: wallet,
  max_payment_amount: '1.0',
  rpc_url: process.env.SOLANA_RPC_URL,
});

console.log(result.result);
```

## Best Practices

1. Define clear state structure
2. Handle errors appropriately
3. Set maximum payment amounts
4. Use conditional edges for flow control
5. Process API responses in separate nodes
6. Monitor payment usage
7. Log payment transactions
8. Test with devnet before production
9. Use secure wallet storage
10. Consider rate limiting

## Next Steps

* Learn about [LangChain Integration](https://docs.shade402.xyz/ai-agent-integration/langchain-integration)
* Check out [Examples](https://docs.shade402.xyz/advanced-topics/examples) for more workflows
* Review [Security](https://docs.shade402.xyz/advanced-topics/security) best practices
