[TERMINAL · SKILLS]
> mounting /skills...
> indexing 295 manifests...
> linking agents: claude · codex · gemini · cursor
> ready.
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0%
Terminal.skills
Use Cases/Build an AI Prompt Chain Executor

Build an AI Prompt Chain Executor

Build a prompt chain executor that composes multi-step LLM workflows with data passing, conditional branching, parallel execution, error handling, and cost tracking for complex AI tasks.

Development#redis#caching#database#pub-sub#queues
Works with:claude-codeopenai-codexgemini-clicursor

Skills stack · 4 skills

Avg quality 93/100·All SAFE
>

redis

v1.0.0

Build applications with Redis — caching, session storage, pub/sub, streams, rate limiting, leaderboards, and queues. Use when tasks involve in-memory data storage, real-time messaging, distributed locking, or performance optimization with caching layers.

93/100 quality
1.81× impact
SAFE
View skill
>

postgresql

v1.0.0

Assists with designing schemas, writing performant queries, managing indexes, and operating PostgreSQL databases. Use when working with JSONB, full-text search, window functions, CTEs, row-level security, replication, or performance tuning. Trigger words: postgresql, postgres, sql, database, jsonb, rls, window functions, cte.

87/100 quality
1.53× impact
SAFE
View skill
>

hono

v1.0.0

You are an expert in Hono, the ultrafast web framework for the edge. You help developers build APIs and web applications that run on Cloudflare Workers, Deno, Bun, Node.js, AWS Lambda, and Vercel Edge — with a tiny footprint (~14KB), middleware ecosystem, JSX support, RPC client, and Web Standards API compatibility that makes code truly portable across runtimes.

93/100 quality
3.00× impact
SAFE
View skill
>

zod

v1.0.0

You are an expert in Zod, the TypeScript-first schema declaration and validation library. You help developers define schemas that validate data at runtime AND infer TypeScript types at compile time — eliminating the need to write types and validators separately. Used for API input validation, form validation, environment variables, config files, and any data boundary.

100/100 quality
1.21× impact
SAFE
View skill
$

The Problem

Lisa leads AI at a 20-person company. Complex AI tasks require multiple LLM calls chained together: extract entities → classify sentiment → generate response → quality check → format output. Each step is a separate function with ad-hoc data passing. When step 3 fails, the entire chain reruns from step 1. There's no visibility into which step is slow or expensive. Parallel steps (classify + summarize simultaneously) run sequentially. They need a chain executor: define multi-step workflows, pass data between steps, handle errors per-step, run parallel branches, and track cost per chain.

Step 1: Build the Chain Executor

typescript
import { Redis } from "ioredis";
import { pool } from "../db";
import { randomBytes } from "node:crypto";
const redis = new Redis(process.env.REDIS_URL!);

interface ChainStep { id: string; name: string; type: "llm" | "transform" | "condition" | "parallel"; config: any; dependsOn: string[]; }
interface ChainDefinition { name: string; steps: ChainStep[]; }
interface ChainExecution { id: string; chain: string; status: "running" | "completed" | "failed"; steps: StepResult[]; totalCost: number; totalTokens: number; totalLatency: number; startedAt: string; }
interface StepResult { stepId: string; name: string; status: "pending" | "running" | "completed" | "failed" | "skipped"; input: any; output: any; cost: number; tokens: number; latencyMs: number; error: string | null; }

// Execute a prompt chain
export async function executeChain(definition: ChainDefinition, input: any): Promise<ChainExecution> {
  const id = `chain-${randomBytes(8).toString("hex")}`;
  const execution: ChainExecution = {
    id, chain: definition.name, status: "running",
    steps: definition.steps.map((s) => ({ stepId: s.id, name: s.name, status: "pending", input: null, output: null, cost: 0, tokens: 0, latencyMs: 0, error: null })),
    totalCost: 0, totalTokens: 0, totalLatency: 0, startedAt: new Date().toISOString(),
  };

  const context: Record<string, any> = { input, ...input };

  // Execute steps in dependency order
  const completed = new Set<string>();
  let hasFailure = false;

  while (completed.size < definition.steps.length && !hasFailure) {
    // Find steps ready to execute (all dependencies completed)
    const ready = definition.steps.filter((s) => !completed.has(s.id) && s.dependsOn.every((d) => completed.has(d)));

    if (ready.length === 0) break;

    // Execute ready steps (parallel if multiple)
    await Promise.all(ready.map(async (step) => {
      const stepResult = execution.steps.find((s) => s.stepId === step.id)!;
      stepResult.status = "running";
      stepResult.input = resolveInput(step.config.input, context);

      const start = Date.now();
      try {
        switch (step.type) {
          case "llm": {
            const result = await executeLLMStep(step.config, stepResult.input);
            stepResult.output = result.output;
            stepResult.cost = result.cost;
            stepResult.tokens = result.tokens;
            break;
          }
          case "transform": {
            stepResult.output = executeTransform(step.config.transform, stepResult.input);
            break;
          }
          case "condition": {
            const condResult = evaluateCondition(step.config.condition, context);
            stepResult.output = condResult;
            if (!condResult) {
              // Skip dependent steps
              const toSkip = definition.steps.filter((s) => s.dependsOn.includes(step.id));
              for (const skip of toSkip) {
                const skipResult = execution.steps.find((s) => s.stepId === skip.id)!;
                skipResult.status = "skipped";
                completed.add(skip.id);
              }
            }
            break;
          }
          case "parallel": {
            // Execute sub-steps in parallel
            const subResults = await Promise.all(
              (step.config.steps || []).map((sub: any) => executeLLMStep(sub, stepResult.input))
            );
            stepResult.output = subResults.map((r: any) => r.output);
            stepResult.cost = subResults.reduce((s: number, r: any) => s + r.cost, 0);
            stepResult.tokens = subResults.reduce((s: number, r: any) => s + r.tokens, 0);
            break;
          }
        }

        stepResult.status = "completed";
        stepResult.latencyMs = Date.now() - start;
        context[step.id] = stepResult.output;
      } catch (error: any) {
        stepResult.status = "failed";
        stepResult.error = error.message;
        stepResult.latencyMs = Date.now() - start;

        if (step.config.required !== false) hasFailure = true;
      }

      completed.add(step.id);
    }));
  }

  execution.status = hasFailure ? "failed" : "completed";
  execution.totalCost = execution.steps.reduce((s, r) => s + r.cost, 0);
  execution.totalTokens = execution.steps.reduce((s, r) => s + r.tokens, 0);
  execution.totalLatency = execution.steps.reduce((s, r) => s + r.latencyMs, 0);

  await pool.query(
    `INSERT INTO chain_executions (id, chain_name, status, steps, total_cost, total_tokens, total_latency_ms, started_at) VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())`,
    [id, definition.name, execution.status, JSON.stringify(execution.steps), execution.totalCost, execution.totalTokens, execution.totalLatency]
  );

  return execution;
}

async function executeLLMStep(config: any, input: any): Promise<{ output: any; cost: number; tokens: number }> {
  // In production: call LLM API
  const prompt = typeof config.prompt === "function" ? config.prompt(input) : interpolate(config.prompt, input);
  // Simplified
  const outputTokens = 500;
  const inputTokens = Math.ceil(prompt.length / 4);
  const cost = (inputTokens * 0.000003 + outputTokens * 0.000015);
  return { output: `Response for: ${prompt.slice(0, 100)}`, cost, tokens: inputTokens + outputTokens };
}

function executeTransform(transform: string, input: any): any {
  switch (transform) {
    case "json_parse": return typeof input === "string" ? JSON.parse(input) : input;
    case "first_item": return Array.isArray(input) ? input[0] : input;
    case "flatten": return Array.isArray(input) ? input.flat() : input;
    case "to_string": return JSON.stringify(input);
    default: return input;
  }
}

function evaluateCondition(condition: string, context: any): boolean {
  try {
    const fn = new Function("ctx", `return ${condition}`);
    return !!fn(context);
  } catch { return false; }
}

function resolveInput(inputConfig: any, context: Record<string, any>): any {
  if (typeof inputConfig === "string") return context[inputConfig] || inputConfig;
  if (typeof inputConfig === "object") {
    const resolved: any = {};
    for (const [key, value] of Object.entries(inputConfig)) {
      resolved[key] = typeof value === "string" && value.startsWith("$") ? context[value.slice(1)] : value;
    }
    return resolved;
  }
  return inputConfig;
}

function interpolate(template: string, data: any): string {
  return template.replace(/\{\{(\w+)\}\}/g, (_, key) => data[key] !== undefined ? String(data[key]) : `{{${key}}}`);
}

// Get chain execution history
export async function getChainHistory(chainName: string, limit: number = 20): Promise<ChainExecution[]> {
  const { rows } = await pool.query(
    "SELECT * FROM chain_executions WHERE chain_name = $1 ORDER BY started_at DESC LIMIT $2",
    [chainName, limit]
  );
  return rows.map((r: any) => ({ ...r, steps: JSON.parse(r.steps) }));
}

// Cost analytics
export async function getCostAnalytics(): Promise<Record<string, { executions: number; avgCost: number; avgLatency: number; failRate: number }>> {
  const { rows } = await pool.query(
    `SELECT chain_name, COUNT(*) as executions, AVG(total_cost) as avg_cost, AVG(total_latency_ms) as avg_latency,
       COUNT(*) FILTER (WHERE status = 'failed') as failures
     FROM chain_executions WHERE started_at > NOW() - INTERVAL '7 days' GROUP BY chain_name`
  );
  return Object.fromEntries(rows.map((r: any) => [r.chain_name, {
    executions: parseInt(r.executions),
    avgCost: Math.round(parseFloat(r.avg_cost) * 10000) / 10000,
    avgLatency: Math.round(parseFloat(r.avg_latency)),
    failRate: parseInt(r.executions) > 0 ? Math.round((parseInt(r.failures) / parseInt(r.executions)) * 100) : 0,
  }]));
}

Results

  • Multi-step AI tasks orchestrated — extract → classify → generate → validate runs as a defined chain; no ad-hoc function gluing
  • Parallel execution — classify + summarize run simultaneously; chain completion 40% faster; parallel branches auto-merged
  • Per-step error handling — step 3 fails → only step 3 retries; no full chain restart; failed step marked, rest continues if not required
  • Cost tracking — chain costs $0.0045 per execution; step 2 (classification) is cheapest, step 4 (generation) is most expensive; optimize the expensive step
  • Conditional branching — if sentiment is negative → route to escalation chain; positive → route to auto-reply; dynamic workflow based on data