Skip to content

Tenor provides official SDKs in three languages: TypeScript, Python, and Go. Each SDK supports local evaluation: the contract runs in your process with no network calls, no API key, and no platform dependency. The SDKs share a conformance test suite that guarantees identical evaluation behavior across all three.

Available SDKs ​

SDKPackageLocal Runtime
TypeScript@tenor/sdkWASM (browser + Node)
Pythontenor-sdkPyO3 native bindings
Gogithub.com/riverline-labs/tenor/sdks/gowazero WASM bridge

Local Evaluation ​

The evaluator runs inside your process. You load a compiled contract, provide facts and entity states, and get back verdicts and an action space. No network calls. No platform dependency.

Local evaluation is a pure function: same contract + same facts + same entity states = same verdicts, every time. The evaluator never modifies state, never contacts external systems, and has no side effects.

When to use local evaluation:

  • Unit and integration tests. Assert that specific fact combinations produce expected verdicts. No test infrastructure needed beyond the SDK.
  • CI/CD pipelines. Validate contracts and run contract-level tests as part of your build.
  • Client-side preview. Show users what actions are available before they commit (TypeScript WASM runs in the browser).
  • Edge evaluation. Run contract logic in serverless functions, Lambda@Edge, or Cloudflare Workers.
  • Offline support. Evaluate contracts when the network is unavailable.
  • Performance-critical paths. Local evaluation takes microseconds, not network round-trip milliseconds.
typescript
// TypeScript — local evaluation, no network
import { TenorEvaluator } from "@tenor/sdk";

const evaluator = await TenorEvaluator.load("./escrow.tenor.wasm");

const result = evaluator.evaluate({
  facts: { payment_received: true, payment_amount: "5000.00" },
  entities: { Order: { order_001: "pending" } },
});

console.log(result.verdicts);
// { payment_ok: { value: true, stratum: 0 }, ... }

Cross-SDK Conformance ​

All three SDKs share a single conformance test suite: a set of contracts, fact inputs, entity states, and expected outputs. The test suite covers:

  • Verdict correctness. Every rule fires (or does not fire) as expected for the given facts.
  • Stratum ordering. Higher-stratum rules see only lower-stratum verdicts.
  • Action space completeness. Every legal operation for every persona is included. No illegal operations appear.
  • Type coercion rejection. Invalid fact types are rejected, not silently coerced.
  • Edge cases. Empty fact sets, all-terminal entities, flows with parallel branches, multi-stratum chains.

The conformance suite runs in CI for every SDK release. If a new version of any SDK produces different verdicts from the reference implementation, the release is blocked.

This means you can mix SDKs freely in your architecture. A Python service can evaluate locally, a TypeScript frontend can preview action spaces in the browser, and a Go service can evaluate in a microservice --- all against the same contract, producing the same verdicts for the same inputs.

Type Generation ​

The tenor generate command produces typed bindings from your contract, so your application code is checked against the contract schema at compile time:

bash
# Generate TypeScript types
tenor generate typescript escrow.tenor --output ./src/generated/escrow.ts

# Generate Python types
tenor generate python escrow.tenor --output ./src/generated/escrow.py

# Generate Go types
tenor generate go escrow.tenor --output ./internal/generated/escrow.go

Generated types include:

  • Fact types. A typed struct/interface for the facts your contract declares.
  • Entity state enums. All states for each entity as a union/enum type.
  • Verdict types. Names and payload types for each verdict the contract produces.
  • Operation names. String literal types for every operation.
  • Persona names. String literal types for every persona.

Using generated types turns contract violations into compile-time errors:

typescript
// Generated type ensures you cannot misspell a fact name or pass the wrong type
import { EscrowFacts, EscrowPersona } from "./generated/escrow";

const facts: EscrowFacts = {
  payment_received: true,
  payment_amount: "5000.00",
  // typo: paymet_received — compile error
  // wrong type: payment_received: "yes" — compile error
};

Choosing Your SDK ​

ScenarioRecommended SDK
Node.js backend serviceTypeScript
React/Next.js frontendTypeScript (WASM in browser)
Django/FastAPI backendPython
Data pipeline / JupyterPython
Go microserviceGo
CI/CD contract testingAny
Serverless / edge functionTypeScript or Go

The right choice depends on your stack and your needs. All three SDKs produce identical results. Pick the one that fits your language and runtime.