@profullstack/therapy
Version:
CLI tool for interacting with an AI therapist
101 lines (82 loc) • 3.21 kB
JavaScript
import readline from 'readline';
import chalk from 'chalk';
import ora from 'ora';
import { getPromptForMode } from './prompts.js';
import { callAI } from './ai-providers.js';
/**
* Creates and manages an interactive therapy session
* @param {Object} options - Session configuration options
*/
export async function createTherapySession(options) {
const { mode = 'cbt', provider = 'openai', model, verbose = false } = options;
// Initialize the chat history with the system prompt
const systemPrompt = getPromptForMode(mode);
const chatHistory = [{ role: 'system', content: systemPrompt }];
// Extract initial greeting from the prompt
const initialPrompt = systemPrompt;
const startMessage = initialPrompt.split('Begin').pop().split('"')[1] ||
"I'm here to support you. What's been on your mind lately?";
console.log(chalk.cyan(`\nAI: ${startMessage}\n`));
// Create config object
const config = { provider, model, verbose };
// Create readline interface for user input
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
// Handle stdin being closed (e.g., when input is piped from echo)
process.stdin.on('end', () => {
// This is triggered when stdin is closed (e.g., when using echo | therapy)
// We don't need to do anything here, just prevent the error
});
// Start the conversation
processUserInput(rl, chatHistory, config);
}
/**
* Process user input and get AI response
*/
function processUserInput(rl, chatHistory, config) {
rl.question(chalk.green('You: '), async (userInput) => {
// Check for exit commands
if (['exit', 'quit', 'bye'].includes(userInput.toLowerCase())) {
console.log(chalk.cyan('\nThank you for the conversation. Take care! 👋'));
rl.close();
return;
}
// Add user message to history
chatHistory.push({ role: 'user', content: userInput });
// Show thinking spinner
const spinner = ora({
text: 'AI is thinking...',
color: 'cyan',
}).start();
try {
// Get AI response
const aiResponse = await callAI(chatHistory, config);
// Stop spinner and display response
spinner.stop();
console.log(chalk.cyan(`\nAI: ${aiResponse}\n`));
// Add AI response to history
chatHistory.push({ role: 'assistant', content: aiResponse });
// Continue the conversation
processUserInput(rl, chatHistory, config);
} catch (error) {
spinner.stop();
console.error(chalk.red(`Error: ${error.message}`));
if (config.verbose) {
console.error(error);
}
console.log(chalk.yellow('\nThere was an error getting a response, but you can continue the conversation.\n'));
// Continue the conversation despite the error
processUserInput(rl, chatHistory, config);
}
});
// Handle errors on the readline interface
rl.on('error', (err) => {
if (err.code === 'ERR_USE_AFTER_CLOSE') {
// Ignore this error, it happens when stdin is closed
return;
}
console.error(chalk.red(`Readline error: ${err.message}`));
});
}