Initial commit
This commit is contained in:
66
packages/ai/README.md
Normal file
66
packages/ai/README.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# @dbis-thirdweb/ai
|
||||
|
||||
Chain-aware AI prompts and actions for Chain 138.
|
||||
|
||||
## Usage
|
||||
|
||||
### Prompt Generation
|
||||
|
||||
```typescript
|
||||
import { createReadPrompt, createWritePrompt, validatePromptForChain138 } from '@dbis-thirdweb/ai';
|
||||
|
||||
// Create read prompt
|
||||
const readPrompt = createReadPrompt('getBalance', { address: '0x...' });
|
||||
|
||||
// Create write prompt
|
||||
const writePrompt = createWritePrompt('transferNative', {
|
||||
to: '0x...',
|
||||
amount: '1000000000000000000', // 1 ETH in wei
|
||||
});
|
||||
|
||||
// Validate prompt targets Chain 138
|
||||
validatePromptForChain138(userPrompt);
|
||||
```
|
||||
|
||||
### Read Actions
|
||||
|
||||
```typescript
|
||||
import {
|
||||
createReadBalanceAction,
|
||||
createReadBlockHeightAction,
|
||||
executeReadAction,
|
||||
} from '@dbis-thirdweb/ai';
|
||||
import { ThirdwebSDK } from '@thirdweb-dev/sdk';
|
||||
import { chain138 } from '@dbis-thirdweb/chain';
|
||||
|
||||
const sdk = new ThirdwebSDK(chain138);
|
||||
const provider = sdk.getProvider();
|
||||
|
||||
// Create action
|
||||
const action = createReadBalanceAction('0x...');
|
||||
|
||||
// Execute
|
||||
const result = await executeReadAction(action, sdk, provider);
|
||||
console.log(result); // { address, balance, balanceFormatted }
|
||||
```
|
||||
|
||||
### Write Actions
|
||||
|
||||
```typescript
|
||||
import { createTransferAction, executeWriteAction } from '@dbis-thirdweb/ai';
|
||||
|
||||
// Create transfer action
|
||||
const action = createTransferAction('0x...', '1000000000000000000');
|
||||
|
||||
// Execute (requires signer)
|
||||
const result = await executeWriteAction(action, sdk);
|
||||
console.log(result); // { txHash, chainId }
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Chain-aware prompt templates with Chain 138 guardrails
|
||||
- Read action templates (balance, block height, token info)
|
||||
- Write action templates (transfers, contract interactions)
|
||||
- Chain ID routing validation
|
||||
- Error handling for unsupported operations
|
||||
38
packages/ai/package.json
Normal file
38
packages/ai/package.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@dbis-thirdweb/ai",
|
||||
"version": "0.1.0",
|
||||
"description": "Chain-aware AI prompts and actions for Chain 138",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsup src/index.ts --format cjs,esm --dts",
|
||||
"lint": "eslint src",
|
||||
"test": "echo \"No tests yet\""
|
||||
},
|
||||
"keywords": [
|
||||
"thirdweb",
|
||||
"ai",
|
||||
"chain-138",
|
||||
"prompts"
|
||||
],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dbis-thirdweb/chain": "workspace:*",
|
||||
"@thirdweb-dev/sdk": "^4.0.0",
|
||||
"ethers": "^5.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.0.0",
|
||||
"tsup": "^8.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
}
|
||||
179
packages/ai/src/actions.ts
Normal file
179
packages/ai/src/actions.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
import type { ThirdwebSDK } from '@thirdweb-dev/sdk';
|
||||
import type { providers } from 'ethers';
|
||||
import { chain138 } from '@dbis-thirdweb/chain';
|
||||
import type { ReadAction, WriteAction } from './types';
|
||||
import { validateChainId } from './types';
|
||||
|
||||
/**
|
||||
* Read action templates
|
||||
*/
|
||||
|
||||
/**
|
||||
* Read balance action template
|
||||
*/
|
||||
export function createReadBalanceAction(address: string): ReadAction {
|
||||
return {
|
||||
type: 'read',
|
||||
description: `Read native token balance for address ${address}`,
|
||||
chainId: chain138.chainId,
|
||||
operation: 'getBalance',
|
||||
params: { address },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Read block height action template
|
||||
*/
|
||||
export function createReadBlockHeightAction(): ReadAction {
|
||||
return {
|
||||
type: 'read',
|
||||
description: 'Read current block height',
|
||||
chainId: chain138.chainId,
|
||||
operation: 'getBlockNumber',
|
||||
params: {},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Read token info action template
|
||||
*/
|
||||
export function createReadTokenInfoAction(contractAddress: string): ReadAction {
|
||||
return {
|
||||
type: 'read',
|
||||
description: `Read token information for contract ${contractAddress}`,
|
||||
chainId: chain138.chainId,
|
||||
operation: 'getTokenInfo',
|
||||
params: { contractAddress },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute read action
|
||||
*/
|
||||
export async function executeReadAction(
|
||||
action: ReadAction,
|
||||
sdk: ThirdwebSDK,
|
||||
provider: providers.Provider
|
||||
): Promise<unknown> {
|
||||
validateChainId(action.chainId);
|
||||
|
||||
switch (action.operation) {
|
||||
case 'getBalance': {
|
||||
const address = action.params.address as string;
|
||||
const balance = await provider.getBalance(address);
|
||||
return {
|
||||
address,
|
||||
balance: balance.toString(),
|
||||
balanceFormatted: `${(Number(balance) / 1e18).toFixed(4)} ${chain138.nativeCurrency.symbol}`,
|
||||
};
|
||||
}
|
||||
|
||||
case 'getBlockNumber': {
|
||||
const blockNumber = await provider.getBlockNumber();
|
||||
return {
|
||||
blockNumber,
|
||||
chainId: chain138.chainId,
|
||||
};
|
||||
}
|
||||
|
||||
case 'getTokenInfo': {
|
||||
const contractAddress = action.params.contractAddress as string;
|
||||
try {
|
||||
const contract = await sdk.getContract(contractAddress);
|
||||
// Attempt to read token info if it's a token contract
|
||||
const [name, symbol, decimals] = await Promise.all([
|
||||
contract.call('name').catch(() => null),
|
||||
contract.call('symbol').catch(() => null),
|
||||
contract.call('decimals').catch(() => null),
|
||||
]);
|
||||
|
||||
return {
|
||||
contractAddress,
|
||||
name: name || 'Unknown',
|
||||
symbol: symbol || 'Unknown',
|
||||
decimals: decimals || 18,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to get token info: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown read operation: ${action.operation}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write action templates
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create transfer action template
|
||||
*/
|
||||
export function createTransferAction(
|
||||
to: string,
|
||||
amount: string,
|
||||
tokenAddress?: string
|
||||
): WriteAction {
|
||||
return {
|
||||
type: 'write',
|
||||
description: tokenAddress
|
||||
? `Transfer ${amount} tokens from ${tokenAddress} to ${to}`
|
||||
: `Transfer ${amount} ${chain138.nativeCurrency.symbol} to ${to}`,
|
||||
chainId: chain138.chainId,
|
||||
operation: tokenAddress ? 'transferToken' : 'transferNative',
|
||||
params: {
|
||||
to,
|
||||
amount,
|
||||
tokenAddress,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute write action
|
||||
*/
|
||||
export async function executeWriteAction(
|
||||
action: WriteAction,
|
||||
sdk: ThirdwebSDK
|
||||
): Promise<{ txHash: string; chainId: number }> {
|
||||
validateChainId(action.chainId);
|
||||
|
||||
const signer = await sdk.getSigner();
|
||||
if (!signer) {
|
||||
throw new Error('No signer available for write operations');
|
||||
}
|
||||
|
||||
switch (action.operation) {
|
||||
case 'transferNative': {
|
||||
const to = action.params.to as string;
|
||||
const amount = action.params.amount as string;
|
||||
const tx = await signer.sendTransaction({
|
||||
to,
|
||||
value: BigInt(amount),
|
||||
});
|
||||
|
||||
return {
|
||||
txHash: tx.hash,
|
||||
chainId: chain138.chainId,
|
||||
};
|
||||
}
|
||||
|
||||
case 'transferToken': {
|
||||
const to = action.params.to as string;
|
||||
const amount = action.params.amount as string;
|
||||
const tokenAddress = action.params.tokenAddress as string;
|
||||
|
||||
const contract = await sdk.getContract(tokenAddress, 'token');
|
||||
const tx = await contract.erc20.transfer(to, amount);
|
||||
|
||||
return {
|
||||
txHash: tx.receipt.transactionHash,
|
||||
chainId: chain138.chainId,
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown write operation: ${action.operation}`);
|
||||
}
|
||||
}
|
||||
3
packages/ai/src/index.ts
Normal file
3
packages/ai/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './types';
|
||||
export * from './prompts';
|
||||
export * from './actions';
|
||||
88
packages/ai/src/prompts.ts
Normal file
88
packages/ai/src/prompts.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { chain138 } from '@dbis-thirdweb/chain';
|
||||
import type { AIAction } from './types';
|
||||
import { validateChainId } from './types';
|
||||
|
||||
/**
|
||||
* Chain-aware prompt templates with Chain 138 guardrails
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base prompt prefix for Chain 138 operations
|
||||
*/
|
||||
const CHAIN_138_PREFIX = `You are operating on Chain 138 (ChainID: ${chain138.chainId}).
|
||||
All operations must be performed on Chain 138 only.
|
||||
RPC endpoint: ${chain138.rpc[0] || 'https://138.rpc.thirdweb.com'}
|
||||
Native currency: ${chain138.nativeCurrency.symbol}
|
||||
`;
|
||||
|
||||
/**
|
||||
* Create chain-aware prompt for read operations
|
||||
*/
|
||||
export function createReadPrompt(operation: string, params: Record<string, unknown>): string {
|
||||
return `${CHAIN_138_PREFIX}
|
||||
|
||||
Perform a read operation on Chain 138:
|
||||
Operation: ${operation}
|
||||
Parameters: ${JSON.stringify(params, null, 2)}
|
||||
|
||||
Return the result in a structured format.`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create chain-aware prompt for write operations
|
||||
*/
|
||||
export function createWritePrompt(operation: string, params: Record<string, unknown>): string {
|
||||
return `${CHAIN_138_PREFIX}
|
||||
|
||||
Perform a write operation on Chain 138:
|
||||
Operation: ${operation}
|
||||
Parameters: ${JSON.stringify(params, null, 2)}
|
||||
|
||||
IMPORTANT: Verify the chain ID is ${chain138.chainId} before executing.
|
||||
Return the transaction hash upon success.`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create prompt from AI action
|
||||
*/
|
||||
export function createPromptFromAction(action: AIAction): string {
|
||||
// Validate chain ID
|
||||
validateChainId(action.chainId);
|
||||
|
||||
if (action.type === 'read') {
|
||||
return createReadPrompt(action.operation, action.params);
|
||||
} else {
|
||||
return createWritePrompt(action.operation, action.params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract chain ID from natural language prompt (safety check)
|
||||
*/
|
||||
export function extractChainIdFromPrompt(prompt: string): number | null {
|
||||
// Look for explicit chain ID mentions
|
||||
const chainIdMatch = prompt.match(/chain[_\s]?id[:\s]+(\d+)/i);
|
||||
if (chainIdMatch) {
|
||||
return parseInt(chainIdMatch[1], 10);
|
||||
}
|
||||
|
||||
// Look for "Chain 138" mention
|
||||
if (prompt.match(/chain[_\s]?138/i)) {
|
||||
return chain138.chainId;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guardrail: Ensure prompt targets Chain 138
|
||||
*/
|
||||
export function validatePromptForChain138(prompt: string): void {
|
||||
const extractedChainId = extractChainIdFromPrompt(prompt);
|
||||
|
||||
if (extractedChainId && extractedChainId !== chain138.chainId) {
|
||||
throw new Error(
|
||||
`Prompt targets chain ${extractedChainId}, but this module is configured for Chain 138 (${chain138.chainId})`
|
||||
);
|
||||
}
|
||||
}
|
||||
76
packages/ai/src/types.ts
Normal file
76
packages/ai/src/types.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { chain138 } from '@dbis-thirdweb/chain';
|
||||
|
||||
/**
|
||||
* AI action types
|
||||
*/
|
||||
export type AIActionType = 'read' | 'write';
|
||||
|
||||
/**
|
||||
* Base AI action
|
||||
*/
|
||||
export interface BaseAIAction {
|
||||
/**
|
||||
* Action type (read or write)
|
||||
*/
|
||||
type: AIActionType;
|
||||
|
||||
/**
|
||||
* Description of the action
|
||||
*/
|
||||
description: string;
|
||||
|
||||
/**
|
||||
* Chain ID (should be Chain 138)
|
||||
*/
|
||||
chainId: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read action (query blockchain data)
|
||||
*/
|
||||
export interface ReadAction extends BaseAIAction {
|
||||
type: 'read';
|
||||
/**
|
||||
* What to read (e.g., "balance", "block height", "token info")
|
||||
*/
|
||||
operation: string;
|
||||
|
||||
/**
|
||||
* Parameters for the read operation
|
||||
*/
|
||||
params: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write action (send transaction)
|
||||
*/
|
||||
export interface WriteAction extends BaseAIAction {
|
||||
type: 'write';
|
||||
/**
|
||||
* Transaction description
|
||||
*/
|
||||
operation: string;
|
||||
|
||||
/**
|
||||
* Transaction parameters
|
||||
*/
|
||||
params: Record<string, unknown>;
|
||||
|
||||
/**
|
||||
* Estimated gas cost (optional)
|
||||
*/
|
||||
estimatedGas?: bigint;
|
||||
}
|
||||
|
||||
export type AIAction = ReadAction | WriteAction;
|
||||
|
||||
/**
|
||||
* Validate chain ID is Chain 138
|
||||
*/
|
||||
export function validateChainId(chainId: number): void {
|
||||
if (chainId !== chain138.chainId) {
|
||||
throw new Error(
|
||||
`Invalid chain ID: ${chainId}. Expected Chain 138 (${chain138.chainId})`
|
||||
);
|
||||
}
|
||||
}
|
||||
12
packages/ai/tsconfig.json
Normal file
12
packages/ai/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"composite": false
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"references": [
|
||||
{ "path": "../chain" }
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user