Build an AI Coding Assistant
Build an AI-powered coding assistant that discovers models on the Tenzro network, streams inference responses, pays for inference with TNZO micropayments, and deploys as a reusable agent on the network. Your assistant will have skills like code generation, debugging, and explanation -- all backed by decentralized AI models.
What We're Building
- Discover available AI models on the network (gemma3-270m, qwen3.5-0.8b, etc.)
- Stream inference responses in real-time via SSE
- Register as an agent with coding-specific skills
- Pay for inference using TNZO micropayment channels
- Deploy as a network plugin agent accessible by other agents and apps
Prerequisites
- Tenzro SDK installed (
cargo add tenzro-sdkornpm install tenzro-sdk) - A funded wallet on testnet (100 TNZO from faucet)
- Basic understanding of AI inference and chat completions
Estimated time: 30 minutes
Step 1: Discover Available Models
First, discover what AI models are available on the network. Models are registered in the model registry and served by providers who earn TNZO:
Rust
use tenzro_sdk::{TenzroClient, config::SdkConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = SdkConfig::testnet();
let client = TenzroClient::connect(config).await?;
let inference = client.inference();
// List all available models
let models = inference.list_models().await?;
println!("Available models:");
for model in &models {
println!(" {} ({}) - {} params - {}",
model.model_id,
model.name,
model.parameters.unwrap_or_default(),
model.status,
);
}
// Filter by category
let code_models = inference.list_models_filtered(Some("code"), None).await?;
println!("Code models: {}", code_models.len());
Ok(())
}TypeScript
import { TenzroClient, TESTNET_CONFIG } from "tenzro-sdk";
const client = new TenzroClient(TESTNET_CONFIG);
// List all models
const models = await client.inference.listModels();
console.log("Available models:");
for (const model of models) {
console.log(` ${model.model_id} - ${model.name} (${model.status})`);
}Example output:
Available models:
gemma3-270m (Gemma 3 270M) - 270M params - available
qwen3.5-0.8b (Qwen 3.5 0.8B) - 800M params - available
qwen3-4b (Qwen 3 4B) - 4B params - available
phi-4-mini (Phi 4 Mini) - 3.8B params - available
gemma4-9b (Gemma 4 9B) - 9B params - availableStep 2: Run Basic Inference
Send a coding question to a model and get a response:
Rust
// Simple inference request
let response = inference.request(
"qwen3.5-0.8b",
"Write a Python function that reverses a linked list in-place.",
Some(500), // max tokens
).await?;
println!("Response:");
println!("{}", response.output);
println!("Tokens: {}", response.tokens_used);
println!("Cost: {} TNZO", response.cost);TypeScript
const response = await client.inference.request(
"qwen3.5-0.8b",
"Write a Python function that reverses a linked list in-place.",
500,
);
console.log("Response:", response.output);
console.log("Tokens:", response.tokens_used);
console.log("Cost:", response.cost, "TNZO");Step 3: Stream Responses in Real-Time
For a better user experience, stream tokens as they are generated. The StreamingClient uses SSE (Server-Sent Events) to deliver tokens in real-time:
Rust
let streaming = client.streaming();
// Stream chat completion
let result = streaming.chat_stream(
"qwen3.5-0.8b",
"Explain the difference between a mutex and a semaphore with Rust examples.",
Some(800),
|token| {
// This callback fires for each generated token
print!("{}", token);
std::io::Write::flush(&mut std::io::stdout()).unwrap();
},
).await?;
println!("\n\nTotal tokens: {}", result.total_tokens);
println!("Complete output length: {}", result.output.len());TypeScript
// Stream chat completion
const result = await client.streaming.chatStream(
"qwen3.5-0.8b",
"Explain the difference between a mutex and a semaphore with Rust examples.",
800,
(token: string) => {
// Print each token as it arrives
process.stdout.write(token);
},
);
console.log("\nTotal tokens:", result.total_tokens);Step 4: Build the Coding Assistant Class
Wrap the inference and streaming capabilities into a reusable coding assistant class with specialized skills:
import { TenzroClient, TESTNET_CONFIG } from "tenzro-sdk";
type Skill = "generate" | "debug" | "explain" | "review" | "refactor";
const SYSTEM_PROMPTS: Record<Skill, string> = {
generate: "You are an expert programmer. Generate clean, well-documented code based on the user's requirements. Include type annotations and error handling.",
debug: "You are a debugging expert. Analyze the provided code, identify bugs, explain the root cause, and provide a corrected version.",
explain: "You are a patient teacher. Explain the provided code or concept clearly, with examples. Adjust complexity to the user's level.",
review: "You are a senior code reviewer. Review the code for bugs, performance issues, security vulnerabilities, and style. Be specific and actionable.",
refactor: "You are a refactoring expert. Improve the code's structure, readability, and performance while preserving behavior. Explain each change.",
};
class CodingAssistant {
private client: TenzroClient;
private modelId: string;
private history: Array<{ role: string; content: string }> = [];
constructor(client: TenzroClient, modelId: string = "qwen3.5-0.8b") {
this.client = client;
this.modelId = modelId;
}
async ask(skill: Skill, prompt: string): Promise<string> {
const systemPrompt = SYSTEM_PROMPTS[skill];
const fullPrompt = `System: ${systemPrompt}\n\nUser: ${prompt}`;
const response = await this.client.inference.request(
this.modelId,
fullPrompt,
1000,
);
this.history.push(
{ role: "user", content: prompt },
{ role: "assistant", content: response.output },
);
return response.output;
}
async stream(skill: Skill, prompt: string, onToken: (t: string) => void): Promise<string> {
const systemPrompt = SYSTEM_PROMPTS[skill];
const fullPrompt = `System: ${systemPrompt}\n\nUser: ${prompt}`;
const result = await this.client.streaming.chatStream(
this.modelId,
fullPrompt,
1000,
onToken,
);
this.history.push(
{ role: "user", content: prompt },
{ role: "assistant", content: result.output },
);
return result.output;
}
getHistory() {
return this.history;
}
clearHistory() {
this.history = [];
}
}
// Usage
async function main() {
const client = new TenzroClient(TESTNET_CONFIG);
const assistant = new CodingAssistant(client, "qwen3.5-0.8b");
// Generate code
const code = await assistant.ask(
"generate",
"Write a TypeScript function that implements binary search on a sorted array.",
);
console.log("Generated code:", code);
// Stream a code review
console.log("\nCode review:");
await assistant.stream(
"review",
code,
(token) => process.stdout.write(token),
);
// Debug some code
const buggyCode = `function sum(arr) {
let total = 0;
for (let i = 0; i <= arr.length; i++) {
total += arr[i];
}
return total;
}`;
const debugResult = await assistant.ask("debug", buggyCode);
console.log("\n\nDebug result:", debugResult);
}
main().catch(console.error);Step 5: Pay for Inference with Micropayments
For production use, set up a micropayment channel to pay for inference per token without on-chain transactions for each call:
Rust
let settlement = client.settlement();
// Open a micropayment channel with 10 TNZO
let channel_id = settlement.open_channel(
"0xProviderAddress1234567890abcdef12345678", // inference provider
10_000_000_000_000_000_000, // 10 TNZO locked
86400, // 24h dispute period
).await?;
println!("Channel opened: {}", channel_id);
// Each inference request sends a signed update to the channel
// instead of an on-chain transaction
let response = inference.request_with_channel(
"qwen3.5-0.8b",
"Write a Rust async TCP server.",
Some(500),
&channel_id,
).await?;
println!("Response: {}", response.output);
println!("Channel balance remaining: {}", response.channel_balance);
// Close channel when done (settles on-chain once)
let close = settlement.close_channel(&channel_id).await?;
println!("Channel closed. Final cost: {} TNZO", close.total_spent);TypeScript
// Open micropayment channel
const channelId = await client.settlement.openChannel(
"0xProviderAddress1234567890abcdef12345678",
10_000_000_000_000_000_000n, // 10 TNZO
86400, // 24h dispute period
);
console.log("Channel:", channelId);
// Use channel for inference payments
const response = await client.inference.requestWithChannel(
"qwen3.5-0.8b",
"Write a Rust async TCP server.",
500,
channelId,
);
console.log("Response:", response.output);
// Close channel
const close = await client.settlement.closeChannel(channelId);
console.log("Total spent:", close.total_spent, "TNZO");Micropayment Channels vs MPP
MPP is session-based: open a session, accumulate charges, settle once at the end. Good for single conversations. Micropayment channels lock funds in escrow and allow unlimited off-chain updates. Good for long-running services like a coding assistant that might handle hundreds of requests per day. Channels settle on-chain only at open and close -- everything in between is gasless.
Step 6: Register as a Network Agent
Deploy the coding assistant as a network agent so other agents and apps can discover and use it:
// Register the coding assistant as an agent
const agentResult = await client.agent.register(
"coding-assistant-v1",
"Tenzro Coding Assistant",
["code-generation", "debugging", "code-review", "refactoring", "explanation"],
);
console.log("Agent registered:", agentResult.agent_id);
console.log("Wallet:", agentResult.wallet_address);
// Register as an agent template in the marketplace
await client.marketplace().registerTemplate({
name: "Coding Assistant",
description: "AI-powered coding assistant with code generation, debugging, review, and refactoring capabilities.",
template_type: "service",
capabilities: [
{ name: "code-generation", description: "Generate code from natural language" },
{ name: "debugging", description: "Find and fix bugs in code" },
{ name: "code-review", description: "Review code for quality and security" },
{ name: "refactoring", description: "Improve code structure and performance" },
],
pricing: {
model: "per_request",
base_price: "0.01",
currency: "TNZO",
},
runtime_requirements: {
min_memory_mb: 512,
requires_gpu: false,
requires_tee: false,
},
});
console.log("Agent template published to marketplace");Step 7: Accept Tasks from Other Agents
Once deployed, your agent can receive tasks via the A2A protocol:
// Listen for incoming tasks
const tasks = await client.task().listTasks({
assignee: agentResult.agent_id,
status: "pending",
});
for (const task of tasks) {
console.log("Received task:", task.task_id);
console.log(" Description:", task.description);
// Process the task with the coding assistant
const assistant = new CodingAssistant(client, "qwen3.5-0.8b");
const result = await assistant.ask("generate", task.description);
// Complete the task
await client.task().completeTask(task.task_id, result);
console.log("Task completed:", task.task_id);
}Complete Example
import { TenzroClient, TESTNET_CONFIG } from "tenzro-sdk";
async function main() {
const client = new TenzroClient(TESTNET_CONFIG);
// 1. Find a suitable model
const models = await client.inference.listModels();
const model = models.find(m => m.status === "available") || models[0];
console.log("Using model:", model.model_id);
// 2. Generate code
const response = await client.inference.request(
model.model_id,
"Write a TypeScript class for a thread-safe queue with enqueue, dequeue, and peek methods.",
800,
);
console.log("Generated code:");
console.log(response.output);
console.log("Cost:", response.cost, "TNZO");
// 3. Stream a code review
console.log("\nStreaming code review...");
await client.streaming.chatStream(
model.model_id,
"Review this code for thread safety issues:\n" + response.output,
500,
(token) => process.stdout.write(token),
);
// 4. Register as agent
const agent = await client.agent.register(
"my-coding-assistant",
"My Coding Assistant",
["code-generation", "debugging", "review"],
);
console.log("\n\nAgent deployed:", agent.agent_id);
}
main().catch(console.error);What You Learned
- Model discovery -- listing and filtering available AI models on the network
- Inference -- sending prompts and receiving completions
- Streaming -- real-time token streaming via SSE
- Coding assistant -- building a class with specialized skills (generate, debug, review, refactor)
- Micropayments -- paying for inference with off-chain micropayment channels
- Agent deployment -- registering as a network agent and publishing to the marketplace
Next Steps
- Build an Agent Swarm -- coordinate multiple coding assistants
- Build an AI Payment Agent -- autonomous payments for inference
- Build a Gasless App -- sponsor inference for your users
Build More with Tenzro
Explore AI model integration and agent capabilities in the documentation.