Migrating from Older Versions

This guide helps you update your Caskada applications when migrating from older versions to newer ones. We strive for backward compatibility, but major refactors sometimes introduce breaking changes for significant improvements.

General Advice

  • Start Small: Migrate one part of your application at a time.

  • Consult the Changelog: Check the release notes on the repository for the specific version you are upgrading to. It will list breaking changes, new features, and bug fixes.

  • Review Core Abstraction Docs: Changes often revolve around the core Node, Flow, or Memory components. Re-reading their documentation can clarify new behaviors or APIs.

Migrating to v2.0

The most significant recent changes revolve around Memory management and Flow execution results.

  1. Memory Management (Memory object/createMemory factory):

    • Explicit Creation:

      • Python: Memory(global_store={...}, local_store={...})

      • TypeScript: createMemory(globalStore, localStore)

  2. Flow Execution (Flow.run()):

    • Return Value: Flow.run() now returns a structured ExecutionTree object instead of a simple dictionary. This ExecutionTree provides a detailed trace of node execution order, triggered actions, and nested results.

    • maxVisits Default: The default maxVisits for cycle detection in Flow has been increased (e.g., from 5 to 15).

  3. Error Handling (NodeError):

    • Python: NodeError is now a typing.Protocol, promoting structural typing. You'd typically catch the specific underlying error and then check isinstance(err, NodeError) if you need to access err.retry_count.

    • TypeScript: NodeError remains an Error subtype with an optional retryCount.

Migrating to v1.0

Version 1.0 includes several major architectural improvements that require code updates:

Key Changes

  1. Memory Management: Changed from dictionary-based shared to object-based memory

  2. Explicit Triggers: Flow control now requires explicit trigger() calls

  3. Node Lifecycle: Minor adjustments to method signatures

  4. Flow Configuration: Added options for configuration

  5. Removal of params: The setParams approach has been removed

  6. Batch Processing: Batch node classes have been removed in favor of flow-based patterns

Memory Management Changes

Explicit Triggers

Flow Configuration

Removal of params and setParams

In v1.0, setParams has been removed in favor of direct property access through the streamlined memory management. Replace params with local memory and remove setParams from the code.

Batch Processing Changes (*BatchNode and *BatchFlow Removal)

In v1.0, dedicated batch processing classes like BatchNode, ParallelBatchNode, BatchFlow, and ParallelBatchFlow have been removed from the core library.

The core concept of batching (processing multiple items, often in parallel) is now achieved using a more fundamental pattern built on standard Nodes and Flows:

  1. Fan-Out Trigger Node: A standard Node (let's call it TriggerNode) is responsible for initiating the processing for each item in a batch.

    • In its prep method, it typically reads the list of items from memory.

    • In its post method, it iterates through the items and calls this.trigger("process_one", forkingData={...}) for each item.

    • The forkingData argument is crucial: it passes the specific item (and potentially its index or other context) to the local memory of the successor node instance created for that trigger. This isolates the data for each parallel branch.

  2. Processor Node: Another standard Node (let's call it ProcessorNode) handles the actual processing of a single item.

    • It's connected to the TriggerNode via the "process_one" action (e.g., triggerNode.on("process_one", processorNode)).

    • Its prep method reads the specific item data from its local memory (e.g., memory.item, memory.index), which was populated by the forkingData from the TriggerNode.

    • Its exec method contains the logic previously found in exec_one. It performs the computation for the single item.

    • Its post method takes the result and typically stores it back into the global memory, often in a list or dictionary indexed by the item's original index to handle potential out-of-order completion in parallel scenarios.

  3. Flow Orchestration:

    • To process items sequentially, use a standard Flow containing the TriggerNode and ProcessorNode. The flow will execute the branch triggered for item 1 completely before starting the branch for item 2, and so on.

    • To process items concurrently, use a ParallelFlow. This flow will execute all the branches triggered by TriggerNode in parallel (using Promise.all or asyncio.gather).

  4. Aggregation (Optional): If you need to combine the results after all items are processed (like a Reduce step), the TriggerNode can fire an additional, final trigger (e.g., this.trigger("aggregate")) after the loop. Alternatively, the ProcessorNode can maintain a counter in global memory and trigger the aggregation step only when the counter reaches zero (see the MapReduce pattern).

This approach simplifies the core library by handling batching as an orchestration pattern rather than requiring specialized node types.

Example: Translating Text into Multiple Languages

Let's adapt the TranslateTextNode example provided earlier. Before, it might have been a BatchNode. Now, we split it into a TriggerTranslationsNode and a TranslateOneLanguageNode.

Need Help?

If you encounter issues during migration, you can:

  1. Check the documentation for detailed explanations

  2. Look at the examples for reference implementations

  3. File an issue on GitHub

Always consult the specific release notes for the version you are migrating to for the most accurate and detailed list of changes.

Happy migrating!

Last updated