Skip to main content

Using Session Store

The Session Store API provides thread-scoped key-value storage for managing dynamic data across agent iterations. Session values persist throughout a conversation thread and can be used to store state, share data between agent calls, and attach additional content to user messages.

Sessions Dashboard


Overview

Session values are scoped to a specific thread ID and can include:

  • Regular values: Key-value pairs for storing arbitrary data
  • User parts: Special prefixed values (__user_part_*) that automatically attach to user messages
  • Expiry: Optional expiration timestamps for temporary data

JavaScript / TypeScript

Using @distri/core

import { Distri } from '@distri/core';

const client = new Distri({
baseUrl: 'http://localhost:8787/api/v1',
});

const threadId = 'thread-123';

// Set a session value
await client.setSessionValue(
threadId,
'user_preference',
{ theme: 'dark', language: 'en' }
);

// Get a single session value
const value = await client.getSessionValue(threadId, 'user_preference');
console.log('User preference:', value);

// Get all session values
const allValues = await client.getSessionValues(threadId);
for (const [key, value] of Object.entries(allValues)) {
console.log(`${key}:`, value);
}

// Delete a specific key
await client.deleteSessionValue(threadId, 'user_preference');

// Clear all values in a session
await client.clearSession(threadId);

Setting User Parts

User parts automatically attach to user messages when prefixed with __user_part_:

// Set a text user part
await client.setUserPartText(
threadId,
'screenshot_description',
'Screenshot shows the login form with validation errors'
);

// Set an image user part
await client.setUserPartImage(
threadId,
'screenshot',
{
bytes: base64ImageString,
mimeType: 'image/png',
name: 'screenshot.png',
}
);

// Delete a specific user part
await client.deleteUserPart(threadId, 'screenshot_description');

// Clear all user parts
await client.clearUserParts(threadId);

Session Value Expiry

// Set a value with 24-hour expiry
const expiry = new Date();
expiry.setHours(expiry.getHours() + 24);

await client.setSessionValue(
threadId,
'temporary_data',
{ data: 'value' },
expiry.toISOString()
);

Rust

Using distri crate

use distri::Distri;
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Distri::from_env();
let session_id = "thread-123";

// Set a session value
client.set_session_value(
session_id,
"user_preference",
json!({ "theme": "dark", "language": "en" }),
None, // Optional expiry ISO timestamp
).await?;

// Get a single session value
let value = client.get_session_value(session_id, "user_preference").await?;
println!("User preference: {:?}", value);

// Get all session values as a HashMap
let all_values = client.get_session_values(session_id).await?;
for (key, value) in all_values {
println!("{}: {:?}", key, value);
}

// Delete a specific key
client.delete_session_value(session_id, "user_preference").await?;

// Clear all values in a session
client.clear_session(session_id).await?;

Ok(())
}

Setting User Parts

use distri::Distri;
use distri_types::Part;

let client = Distri::from_env();
let session_id = "thread-123";

// Set a named user part (automatically prefixed with __user_part_)
client.set_user_part(
session_id,
"observation", // Name for this part
Part::Text("The user clicked the submit button".to_string()),
).await?;

// Set a text user part (convenience method)
client.set_user_part_text(
session_id,
"screenshot_description",
"Screenshot shows the login form with validation errors",
).await?;

// Set an image user part (with automatic gzip compression)
client.set_user_part_image(
session_id,
"screenshot",
distri_types::FileType::Bytes {
bytes: base64_image_string,
mime_type: "image/png".to_string(),
name: Some("screenshot.png".to_string()),
},
).await?;

// Delete a specific user part
client.delete_user_part(session_id, "observation").await?;

// Clear all user parts
client.clear_user_parts(session_id).await?;

Session Value Expiry

use chrono::Utc;

let expiry = Utc::now() + chrono::Duration::hours(24);
client.set_session_value(
session_id,
"temporary_data",
json!({ "data": "value" }),
Some(&expiry.to_rfc3339()),
).await?;

API Endpoints

Set Session Value

POST /api/v1/threads/{thread_id}/session
Content-Type: application/json

{
"key": "user_preference",
"value": { "theme": "dark", "language": "en" },
"expires_at": "2024-12-31T23:59:59Z" // Optional
}

Get Session Value

GET /api/v1/threads/{thread_id}/session/{key}

Get All Session Values

GET /api/v1/threads/{thread_id}/session

Delete Session Value

DELETE /api/v1/threads/{thread_id}/session/{key}

Clear Session

DELETE /api/v1/threads/{thread_id}/session

Set User Part

POST /api/v1/threads/{thread_id}/session
Content-Type: application/json

{
"key": "__user_part_screenshot",
"value": {
"part_type": "image_url",
"image_url": {
"url": "data:image/png;base64,..."
}
}
}

Transient Parts (save: false)

Some data should be included in agent context but not persisted permanently. Use the __metadata: { save: false } flag on message parts to keep them transient:

// This observation is included in context but not saved to the thread
const observationPart: DistriPart = {
part_type: 'data',
data: {
screenshot: base64ImageString,
page_url: 'https://example.com',
dom_state: extractedDomText,
},
__metadata: { save: false },
};
Keep context lean

Without save: false, every tool response is persisted to the thread. For multi-step agents (e.g., browser automation with 30+ steps), this quickly fills the context window. Mark ephemeral parts as save: false to keep only what the agent needs right now.


Dynamic Context with getMetadata

The getMetadata callback on the Chat component lets you inject fresh context into every message. This is useful for passing application state that changes between agent turns:

import { Chat, useAgent } from '@distri/react';

function LessonChat({ lessonId, currentPage, questions, answers }) {
const { agent } = useAgent({ agentIdOrDef: 'teaching-assistant' });
if (!agent) return null;

return (
<Chat
threadId={`lesson:${lessonId}:page:${currentPage}`}
agent={agent}
getMetadata={async () => ({
dynamic_sections: {
lesson_context: `Page ${currentPage} of lesson ${lessonId}`,
current_questions: JSON.stringify(questions),
student_answers: JSON.stringify(answers),
},
})}
/>
);
}

The dynamic_sections are injected into the agent's system prompt as additional context, updated before each message is sent.


Use Cases

Use CaseDescription
Browser AutomationStore screenshots, DOM observations, and user interactions
State ManagementMaintain conversation context and user preferences
Tool IntegrationShare data between external tools and agent iterations
Multi-step WorkflowsPersist intermediate results across agent calls
Rich User InputAttach images, observations, and structured data to user messages

References