Enhance Persuader: Add InitSession For Flexible Workflows
Hey, guys! Let's dive into a cool enhancement for Persuader, a feature that's all about making your workflows smoother and more flexible. We're talking about the addition of an initSession()
function, designed to revolutionize how you kickstart and manage your sessions. This new function will enable a schema-free session initialization, opening up a world of possibilities for your projects. It's all about making Persuader more adaptable to your specific needs, whether you're building intricate multi-step workflows or simply aiming to optimize your costs. Sounds interesting, right?
The Problem: Current Limitations
Currently, Persuader relies on a Zod schema for all persuade()
calls. While this is great for structure and validation, it does present some limitations. Imagine you're trying to build a complex application or simply want to explore different interaction approaches without being tied down by a rigid schema right from the start. It's akin to trying to build a house without a clear blueprint—you might not know where to start! The current implementation could limit you from: session initialization and context setup, exploratory interactions before defining response structure, multi-step workflows that mix raw and validated responses, and cost optimization through context reuse. This is what led us to this solution.
These limitations are especially noticeable when dealing with dynamic interactions and exploratory phases. You might want to first set up the context, then build your schema, or you may have a workflow that incorporates both raw and validated responses.
Limitations Breakdown
Let's explore those limitations:
- Session initialization and context setup: This is when you need to prepare the ground before starting, for instance, in a customer support bot. Before validating any answers, you want the bot to know about the user context. This setup is hard to do with current implementations.
- Exploratory interactions before defining response structure: This is the classic case. Imagine you want to test how the bot is answering but you don't know the answer in advance. This is an exploration phase that is hard to tackle with current implementations.
- Multi-step workflows that mix raw and validated responses: This is when you want the bot to give a few answers before asking for validation. This can be useful in many situations. Currently, you are constrained to validate every answer.
- Cost optimization through context reuse: This is the most important. Creating a context takes a lot of tokens. Reusing this context is fundamental to cut down on costs and keep the bot running with more budget.
Proposed Solution: Introducing initSession()
The proposed solution introduces a separate function, initSession()
, specifically designed for schema-free session management. The persuade()
function will remain untouched. By adding initSession()
, we maintain backward compatibility while providing a new level of flexibility and control. You'll be able to kick-start your sessions without immediately enforcing a schema, allowing for a more fluid and exploratory approach. Think of it as a warm-up before the main event; you set the stage with initSession()
and then bring in the main actor, persuade()
, when you're ready. With this enhancement, Persuader becomes even more versatile, catering to both simple and complex use cases.
initSession()
Overview
The function will accept options such as context
, initialPrompt
, sessionId
, provider
, model
, and providerOptions
. The key here is that you don't need a schema at this stage, meaning you can freely set up the environment. It will return a sessionId
, and optionally a response
(if initialPrompt
is provided), along with metadata
about the execution time. This gives you full control of the session and opens the door to more dynamic interactions.
Implementation Plan: Building the Foundation
To implement this enhancement, we'll go through a series of carefully planned steps. Let's take a closer look.
1. Adding the initSession()
Function
The first step is to add the initSession()
function to src/core/runner/index.ts
. This function will handle the session initialization, taking in options like context
, initialPrompt
, sessionId
, etc., and returning the sessionId
along with the raw response if an initialPrompt
is provided. The structure will be like this:
export async function initSession(
options: {
context: string;
initialPrompt?: string;
sessionId?: string; // Use existing or create new
provider?: ProviderAdapter;
model?: string;
providerOptions?: Record<string, unknown>;
}
): Promise<{
sessionId: string;
response?: string; // Raw response if initialPrompt provided
metadata: Pick<ExecutionMetadata, 'executionTimeMs' | 'startedAt' | 'completedAt' | 'provider' | 'model'>;
}>
This initial function will be the backbone of the enhancement, setting the stage for the subsequent steps. Remember, flexibility is key.
2. Supporting Types
Next up, we'll add the necessary types to ensure everything works smoothly. We'll define InitSessionOptions
and InitSessionResult
in src/types/pipeline.ts
. These types will define the shape of the options passed to initSession()
and the data it returns, respectively. This will ensure type safety, making the development process more robust and less error-prone.
export interface InitSessionOptions {
readonly context: string;
readonly initialPrompt?: string;
readonly sessionId?: string;
readonly provider?: ProviderAdapter;
readonly model?: string;
readonly providerOptions?: Record<string, unknown>;
}
export interface InitSessionResult {
readonly sessionId: string;
readonly response?: string;
readonly metadata: Pick<ExecutionMetadata, 'executionTimeMs' | 'startedAt' | 'completedAt' | 'provider' | 'model'>;
}
3. Exporting from the Main Index
Finally, we need to make the initSession()
function accessible. We'll export it from src/index.ts
alongside persuade()
. This allows developers to easily use the new function within their projects, completing the implementation.
Usage Examples: Putting It into Action
Let's see how you can use initSession()
in different scenarios.
Session-Based Workflow
In this approach, you start by initializing a session with initSession()
, and then use the returned sessionId
for subsequent persuade()
calls. This is perfect for multi-turn conversations or scenarios where you need to maintain context across multiple interactions.
// 1. Initialize session with context
const { sessionId, response } = await initSession({
context: 'You are a BJJ expert analyzing techniques',
initialPrompt: 'Introduce yourself and explain your analysis approach'
});
console.log(response); // Raw introduction
// 2. Continue with validated calls
const analysis = await persuade({
schema: BJJAnalysisSchema,
input: 'Analyze this guard pass...',
sessionId // Continue same conversation
});
Atomic Workflow (Unchanged)
Don't worry; the original persuade()
function still works exactly as before, maintaining the ease of use for simpler scenarios.
// Still works exactly as before
const result = await persuade({
schema: MySchema,
input: 'Process this data...',
context: 'You are an expert...'
// No sessionId = atomic operation
});
Cost Optimization
By using initSession()
to set the context once and reusing the sessionId
for subsequent persuade()
calls, you can significantly reduce token usage. This is especially beneficial when dealing with long contexts or numerous interactions.
// ✅ Efficient: Context set once
const { sessionId } = await initSession({
context: longContextString
});
for (const item of items) {
await persuade({
schema: ProcessingSchema,
input: item,
sessionId // Reuses context, saves tokens
});
}
Documentation Updates: Keeping Everyone Informed
To ensure that everyone understands how to use this enhancement, we need to update the documentation.
4. Updating README.md
We'll add a new section, "Sessions vs. Single Calls", to README.md
. This section will explain when to use session-based and atomic workflows, provide cost optimization strategies, illustrate usage patterns with examples, and discuss performance considerations. This will make sure everyone knows how to use the new functionalities.
5. Updating CLAUDE.md
We'll add detailed session development patterns to CLAUDE.md
, including setup and initialization patterns, testing strategies, error handling, and performance considerations. This will give the user a more in-depth guide of how to use the tool.
6. Adding Examples
To help users get started quickly, we'll create an examples/session-management/
directory with example files: basic-session.ts
, mixed-workflow.ts
, and cost-optimization.ts
. This will demonstrate simple session workflows, combinations of initSession()
and persuade()
, and token usage comparisons.
Benefits: What's in It for You?
This enhancement has several key benefits that will improve your experience.
✅ No Breaking Changes: The existing persuade()
function remains unchanged, ensuring backward compatibility.
✅ Clear Separation: The distinction between initialization and validation becomes clearer.
✅ Flexible Workflows: Support for both atomic and session-based patterns.
✅ Type Safety: Both functions have clear, predictable return types.
✅ Cost Optimization: Token reuse becomes possible through session context.
✅ Backward Compatible: Existing atomic usage continues to work as before.
Files to Modify: A Quick Overview
Here's a list of files you'll need to modify to implement this feature:
Implementation
src/core/runner/index.ts
- Add theinitSession()
function.src/types/pipeline.ts
- Add supporting types.src/index.ts
- Export the new function.
Documentation
README.md
- Add a "Sessions vs. Single Calls" section.CLAUDE.md
- Add session development patterns.examples/session-management/
- Create example files.
Testing
- Add comprehensive tests for
initSession()
functionality.
This enhancement promises to make your experience with Persuader even better. The key is flexibility and ease of use.