Skip to main content
The Simforge TypeScript SDK captures your AI function calls to automatically generate evaluations. Re-run your prompts with different models, parameters, and inputs to iterate faster.

Installation

# npm
npm install @goharvest/simforge

# pnpm
pnpm add @goharvest/simforge

# yarn
yarn add @goharvest/simforge

Quick Start

import { Simforge } from "@goharvest/simforge"

const simforge = new Simforge({ apiKey: process.env.SIMFORGE_API_KEY })
Copy this prompt into your coding agent (tested with Cursor and Claude Code using Sonnet 4.5):
Modify existing TypeScript code to add Simforge tracing.
Do NOT browse or web search. Use ONLY the API described below.

Simforge TypeScript SDK (authoritative excerpt):
- Install: `npm install @goharvest/simforge` or `pnpm add @goharvest/simforge`
- Init:
  import { Simforge } from "@goharvest/simforge"
  const simforge = new Simforge({ apiKey: process.env.SIMFORGE_API_KEY })
- Instrumentation (ONLY allowed form - use getFunction):
  // Declare trace function key once
  const myService = simforge.getFunction("<trace_function_key>")

  // Wrap functions with withSpan
  const tracedFn = myService.withSpan(originalFunction)

  // Or with options:
  const tracedFn = myService.withSpan({ name: "DisplayName", type: "function" }, originalFunction)

  // Span types: "llm", "agent", "function", "guardrail", "handoff", "custom"
- DO NOT modify the original function.
- DO NOT extract helper methods.

STRICT RULE:
You MUST ask me which function to instrument before making ANY code changes.
Do NOT choose a function yourself.

Task:
1) Ensure @goharvest/simforge is installed and initialization exists.
2) Ask me which EXISTING function should be instrumented with Simforge.
3) After I confirm the function name:
   - Create a function wrapper with `simforge.getFunction("<trace_function_key>")`
   - Wrap the function with `myService.withSpan(originalFunction)`
   - Replace usages of the original function with the traced version
4) Do not change function signature, behavior, or return value. Minimal diff.

Output:
- First: your question asking which function to instrument
- After confirmation: minimal diffs for dependencies, initialization, and the function wrapping

Basic Configuration

new Simforge({ apiKey: string })

// Disable tracing (functions still execute, but no spans are sent)
new Simforge({ apiKey: string, enabled: false })

Tracing

Declare the trace function key once and wrap multiple functions:
const orderService = simforge.getFunction("order-processing")

async function processOrder(orderId: string) {
  return { orderId }
}

async function validateOrder(orderId: string) {
  return { valid: true }
}

// Wrap functions - all share the same trace function key
const tracedProcessOrder = orderService.withSpan(processOrder)
const tracedValidateOrder = orderService.withSpan(validateOrder)

Using withSpan() Directly

For a single span without linking to a function group:
const standaloneTask = simforge.withSpan("one-off-operation", () => {
  return "done"
})

Automatic Nesting

Spans nest automatically based on call stack:
const outer = simforge.withSpan("outer", { type: "agent" }, async () => {
  await inner()  // Becomes a child of "outer"
})

const inner = simforge.withSpan("inner", { type: "function" }, async () => {
  // ...
})

Span Options

Parameters:
  • traceFunctionKey (required): String identifier for grouping spans
  • name (optional): Display name. Defaults to function name, then trace function key
  • type (optional): Span type. Defaults to "custom"
  • metadata (optional): Key-value object (Record<string, unknown>) attached to the span for custom context (e.g. user ID, region, request ID)
Span Types:
type SpanType =
  | "llm"        // LLM calls
  | "agent"      // Agent workflows
  | "function"   // Function calls
  | "guardrail"  // Safety checks
  | "handoff"    // Human handoffs
  | "custom"     // Default
Examples:
// Function name is automatically captured as span name
async function processOrder(orderId: string) {
  return { orderId }
}
const traced = simforge.withSpan("order-processing", processOrder)
// Span name: "processOrder"

// Override with name option
const traced = simforge.withSpan(
  "order-processing",
  { name: "OrderProcessor" },
  processOrder
)
// Span name: "OrderProcessor"

// Set span type
const checkSafety = simforge.withSpan(
  "safety-check",
  { type: "guardrail" },
  async (content: string) => ({ safe: true })
)

// With metadata (definition-time)
const tracedWithMeta = simforge.withSpan(
  "order-processing",
  { type: "function", metadata: { user_id: "u-123", region: "us-east" } },
  processOrder
)

// With getFunction()
const service = simforge.getFunction("order-processing")
const traced = service.withSpan({ name: "CustomName", type: "function" }, processOrder)

Runtime Metadata

Use getCurrentSpan() to get a handle to the active span, then call .setMetadata() to attach metadata from inside a traced function — useful when metadata depends on runtime values like request IDs, computed scores, or dynamic context:
import { getCurrentSpan } from "@goharvest/simforge"

async function processOrder(orderId: string) {
  const userId = await getCurrentUser()
  getCurrentSpan()?.setMetadata({ user_id: userId, order_id: orderId })
  return { orderId, status: "completed" }
}

const traced = simforge.withSpan("order-processing", { type: "function" }, processOrder)
Runtime metadata merges with definition-time metadata. On conflict, runtime values win:
// Definition-time: { user_id: "u-123", region: "us-east" }
// Runtime call: getCurrentSpan()?.setMetadata({ region: "eu-west", request_id: "req-789" })
// Result: { user_id: "u-123", region: "eu-west", request_id: "req-789" }

Error Handling

Errors are captured in the span and re-raised:
const risky = simforge.withSpan("risky-service", () => {
  throw new Error("error")
})

try {
  risky()
} catch (e) {
  // Span records error and timing
}

OpenAI Agents SDK

Attach a trace processor to capture agent runs:
npm install @openai/agents
import { setTraceProcessors } from "@openai/agents"

const processor = simforge.getOpenAiTracingProcessor()
setTraceProcessors([processor])

Native Functions

Simforge’s native functions improve prompt tuning efficacy. The auto-tuning engine has full access to prompts—unlike other Agent SDKs that nest user instructions inside system prompts, making prompts inaccessible to tracing.
const result = await simforge.call("ExtractName", { text: "My name is John Doe" })
// Returns typed result

Advanced Configuration

new Simforge({
  apiKey: string,                    // Required
  serviceUrl?: string,               // Default: https://simforge.goharvest.ai
  envVars?: { OPENAI_API_KEY: string },  // For local function execution
  executeLocally?: boolean,          // Default: true
  enabled?: boolean                  // Default: true
})
  • envVars: Pass LLM provider API keys for local execution
  • executeLocally: When true (default), fetches prompts from Simforge and executes locally. When false, executes on Simforge servers.
  • enabled: When false, all tracing is disabled. Wrapped functions still execute normally but no spans are sent.

Sim Runners

Coming soon.