18  Migration Guide

18.1 From Python Scripts

18.1.1 Before (Python)

import openai

def run_agent(prompt):
    messages = [{"role": "user", "content": prompt}]
    while True:
        response = openai.chat.completions.create(
            model="gpt-4",
            messages=messages,
            tools=[search_tool, done_tool]
        )
        if done_called(response):
            return extract_result(response)
        messages.append(response.choices[0].message)

18.1.2 After (Tactus)

local done = require("tactus.tools.done")
search = Tool { ... }

worker = Agent {
  model = "openai/gpt-4o",
  system_prompt = "...",
  tools = {search, done}
}

Procedure {
  input = {prompt = field.string{required = true}},
  output = {result = field.string{required = true}},
  function(input)
    worker({message = input.prompt})
    repeat worker() until done.called() or Iterations.exceeded(20)
    return {result = done.last_result() or ""}
  end
}

Benefits: - Automatic checkpointing - HITL built-in - BDD testing - Type-safe I/O

18.2 From LangChain/LangGraph

18.2.1 Before (LangGraph)

class State(TypedDict):
    messages: list
    done: bool

graph = StateGraph(State)
graph.add_node("agent", agent_node)
graph.add_node("tools", tool_node)
graph.add_conditional_edges(...)
app = graph.compile(checkpointer=memory)

18.2.2 After (Tactus)

local done = require("tactus.tools.done")
worker = Agent { model = "openai/gpt-4o-mini", tools = {search, done} }

Procedure {
  function(input)
    repeat worker() until done.called()
    return {result = done.last_result() or ""}
  end
}

Benefits: - Imperative code instead of graph definition - Simpler mental model - Same durability guarantees

18.3 Key Differences

Python/LangChain Tactus
Classes and decorators Assignment-based declarations
Manual state management Durable state + checkpoints
Graph-based flow Imperative loops
Separate test files Embedded Specifications([[]])
Multiple files Single .tac file

18.4 From Older Tactus Syntax

Older examples often use “named blocks” (e.g., Agent "worker" { ... }). In v5, declarations are assignment-based and referenced as variables:

worker = Agent { ... }
repeat worker() until done.called()

18.5 Script Mode (Zero-Wrapper)

Script mode lets Tactus wrap simple Lua scripts as a procedure automatically. It’s useful when migrating a quick script into a full Procedure { ... } file.