Shiny.AiConversation.GithubCopilot
3.0.0
Prefix Reserved
dotnet add package Shiny.AiConversation.GithubCopilot --version 3.0.0
NuGet\Install-Package Shiny.AiConversation.GithubCopilot -Version 3.0.0
<PackageReference Include="Shiny.AiConversation.GithubCopilot" Version="3.0.0" />
<PackageVersion Include="Shiny.AiConversation.GithubCopilot" Version="3.0.0" />
<PackageReference Include="Shiny.AiConversation.GithubCopilot" />
paket add Shiny.AiConversation.GithubCopilot --version 3.0.0
#r "nuget: Shiny.AiConversation.GithubCopilot, 3.0.0"
#:package Shiny.AiConversation.GithubCopilot@3.0.0
#addin nuget:?package=Shiny.AiConversation.GithubCopilot&version=3.0.0
#tool nuget:?package=Shiny.AiConversation.GithubCopilot&version=3.0.0
Shiny.AiConversation
Session-based AI conversations for .NET apps on top of Microsoft.Extensions.AI: streamed replies, typed questions, persisted and resumable sessions, and token/context tracking.
v3 is a session-based rewrite (breaking:
IAiConversationService,IMessageStoreandIContextProviderare gone). The core package no longer depends on Shiny.Speech or Shiny.Audio; voice lives inShiny.AiConversation.Voice, the chat UI inShiny.AiConversation.Maui.
Features
- Sessions —
IAiSessionManagerstarts, resumes (Get/GetLatest), queries and deletes sessions. Sessions never end; they're persisted and can be picked up again at any time - Streaming turns —
session.Send(...)returns anIAiTurnwhose events stream text as it arrives; a newSendsupersedes the active turn - Typed questions — the model calls the built-in
ask_usertool when it needs an answer; the session moves toAwaitingUserwithPendingQuestions(optionally with multiple-choiceAiChoices). A reply that ends in?still keeps the conversation open - Interruption —
turn.Interrupt(spokenText)records what the user actually heard, and the model is told the reply was cut off - Usage & context — cumulative
SessionUsagefor billing and aContextSnapshotfor how full the context window is, withNormal/Warning/Criticalpressure for the UI to act on - Interceptors —
ISessionInterceptoradds tools and system prompts per session (OnSessionLoaded) or per turn (OnBeforeTurn), optionally byProfile - Honest persistence — the user message is stored before the model is called and the reply is stored as streaming then finalized, so failures and crashes leave an accurate record. Tool calls are persisted too
- One event stream —
session.GetEvents()is anIAsyncEnumerablethat starts with aSnapshot, so a UI binds to one ordered stream - Compaction — sessions never end, so older messages are folded into a rolling summary: on demand with
session.Compact(), or automatically as a safety net near the context limit. A failed summary degrades to a hard trim, never a failed turn - Assistant memory — a silent
memorytool lets the AI keep notes across conversations (session, profile or global scope) without reading them aloud. Only an index goes into each prompt, limits are fed back to the model, andmanager.Memorylets the user see, edit and delete everything. On by default - Conversation search — opt-in
search_conversations/read_conversationtools find things in past (and compacted) conversations; plus optional session titles and long-term memory from recent sessions' summaries - SQLite persistence —
Shiny.AiConversation.Stores.SqliteDocDbstores sessions, messages and memory with database-checked concurrency and full-text search - Voice —
Shiny.AiConversation.Voicedrives a session by voice: push-to-talk, optional wake word, follow-up answers without the wake word, interruption (quiet words or full barge-in) and sentence-by-sentence text-to-speech that starts before the reply is finished. Works with any Shiny.Speech provider - MAUI chat UI —
AiChatViewbinds aChatViewto a session: streamed bubbles, choice chips, history paging, photos, a push-to-talk action and context warnings;AiMemoryViewshows (and edits) what the assistant remembers - Images & camera —
AiInput.WithImage(...)sends photos (stored as attachments, never inline);Shiny.AiConversation.Maui.Cameralets the assistant ask to use the camera (photo, or text/barcode/document scans) after asking the user
Quick Start
// Register an IChatClient in DI (from any Microsoft.Extensions.AI-compatible provider)
builder.Services.AddChatClient(new OpenAIClient("your-api-key").GetChatClient("gpt-4o").AsIChatClient());
builder.Services.AddShinyAiConversation(opts =>
{
opts.SetSqliteDocDbStores(Path.Combine(FileSystem.AppDataDirectory, "ai.db")); // default: in-memory
opts.AddInterceptor<MyInterceptor>(); // optional: tools + prompts
opts.AddTool(AIFunctionFactory.Create(GetWeather), AiToolFlags.None);
opts.AddSessionTitles(); // optional built-ins
opts.AddConversationSearch();
opts.AddLongTermMemory();
// opts.ConfigureMemory(m => m with { Enabled = false }); // memory is on by default
});
// Voice (register your Shiny.Speech providers yourself, e.g. AddSpeechServices() or a cloud provider)
builder.Services.AddShinyAiConversationVoice(voice => voice.AddVoiceSelectionTools());
// MAUI: let the assistant use the camera (needs .UseShinyCamera() on the app builder)
builder.Services.AddAiConversationCamera();
Chat client providers
// Ready-made static OpenAI-compatible provider (Shiny.AiConversation.OpenAi)
opts.AddStaticOpenAIChatClient(apiToken: "your-api-key", endpointUri: "https://api.openai.com/v1", modelName: "gpt-4o");
// MAUI GitHub Copilot — self-contained device-code OAuth, tokens in SecureStorage (Shiny.AiConversation.Maui.GithubCopilot)
opts.AddGithubCopilotChatClient();
For other backends, implement IChatClientProvider and register with opts.SetChatClientProvider<MyProvider>().
Set ContextWindow on the provider (or AiSessionSettings.ContextWindow) to get context pressure.
Talk to the AI
public class ChatViewModel(IAiSessionManager sessions)
{
IAiSession? session;
public async Task Send(string text)
{
this.session ??= await sessions.GetLatest() ?? await sessions.Start();
var turn = this.session.Send(AiInput.Text(text));
await foreach (var e in turn.Events)
{
if (e is TextDelta delta)
AppendToBubble(delta.Text);
}
var result = await turn.Completion; // never throws; check result.Outcome
if (result.Outcome == AiTurnOutcome.AwaitingUser)
ShowChoices(this.session.PendingQuestions);
}
// a tapped choice goes back as a normal message
public void Answer(AiQuestion question, AiChoice choice)
=> this.session!.Send(AiInput.Answer(question, choice.Id));
}
A UI that wants everything (both sides of the conversation, state, usage, errors) subscribes once:
await foreach (var e in session.GetEvents(ct)) // first item is always a Snapshot
{
switch (e)
{
case MessageAdded m: ...; break;
case TurnEventOccurred { Event: TextDelta d }: ...; break;
case StateChanged s: ...; break;
case ContextPressureChanged p: ...; break; // e.g. "this conversation is getting long"
}
}
API Overview
| Type | Description |
|---|---|
IAiSessionManager |
Start(settings), Get(id), GetLatest(), Query(query), Delete(id). Stateless: every load returns a new instance that the caller disposes |
IAiSession |
Send(input), GetEvents(), GetMessages(beforeSequence, count), SetTitle, UpdateSettings; State, PendingQuestions, Usage, Context, ActiveTurn |
IAiTurn |
Events (replayable), Completion (AiTurnResult with Outcome: Completed / AwaitingUser / Interrupted / Superseded / Failed), Interrupt(spokenText) |
AiInput |
Text(...), Voice(...) (asks for spoken-style replies and at most one question), Answer(question, choiceIds) |
AiSessionSettings |
Persisted per session: Profile, ContextWindow, warning/critical thresholds, Tags, Metadata, ExcludeFromSearch |
ISessionInterceptor |
OnSessionLoaded (runs on Start and every Get), OnBeforeTurn, OnAfterTurn |
IAiSessionStore |
Session headers + append-only messages. InMemoryAiSessionStore is the default; SetSqliteDocDbStores(path) for SQLite |
IAiMemoryService |
manager.Memory: list, view, edit and delete what the assistant remembers ("forget everything" = Clear()) |
CompactionSettings |
Per session: AutoCompactThreshold (0.95, null disables), KeepRecentMessages, MaxMessages, FlushToMemory |
AiMemoryOptions |
Enabled, DisabledProfiles and the soft limits (document size, index size, scope size, writes per turn) |
IChatClientProvider |
Supplies the IChatClient; optional ContextWindow. The session adds function invocation itself when the client lacks it |
IVoiceConversation |
Attach(session, options), PushToTalk(), StopSpeaking(), Pause() / Resume(), Detach(), GetEvents(); State (Off / WaitingForWakeWord / Listening / FollowUp / Thinking / Speaking / Paused) |
VoiceOptions |
WakeWord (off by default), Interruption (Off / QuietWords / Full), QuietWords, FollowUpTimeout, Acknowledgement (None / AudioBlip / LessWordy / Full) |
AiChatView |
MAUI ChatView bound to Session (or it resumes the latest session itself) |
Dependencies
| Package | Purpose |
|---|---|
| Microsoft.Extensions.AI | IChatClient abstraction and the tool loop |
Documentation
Full documentation: https://shinylib.net/aiconversation/
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.AI.OpenAI (>= 10.7.0)
- Shiny.AiConversation (>= 3.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.0.0 | 51 | 10/30/2026 |
| 3.0.0-beta-0036 | 57 | 10/25/2026 |
| 3.0.0-beta-0035 | 62 | 10/24/2026 |
| 3.0.0-beta-0034 | 55 | 10/23/2026 |
| 3.0.0-beta-0033 | 56 | 10/22/2026 |
| 3.0.0-beta-0032 | 58 | 10/21/2026 |
| 3.0.0-beta-0030 | 96 | 8/15/2026 |
| 3.0.0-beta-0029 | 81 | 8/8/2026 |
| 3.0.0-beta-0028 | 83 | 8/7/2026 |
| 3.0.0-beta-0027 | 71 | 8/5/2026 |
| 3.0.0-beta-0026 | 85 | 8/5/2026 |
| 3.0.0-beta-0024 | 76 | 8/5/2026 |
| 3.0.0-beta-0023 | 81 | 8/5/2026 |
| 3.0.0-beta-0022 | 86 | 8/2/2026 |
| 3.0.0-beta-0021 | 79 | 7/31/2026 |
| 3.0.0-beta-0020 | 74 | 7/30/2026 |
| 3.0.0-beta-0019 | 80 | 7/30/2026 |
| 3.0.0-beta-0018 | 74 | 7/27/2026 |
| 3.0.0-beta-0017 | 75 | 7/26/2026 |
| 3.0.0-beta-0016 | 78 | 7/24/2026 |