@claytondcruze/chat-sdk
Version:
TypeScript SDK for AgentStable Chat System with CRUD operations and AI streaming
613 lines • 21.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChatSDK = void 0;
class ChatSDK {
constructor(config) {
this.config = config;
}
getHeaders() {
return {
"Content-Type": "application/json",
"x-user-id": this.config.userId,
...(this.config.apiKey && { Authorization: `Bearer ${this.config.apiKey}` }),
};
}
async fetchAPI(endpoint, options = {}) {
const url = `${this.config.baseUrl}/${endpoint}`;
const response = await fetch(url, {
...options,
headers: {
...this.getHeaders(),
...options.headers,
},
signal: AbortSignal.timeout(this.config.timeout || 30000),
});
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status} ${response.statusText}`);
}
return response.json();
}
// ============================================================================
// CHAT OPERATIONS
// ============================================================================
/**
* Create a new chat
*/
async createChat(data) {
const response = await this.fetchAPI("chatCrud", {
method: "POST",
body: JSON.stringify(data),
});
if (!response.success) {
throw new Error(response.error || "Failed to create chat");
}
return response.data;
}
/**
* Get a specific chat by ID
*/
async getChat(chatId) {
const response = await this.fetchAPI(`chatCrud?chatId=${chatId}`, {
method: "GET",
});
if (!response.success) {
throw new Error(response.error || "Failed to get chat");
}
return response.data;
}
/**
* List chats with pagination
*/
async listChats(limit = 20, cursor) {
const params = new URLSearchParams({
limit: limit.toString(),
...(cursor && { cursor }),
});
const response = await this.fetchAPI(`chatCrud?${params.toString()}`, { method: "GET" });
if (!response.success) {
throw new Error(response.error || "Failed to list chats");
}
return response.data;
}
/**
* Update a chat
*/
async updateChat(chatId, updates) {
const response = await this.fetchAPI(`chatCrud?chatId=${chatId}`, {
method: "PUT",
body: JSON.stringify(updates),
});
if (!response.success) {
throw new Error(response.error || "Failed to update chat");
}
return response.data;
}
/**
* Delete a chat
*/
async deleteChat(chatId) {
const response = await this.fetchAPI(`chatCrud?chatId=${chatId}`, {
method: "DELETE",
});
if (!response.success) {
throw new Error(response.error || "Failed to delete chat");
}
}
// ============================================================================
// MESSAGE OPERATIONS
// ============================================================================
/**
* Create a new message
*/
async createMessage(data) {
const response = await this.fetchAPI("messageCrud", {
method: "POST",
body: JSON.stringify(data),
});
if (!response.success) {
throw new Error(response.error || "Failed to create message");
}
return response.data;
}
/**
* Get messages for a chat with pagination
*/
async getMessages(chatId, limit = 50, cursor) {
const params = new URLSearchParams({
chatId,
limit: limit.toString(),
...(cursor && { cursor }),
});
const response = await this.fetchAPI(`messageCrud?${params.toString()}`, { method: "GET" });
if (!response.success) {
throw new Error(response.error || "Failed to get messages");
}
return response.data;
}
/**
* Delete a message
*/
async deleteMessage(messageId) {
const response = await this.fetchAPI(`messageCrud?messageId=${messageId}`, {
method: "DELETE",
});
if (!response.success) {
throw new Error(response.error || "Failed to delete message");
}
}
// ============================================================================
// AI STREAMING OPERATIONS
// ============================================================================
/**
* Send a message and get streaming AI response
*/
async streamChat(data) {
const url = `${this.config.baseUrl}/aiChatStream`;
const abortController = new AbortController();
const response = await fetch(url, {
method: "POST",
headers: this.getHeaders(),
body: JSON.stringify(data),
signal: abortController.signal,
});
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status} ${response.statusText}`);
}
if (!response.body) {
throw new Error("No response body for streaming");
}
// Transform the ReadableStream to return strings
const stream = new ReadableStream({
start(controller) {
const reader = response.body.getReader();
const decoder = new TextDecoder();
function pump() {
return reader.read().then(({ done, value }) => {
if (done) {
controller.close();
return;
}
const chunk = decoder.decode(value, { stream: true });
controller.enqueue(chunk);
return pump();
});
}
return pump();
},
});
return {
stream,
cancel: () => abortController.abort(),
};
}
// ============================================================================
// UTILITY METHODS
// ============================================================================
/**
* Helper method to consume a streaming response as a string
*/
async streamToString(streamResponse) {
const reader = streamResponse.stream.getReader();
let result = "";
try {
while (true) {
const { done, value } = await reader.read();
if (done)
break;
result += value;
}
}
finally {
reader.releaseLock();
}
return result;
}
/**
* Helper method to get full chat conversation (all messages)
*/
async getChatHistory(chatId) {
let allMessages = [];
let cursor;
let hasMore = true;
while (hasMore) {
const response = await this.getMessages(chatId, 50, cursor);
allMessages = allMessages.concat(response.messages);
hasMore = response.hasMore;
cursor = response.nextCursor;
}
return allMessages;
}
/**
* Helper method for simple chat completion (create message + get AI response)
*/
async sendMessage(chatId, message, options = {}) {
const streamResponse = await this.streamChat({
chatId,
message,
...options,
});
return this.streamToString(streamResponse);
}
// Classification helper methods
/**
* Get classification data from a message
*/
getMessageClassification(message) {
return message.metadata?.classification || null;
}
/**
* Check if a message was classified as complex
*/
isComplexMessage(message) {
return message.metadata?.classification?.complexity === "COMPLEX_ACTIONABLE" ||
message.metadata?.classification?.complexity === "COMPLEX_INFORMATIONAL";
}
/**
* Get topics identified in a message
*/
getMessageTopics(message) {
return message.metadata?.classification?.topics || [];
}
/**
* Get classification statistics for a chat
*/
async getChatClassificationStats(chatId) {
const messages = await this.getMessages(chatId, 100); // Get up to 100 messages
const userMessages = messages.messages.filter(m => m.role === 'user');
const classificationsWithData = userMessages
.map(m => m.metadata?.classification)
.filter(c => c !== undefined);
if (classificationsWithData.length === 0) {
return {
totalMessages: 0,
simpleMessages: 0,
complexMessages: 0,
averageConfidence: 0,
topTopics: []
};
}
const simpleCount = classificationsWithData.filter(c => c.complexity === 'SIMPLE').length;
const complexCount = classificationsWithData.filter(c => c.complexity === 'COMPLEX_ACTIONABLE' || c.complexity === 'COMPLEX_INFORMATIONAL').length;
const avgConfidence = classificationsWithData.reduce((sum, c) => sum + c.confidence, 0) / classificationsWithData.length;
// Count topics
const topicCounts = {};
classificationsWithData.forEach(c => {
c.topics.forEach(topic => {
topicCounts[topic] = (topicCounts[topic] || 0) + 1;
});
});
const topTopics = Object.entries(topicCounts)
.sort(([, a], [, b]) => b - a)
.slice(0, 10)
.map(([topic, count]) => ({ topic, count }));
return {
totalMessages: classificationsWithData.length,
simpleMessages: simpleCount,
complexMessages: complexCount,
averageConfidence: Math.round(avgConfidence * 100) / 100,
topTopics
};
}
/**
* Filter messages by complexity
*/
filterMessagesByComplexity(messages, complexity) {
return messages.filter(m => m.metadata?.classification?.complexity === complexity);
}
/**
* Search messages by topics
*/
searchMessagesByTopics(messages, topics) {
return messages.filter(m => {
const messageTopics = m.metadata?.classification?.topics || [];
return topics.some(topic => messageTopics.some(mt => mt.toLowerCase().includes(topic.toLowerCase())));
});
}
// ============================================================================
// FILESYSTEM OPERATIONS
// ============================================================================
/**
* Create a new filesystem
*/
async createFileSystem(data) {
const response = await this.fetchAPI("filesystemCrud", {
method: "POST",
body: JSON.stringify(data),
});
if (!response.success) {
throw new Error(response.error || "Failed to create filesystem");
}
return response.data;
}
/**
* Get a specific filesystem by ID
*/
async getFileSystem(filesystemId) {
const response = await this.fetchAPI(`filesystemCrud?filesystemId=${filesystemId}`, {
method: "GET",
});
if (!response.success) {
throw new Error(response.error || "Failed to get filesystem");
}
return response.data;
}
/**
* List all filesystems for the user
*/
async listFileSystems() {
const response = await this.fetchAPI("filesystemCrud", {
method: "GET",
});
if (!response.success) {
throw new Error(response.error || "Failed to list filesystems");
}
return response.data;
}
/**
* Update a filesystem
*/
async updateFileSystem(filesystemId, updates) {
const response = await this.fetchAPI(`filesystemCrud?filesystemId=${filesystemId}`, {
method: "PUT",
body: JSON.stringify(updates),
});
if (!response.success) {
throw new Error(response.error || "Failed to update filesystem");
}
return response.data;
}
/**
* Delete a filesystem
*/
async deleteFileSystem(filesystemId) {
const response = await this.fetchAPI(`filesystemCrud?filesystemId=${filesystemId}`, {
method: "DELETE",
});
if (!response.success) {
throw new Error(response.error || "Failed to delete filesystem");
}
}
// ============================================================================
// FOLDER OPERATIONS
// ============================================================================
/**
* Create a new folder
*/
async createFolder(data) {
const response = await this.fetchAPI(`folderCrud/${data.filesystem_id}`, {
method: "POST",
body: JSON.stringify(data),
});
if (!response.success) {
throw new Error(response.error || "Failed to create folder");
}
return response.data;
}
/**
* Get a specific folder by ID
*/
async getFolder(filesystemId, folderId) {
const response = await this.fetchAPI(`folderCrud/${filesystemId}/${folderId}`, {
method: "GET",
});
if (!response.success) {
throw new Error(response.error || "Failed to get folder");
}
return response.data;
}
/**
* List folders in a filesystem
*/
async listFolders(filesystemId) {
const response = await this.fetchAPI(`folderCrud/${filesystemId}`, {
method: "GET",
});
if (!response.success) {
throw new Error(response.error || "Failed to list folders");
}
return response.data;
}
/**
* Update a folder
*/
async updateFolder(filesystemId, folderId, updates) {
const response = await this.fetchAPI(`folderCrud/${filesystemId}/${folderId}`, {
method: "PUT",
body: JSON.stringify(updates),
});
if (!response.success) {
throw new Error(response.error || "Failed to update folder");
}
return response.data;
}
/**
* Delete a folder
*/
async deleteFolder(filesystemId, folderId) {
const response = await this.fetchAPI(`folderCrud/${filesystemId}/${folderId}`, {
method: "DELETE",
});
if (!response.success) {
throw new Error(response.error || "Failed to delete folder");
}
}
// ============================================================================
// FILE OPERATIONS
// ============================================================================
/**
* Create a new file
*/
async createFile(data) {
const response = await this.fetchAPI(`fileCrud/${data.filesystem_id}/${data.folder_id}`, {
method: "POST",
body: JSON.stringify(data),
});
if (!response.success) {
throw new Error(response.error || "Failed to create file");
}
return response.data;
}
/**
* Get a specific file by ID
*/
async getFile(filesystemId, folderId, fileId) {
const response = await this.fetchAPI(`fileCrud/${filesystemId}/${folderId}/${fileId}`, {
method: "GET",
});
if (!response.success) {
throw new Error(response.error || "Failed to get file");
}
return response.data;
}
/**
* List files in a folder
*/
async listFiles(filesystemId, folderId) {
const response = await this.fetchAPI(`fileCrud/${filesystemId}/${folderId}`, {
method: "GET",
});
if (!response.success) {
throw new Error(response.error || "Failed to list files");
}
return response.data;
}
/**
* Update a file
*/
async updateFile(filesystemId, folderId, fileId, updates) {
const response = await this.fetchAPI(`fileCrud/${filesystemId}/${folderId}/${fileId}`, {
method: "PUT",
body: JSON.stringify(updates),
});
if (!response.success) {
throw new Error(response.error || "Failed to update file");
}
return response.data;
}
/**
* Delete a file
*/
async deleteFile(filesystemId, folderId, fileId) {
const response = await this.fetchAPI(`fileCrud/${filesystemId}/${folderId}/${fileId}`, {
method: "DELETE",
});
if (!response.success) {
throw new Error(response.error || "Failed to delete file");
}
}
// ============================================================================
// AI FILESYSTEM PIPELINE
// ============================================================================
/**
* Use AI to analyze user intent and create intelligent filesystem structures
*/
async aiFilesystemPipeline(data) {
const response = await this.fetchAPI("aiFilesystemPipeline", {
method: "POST",
body: JSON.stringify(data),
});
if (!response.success) {
throw new Error(response.error || "Failed to run AI filesystem pipeline");
}
// The API returns the result directly in the response, not wrapped in data
return {
pipeline_results: response.pipeline_results,
execution_results: response.execution_result
};
}
/**
* Get user's filesystem context for AI analysis
*/
async getFilesystemContext() {
const response = await this.fetchAPI("filesystemContext", {
method: "GET",
});
if (!response.success) {
throw new Error(response.error || "Failed to get filesystem context");
}
return response.data;
}
// ============================================================================
// UTILITY METHODS FOR FILESYSTEM
// ============================================================================
/**
* Helper method to create a complete filesystem structure from AI pipeline results
*/
async createFilesystemFromPipeline(pipelineResult) {
const structure = pipelineResult.pipeline_results.proposed_structure;
if (!structure) {
throw new Error("No proposed structure found in pipeline results");
}
// Create filesystem
const filesystem = await this.createFileSystem({
name: structure.filesystem_name,
description: structure.description,
tags: structure.tags,
});
// Create folders
const folders = [];
for (const folderSpec of structure.folders) {
const folder = await this.createFolder({
name: folderSpec.name,
description: folderSpec.description,
filesystem_id: filesystem.id,
tags: folderSpec.tags,
});
folders.push(folder);
}
// Create files if specified
const files = [];
if (structure.files) {
for (const fileSpec of structure.files) {
// Find the target folder
const targetFolder = folders.find(f => f.path === fileSpec.folder_path);
if (targetFolder) {
const file = await this.createFile({
name: fileSpec.name,
description: fileSpec.description,
content: fileSpec.content_template,
filesystem_id: filesystem.id,
folder_id: targetFolder.id,
mime_type: fileSpec.mime_type,
tags: fileSpec.tags,
});
files.push(file);
}
}
}
return {
filesystem,
folders,
files,
};
}
/**
* Search for files and folders by tags
*/
async searchByTags(filesystemId, tags) {
const folders = await this.listFolders(filesystemId);
const matchingFolders = folders.filter(folder => {
const folderTags = Array.isArray(folder.metadata.tags)
? folder.metadata.tags
: Object.values(folder.metadata.tags || {});
return tags.some(tag => folderTags.includes(tag));
});
const allFiles = [];
for (const folder of folders) {
const files = await this.listFiles(filesystemId, folder.id);
allFiles.push(...files);
}
const matchingFiles = allFiles.filter(file => {
const fileTags = Array.isArray(file.metadata.tags)
? file.metadata.tags
: Object.values(file.metadata.tags || {});
return tags.some(tag => fileTags.includes(tag));
});
return {
folders: matchingFolders,
files: matchingFiles,
};
}
}
exports.ChatSDK = ChatSDK;
//# sourceMappingURL=client.js.map