Migration from Python
Migrate Edictum contracts and patterns from Python to TypeScript. API comparison, key differences, and common patterns.
Right page if: you are moving an Edictum Python agent to TypeScript, or maintaining both and need to understand the API differences. Wrong page if: you are starting fresh with TypeScript -- see https://docs.edictum.ai/docs/typescript. For Python-only docs, see https://docs.edictum.ai/docs/quickstart. Gotcha: contracts YAML is identical between Python and TypeScript. Only the host language API changes. `fromYaml()` is synchronous in TypeScript (no `await`), but `evaluate()` and `evaluateBatch()` are async (return Promises).
The TypeScript SDK has full feature parity with the Python library. Your contract YAML files work unchanged -- only the host language API differs. This page maps Python patterns to their TypeScript equivalents.
API Comparison
Guard Construction
| Python | TypeScript |
|---|---|
Edictum.from_yaml("contracts.yaml") | Edictum.fromYaml('contracts.yaml') |
Edictum.from_yaml_string(content) | Edictum.fromYamlString(content) |
Edictum.from_template("file-agent") | Not yet available -- use YAML |
Edictum.from_server(url=..., api_key=...) | createServerGuard({ url, apiKey, agentId }) |
Edictum(contracts=[...]) | new Edictum({ contracts: [...] }) |
Running Tool Calls
| Python | TypeScript |
|---|---|
await guard.run("tool", args, fn) | await guard.run('tool', args, fn) |
guard.evaluate("tool", args) | await guard.evaluate('tool', args) |
guard.evaluate_batch([...]) | await guard.evaluateBatch([...]) |
Sync vs async. In Python, evaluate() and evaluate_batch() are synchronous. In TypeScript, they return Promises. fromYaml() is synchronous in both.
Errors
| Python | TypeScript |
|---|---|
EdictumDenied | EdictumDenied |
e.reason | e.reason |
EdictumToolError | EdictumToolError |
EdictumConfigError | EdictumConfigError |
Contracts
| Python | TypeScript |
|---|---|
Verdict.fail("reason") | Verdict.fail('reason') |
Verdict.pass() | Verdict.pass() |
{"tool": "Bash", "check": fn} | { tool: 'Bash', check: fn } |
Postcondition: {"tool": "X", "check": fn} (2-arg check auto-detected) | { tool: 'X', contractType: 'post', check: fn } |
Postcondition classification. In Python, a 2-argument check function is auto-detected as a postcondition. In TypeScript, you must set contractType: 'post' explicitly. The SDK throws EdictumConfigError if a check function has 2+ parameters without contractType: 'post'.
Adapters
| Python | TypeScript |
|---|---|
pip install edictum[langchain] | pnpm add @edictum/langchain |
from edictum.adapters.langchain import LangChainAdapter | import { LangChainAdapter } from '@edictum/langchain' |
adapter = LangChainAdapter(guard) | const adapter = new LangChainAdapter(guard) |
adapter.as_tool_wrapper() | adapter.asMiddleware() or adapter.asToolWrapper() |
adapter.set_principal(p) | adapter.setPrincipal(p) |
principal_resolver=fn | principalResolver: fn |
Audit
| Python | TypeScript |
|---|---|
FileAuditSink("audit.jsonl") | new FileAuditSink('audit.jsonl') |
RedactionPolicy() | new RedactionPolicy() |
audit_sink=sink | auditSink: sink |
Server
| Python | TypeScript |
|---|---|
Edictum.from_server(url=..., api_key=...) | createServerGuard({ url, apiKey, agentId }) |
guard.close() | close() (from the returned ServerGuard) |
verify_signatures=True, signing_public_key="..." | verifySignatures: true, signingPublicKey: '...' |
What Stays the Same
Contract YAML is identical. The same contracts.yaml file works with both SDKs. Same apiVersion, same operators, same effects, same variable interpolation. No conversion needed.
Pipeline semantics are identical. Preconditions before execution, postconditions after, session limits across turns, sandbox allowlists. Same evaluation order, same fail-closed behavior.
Audit events are identical. Same AuditAction enum values, same event structure. Audit events from TypeScript and Python agents can be mixed in the same Edictum Console instance.
Key Differences
Async Everywhere
TypeScript's evaluate() and evaluateBatch() return Promises (Python's are synchronous):
// TypeScript -- must await
const result = await guard.evaluate('readFile', { path: '.env' })
// Python -- no await
result = guard.evaluate("read_file", {"path": ".env"})ESM + CJS Dual Build
All packages ship both ESM and CJS builds. If you hit module resolution issues with fromYaml() in an ESM context, use the async variant:
// ESM-safe -- always works
const guard = await Edictum.fromYamlAsync('contracts.yaml')Server Guard is a Separate Package
In Python, Edictum.from_server() is a class method on the core class. In TypeScript, server functionality lives in @edictum/server to keep @edictum/core at zero runtime dependencies:
// Python
from edictum import Edictum
guard = Edictum.from_server(url="https://...", api_key="ek_...")
// TypeScript
import { createServerGuard } from '@edictum/server'
const { guard, close } = await createServerGuard({
url: 'https://...',
apiKey: 'ek_...',
agentId: 'my-agent',
})Naming Convention
Python uses snake_case. TypeScript uses camelCase:
| Python | TypeScript |
|---|---|
from_yaml() | fromYaml() |
from_yaml_string() | fromYamlString() |
session_id | sessionId |
principal_resolver | principalResolver |
audit_sink | auditSink |
on_deny | onDeny |
on_allow | onAllow |
policy_version | policyVersion |
set_principal() | setPrincipal() |
Adapter Package Structure
Python uses extras (pip install edictum[langchain]). TypeScript uses separate npm packages:
| Python Extra | TypeScript Package |
|---|---|
edictum[langchain] | @edictum/langchain |
edictum[openai-agents] | @edictum/openai-agents |
| Built-in (no extra) | @edictum/vercel-ai |
| Built-in (no extra) | @edictum/claude-sdk |
| N/A | @edictum/openclaw |
edictum[server] | @edictum/server |
edictum[otel] | @edictum/otel |
Next Steps
- TypeScript SDK overview -- full getting started guide
- TypeScript adapters -- framework integrations
- Python quickstart -- Python equivalent
- YAML reference -- shared contract syntax
Last updated on