Working with Documents
This example demonstrates how to create and update documents in Loopstack using the create_document
, update_document
, and load_document
tools. Weβll build a workflow that manages customer profiles, showing how documents can be created with initial data and then updated with additional information.
Document Definition
First, letβs define a customer profile document that will serve as our template:
documents:
- name: customer_profile
description: "Customer information and preferences"
schema:
type: object
properties:
name:
title: "Name"
type: string
email:
title: "Email"
type: string
format: email
status:
title: "Status"
type: string
enum: ["prospect", "active", "inactive"]
metadata:
title: "Metadata"
type: object
properties:
source:
title: "Source"
type: string
required:
- name
- email
- status
content:
status: "prospect"
metadata:
source: "unknown"
ui:
order:
- name
- email
- status
- metadata
Step 1: Create and Update Customer Profile
The first workflow creates a new customer profile document with initial data and then updates it within the same workflow:
workflows:
- name: create_profile
title: "Create Customer Profile"
type: stateMachine
transitions:
# Create the initial document
- name: create_initial_profile
from: start
to: profile_created
call:
- tool: create_document
arguments:
document: customer_profile # Reference to document template
content: # Content object to merge with template
name: "John Smith"
email: "john.smith@example.com"
metadata:
source: "website_signup" # Override template default
as: CREATED_PROFILE # Store document reference
# Update the document from the same workflow
- name: update_status
from: profile_created
to: profile_updated
call:
- tool: load_document
arguments:
where:
name: customer_profile
as: LOADED_PROFILE
- tool: update_document
arguments:
id: ${ LOADED_PROFILE.id } # Use ID from loaded document
content:
status: "active" # Update status
as: UPDATED_PROFILE
- name: log_result
from: profile_updated
to: end
call:
- tool: create_chat_message
arguments:
role: assistant
content: |
β
Customer profile created successfully!
**Profile Details:**
- Name: {{ UPDATED_PROFILE.content.name }}
- Email: {{ UPDATED_PROFILE.content.email }}
- Status: {{ UPDATED_PROFILE.content.status }}
Key Concepts:
- Document Reference:
document: customer_profile
references the document template - Content Merging: The
content
object merges with the templateβs default content - Override Behavior: Specified values override template defaults (e.g.,
source: "website_signup"
) - Export Patterns: Use
as
for workflow-local data - Document Updates: Within the same workflow, you can update documents using
update_document
Step 2: Cross-Workflow Document Handling
The second workflow demonstrates how to handle documents across different workflows. Since we cannot directly update documents from other workflows, we create a new document with updated content:
workflows:
- name: update_profile
title: "Update Customer Profile"
type: stateMachine
transitions:
# Load existing document and create updated version
- name: update_profile
from: start
to: profile_updated
call:
- tool: load_document
arguments:
where:
name: customer_profile
as: LOADED_PROFILE
# Create new document with updated content
- tool: create_document
arguments:
document: customer_profile
content: # Content object to merge with template
name: ${ LOADED_PROFILE.content.name }
email: ${ LOADED_PROFILE.content.email }
metadata:
source: ${ LOADED_PROFILE.content.metadata.source }
status: 'inactive' # We are setting the profile to inactive
as: UPDATED_PROFILE
- name: log_result
from: profile_updated
to: end
call:
- tool: create_chat_message
arguments:
role: assistant
content: |
β
Customer profile updated successfully!
**Updated Information:**
- Status: {{ UPDATED_PROFILE.content.status }}
Key Concepts:
- Cross-Workflow Limitations: Documents cannot be directly updated across workflows
- Document Loading: Use
load_document
withwhere
conditions to find existing documents - Replace Document: When direct updates arenβt possible, create the document again
- Newest Document:
load_document
will always load the newest document when multiple exists from different workflows
Document Loading Patterns
Load by Document Name
- tool: load_document
arguments:
where:
name: customer_profile # Load by document template name
Load by Document ID
- tool: load_document
arguments:
where:
id: ${ CREATED_PROFILE.id } # Load by specific document ID
Note: We always only query within the current root pipeline context. Documents are not designed to be accessed cross root pipeline executions although such queries are technically possible using the
global
flag. For storing data permanently and accessing them through different root pipeline runs, implement a custom data storage mechanism using custom tools.
Variable Interpolation Syntax
The examples use two different interpolation syntaxes:
- Single Braces:
${ variable }
- For accessing variables and object properties directly - Double Braces:
{{ variable }}
- For rendering variables in content and messages using Handlebars syntax.
Best Practices
- Use Document Templates: Define document schemas for data validation and structure
- Within-Workflow Updates: Use
update_document
for modifications within the same workflow - Cross-Workflow Handling: Use
load_document
+create_document
pattern for cross-workflow scenarios - Content Merging: Understand how template defaults merge with provided content
- Explicit Field Mapping: When recreating documents, explicitly map fields rather than using spread operators
Running This Example
This example is available in Loopstack Studio:
- Navigate to the Studio interface
- Switch to the examples workspace
- Select βExample 6: Working with Documentsβ
- Watch as documents are created, updated, and displayed
Complete Example:
include:
- core/tools/create-chat-message.yaml
- core/tools/create-document.yaml
- core/tools/update-document.yaml
- core/tools/load-document.yaml
documents:
- name: customer_profile
description: "Customer information and preferences"
schema:
type: object
properties:
name:
title: "Name"
type: string
email:
title: "Email"
type: string
format: email
status:
title: "Status"
type: string
enum: ["prospect", "active", "inactive"]
metadata:
title: "Metadata"
type: object
properties:
source:
title: "Source"
type: string
required:
- name
- email
- status
content:
status: "prospect"
metadata:
source: "unknown"
ui:
order:
- name
- email
- status
- metadata
pipelines:
- name: documents_example
title: "Example 6: Working with Documents"
type: root
workspace: examples
sequence:
- workflow: create_profile
- workflow: update_profile
workflows:
- name: create_profile
title: "Create Customer Profile"
type: stateMachine
transitions:
# Create the initial document
- name: create_initial_profile
from: start
to: profile_created
call:
- tool: create_document
arguments:
document: customer_profile # Reference to document template
content: # Content object to merge with template
name: "John Smith"
email: "john.smith@example.com" # Override template default
metadata:
source: "website_signup"
as: CREATED_PROFILE # Store document reference
# Update a document from the same workflow
- name: update_status
from: profile_created
to: profile_updated
call:
- tool: load_document
arguments:
where:
name: customer_profile
as: LOADED_PROFILE
- tool: update_document
arguments:
id: ${ LOADED_PROFILE.id } # Use ID from loaded document
content:
status: "active" # Update status
as: UPDATED_PROFILE
- name: log_result
from: profile_updated
to: end
call:
- tool: create_chat_message
arguments:
role: assistant
content: |
β
Customer profile created successfully!
**Profile Details:**
- Name: {{ UPDATED_PROFILE.content.name }}
- Email: {{ UPDATED_PROFILE.content.email }}
- Status: {{ UPDATED_PROFILE.content.status }}
- name: update_profile
title: "Update Customer Profile"
type: stateMachine
transitions:
# We cannot update a document from another workflow
# So we create a new document instead of updating it
- name: update_profile
from: start
to: profile_updated
call:
- tool: load_document
arguments:
where:
name: customer_profile
as: LOADED_PROFILE
- tool: create_document
arguments:
document: customer_profile
content: # Content object to merge with template
name: ${ LOADED_PROFILE.content.name }
email: ${ LOADED_PROFILE.content.email }
metadata:
source: ${ LOADED_PROFILE.content.metadata.source }
status: 'inactive'
as: UPDATED_PROFILE
- name: log_result
from: profile_updated
to: end
call:
- tool: create_chat_message
arguments:
role: assistant
content: |
β
Customer profile updated successfully!
**Updated Information:**
- Status: {{ UPDATED_PROFILE.content.status }}