OpenAI Code Sandbox for Veterinary Practice Performance Analytics
Secure, cost-capped AI data sandbox that lets veterinary practice managers run ad‑hoc analysis on clinic data without exposing sensitive information to external services.
Veterinary practices generate appointment, revenue, and treatment data that managers want to analyze to improve margins, but they can’t share proprietary patient and financial records with a public AI assistant. They need a safe, isolated environment where they can ask natural-language questions and get answers backed by real clinic data.
A complete, working implementation of this recipe — downloadable as a zip or browsable file by file. Generated by our build pipeline; tested with full coverage before publishing.
This recipe builds a cost-capped AI code sandbox that lets veterinary practice managers run ad-hoc analytics on clinic data without exposing sensitive information to external services. You’ll wire together the OpenAI API, E2B’s code interpreter sandbox, REAA’s agent-budget-engine for spend governance, and session-continuity for multi-turn conversations — all inside a Next.js 16 App Router project. By the end, practice managers can ask natural-language questions like “What was our revenue trend last quarter?” and get answers backed by real clinic data running in an isolated Python/Pandas sandbox, with hard budget limits that prevent runaway costs.
Prerequisites
Node.js 22+ and pnpm 10 installed on your machine
An OpenAI API key (set as OPENAI_API_KEY)
An E2B API key (set as E2B_API_KEY) — sign up at e2b.dev
Familiarity with TypeScript, Next.js App Router, and basic async/await patterns
Step 1: Scaffold the Next.js project and install dependencies
Create a new Next.js project with the App Router and install the exact dependency versions you’ll need.
Expected output:pnpm resolves all versions and writes them to package.json with exact semver (no ^ or ~).
Step 2: Configure environment variables
Create an .env.example file with placeholder values for every environment variable the application reads at runtime:
env
# Env vars used by openai-code-sandbox-for-veterinary-practice-performance-analytics.# Keep placeholders only — never commit real values.NODE_ENV=developmentOPENAI_API_KEY=<your-openai-key>E2B_API_KEY=<your-e2b-api-key>TRACELOOP_API_KEY=<your-traceloop-key>BUDGET_LIMIT=10.0BUDGET_SOFT_CAP=0.80BUDGET_HARD_CAP=1.0OPENAI_MODEL=gpt-5.2-mini
Copy this to .env.local and fill in real values:
terminal
cp .env.example .env.local
Expected output: Both .env.example and .env.local exist. The application reads these via process.env at runtime.
Step 3: Define shared types and Zod schemas
Create the TypeScript interfaces and Zod schemas that every module will reference.
Expected output: Two files under src/lib/ that export clean TypeScript types and Zod schemas. The route handler will use AnalyticsRequestSchema.safeParse() to validate incoming POST bodies.
Step 4: Create the OpenAI client module
This module manages a singleton OpenAI client and provides the generateAnalysisCode function that turns natural-language questions into executable Python/Pandas code.
Expected output:src/lib/openai-client.ts exports createOpenAIClient (singleton factory) and generateAnalysisCode (prompts the model with a strict “code only” system instruction and returns the generated Python plus token counts).
Step 5: Create the E2B sandbox module
The sandbox module wraps the @e2b/code-interpreter package. It creates isolated Python environments, loads clinic datasets as CSV/JSON files, and executes the generated analysis code.
Expected output: Four exported async functions: createSandbox (spawns a new E2B sandbox), loadDataset (writes files and loads them as Pandas DataFrames), executeCode (runs Python code and maps results to a typed interface), and cleanupSandbox (terminates the sandbox to free resources).
Step 6: Create the budget guard
The budget guard wraps @reaatech/agent-budget-engine’s BudgetController with per-practice spend tracking. It enforces daily caps and prevents runaway costs.
Expected output: A controller singleton wired to an InMemorySpendStore, plus four async helper functions: initBudget (sets per-practice limits from env vars), checkBudget (queries whether a request is allowed), recordSpend (logs cost after execution), and getBudgetState (returns current spend/remaining for monitoring). InMemorySpendStore is also exported for testing.
Step 7: Create the session manager
The session module wraps @reaatech/session-continuity’s SessionManager with an in-memory storage adapter. It preserves conversation history across multi-turn analytical Q&A sessions.
Create src/lib/session.ts:
ts
import { SessionManager, SessionNotFoundError,} from "@reaatech/session-continuity";import type { Message, Session } from "@reaatech/session-continuity";class InMemoryStorageAdapter { private sessions = new Map<string, Session>(); private messagesBySession = new Map<string, Message[]>(); createSession( session: Omit<Session, "id" | "createdAt" | "lastActivityAt"> ): Promise<Session
Expected output: A manager singleton backed by an in-memory adapter, plus helper functions that create sessions per practice, attach user and assistant messages, and retrieve the conversation context for AI prompt building. The manager automatically compresses long conversations via sliding window when token budgets are reached.
Step 8: Create the concurrency limiter
Use p-limit to prevent too many simultaneous E2B sandboxes and code executions from exhausting resources.
Expected output: Two exported pLimit instances. sandboxLimiter caps concurrent sandbox creations at 3, and codeExecLimiter caps simultaneous code executions at 5. These are used in the analytics service to wrap sandbox and execution calls.
Step 9: Wire everything together in the analytics service
This is the main orchestrator. It accepts a natural-language question, checks the budget, generates analysis code via OpenAI, executes it in an E2B sandbox, records the spend, and stores the conversation.
Create src/lib/analytics-service.ts:
ts
import { createOpenAIClient, generateAnalysisCode } from "./openai-client.js";import { createSandbox, loadDataset, executeCode, cleanupSandbox,} from "./sandbox.js";import { controller, checkBudget, recordSpend,} from "./budget-guard.js";import { manager, getOrCreateSession, addMessageToSession, getConversationContext,} from "./session.js";import { sandboxLimiter, codeExecLimiter } from "./concurrency.js";import type { AnalyticsRequest, AnalyticsResponse } from "./types.js";export
Expected output: The processAnalyticsQuestion function that orchestrates the full flow: session retrieval, budget checking, AI code generation, sandbox execution (with retry), cost recording, and message persistence. If the budget is exceeded, it throws Error("BUDGET_EXCEEDED") which the route handler catches as a 429 response.
Step 10: Create the API route handler
The Next.js App Router route at app/api/analytics/route.ts exposes POST and GET endpoints. The POST accepts a question and practiceId, validates them with Zod, and delegates to the analytics service.
Expected output: Two route handlers. POST validates the body, delegates to processAnalyticsQuestion, and returns a JSON response. It catches BUDGET_EXCEEDED errors as HTTP 429, validation failures as 400, and unexpected errors as 500. GET returns a simple health-check response.
Step 11: Add OpenTelemetry observability
Wire up tracing with @traceloop/node-server-sdk and the REAA OpenAI semantic conventions wrapper. This emits spans for every LLM call so you can audit usage per practice.
Create src/lib/otel.ts:
ts
import type OpenAI from "openai";import * as traceloop from "@traceloop/node-server-sdk";import { OpenAIInstrumentation } from "@reaatech/otel-genai-semconv-openai";export function setupObservability(openaiClient: OpenAI): void { traceloop.initialize(); new OpenAIInstrumentation({ trackCosts: true, onStart: (span, request) => { span.setAttribute( "app.practice_id", typeof request.messages[0]?.content === "string" ? request.messages[0].content.slice(0, 128) : "unknown" ); }, onEnd: (span, response) => { span.setAttribute( "llm.response_tokens", response.usage?.completion_tokens ?? 0 ); span.setAttribute( "llm.finish_reason", response.choices[0]?.finish_reason ?? "unknown" ); }, }).instrument(openaiClient);}export async function shutdownObservability(): Promise<void> { const sdk = traceloop as Record<string, unknown>; if (typeof sdk.shutdown === "function") { await (sdk.shutdown as () => Promise<void>)(); }}
Expected output: A setupObservability function that initializes Traceloop and instruments the OpenAI client with custom span attributes (practice ID, response tokens, finish reason). Call it once at application startup.
Step 12: Export the public API surface
Create src/index.ts as a barrel file so consumers can import everything from a single path:
ts
export { processAnalyticsQuestion } from "./lib/analytics-service.js";export { createSandbox, executeCode, cleanupSandbox } from "./lib/sandbox.js";export { checkBudget, recordSpend, getBudgetState } from "./lib/budget-guard.js";export { createAnalysisSession, getOrCreateSession, addMessageToSession, getConversationContext, endAnalysisSession,} from "./lib/session.js";export { setupObservability } from "./lib/otel.js";
Expected output: A clean barrel file that re-exports the key functions from each module.
Step 13: Run the tests
The project includes a test suite covering all modules. Run it to verify everything works:
terminal
pnpm test
Expected output: All tests pass with zero failures. The coverage report shows >= 90% line/function/statement coverage across src/ and app/**/route.ts.
You can also run a quick typecheck:
terminal
pnpm typecheck
Expected output:tsc --noEmit exits 0 with no errors.
Next steps
Add a database-backed spend store — replace InMemorySpendStore with a Postgres or Redis adapter so budgets survive server restarts across practices.
Wire in the instrumentation hook — add src/instrumentation.ts with experimental.instrumentationHook: true in next.config.ts so setupObservability() runs at server startup automatically.
Build a dashboard UI — create a Next.js page component that calls GET /api/analytics for status and renders a form to submit analysis questions with streaming responses.
Add practice-level authentication — protect the analytics endpoint with NextAuth or custom middleware that reads a practice-specific API key from the request header.
;
hardCap: number;
downgradeRules: Array<{ from: string[]; to: string }>;
}
class InMemorySpendStore {
private budgets = new Map<string, BudgetDefinition>();