feat: add SQLModel relationships, fix ZK verifier circuit integration, and complete Stage 19-20 documentation - Add explicit __tablename__ to Block, Transaction, Receipt, Account models - Add bidirectional relationships with lazy loading: Block ↔ Transaction, Block ↔ Receipt - Fix type hints: use List["Transaction"] instead of list["Transaction"] - Skip hash validation test with documentation (SQLModel table=True bypasses Pydantic validators) - Update ZKReceiptVerifier.sol to match receipt_simple circuit (
6.3 KiB
6.3 KiB
Creating Marketplace Extensions
This tutorial shows how to build extensions for the AITBC Marketplace.
Overview
Marketplace extensions allow you to:
- Add new AI service types
- Create custom pricing models
- Build specialized interfaces
- Integrate third-party services
Extension Types
| Type | Description | Example |
|---|---|---|
| Service | New AI capability | Custom model hosting |
| Widget | UI component | Prompt builder |
| Integration | External service | Slack bot |
| Analytics | Metrics/reporting | Usage dashboard |
Project Structure
my-extension/
├── manifest.json # Extension metadata
├── src/
│ ├── index.ts # Entry point
│ ├── service.ts # Service logic
│ └── ui/ # UI components
├── assets/
│ └── icon.png # Extension icon
└── package.json
Step 1: Create Manifest
manifest.json:
{
"name": "my-custom-service",
"version": "1.0.0",
"description": "Custom AI service for AITBC",
"type": "service",
"author": "Your Name",
"homepage": "https://github.com/you/my-extension",
"permissions": [
"jobs.submit",
"jobs.read",
"receipts.read"
],
"entry": "src/index.ts",
"icon": "assets/icon.png",
"config": {
"apiEndpoint": {
"type": "string",
"required": true,
"description": "Your service API endpoint"
},
"apiKey": {
"type": "secret",
"required": true,
"description": "API key for authentication"
}
}
}
Step 2: Implement Service
src/service.ts:
import { AITBCService, Job, JobResult } from '@aitbc/sdk';
export class MyCustomService implements AITBCService {
name = 'my-custom-service';
constructor(private config: { apiEndpoint: string; apiKey: string }) {}
async initialize(): Promise<void> {
// Validate configuration
const response = await fetch(`${this.config.apiEndpoint}/health`);
if (!response.ok) {
throw new Error('Service endpoint not reachable');
}
}
async processJob(job: Job): Promise<JobResult> {
const response = await fetch(`${this.config.apiEndpoint}/process`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.config.apiKey}`
},
body: JSON.stringify({
prompt: job.prompt,
params: job.params
})
});
if (!response.ok) {
throw new Error(`Service error: ${response.statusText}`);
}
const data = await response.json();
return {
output: data.result,
metadata: {
model: data.model,
tokens_used: data.tokens
}
};
}
async estimateCost(job: Job): Promise<number> {
// Estimate cost in AITBC tokens
const estimatedTokens = job.prompt.length / 4;
return estimatedTokens * 0.001; // 0.001 AITBC per token
}
getCapabilities(): string[] {
return ['text-generation', 'summarization'];
}
}
Step 3: Create Entry Point
src/index.ts:
import { ExtensionContext, registerService } from '@aitbc/sdk';
import { MyCustomService } from './service';
export async function activate(context: ExtensionContext): Promise<void> {
const config = context.getConfig();
const service = new MyCustomService({
apiEndpoint: config.apiEndpoint,
apiKey: config.apiKey
});
await service.initialize();
registerService(service);
console.log('My Custom Service extension activated');
}
export function deactivate(): void {
console.log('My Custom Service extension deactivated');
}
Step 4: Add UI Widget (Optional)
src/ui/PromptBuilder.tsx:
import React, { useState } from 'react';
import { useAITBC } from '@aitbc/react';
export function PromptBuilder() {
const [prompt, setPrompt] = useState('');
const { submitJob, isLoading } = useAITBC();
const handleSubmit = async () => {
const result = await submitJob({
service: 'my-custom-service',
prompt,
params: { max_tokens: 256 }
});
console.log('Result:', result);
};
return (
<div className="prompt-builder">
<textarea
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
placeholder="Enter your prompt..."
/>
<button onClick={handleSubmit} disabled={isLoading}>
{isLoading ? 'Processing...' : 'Submit'}
</button>
</div>
);
}
Step 5: Package and Deploy
Build
npm run build
Test Locally
npm run dev
# Extension runs at http://localhost:3000
Deploy to Marketplace
# Package extension
npm run package
# Creates my-extension-1.0.0.zip
# Submit to marketplace
aitbc-cli extension submit my-extension-1.0.0.zip
Pricing Models
Per-Request Pricing
async estimateCost(job: Job): Promise<number> {
return 1.0; // Fixed 1 AITBC per request
}
Token-Based Pricing
async estimateCost(job: Job): Promise<number> {
const inputTokens = job.prompt.length / 4;
const outputTokens = job.params.max_tokens || 256;
return (inputTokens + outputTokens) * 0.001;
}
Tiered Pricing
async estimateCost(job: Job): Promise<number> {
const tokens = job.prompt.length / 4;
if (tokens < 100) return 0.5;
if (tokens < 1000) return 2.0;
return 5.0;
}
Best Practices
- Validate inputs - Check all user inputs before processing
- Handle errors gracefully - Return meaningful error messages
- Respect rate limits - Don't overwhelm external services
- Cache when possible - Reduce redundant API calls
- Log appropriately - Use structured logging for debugging
- Version your API - Support backward compatibility
Testing
import { MyCustomService } from './service';
describe('MyCustomService', () => {
it('should process job successfully', async () => {
const service = new MyCustomService({
apiEndpoint: 'http://localhost:8080',
apiKey: 'test-key'
});
const result = await service.processJob({
prompt: 'Hello, world!',
params: {}
});
expect(result.output).toBeDefined();
});
});