Skip to main content

Serenity Star SDK for .NET

NuGet Version License: MIT

Official .NET SDK for Serenity Star API. The Serenity Star .NET SDK provides a comprehensive interface for interacting with Serenity's different types of agents, such as activities, assistants, proxies, and chat completions.

Table of Contents

Installation

Install the package via NuGet Package Manager:

dotnet add package SubgenAI.SerenityStar.SDK

Or via Package Manager Console:

Install-Package SubgenAI.SerenityStar.SDK

Getting Started

This section will guide you through the initial setup and usage of the Serenity Star SDK.

Direct Instantiation with Factory Method

To directly instantiate the SerenityClient, you can use the static Create method. This requires an API key which you can obtain from your Serenity Star account.

using SerenityStar.Client;
using SerenityStar.Agents.System;

SerenityClient client = SerenityClient.Create("your-api-key");

// Create and execute an activity agent
Activity activity = client.Agents.Activities.Create("marketing-campaign");
AgentResult response = await activity.ExecuteAsync();
Console.WriteLine(response.Content);

Direct Instantiation with Builder Pattern

For more control over the client configuration, use the builder pattern:

using SerenityStar.Client;
using SerenityStar.Agents.System;

SerenityClient client = new SerenityClientBuilder()
.WithApiKey("your-api-key")
.WithBaseUrl("https://api.serenitystar.ai") // Optional
.WithTimeout(30) // Optional
.Build();

// Create and execute an activity agent
Activity activity = client.Agents.Activities.Create("marketing-campaign");
AgentResult response = await activity.ExecuteAsync();
Console.WriteLine(response.Content);

Using a Custom HttpClient

If you need to use a custom HttpClient (for example, with specific handlers or configurations):

using SerenityStar.Client;
using SerenityStar.Agents.System;

HttpClient customHttpClient = new HttpClient();
// Configure your custom HttpClient as needed

SerenityClient client = new SerenityClientBuilder()
.WithApiKey("your-api-key")
.WithHttpClient(customHttpClient)
.Build();

// Create and execute an activity agent
Activity activity = client.Agents.Activities.Create("marketing-campaign");
AgentResult response = await activity.ExecuteAsync();
Console.WriteLine(response.Content);

Using Dependency Injection

For ASP.NET Core applications, you can register the client with dependency injection:

// In your startup/program.cs
using SerenityStar.Extensions;

services.AddSerenityStar("your-api-key");

// With custom base URL and timeout
services.AddSerenityStar(
apiKey: "your-api-key",
timeoutSeconds: 60,
baseUrl: "https://custom-api.serenitystar.ai"
);

Then inject and use the client in your services:

using SerenityStar.Agents.System;

public class YourService
{
private readonly ISerenityClient _client;

public YourService(ISerenityClient client)
{
_client = client;
}

public async Task DoSomething()
{
Activity activity = _client.Agents.Activities.Create("marketing-campaign");
AgentResult response = await activity.ExecuteAsync();
Console.WriteLine(response.Content);
}
}

Assistants / Copilots

Assistants and Copilots are conversational agents that maintain context across multiple messages. They're perfect for building chatbots, virtual assistants, and interactive applications. Both provide the same functionality - choose the terminology that best fits your use case.

Create a Conversation and Send Messages

using SerenityStar.Client;
using SerenityStar.Agents.System;

SerenityClient client = SerenityClient.Create("your-api-key");

// Using Assistants
Conversation conversation = client.Agents.Assistants.CreateConversation("chef-assistant");

// Or using Copilots (same functionality)
Conversation copilotConversation = client.Agents.Copilots.CreateConversation("chef-assistant");

// First message - creates the conversation in Serenity Star automatically
AgentResult response = await conversation.SendMessageAsync("I would like to get a recipe for parmesan chicken");
Console.WriteLine(response.Content);
Console.WriteLine($"Conversation ID: {conversation.ConversationId}");

// Subsequent messages use the conversation ID automatically
AgentResult response2 = await conversation.SendMessageAsync("Can you suggest a side dish?");
Console.WriteLine(response2.Content);

Resume an Existing Conversation

Use this when you don't already have a Conversation instance in memory, for example, when you load a stored conversation ID from your database:

// Resume a conversation using an existing conversation ID with Assistants
Conversation existingConversation = client.Agents.Assistants.CreateConversation(
"chef-assistant",
conversationId: "existing-conversation-id"
);

// Or with Copilots
Conversation existingCopilot = client.Agents.Copilots.CreateConversation(
"chef-assistant",
conversationId: "existing-conversation-id"
);

AgentResult response = await existingConversation.SendMessageAsync("What was the recipe you suggested earlier?");
Console.WriteLine(response.Content);

Get Conversation Information

You can retrieve detailed information about an assistant/copilot agent, including its configuration, capabilities, and metadata. This is useful for understanding what parameters the agent accepts and how it's configured.

using SerenityStar.Models.Conversation;
using SerenityStar.Models.Execute;

// Get basic agent information by code (works with both Assistants and Copilots)
ConversationInfoResult agentInfo = await client.Agents.Assistants.GetInfoByCodeAsync("chef-assistant");

Console.WriteLine($"Initial Message: {agentInfo.Conversation.InitialMessage}");
Console.WriteLine($"Starters: {string.Join(", ", agentInfo.Conversation.Starters)}");
Console.WriteLine($"Version: {agentInfo.Agent.Version}");
Console.WriteLine($"Vision Enabled: {agentInfo.Agent.VisionEnabled}");
Console.WriteLine($"Is Realtime: {agentInfo.Agent.IsRealtime}");
if (agentInfo.Channel != null)
Console.WriteLine($"Channel: {agentInfo.Channel}");
Console.WriteLine($"Image ID: {agentInfo.Agent.ImageId}");

Advanced example with options:

ConversationInfoResult agentInfoAdvanced = await client.Agents.Assistants.GetInfoByCodeAsync(
"chef-assistant",
new AgentExecutionReq
{
AgentVersion = 2,
InputParameters = new Dictionary<string, object>
{
["dietaryRestrictions"] = "vegetarian",
["cuisinePreference"] = "italian",
["skillLevel"] = "beginner"
},
UserIdentifier = "user-123",
Channel = "web"
}
);

Console.WriteLine($"Initial Message: {agentInfoAdvanced.Conversation.InitialMessage}");
Console.WriteLine($"Starters: {string.Join(", ", agentInfoAdvanced.Conversation.Starters)}");
Console.WriteLine($"Version: {agentInfoAdvanced.Agent.Version}");

Get Conversation by ID

You can retrieve an existing conversation by its ID, including all messages and metadata:

using SerenityStar.Models.Conversation;

SerenityClient client = SerenityClient.Create("your-api-key");

// Get conversation by id (basic example)
ConversationRes conversation = await client.Agents.Assistants.GetConversationByIdAsync(
"chef-assistant",
"conversation-id-here"
);

Console.WriteLine($"Conversation ID: {conversation.Id}");
Console.WriteLine($"Is Open: {conversation.Open}");
Console.WriteLine($"Message Count: {conversation.Messages.Count}");

// Display all messages
foreach (ConversationMessage message in conversation.Messages)
Console.WriteLine($"{message.Role}: {message.Content}");

// Get conversation by id with executor task logs
ConversationRes conversationWithLogs = await client.Agents.Assistants.GetConversationByIdAsync(
"chef-assistant",
"conversation-id-here",
new GetConversationOptions
{
ShowExecutorTaskLogs = true
}
);

Console.WriteLine($"Executor Task Logs: {conversationWithLogs.ExecutorTaskLogs}");

Stream Message with SSE

You can stream responses from assistant agents using Server-Sent Events (SSE). This is useful for real-time response processing and providing a better user experience with progressive message display.

using SerenityStar.Client;
using SerenityStar.Models.Streaming;

SerenityClient client = SerenityClient.Create("your-api-key");

// Create a conversation with an assistant
Conversation conversation = client.Agents.Assistants.CreateConversation("chef-assistant");

// Stream a message - get updates as they arrive
await foreach (StreamingAgentMessage message in conversation.StreamMessageAsync("Tell me a quick recipe"))
{
switch (message)
{
case StreamingAgentMessageStart start:
Console.WriteLine("Stream started");
break;

case StreamingAgentMessageContent content:
Console.Write(content.Text); // Display content as it arrives
break;

case StreamingAgentMessageTaskStart taskStart:
Console.WriteLine($"Task started: {taskStart.TaskKey}");
break;

case StreamingAgentMessageTaskStop taskStop:
Console.WriteLine($"Task completed: {taskStop.TaskKey} (Duration: {taskStop.Duration.TotalMilliseconds}ms)");
break;

case StreamingAgentMessageStop stop:
Console.WriteLine("\nStream completed");
Console.WriteLine($"Conversation ID: {stop.Result?.InstanceId}");
if (stop.Result?.CompletionUsage != null)
{
Console.WriteLine($"Tokens used - Input: {stop.Result.CompletionUsage.PromptTokens}, Output: {stop.Result.CompletionUsage.CompletionTokens}");
}
break;

case StreamingAgentMessageError error:
Console.WriteLine($"Error: {error.Message}");
break;
}
}

Stream Subsequent Messages

Once a conversation is created, you can stream additional messages using the same conversation instance:

// First message stream (creates the conversation)
await foreach (StreamingAgentMessage message in conversation.StreamMessageAsync("What's a healthy breakfast?"))
{
if (message is StreamingAgentMessageContent content)
Console.Write(content.Text);
}

// Subsequent message stream (uses existing conversation)
await foreach (StreamingAgentMessage message in conversation.StreamMessageAsync("Can you add protein options?"))
{
if (message is StreamingAgentMessageContent content)
Console.Write(content.Text);
}

Stream with Execution Options

You can customize the streaming behaviour with execution options:

// Create conversation with options
Conversation conversation = client.Agents.Assistants.CreateConversation(
"chef-assistant",
options: new AgentExecutionReq
{
InputParameters = new Dictionary<string, object>
{
["mealType"] = "dinner",
["servings"] = 4
},
UserIdentifier = "user-456",
Channel = "mobile-app",
AgentVersion = 2
}
);

// Stream message with the configured options
await foreach (StreamingAgentMessage message in conversation.StreamMessageAsync("I need a recipe"))
{
if (message is StreamingAgentMessageContent content)
Console.Write(content.Text);
}

Message Feedback

You can collect user feedback on agent responses to help improve the quality of your assistant.

Submit Feedback

using SerenityStar.Client;
using SerenityStar.Models.MessageFeedback;

SerenityClient client = SerenityClient.Create("your-api-key");

// Create conversation with an assistant
Conversation conversation = client.Agents.Assistants.CreateConversation("chef-assistant");

// Send a message
AgentResult response = await conversation.SendMessageAsync("I would like to get a recipe for parmesan chicken");

// Submit positive feedback (thumbs up)
await conversation.SubmitFeedbackAsync(new SubmitFeedbackReq
{
AgentMessageId = response.AgentMessageId!.Value,
Feedback = true
});

// Or submit negative feedback (thumbs down)
await conversation.SubmitFeedbackAsync(new SubmitFeedbackReq
{
AgentMessageId = response.AgentMessageId!.Value,
Feedback = false
});

Remove Feedback

using SerenityStar.Client;
using SerenityStar.Models.MessageFeedback;

SerenityClient client = SerenityClient.Create("your-api-key");

// Create conversation with an assistant
Conversation conversation = client.Agents.Assistants.CreateConversation("chef-assistant");

// Send a message
AgentResult response = await conversation.SendMessageAsync("I would like to get a recipe for parmesan chicken");

// Submit feedback first
await conversation.SubmitFeedbackAsync(new SubmitFeedbackReq
{
AgentMessageId = response.AgentMessageId!.Value,
Feedback = true
});

// Remove the feedback if the user changes their mind
await conversation.RemoveFeedbackAsync(new RemoveFeedbackReq
{
AgentMessageId = response.AgentMessageId!.Value
});

Connector Status

Check the connection status of an agent's connector:

using SerenityStar.Client;
using SerenityStar.Models.Connector;
using SerenityStar.Models.Execute;

SerenityClient client = SerenityClient.Create("your-api-key");

// Create conversation with an assistant
Conversation conversation = client.Agents.Assistants.CreateConversation("chef-assistant");

// Send a message that might require a connector
AgentResult response = await conversation.SendMessageAsync("I need a summary of my latest meeting notes stored in google drive");

// Check if there are pending actions (e.g., authentication required)
if (response.PendingActions.Count > 0)
{
foreach (PendingAction action in response.PendingActions)
{
if (action is PendingAction.Connection connectionAction)
{
Console.WriteLine($"Authentication required for {connectionAction.ConnectorName}");
Console.WriteLine($"Please visit: {connectionAction.Url}");

// Here the user should complete the authentication process.

// Check connector status for this conversation (you can use a loop to check every 5 seconds)
ConnectorStatusRes status = await conversation.GetConnectorStatusAsync(connectionAction.ConnectorId);

Console.WriteLine($"Is Connected: {status.IsConnected}");

// You can use this to determine if a connector needs authentication
if (!status.IsConnected)
{
Console.WriteLine("Connector is not connected. Please authenticate.");
// Wait and check again...
}
}
}
}

// Once connected, send the message again
// The agent will now have access to Google Drive to retrieve the meeting notes
AgentResult newResponse = await conversation.SendMessageAsync("I need a summary of my latest meeting notes stored in google drive");
Console.WriteLine(newResponse.Content);

Activities

Activities are single-execution agents designed for one-time tasks like translations, data processing, or content generation. Unlike assistants, they don't maintain conversation history.

Execute an Activity Agent

using SerenityStar.Client;
using SerenityStar.Models.Execute;
using SerenityStar.Agents.System;

SerenityClient client = SerenityClient.Create("your-api-key");

// Create and execute activity (basic example)
Activity activity = client.Agents.Activities.Create("translator-activity");
AgentResult response = await activity.ExecuteAsync();

Console.WriteLine(response.Content);

Advanced example with parameters:

// Create and execute activity (advanced example)
Activity activityAdvanced = client.Agents.Activities.Create(
"translator-activity",
new AgentExecutionReq
{
InputParameters = new Dictionary<string, object>
{
["targetLanguage"] = "russian",
["textToTranslate"] = "hello world"
}
}
);

AgentResult responseAdvanced = await activityAdvanced.ExecuteAsync();

Console.WriteLine(responseAdvanced.Content); // Привет, мир!
Console.WriteLine($"Completion Usage: {responseAdvanced.CompletionUsage?.TotalTokens}");

if (responseAdvanced.ExecutorTaskLogs != null)
foreach (AgentResult.Task log in responseAdvanced.ExecutorTaskLogs)
Console.WriteLine($"Task: {log.Description}, Duration: {log.Duration}ms, Success: {log.Success}");

Stream Activity with SSE

Activities support streaming responses using Server-Sent Events (SSE). This allows you to process results as they're generated.

using SerenityStar.Client;
using SerenityStar.Models.Streaming;
using SerenityStar.Models.Execute;
using SerenityStar.Agents.System;

SerenityClient client = SerenityClient.Create("your-api-key");

// Create an activity with options
Activity activity = client.Agents.Activities.Create(
"translator-activity",
new AgentExecutionReq
{
InputParameters = new Dictionary<string, object>
{
["textToTranslate"] = "hello world",
["targetLanguage"] = "spanish"
}
}
);

// Stream the response
await foreach (StreamingAgentMessage message in activity.StreamAsync())
{
switch (message)
{
case StreamingAgentMessageStart:
Console.WriteLine("Translation started...");
break;

case StreamingAgentMessageContent content:
Console.Write(content.Text);
break;

case StreamingAgentMessageTaskStart taskStart:
Console.WriteLine($"Task: {taskStart.TaskKey}");
break;

case StreamingAgentMessageTaskStop taskStop:
Console.WriteLine($"Task completed in {taskStop.Duration.TotalMilliseconds}ms");
break;

case StreamingAgentMessageStop stop:
Console.WriteLine($"\nTranslation complete!");
if (stop.Result?.CompletionUsage != null)
Console.WriteLine($"Tokens used: {stop.Result.CompletionUsage.TotalTokens}");
break;

case StreamingAgentMessageError error:
Console.WriteLine($"Error: {error.Message}");
break;
}
}

AI Proxy

AI Proxy provides direct access to AI models with consistent interfaces across different providers. They're ideal when you need fine-grained control over model parameters.

Execute a Proxy Agent

using SerenityStar.Client;
using SerenityStar.Models.AIProxy;
using SerenityStar.Models.Execute;
using SerenityStar.Agents.System;

SerenityClient client = SerenityClient.Create("your-api-key");

// Create and execute a proxy agent
Proxy proxy = client.Agents.AIProxy.Create(
"proxy-agent",
new ProxyExecutionReq
{
Model = "gpt-4o-mini-2024-07-18",
Messages = new List<ProxyExecutionMessage>
{
new ProxyExecutionMessage
{
Role = "system",
Content = "You are a helpful AI assistant specialized in explaining complex topics."
},
new ProxyExecutionMessage
{
Role = "user",
Content = "What is artificial intelligence?"
}
},
Temperature = 0.7,
MaxTokens = 500
}
);

AgentResult response = await proxy.ExecuteAsync();

Console.WriteLine(response.Content);
Console.WriteLine(response.CompletionUsage);
Console.WriteLine(response.ExecutorTaskLogs);

Stream Proxy with SSE

AI Proxy supports streaming responses, perfect for real-time AI model interactions:

using SerenityStar.Client;
using SerenityStar.Models.AIProxy;
using SerenityStar.Models.Streaming;
using SerenityStar.Agents.System;

SerenityClient client = SerenityClient.Create("your-api-key");

// Create a proxy for streaming
ProxyExecutionReq options = new()
{
Model = "gpt-4o-mini-2024-07-18",
Messages = new List<ChatCompletionMessage>
{
new() { Role = "user", Content = "Write a short poem about programming" }
},
Temperature = 0.7f,
MaxTokens = 200
};

Proxy proxy = client.Agents.AIProxy.Create("openai-proxy", options);

// Stream the response
Console.WriteLine("Poem: ");
await foreach (StreamingAgentMessage message in proxy.StreamAsync())
{
switch (message)
{
case StreamingAgentMessageStart:
break; // Generation started

case StreamingAgentMessageContent content:
Console.Write(content.Text); // Print each chunk as it arrives
break;

case StreamingAgentMessageTaskStart taskStart:
Console.WriteLine($"\nStarting: {taskStart.Key}");
break;

case StreamingAgentMessageTaskStop taskStop:
Console.WriteLine($"Completed in {taskStop.Duration.TotalMilliseconds}ms");
break;

case StreamingAgentMessageStop stop:
Console.WriteLine("\n\nGeneration complete!");
if (stop.Result?.CompletionUsage != null)
{
Console.WriteLine($"Input tokens: {stop.Result.CompletionUsage.PromptTokens}");
Console.WriteLine($"Output tokens: {stop.Result.CompletionUsage.CompletionTokens}");
}
break;

case StreamingAgentMessageError error:
Console.WriteLine($"Error: {error.Message}");
break;
}
}

Proxy Execution Options

The following options can be passed when executing a proxy agent via ProxyExecutionReq:

using SerenityStar.Client;
using SerenityStar.Models.AIProxy;
using SerenityStar.Agents.System;

SerenityClient client = SerenityClient.Create("your-api-key");

Proxy proxy = client.Agents.AIProxy.Create(
"proxy-agent",
new ProxyExecutionReq
{
// Specify the model to use
Model = "gpt-4-turbo",

// Define conversation messages
Messages = new List<ProxyExecutionMessage>
{
new ProxyExecutionMessage
{
Role = "system",
Content = "You are a knowledgeable AI assistant."
},
new ProxyExecutionMessage
{
Role = "user",
Content = "Can you explain the theory of relativity in simple terms?"
}
},

// Model parameters
Temperature = 0.7, // Controls randomness (0-1)
MaxTokens = 500, // Maximum length of response
TopP = 0.9, // Nucleus sampling parameter
TopK = 50, // Top-k sampling parameter
FrequencyPenalty = 0.5, // Reduces repetition (-2 to 2)
PresencePenalty = 0.2, // Encourages new topics (-2 to 2)

// Additional options
Vendor = "openai", // AI provider
UserIdentifier = "user_123", // Unique user ID
GroupIdentifier = "org_456", // Organization ID
UseVision = false // Enable/disable vision features
}
);

AgentResult response = await proxy.ExecuteAsync();

Chat Completions

Chat Completions provide a simplified interface for single-turn or multi-turn conversations without the overhead of managing conversation instances.

Execute a Chat Completion

using SerenityStar.Client;
using SerenityStar.Models.ChatCompletion;
using SerenityStar.Models.Execute;
using SerenityStar.Agents.System;

SerenityClient client = SerenityClient.Create("your-api-key");

// Create and execute chat completion (basic example)
ChatCompletion chatCompletion = client.Agents.ChatCompletions.Create("AgentCreator", new ChatCompletionReq
{
Message = "Hello!!!"
});

AgentResult response = await chatCompletion.ExecuteAsync();

Console.WriteLine(response.Content);
Console.WriteLine($"Completion Usage: {response.CompletionUsage?.TotalTokens}");

Advanced example:

// Create and execute chat completion (advanced example)
ChatCompletion chatCompletionAdvanced = client.Agents.ChatCompletions.Create("Health-Coach", new ChatCompletionReq
{
UserIdentifier = "user-123",
AgentVersion = 2,
Channel = "web",
VolatileKnowledgeIds = new List<string> { "knowledge-1", "knowledge-2" },
Message = "Hi! How can I eat healthier?",
Messages = new List<ChatCompletionMessage>
{
new ChatCompletionMessage
{
Role = "assistant",
Content = "Hi there! How can I assist you?"
}
}
});

AgentResult responseAdvanced = await chatCompletionAdvanced.ExecuteAsync();

Console.WriteLine(responseAdvanced.Content);
Console.WriteLine($"Completion Usage: {responseAdvanced.CompletionUsage?.TotalTokens}");

Stream Chat Completion with SSE

Chat completions support streaming for real-time chat interactions:

using SerenityStar.Client;
using SerenityStar.Models.ChatCompletion;
using SerenityStar.Models.Streaming;
using SerenityStar.Agents.System;

SerenityClient client = SerenityClient.Create("your-api-key");

// Create a chat completion for streaming
ChatCompletion chatCompletion = client.Agents.ChatCompletions.Create(
"assistant-chat",
new ChatCompletionReq
{
Message = "Explain quantum computing in simple terms",
UserIdentifier = "user-123"
}
);

// Stream the response
Console.WriteLine("Answer: ");
await foreach (StreamingAgentMessage message in chatCompletion.StreamAsync())
{
switch (message)
{
case StreamingAgentMessageStart:
break; // Stream initialized

case StreamingAgentMessageContent content:
Console.Write(content.Text); // Print each chunk
break;

case StreamingAgentMessageTaskStart taskStart:
Console.WriteLine($"\n[Processing: {taskStart.TaskKey}]");
break;

case StreamingAgentMessageTaskStop taskStop:
Console.WriteLine($"[Task completed in {taskStop.Duration.TotalMilliseconds}ms]");
break;

case StreamingAgentMessageStop stop:
Console.WriteLine("\n\n[Response complete]");
if (stop.Result?.CompletionUsage != null)
{
Console.WriteLine($"Tokens - Input: {stop.Result.CompletionUsage.PromptTokens}, Output: {stop.Result.CompletionUsage.CompletionTokens}");
}
if (stop.Result?.TimeToFirstToken.HasValue == true)
{
Console.WriteLine($"Time to first token: {stop.Result.TimeToFirstToken}ms");
}
break;

case StreamingAgentMessageError error:
Console.WriteLine($"[Error: {error.Message}]");
break;
}
}

Volatile Knowledge

Volatile knowledge allows you to upload temporary documents that can be used in agent executions. These documents are processed and made available for a limited time based on your configuration. The volatile knowledge is scoped to the conversation or activity it's uploaded to and is automatically included in the next message.

Upload and Use Volatile Knowledge in a Conversation

using SerenityStar.Client;
using SerenityStar.Agents.Conversational;
using SerenityStar.Models.VolatileKnowledge;

SerenityClient client = SerenityClient.Create("your-api-key");

// Step 1: Create a conversation
Conversation conversation = client.Agents.Assistants.CreateConversation("chef-assistant");

// Step 2: Upload volatile knowledge to the conversation
using FileStream fileStream = File.OpenRead("document.pdf");
UploadVolatileKnowledgeReq uploadRequest = new()
{
FileStream = fileStream,
FileName = "document.pdf"
};

VolatileKnowledgeRes knowledge = await conversation.VolatileKnowledge.UploadAsync(uploadRequest);
Console.WriteLine($"Uploaded knowledge ID: {knowledge.Id}, Status: {knowledge.Status}");

// Step 3: Check status until ready
VolatileKnowledgeRes response = await conversation.VolatileKnowledge.GetStatusAsync(knowledge.Id);

// Wait until the knowledge is ready
while (response.Status != "success" && response.Status != "error")
{
await Task.Delay(1000);
response = await conversation.VolatileKnowledge.GetStatusAsync(knowledge.Id);
}

if (response.Status == "error")
Console.WriteLine($"Processing failed: {response.Error}");
else
{
// Step 4: Send a message - the volatile knowledge is automatically included
AgentResult result = await conversation.SendMessageAsync("What does the document say about cooking techniques?");
Console.WriteLine(result.Content);

// The volatile knowledge is automatically cleared after sending the message
// Subsequent messages won't include it unless you upload new knowledge
}

Upload Volatile Knowledge with Text Content

Conversation conversation = client.Agents.Assistants.CreateConversation("research-assistant");

UploadVolatileKnowledgeReq uploadRequest = new()
{
Content = "https://serenitystar.ai"
};

VolatileKnowledgeRes knowledge = await conversation.VolatileKnowledge.UploadAsync(uploadRequest);
Console.WriteLine($"Uploaded knowledge ID: {knowledge.Id}, Status: {knowledge.Status}");

Upload with Custom Parameters

Conversation conversation = client.Agents.Assistants.CreateConversation("data-analyst");

using FileStream fileStream = File.OpenRead("document.pdf");
UploadVolatileKnowledgeReq uploadRequest = new()
{
FileStream = fileStream,
FileName = "document.pdf",
CallbackUrl = "https://your-app.com/knowledge-callback"
};

VolatileKnowledgeRes knowledge = await conversation.VolatileKnowledge.UploadAsync(
uploadRequest,
processEmbeddings: true, // Process embeddings for semantic search
noExpiration: false, // Allow expiration
expirationDays: 7); // Expire in 7 days

Console.WriteLine($"Uploaded knowledge ID: {knowledge.Id}");
if (knowledge.ExpirationDate.HasValue)
Console.WriteLine($"Expires on: {knowledge.ExpirationDate.Value}");

Use Volatile Knowledge with Activities

using SerenityStar.Agents.System;

SerenityClient client = SerenityClient.Create("your-api-key");

// Create an activity
Activity activity = client.Agents.Activities.Create("document-analyzer");

// Upload volatile knowledge to the activity
using FileStream fileStream = File.OpenRead("report.pdf");
UploadVolatileKnowledgeReq uploadRequest = new()
{
FileStream = fileStream,
FileName = "report.pdf"
};

VolatileKnowledgeRes knowledge = await activity.VolatileKnowledge.UploadAsync(uploadRequest);

// Wait for processing
VolatileKnowledgeRes response = await activity.VolatileKnowledge.GetStatusAsync(knowledge.Id);
while (response.Status != "success" && response.Status != "error")
{
await Task.Delay(1000);
response = await activity.VolatileKnowledge.GetStatusAsync(knowledge.Id);
}

if (response.Status == "success")
{
// Execute the activity - volatile knowledge is automatically included
AgentResult result = await activity.ExecuteAsync();
Console.WriteLine(result.Content);
// Volatile knowledge is cleared after execution
}

Multiple Knowledge Documents

You can upload multiple documents to the same conversation or activity. All uploaded documents will be included in the next message and then automatically cleared.

Conversation conversation = client.Agents.Assistants.CreateConversation("multi-doc-assistant");

// Upload first document
using (FileStream file1 = File.OpenRead("doc1.pdf"))
{
VolatileKnowledgeRes knowledge1 = await conversation.VolatileKnowledge.UploadAsync(new()
{
FileStream = file1,
FileName = "doc1.pdf"
});

// Wait for processing
while ((await conversation.VolatileKnowledge.GetStatusAsync(knowledge1.Id)).Status == "analyzing")
await Task.Delay(1000);
}

// Upload second document
using (FileStream file2 = File.OpenRead("doc2.pdf"))
{
VolatileKnowledgeRes knowledge2 = await conversation.VolatileKnowledge.UploadAsync(new()
{
FileStream = file2,
FileName = "doc2.pdf"
});

// Wait for processing
while ((await conversation.VolatileKnowledge.GetStatusAsync(knowledge2.Id)).Status == "analyzing")
await Task.Delay(1000);
}

// Both documents are included in this message
AgentResult result = await conversation.SendMessageAsync("What can you say me about doc1 and doc2?");
Console.WriteLine(result.Content);

// Both documents are cleared after the message is sent

Best Practices

  • Error Handling: Always handle exceptions such as HttpRequestException when making API calls to ensure your application can gracefully handle errors.
  • Cancellation Tokens: Use CancellationToken to manage request cancellations, especially for long-running operations.
  • Dependency Injection: Integrate the SerenityClient using dependency injection for better testability and manageability of your application.
  • Streaming for UX: Use SSE streaming for real-time responses to provide a better user experience with progressive message display.
  • Volatile Knowledge Lifecycle: Remember that volatile knowledge is automatically cleared after some time, so upload documents before each interaction that requires them.

This documentation provides a comprehensive overview of the Serenity Star .NET SDK, including installation, usage examples for all agent types, streaming capabilities, and best practices. For further information, please refer to the official repository.