Mythosia.AI 8.2.0

dotnet add package Mythosia.AI --version 8.2.0
                    
NuGet\Install-Package Mythosia.AI -Version 8.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Mythosia.AI" Version="8.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Mythosia.AI" Version="8.2.0" />
                    
Directory.Packages.props
<PackageReference Include="Mythosia.AI" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Mythosia.AI --version 8.2.0
                    
#r "nuget: Mythosia.AI, 8.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Mythosia.AI@8.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Mythosia.AI&version=8.2.0
                    
Install as a Cake Addin
#tool nuget:?package=Mythosia.AI&version=8.2.0
                    
Install as a Cake Tool

Mythosia.AI

Build applications that can switch AI providers while keeping the same completion, streaming and tool workflows. Mythosia.AI connects OpenAI, Anthropic, Google, xAI, DeepSeek and Perplexity, with multimodal input, reasoning, hosted search, citations and optional image generation/editing.

Choose premium low-latency processing only for requests that need it, while keeping the same model and reasoning effort. The WithSpeed(InferenceSpeed.ProviderDefault/Standard/Fast) API works on immutable request builders and next-request service extensions. Inspect model capabilities, then read AIRunResult.Processing or LastProcessing to distinguish requested from reported processing; unknown reporting remains unknown. Fast can cost more and is limited by provider/model/API and account access. See speed selection and examples.

Pending release: 8.2.0

This minor release requires Mythosia.AI.Abstractions 4.2.0. It adds AIModels.OpenAI.Gpt6_1Sol (gpt-6.1-sol) through the existing completion, streaming, structured-output, image-input, local-tool and Run workflows. It supports Low through Max reasoning, including XHigh, and defaults to Medium; None and Minimal are rejected. Existing GPT-6 Sol/Luna reasoning-off behavior and service defaults remain. See the v8.2.0 release notes and GPT-6.1 Sol migration guide.

Claude Sonnet 5.5 is also available through AIModels.Anthropic.ClaudeSonnet5_5 (claude-sonnet-5-5). Untouched settings use adaptive/high thinking with readable thinking omitted. WithBetweenToolsThinking(...) and ClaudeThinkingMode provide an explicit lowest-thinking mode; ReasoningLevel.None selects between_tools at high effort, which disables up-front thinking while retaining tool progress. See Sonnet 5.5 configuration and migration.

Completion, streaming, structured output and Run use the same request preparation: apply the actual profile once and validate the effective settings before automatic summaries, history changes or transport. Auxiliary requests retain native provider validation while isolating the parent conversation and output schema. See the request settings guide.

Application calls remain independent when profiles or messages are reused, including ordinary nested context and tool callbacks. Framework dispatch into a virtual provider adapter and its first matching base call share the prepared request even when input is replaced; the default callback-streaming adapter also retains captured settings. An unrelated helper invoked at that same base entry before forwarding uses the protected BeginIndependentRequestScope(). See the provider adapter rules and example. Built-in input snapshots keep later calls from rewriting accepted history.

Changed adapter profiles are revalidated before automatic summaries; callback streaming waits for producer cleanup; Claude compaction keeps retained tool dependencies, including input overrides, and protects Mythos 5.1 bound thinking. Stateless OpenAI helpers preserve the parent history guard. See the request adapter guide.

Pending release — known limitations: Sonnet 5.5 / Opus 5.5 can reject a pause_turn continuation ending in a pending server_tool_use; see Claude continuation limits. A custom buffering HttpContent can delay cancellation or policy timeout during successful SSE body acquisition and keep the Run active; see SSE cancellation limits.

These pages describe pending changes, not a completed release validation. See the release notes for included changes, remaining limitations and validation scope.

The v8 API contracts below remain available. When upgrading from 7.x or earlier, follow the v8 migration guide and rebuild dependent applications and custom providers.

When you need to... Use...
Choose valid image options without remembering string spellings Typed quality/background/format and ImageSize.Pixels(...) / .Preset(...); image migration
Prepare independent settings for different requests Immutable CreateRequest(...) builders; request settings
Return business objects from asynchronous tools and stop cooperative work Task<T> / ValueTask<T> results and injected cancellation tokens; tool contract
Stop waiting when a screen closes or a task is cancelled GetCompletionAsync(..., cancellationToken: token); cancellation and implementation migration
Keep the final answer together with usage and sources AIRunResult from await run.Result; Run result migration
Show only appropriate model controls before making a request Local GetCapabilities() / GetImageCapabilities(...) snapshots; capability inspection

Run string callers now read (await run.Result).Text. GetCompletionAsync and typed StructuredStreamRun<T>.Result keep their return types. Cancellation stops cooperative client work; cleanup may wait for tools that ignore cancellation and does not guarantee that the provider stops inference or billing. Request builders isolate settings while conversation history remains shared at execution time.

Provider additions include Claude Fable/Mythos 5.1, Gemini 3.7/3.8 Flash, Grok 4.6, DeepSeek Flash, Grok Imagine Image 2.0 and GPT Image 2.5 Sunburst/Flare. Perplexity moves to the Agent API with research presets, hosted tools, background tasks and independent Search; removed Sonar-specific APIs require migration. GPT-6 Astra, supported steering and opt-in native asynchronous tools remain available from v7.1.

Reliability fixes preserve request snapshots and reported token totals, complete cleanup on failure, validate stream termination and image payloads, and reject invalid structured-output retry budgets. See the v8.0.0 release notes for exact changes and compatibility limits. For older upgrades, apply the v7 migration as well.

Grok 4.7

For fast drafts and careful code/document reviews, select AIModels.xAI.Grok4_7 and choose Low, Medium, High or XHigh through the existing request builder. Completion, streaming, Run, local tools, structured output, image input and model capabilities share the same model-specific validation. Native Auto uses the provider’s High default; reasoning cannot be disabled. WithSpeed(InferenceSpeed.Fast) selects paid priority processing, not the separate Cursor/Grok Build-only Grok 4.7 Fast variant. Read the reported tier in AIRunResult.Processing. This addition requires Mythosia.AI 8.1.0 and Abstractions 4.1.0; the service default remains Grok 4.5. See usage, limits and transport scope.

Claude Sonnet 5.5

Use the existing completion, streaming, Run, local-tool, web-search, structured-output and image-input APIs. Structured output uses schema instructions, deserialization and repair retries; native output_config.format constraints are not sent. Adaptive mode accepts Low through Max, including XHigh; the between_tools mode accepts Low/Medium/High and sends no display, budget or binding fields. Minimal, forced tools and assistant prefills are rejected locally. Signed thinking, including empty blocks and progress-update metadata, is retained; stored assistant edits fail locally, while earlier user/system/tool prefix changes are sent to the provider binding policy. Use append-only history; adaptive mode can request Error or DropBlock explicitly. Fast, native async tools and Run steering are unsupported. ThinkingToggle means switching up-front thinking, not eliminating every thinking block. Requires Mythosia.AI 8.2.0 / Abstractions 4.2.0. Examples, history constraints and migration.

Claude Opus 5.5

For long coding and document tasks, select AIModels.Anthropic.ClaudeOpus5_5 through the existing completion, streaming and Run APIs. Untouched settings use medium adaptive effort and omit readable thinking; explicitly choose summarized reasoning or progress updates for your interface. Signed thinking, including empty blocks, is retained across ordinary turns and tool rounds. Forced tools, assistant prefills and reasoning-off requests are unsupported; native server features are not all exposed. This addition is available with Mythosia.AI 8.1.0 and Abstractions 4.1.0. See Opus 5.5 configuration and migration.

Supported Providers

  • OpenAI — GPT-6.1 Sol / GPT-6 Astra / Sol / Luna, GPT-5.6 alias / Sol / Terra / Luna, GPT-5.5 / 5.5 Pro, GPT-5.4 / 5.4 Mini / 5.4 Nano / 5.4 Pro, GPT-5.3 Codex, GPT-5.2 / 5.2 Pro, GPT-5.1, GPT-4.1 / 4.1 Mini, GPT-4o / 4o Mini
  • Anthropic — Claude Fable 5.1 / 5, Mythos 5.1 / 5 (limited), Opus 5.5 / 5 / 4.8 / 4.7 / 4.6 / 4.5, Sonnet 5.5 / 5 / 4.6 / 4.5, Haiku 4.5
  • Google — Gemini 3.8 Flash, Gemini 3.7 Flash, Gemini 3.6 Flash, Gemini 3.5 Flash/Flash-Lite, Gemini 3.1 Pro Preview/Flash-Lite, Gemini 3 Flash Preview, Gemini 2.5 Pro/Flash/Flash-Lite, Gemini 3.1 Flash Image, Gemini 3.1 Flash-Lite Image, and Gemini 3 Pro Image
  • DeepSeek — Flash (V4.1 Flash) with image input and text-only V4 Pro; local functions, optional thinking, opt-in Responses, and reusable image uploads
  • xAI — Grok 4.7, Grok 4.6, Grok 4.5 (default), Grok 4.3, Grok 4.20 (reasoning / non-reasoning), Grok Build
  • Perplexity — Agent API research presets and perplexity/sonar, hosted search, local tools, and source citations

📚 Documentation

Claude Fable 5 and Claude Mythos 5 require 30-day data retention and cannot use zero-data-retention arrangements. Adaptive thinking is always on; a reasoning-off request is represented by low effort with readable reasoning omitted. Mythos 5 is limited to approved Project Glasswing customers.

Fable 5.1 and Mythos 5.1 also require the provider's 30-day retention arrangement; ZDR needs explicit Anthropic authorization. They reject forced tool choice. Fable 5.1 validates preserved thinking against the earlier conversation, while Mythos 5.1 does not enforce that prefix check. See the 5.1 migration guidance.

Installation

dotnet add package Mythosia.AI

For advanced LINQ operations with streams:

dotnet add package System.Linq.Async

For RAG (Retrieval-Augmented Generation) support:

dotnet add package Mythosia.AI.Rag

This adds .WithRag() to any AIService, enabling document-based context augmentation. See the Mythosia.AI.Rag README for full usage details.

using Mythosia.AI.Rag;

var service = new AnthropicService(apiKey, httpClient)
    .WithRag(rag => rag
        .AddDocument("manual.txt")
        .AddDocument("policy.txt")
    );

var response = await service.GetCompletionAsync("What is the refund policy?");

Quick Start

using Mythosia.AI.Models;
using Mythosia.AI.Services.Anthropic;
using Mythosia.AI.Services.Google;
using Mythosia.AI.Services.OpenAI;

// OpenAI GPT
var gptService = new OpenAIService(apiKey, httpClient);
var openAiResponse = await gptService.GetCompletionAsync("Hello!");

// Anthropic Claude
var claudeService = new AnthropicService(apiKey, httpClient);
var claudeResponse = await claudeService.GetCompletionAsync("Hello!");

// Google Gemini
var geminiService = new GoogleAIService(apiKey, httpClient);
geminiService.ChangeModel(AIModels.Google.Gemini3_6Flash);
var geminiResponse = await geminiService.GetCompletionAsync("Hello!");

Control ongoing tasks

A report or tool-assisted investigation may take time. Use a run to display progress, connect a Stop button, and submit additional requirements on a supported model. See the Run guide for choosing between a run and GetCompletionAsync.

await using var run = await service.StartRunAsync(
    "Read the documents and write a report.",
    onText: text => Console.Write(text),
    cancellationToken: cancellationToken);
string answer = (await run.Result).Text;

The same run can expose run.StreamAsync() events for tools and usage. (await run.Result).Text accumulates all emitted text, including intermediate tool-round and pre-steering output; observing the stream is optional. For supported GPT-6.1 Sol (Standard) / GPT-6 Astra / Sol / Luna runs, call run.SteerAsync(...) while work is active. Success acknowledges queued input; it does not undo earlier output or actions.

For GPT-6.1 Sol, steering requires Standard mode. Pro supports normal Run execution, function calls and native async tools, but reports Steering = Unsupported and run.CanSteer = false. Calling SteerAsync on that Pro run is rejected locally without cancelling or aborting its normal execution.

GPT-6 family runs resolve responses against the configured HttpClient.BaseAddress exactly as HTTP requests do, then map https to wss and http to ws, retaining the resolved host, port and path. The trailing slash matters: https://example.com/proxy/v1/ resolves to wss://example.com/proxy/v1/responses, while https://example.com/proxy/v1 resolves to wss://example.com/proxy/responses. A missing base address or a scheme other than HTTP(S) is rejected before connecting; there is no fallback to the default OpenAI endpoint. Runs use a dedicated ClientWebSocket, so the supplied HttpClient message handlers do not intercept the socket. Custom transports can still override OpenAIService.ConnectRunWebSocketAsync.

Cancelling a token used only for SteerAsync while it is waiting to send, including behind another send, cancels that call without stopping the Run. Once submission to the transport starts, cancellation or a send failure may abort the Run because delivery is uncertain. After sending completes, cancellation while waiting for acknowledgement stops the wait but does not retract the submitted input; continue observing the same Run. Cancelling the token passed to StartRunAsync, or calling run.Cancel(), still cancels the Run.

In native OpenAI runs, accepted steering instructions are recorded in conversation history before their corresponding continuation response, even if output processing lags behind received events. A nonrecoverable transport failure cancels cooperative local tools, and run.Result reports the original failure after cleanup. Cleanup still waits for tools that ignore cancellation.

A fully received final response is retained during normal WebSocket closure if the peer's Close frame arrives before the corresponding initial request or tool-result send finishes. The send is not replayed; caller cancellation is still honored, and send failures without a confirmed final response are not suppressed. An already received terminal API failure keeps its original reason if the connection subsequently closes while local tools are still running.

Image Generation and Editing

To create visual drafts from text or revise existing images, use the optional IImageGenerationService implemented by OpenAIService, GoogleAIService, and XAIService. OpenAI defaults image requests to AIModels.OpenAI.GptImage2 (gpt-image-2); Google uses AIModels.Google.Images.Gemini3_1FlashImage (gemini-3.1-flash-image); xAI uses AIModels.xAI.GrokImagineImage2_0 (grok-imagine-image-2.0). The image model is independent from the service's chat Model and can be overridden per request.

Choose AIModels.OpenAI.GptImage2_5Flare for quick visual drafts, or GptImage2_5Sunburst when editing precision matters most. Both use the same generation and editing methods. The GptImage2_5Flare_260908 and GptImage2_5Sunburst_260908 constants pin the September 8, 2026 snapshots. These models add ImageQuality.XHigh / Max quality and support transparent PNG/WebP output; see the GPT Image 2.5 guide for format, size, reference-image, and mask limits. The default remains GPT Image 2.

using Mythosia.AI.Models;
using Mythosia.AI.Models.Images;
using Mythosia.AI.Services;
using Mythosia.AI.Services.OpenAI;

IImageGenerationService images = new OpenAIService(apiKey, httpClient);

var generated = await images.GenerateImagesAsync(new ImageGenerationRequest
{
    Model = AIModels.OpenAI.GptImage2_5Flare,
    Prompt = "A futuristic city at night",
    Count = 2,
    Size = ImageSize.Pixels(1024, 1024),
    Quality = ImageQuality.High,
    OutputFormat = ImageOutputFormat.Png
});

await File.WriteAllBytesAsync("city.png", generated.Images[0].Data);

var edited = await images.EditImagesAsync(new ImageEditRequest
{
    Model = AIModels.OpenAI.GptImage2_5Sunburst,
    Prompt = "Add warm interior lighting",
    Quality = ImageQuality.XHigh,
    InputImages = new[]
    {
        new ImageInput(await File.ReadAllBytesAsync("building.png"), "image/png", "building.png")
    }
});

For Gemini image generation or reference-image editing, construct the capability from the Google provider instead. Gemini accepts one requested output per call and has no separate mask input.

using Mythosia.AI.Services.Google;

IImageGenerationService geminiImages = new GoogleAIService(geminiApiKey, httpClient);

var generated = await geminiImages.GenerateImagesAsync(new ImageGenerationRequest
{
    Prompt = "A precise architectural facade study",
    Model = AIModels.Google.Images.Gemini3_1FlashImage,
    Size = ImageSize.Preset(ImageResolution.TwoK, ImageAspectRatio.SixteenByNine),
    OutputFormat = ImageOutputFormat.Jpeg
});

Google accepts ImageOutputFormat.Auto or explicit ImageOutputFormat.Jpeg. Explicit Png and WebP are rejected before sending; the API has no corresponding selectors. Use each returned GeneratedImage.MediaType as the format of Data.

Google image presets are model-specific: Flash supports 512/1K/2K/4K, Flash-Lite currently allows 1K, and Pro supports 1K/2K/4K. Flash/Lite offer 14 ratios; Pro offers the 10 standard ratios. All accept Auto. Inspect GetImageCapabilities(model) before presenting choices; unsupported explicit sizes or ratios fail before HTTP in generation and editing. See the model matrix and Flash-Lite documentation discrepancy.

Grok Imagine Image 2.0 uses the same methods for generation and editing with up to five ordered reference images. Its output codec is provider-selected: keep ImageOutputFormat.Auto and select the saved extension from GeneratedImage.MediaType.

using Mythosia.AI.Services.xAI;

IImageGenerationService grokImages = new XAIService(xaiApiKey, httpClient);
var grokImage = await grokImages.GenerateImagesAsync(new ImageGenerationRequest
{
    Prompt = "A glass pavilion at sunrise, wide composition",
    Model = AIModels.xAI.GrokImagineImage2_0,
    Size = ImageSize.Preset(ImageResolution.OneK, ImageAspectRatio.SixteenByNine),
    OutputFormat = ImageOutputFormat.Auto
});
var image = grokImage.Images[0];
var extension = image.MediaType switch
{
    "image/jpeg" => ".jpg",
    "image/png" => ".png",
    "image/webp" => ".webp",
    _ => throw new NotSupportedException(image.MediaType)
};
await File.WriteAllBytesAsync("pavilion" + extension, image.Data);

xAI accepts 1–10 outputs, ImageQuality.Auto / Low / Medium, and JPEG/PNG/WebP references. Use ImageSize.Auto or Preset with ImageResolution.Auto, OneK, or TwoK. Keep ImageBackground.Auto; masks and explicit compression are rejected. Only ImageOutputFormat.Auto is supported; explicit formats fail before HTTP and no transcoding is added.

The upcoming major API replaces string options with ImageQuality, ImageBackground, ImageOutputFormat, and immutable ImageSize. The separate request AspectRatio property is removed; pass a ratio to ImageSize.Preset. OpenAI accepts exact Pixels but rejects Preset; Google and xAI accept Preset but reject Pixels instead of approximating them. The output-format default is now ImageOutputFormat.Auto. See before/after migration. Image methods remain independent of chat Run/streaming.

The result contains every returned image along with provider/model provenance, an optional request ID, and optional usage. GeneratedImage.Url is optional; use GeneratedImage.Data when the provider returns inline image bytes.

Migration from v6.x

Removed API Replacement
GenerateImageAsync / GenerateImageUrlAsync IImageGenerationService.GenerateImagesAsync and GeneratedImage.Data / Url
AIService.MaxMessageCount Configure ConversationPolicy; without one, the full active history is sent
ChatBlock.RemoveFunctionMessages() Keep function call/result pairs intact, or explicitly clear/rebuild the conversation with ClearMessages()
AIService.ExtractFunctionCall(...) Override ExtractFunctionCalls(...) and return a FunctionCallBatch
CompletionProtocol.ExtractFunctionCall(...) Override ExtractFunctionCalls(...) and return a FunctionCallBatch
ProcessFunctionCallAsync(string, Dictionary<string, object>) Override ProcessFunctionCallAsync(FunctionCall); batch scheduling is handled by ProcessFunctionCallsAsync(...)
GrokReasoning.Off Use Auto to omit reasoning_effort, or None to disable reasoning on Grok 4.3; Grok 4.5, 4.6 and 4.7 cannot disable reasoning
AIModels.xAI.Grok3Mini / XAIService.UseMiniModel() Select Grok 4.3 when reasoning must be switchable, or explicitly select Grok 4.7 for the latest model and its XHigh level
Retired OpenAI and Claude snapshot constants Select a current constant from AIModels; see the release notes for the complete removal list
Implicit gpt-image-1 default Use the independent IImageGenerationService.DefaultImageModel, which defaults to GPT Image 2 on OpenAI

AIModels Catalog

Model selection is now documented around provider-grouped string constants via AIModels.

service.ChangeModel(AIModels.OpenAI.Gpt5_6Sol);
service.ChangeModel(AIModels.Anthropic.ClaudeSonnet4_6);
service.ChangeModel(AIModels.Google.Gemini3_6Flash);
service.ChangeModel(AIModels.xAI.Grok4_7);

AIModels.OpenAI.Gpt6Astra selects gpt-6-astra. Pro execution uses the same model ID with Gpt6ReasoningMode.Pro.

AIModels.OpenAI.Gpt5_6 is the rolling GPT-5.6 alias and currently routes to Sol. Use Gpt5_6Sol for an explicit Sol selection, Gpt5_6Terra for strong performance at a lower price, or Gpt5_6Luna for efficient high-volume workloads.

Static Quick Helpers

For simple stateless usage, use AIService static helpers.

var answer = await AIService.QuickAskAsync(apiKey, "Summarize this text.");
var vision = await AIService.QuickAskWithImageAsync(apiKey, "Describe this image.", imagePath);

<a id="gpt-6-sol-luna"></a>

GPT-6 Sol / Luna

Choose GPT-6 Sol for demanding coding, tool use and agent tasks; choose Luna when cost and throughput matter for large volumes of text or image-input work. Both use the existing completion, streaming and Run APIs, so switching models does not require a new application workflow.

Gpt6Sol, Gpt6Luna and Gpt6Reasoning.None require Mythosia.AI 8.1.0 / Abstractions 4.1.0. Existing Astra support retains its earlier minimum versions; the service default is unchanged.

Use AIModels.OpenAI.Gpt6Sol (gpt-6-sol) or AIModels.OpenAI.Gpt6Luna (gpt-6-luna). Both accept text and images and produce text, with a 1,050,000-token context window, at most 922,000 input tokens and 128,000 output tokens. Input, reasoning and output must still fit the context budget. MaxTokens sets the requested output budget, not the context size.

using Mythosia.AI.Models;
using Mythosia.AI.Services.OpenAI;

var service = new OpenAIService(apiKey, httpClient);
service.ChangeModel(AIModels.OpenAI.Gpt6Sol);
await using var run = await service.CreateRequest("Review this design.")
    .WithReasoning(ReasoningLevel.High)
    .StartRunAsync(onText: text => Console.Write(text));
var result = await run.Result;

service.ChangeModel(AIModels.OpenAI.Gpt6Luna);
string answer = await service.CreateRequest("Summarize this paragraph.")
    .WithReasoning(ReasoningLevel.None)
    .WithTemperature(0.2f)
    .GetCompletionAsync();

Auto resolves to Medium. Sol/Luna support None, Low, Medium, High, XHigh and Max; Minimal is unsupported. Use WithReasoning(ReasoningLevel.None) per request, or Gpt6Reasoning.None in WithGpt6Parameters. Only Sol/Luna with None send Temperature / TopP; reasoning-enabled requests omit them. Astra always requires reasoning and omits sampling. AIRequestProfile.DisableReasoning selects None for Sol/Luna and Low in Standard mode for Astra, omitting reasoning summaries.

Gpt6ReasoningMode.Standard and .Pro use the same selected model ID. All three GPT-6 models use Responses for tool calling and support opt-in async tools, steering through a WebSocket Run, and cache-preserving reasoning updates in Standard single-agent mode. Check run.CanSteer before steering; acceptance does not retract earlier output. WithSpeed(InferenceSpeed.Fast) requests paid Fast processing independently of reasoning effort; inspect result.Processing to see the applied tier. Account access and server downgrades remain separate from local capability support.

GPT-6 Sol · GPT-6 Luna · Reasoning · Fast

GPT-6 Astra Configuration

using Mythosia.AI.Models;
using Mythosia.AI.Services.OpenAI;

var astra = new OpenAIService(apiKey, httpClient);
astra.ChangeModel(AIModels.OpenAI.Gpt6Astra);
astra.WithGpt6Parameters(
    reasoningEffort: Gpt6Reasoning.Medium,
    verbosity: Verbosity.Medium,
    reasoningSummary: ReasoningSummary.Auto,
    reasoningMode: Gpt6ReasoningMode.Standard);

var answer = await astra.GetCompletionAsync("Explain this design.");

On Astra, Gpt6Reasoning supports Auto, Low, Medium, High, XHigh, and Max; Auto resolves to the library default, Medium. The values shown above are the WithGpt6Parameters() defaults. Configure them individually through Gpt6ReasoningEffort, Gpt6Verbosity, Gpt6ReasoningSummary, and Gpt6ReasoningMode. Select Gpt6ReasoningMode.Pro for Pro execution on the same gpt-6-astra model ID. None is available only on Sol/Luna.

Mythosia routes GPT-6 Astra through the Responses API by default, including completions, streaming, structured outputs, vision input, and function calling. GPT-6 tool calling requires Responses. Temperature and TopP are omitted automatically, including request-profile overrides. The model supports up to 128,000 output tokens; MaxTokens controls the requested budget. See the official model details and migration guidance.

GPT-6 Astra reasoning is always enabled: None and Minimal are unavailable. AIRequestProfile.DisableReasoning = true temporarily uses Low effort in Standard mode and omits the reasoning summary, then restores the configured settings. Set Gpt6ReasoningSummary = null when only the summary should be disabled.

GPT-6 Astra internal summarization and query-rewrite profiles reserve at least 4,096 output tokens to accommodate mandatory reasoning. General request token budgets remain caller-controlled.

To continue independent work during a slow lookup, use GPT-6.1 Sol / GPT-6 Astra / Sol / Luna opt-in async tools through FunctionDefinition.AllowAsync or FunctionBuilder.WithAsync(). See Async Tool Calling. To add a requirement while the model is working, start a run, check run.CanSteer, and call run.SteerAsync(...); see control ongoing tasks.

GPT-5 Family Configuration

Current GPT-5 family models (5.1 / 5.2 / 5.3 Codex / 5.4 / 5.5 / 5.6) support type-safe reasoning configuration with per-model enums.

Reasoning Effort (Per-Model Enums)

Each GPT-5 variant has its own enum to ensure only valid options are available at compile time.

var gptService = (OpenAIService)service;

// GPT-5.1: Gpt5_1Reasoning (Auto/None/Low/Medium/High) + Verbosity
gptService.WithGpt5_1Parameters(
    reasoningEffort: Gpt5_1Reasoning.Medium,
    verbosity: Verbosity.Low,
    reasoningSummary: ReasoningSummary.Concise);

// GPT-5.2: Gpt5_2Reasoning (Auto/None/Low/Medium/High/XHigh) + Verbosity
gptService.WithGpt5_2Parameters(
    reasoningEffort: Gpt5_2Reasoning.XHigh,
    verbosity: Verbosity.High);

// GPT-5.3 Codex: Gpt5_3Reasoning (Auto/None/Low/Medium/High/XHigh) + Verbosity
gptService.WithGpt5_3Parameters(
    reasoningEffort: Gpt5_3Reasoning.Medium,
    verbosity: Verbosity.Medium,
    reasoningSummary: ReasoningSummary.Concise);

// GPT-5.4 / 5.4 Pro: Gpt5_4Reasoning (Auto/None/Low/Medium/High/XHigh) + Verbosity
gptService.WithGpt5_4Parameters(
    reasoningEffort: Gpt5_4Reasoning.Auto,
    verbosity: Verbosity.High,
    reasoningSummary: ReasoningSummary.Auto);

// GPT-5.5 / 5.5 Pro: Gpt5_5Reasoning (Auto/None/Low/Medium/High/XHigh) + Verbosity
gptService.WithGpt5_5Parameters(
    reasoningEffort: Gpt5_5Reasoning.High,
    verbosity: Verbosity.Medium,
    reasoningSummary: ReasoningSummary.Concise);

// GPT-5.6 Sol / Terra / Luna: adds Max effort; Pro is a reasoning mode, not a model ID
gptService.WithGpt5_6Parameters(
    reasoningEffort: Gpt5_6Reasoning.Max,
    verbosity: Verbosity.High,
    reasoningSummary: ReasoningSummary.Detailed,
    reasoningMode: Gpt5_6ReasoningMode.Pro);

Auto uses the model-appropriate default (e.g., Medium for GPT-5 and GPT-5.6, None for GPT-5.1/5.2, Medium for GPT-5.2 Pro and GPT-5.3 Codex, None for GPT-5.4, Medium for GPT-5.4 Pro, Medium for GPT-5.5, and High for GPT-5.5 Pro). GPT-5 Pro is forced to High; GPT-5.2/5.4/5.5 Pro clamp unsupported None/Low values to Medium. GPT-5.6 Pro is selected with Gpt5_6ReasoningMode.Pro on the same model ID.

GPT-5.6 and GPT-6 requests use reasoning.context: "current_turn" because Mythosia rebuilds conversation history locally instead of relying on previous_response_id. During tool calls, the original reasoning and function output items are replayed within the active turn.

For OpenAI Responses API calls, Mythosia consumes output and executes tools only after a top-level status: "completed". Failed, incomplete, refused, malformed, or prematurely ended responses surface as errors; collected function calls are discarded before a handler can run. Function-call requests preserve multimodal message parts and image detail, structured-output text.format, forced function selection, and each parameter's declared required/optional contract. Empty, malformed, or non-object function arguments also fail before handler execution.

Reasoning Summary

All GPT-5 family models support ReasoningSummary enum (Auto / Concise / Detailed). Set to null to disable.

Gemini Configuration

Gemini 3 — ThinkingLevel

For long document reviews or repeated tool calls, choose Gemini 3.7 Flash or 3.8 Flash explicitly. The service default remains Gemini 3.6 Flash.

using Mythosia.AI.Extensions;
using Mythosia.AI.Models;
using Mythosia.AI.Models.Enums;

var geminiService = new GoogleAIService(apiKey, httpClient);
geminiService.ChangeModel(AIModels.Google.Gemini3_8Flash);
// Gemini 3.7: AIModels.Google.Gemini3_7Flash
geminiService.ThinkingLevel = GeminiThinkingLevel.Low;

string review = await geminiService
    .CreateRequest("Compare rolling and blue-green deployments, including rollback risks.")
    .WithReasoning(ReasoningLevel.High)
    .GetCompletionAsync();

ThinkingLevel sets the service baseline; WithReasoning(...) overrides one logical request. Use Low for a lighter first pass and High for demanding review; more reasoning can increase latency and token use. Gemini 3.7/3.8 Flash accept Low, Medium, and High, but reject Minimal and common ReasoningLevel.None. GeminiThinkingLevel.Auto omits the override; Gemini 3.8's provider default is Medium. Both models have provider limits of 1,048,576 input and 65,536 output tokens.

Gemini 3 thinking cannot be fully disabled. Earlier models retain their own defaults: Medium for Gemini 3.6 Flash and Gemini 3.5 Flash, Minimal for Gemini 3.5 Flash-Lite, and High for the current preview models. Gemini 3 Pro models reject Minimal; their floor is Low.

Gemini 3.8 Flash, 3.7 Flash, 3.6 Flash, and 3.5 Flash-Lite use the latest request contract, so Mythosia omits legacy temperature, topP, topK, and candidateCount fields for those models. Other Gemini 3 models omit candidateCount while retaining their supported sampling controls. The new models reuse completion, streaming, Run, function calling, and native web/file search with the existing adapter's limits; this is not support for every Google API capability. See the Google guide.

Gemini 2.5 — ThinkingBudget

geminiService.ChangeModel(AIModels.Google.Gemini2_5Pro);
geminiService.ThinkingBudget = 8192;  // -1 = dynamic (default), 0 = disable

0 disables thinking only on Gemini 2.5 Flash and Flash-Lite. Gemini 2.5 Pro requires at least 128 thinking tokens.

Gemini Safety Thresholds

Provider defaults are preserved unless a category is explicitly configured:

geminiService.HarassmentSafetyThreshold = GeminiSafetyThreshold.BlockMediumAndAbove;
geminiService.HateSpeechSafetyThreshold = GeminiSafetyThreshold.BlockOnlyHigh;
geminiService.SexuallyExplicitSafetyThreshold = GeminiSafetyThreshold.Off;
geminiService.DangerousContentSafetyThreshold = GeminiSafetyThreshold.ProviderDefault;

Gemini Streaming Reasoning (includeThoughts)

When streaming with StreamOptions.WithReasoning(), Mythosia.AI requests Gemini thought summaries (includeThoughts: true) and emits provider-returned chunks as StreamingContentType.Reasoning. This observes exposed reasoning output; it does not guarantee a complete internal reasoning trace or set the model's effort.

await foreach (var content in geminiService.StreamAsync(message, new StreamOptions().WithReasoning()))
{
    if (content.Type == StreamingContentType.Reasoning)
        Console.Write($"[Gemini Thinking] {content.Content}");
    else if (content.Type == StreamingContentType.Text)
        Console.Write(content.Content);
}

Grok Configuration

Reasoning Effort

Use lower effort for a quick first pass and more reasoning for difficult checks where answer quality matters more than response time. Select Grok 4.6 explicitly for its additional XHigh level.

using Mythosia.AI.Extensions;
using Mythosia.AI.Models;
using Mythosia.AI.Services.xAI;

var grokService = new XAIService(apiKey, httpClient);
grokService.ChangeModel(AIModels.xAI.Grok4_6);
grokService.WithGrokReasoning(GrokReasoning.Low);

string review = await grokService
    .CreateRequest("Compare rolling and blue-green deployment, including failure recovery.")
    .WithReasoning(ReasoningLevel.XHigh)
    .GetCompletionAsync();

GrokReasoning is Auto, None, Low, Medium, High, or XHigh. Grok 4.6 accepts Low through XHigh; Auto omits the parameter and uses the provider's High default. It cannot disable reasoning. More effort can increase latency and token use. Grok 4.5 remains the service default and accepts Low through High; Grok 4.3 accepts None through High. Unsupported combinations, including XHigh on earlier models, fail before sending.

WithGrokReasoning(...) and the existing WithGrokParameters(...) set the service baseline. Common WithReasoning(...) overrides one logical request on Grok 4.6, including its tool rounds and structured-output repairs, then restores the baseline. Internal DisableReasoning profiles use Low for Grok 4.6. Common cache-preserving updates and hosted search are not integrated for xAI. See the official Grok 4.6 and reasoning guides.

Reasoning Content Streaming

Grok 4.6 can stream provider-generated reasoning summaries when observation enables reasoning output. These summaries are optional and do not expose the complete internal reasoning; changing observation options does not change the requested effort. The same options work with Run:

using Mythosia.AI.Models.Streaming;

await using var run = await grokService.StartRunAsync(
    "Compare deployment strategies.", streamOptions: new StreamOptions().WithReasoning());
await foreach (var content in run.StreamAsync())
{
    if (content.Type == StreamingContentType.Reasoning)
        Console.Write($"[Summary] {content.Content}");
    else if (content.Type == StreamingContentType.Text)
        Console.Write(content.Content);
}
string answer = (await run.Result).Text;

DeepSeek Configuration

Use DeepSeek Flash when a task needs a quick answer, a more careful review, or an explanation of a chart or screenshot. AIModels.DeepSeek.Flash (deepseek-flash) selects V4.1 Flash, released on September 10, 2026, with native visual understanding. The existing completion, streaming, Run, function-calling, and RAG APIs remain the entry points; support starts with Mythosia.AI 8.0.0 / Mythosia.AI.Abstractions 4.0.0.

V4 Pro, Responses and Files support below requires Mythosia.AI 8.1.0 / Abstractions 4.1.0. Earlier 8.0.0 / 4.0.0 packages include the Flash integration described above.

Select AIModels.DeepSeek.V4Pro (deepseek-v4-pro, V4-Pro-0813) for text-only work; Flash remains the default and supports images. Both expose Low/High/Max thinking and the same output ceiling. To use DeepSeek Responses with the existing completion, streaming, Run and local-function APIs, set UseResponsesApi = true before creating the request. The default stays false so existing applications keep Chat Completions; the choice is captured for the whole request and its tool rounds. Responses resends full conversation and native reasoning history instead of relying on server-stored response IDs.

using Mythosia.AI.Extensions;
using Mythosia.AI.Models;
using Mythosia.AI.Services.DeepSeek;

var pro = new DeepSeekService(apiKey, AIModels.DeepSeek.V4Pro, httpClient)
{
    UseResponsesApi = true
};
pro.WithDeepSeekReasoning(DeepSeekReasoning.High);
string answer = await pro.CreateRequest("Review this deployment plan.").GetCompletionAsync();

Upload an image once when several questions or conversations need to reuse it. UploadFileAsync accepts a path or caller-owned stream plus filename; the upload purpose is user_data. JPEG, PNG, GIF and WebP uploads are limited to 64 MiB. DeepSeekImageFileContent refers to that uploaded image on Flash through either transport; it is not PDF/document input and V4 Pro rejects it. Omitted expiration keeps the file permanently; expiresAfterSeconds accepts 3600–2592000 seconds. Keep the file until every conversation that references it has finished.

using Mythosia.AI.Models.Messages;

var vision = new DeepSeekService(apiKey, AIModels.DeepSeek.Flash, httpClient)
{
    UseResponsesApi = true
};
var file = await vision.UploadFileAsync("chart.png", expiresAfterSeconds: 3600);
var question = new Message(ActorRole.User, new List<MessageContent>
{
    new TextContent("Explain the trend in this chart."),
    new DeepSeekImageFileContent(file.Id)
});
string uploadedDescription = await vision.GetCompletionAsync(question);
var metadata = await vision.GetFileAsync(file.Id);

Use GetFileAsync for metadata, ListFilesAsync(new DeepSeekFileListOptions { After = lastId, Limit = 20, Order = DeepSeekFileOrder.Ascending }) for a page, and DeleteFileAsync when the image is no longer needed. Pass the returned LastId as After while HasMore is true; Descending is also supported. There is no documented file-content download endpoint. The Chat UI offers Flash and V4 Pro; query rewriting uses the current catalogue and migrates the former saved DeepSeekChat label to Flash while preserving arbitrary model IDs.

Responses · Files · Models and limits

using Mythosia.AI.Extensions;
using Mythosia.AI.Models;
using Mythosia.AI.Models.Streaming;
using Mythosia.AI.Services.DeepSeek;

var deepseek = new DeepSeekService(apiKey, httpClient);
deepseek.ChangeModel(AIModels.DeepSeek.Flash);
deepseek.WithDeepSeekReasoning(DeepSeekReasoning.Low);

string draft = await deepseek.GetCompletionAsync("Compare rolling and blue-green deployment, including rollback risks.");

await using var run = await deepseek
    .CreateRequest("Review the assumptions in that comparison.")
    .WithReasoning(ReasoningLevel.High)
    .StartRunAsync(
        options: new StreamOptions().WithReasoning());
await foreach (var item in run.StreamAsync())
{
    if (item.Type == StreamingContentType.Reasoning)
        Console.Write(item.Content);
}
string review = (await run.Result).Text;

ThinkingEnabled remains false by default in the library. WithDeepSeekReasoning(...) enables thinking and sets the persistent service ReasoningEffort (Auto, Low, High, Max); native Auto omits effort and uses the provider's High default. Common WithReasoning(...) overrides one logical request and its tool rounds: None disables thinking, Minimal/Low maps to Low, Medium/High/XHigh to High, and Max to Max. Common Auto keeps the configured baseline. More effort can increase response time and token usage. Setting ReasoningEffort alone does not enable thinking.

Register local functions with WithFunction(...) to let the model fetch data or act through your code. Tool calls work with or without thinking. Chat Completions rejects forced/required tool choice while thinking, so use automatic selection there. With UseResponsesApi = true, ForceFunctionName can select a named function even while thinking; the adapter emits a flat Responses tool_choice with type and name. This does not enable native asynchronous tools. The adapter retains native reasoning_content and call IDs for later tool rounds. Run and legacy streaming expose provider reasoning as StreamingContentType.Reasoning when StreamOptions.WithReasoning() is enabled; this observation option does not itself enable thinking. Token usage includes cache and reasoning counts when reported. Automatic context recovery uses the shared streaming loop. When tools require earlier native reasoning history, automatic compaction is blocked to preserve that history; the overflow error remains visible.

For a chart or screenshot, send image bytes with the existing message types:

using Mythosia.AI.Models.Messages;

var message = new Message(ActorRole.User, new List<MessageContent>
{
    new TextContent("Explain the trend in this chart and identify any unclear labels."),
    new ImageContent(await File.ReadAllBytesAsync("chart.png"), "image/png")
});
string description = await deepseek.GetCompletionAsync(message);

ImageContent accepts JPEG, PNG, GIF, or WebP bytes, or a public HTTP(S) URL retrieved by the provider. The example uses a user message. The current API also accepts image content in tool messages, but registered function handlers still return text through the shared result contract. Manually supplied ActorRole.Function image messages must include the matching call ID through MessageMetadataKeys.FunctionId (tool_call_id on the wire). See the current official vision guide for image size and aggregate limits. Image generation remains unsupported.

Both models advertise 1M context and up to 384K (393216) output tokens; the library keeps its 8,000-token request default. Thinking omits temperature/penalties and uses top_p of at least 0.95; non-thinking omits top_p. Responses uses existing typed-output APIs for native JSON schema. Background execution, server-side store/previous_response_id, hosted web/file search, CachePreservation.Required, native asynchronous tools, SteerAsync and image generation are unsupported. Local RAG and ordinary tool rounds remain available.

V4Flash, Chat, and Reasoner remain warning-only obsolete constants with their original wire IDs. The provider temporarily routes the retired deepseek-v4-flash alias to V4.1 Flash; the library does not rewrite that constant. Prefer Flash explicitly. UseReasonerModel() selects Flash with thinking enabled at High.

DeepSeek V4.1 Flash · Thinking · Vision · Tools · Limits

AIRequestProfile

Apply one-shot runtime overrides per request without mutating long-lived service configuration.

var response = await service.GetCompletionAsync(
    "Rewrite this query for retrieval.",
    RequestProfiles.QueryRewrite);

AIRequestContext

Use request-scoped prompt injection when you need to pass derived prompt data only for the current call without polluting the real conversation history or the service's base system message.

Available fields:

Field Purpose
SystemMessagePrefix Text prepended to the system message for this request only
SystemMessageSuffix Text appended to the system message for this request only
AdditionalMessages Extra messages injected into the conversation for this request only (reference docs, few-shot examples)
RequestMessageOverride Replaces the initial input of the current logical request sent to the model, while preserving the original input in history and later tool calls and results

Example — a query rewriter flow where the original user question should remain in chat history, but a retrieval-friendly rewrite is what actually gets sent to the model:

var rewrittenQuery = await service.GetCompletionAsync(
    "Rewrite this question for retrieval.",
    RequestProfiles.QueryRewrite);

var response = await service.GetCompletionAsync(
    originalUserQuestion,
    context: new AIRequestContext
    {
        RequestMessageOverride = new Message(ActorRole.User, rewrittenQuery)
    });

Example — injecting retrieved RAG context as a suffix on the system message, without leaking it into conversation history:

var answer = await service.GetCompletionAsync(userQuestion,
    context: new AIRequestContext
    {
        SystemMessageSuffix = $"\n\nUse the following context to answer:\n{retrievedDocs}"
    });

For the full flow and before/after comparisons, see docs/request-contexts.md.

SystemMessageProvider — Automatic Baseline Injection

When the same dynamic data (today's date, active folder, session info) must be injected on every LLM call, passing an AIRequestContext at every entry point gets tedious and error-prone. AIService.SystemMessageProvider lets you register a callback once, and every outbound call (GetCompletionAsync, StartRunAsync, and the retained legacy entry points) automatically invokes it to build a baseline context.

// Register once — typically at service construction / DI setup
service.WithSystemMessageProvider(() => new AIRequestContext
{
    SystemMessageSuffix =
        $"Today is {DateTime.UtcNow:yyyy-MM-dd}.\n" +
        $"Current folder: {_uiContext.CurrentFolder}"
});

// Every call below automatically receives the baseline context
var answer = await service.GetCompletionAsync(userQuery);
await foreach (var chunk in service.StreamAsync(msg, options)) { /* ... */ }
await using var run = await service.WithMaxRounds(10)
    .StartRunAsync(goal);
var agentResult = (await run.Result).Text;

When the baseline comes from a database, cache, or HTTP call, use the async overload so the provider does not have to block on .Result. Overload resolution picks the right one by lambda arity — no arg for sync, one CancellationToken for async:

service.WithSystemMessageProvider(async ct =>
{
    var prefs = await _db.UserPreferences.FirstOrDefaultAsync(ct);
    return new AIRequestContext
    {
        SystemMessageSuffix = $"User language: {prefs?.Language ?? "en"}"
    };
});

StartRunAsync forwards its cancellation token to the async context provider whether or not output is observed. Legacy streaming paths also forward the caller token. Existing GetCompletionAsync and RunAgentAsync signatures do not accept a cancellation token; use a run when cancellation is needed.

When a call also passes an explicit AIRequestContext, the two merge field-by-field: explicit values win on scalar fields (SystemMessagePrefix, SystemMessageSuffix, RequestMessageOverride); AdditionalMessages concatenates (provider first, then explicit).

Available in Mythosia.AI v6.3.0+. Full details in docs/request-contexts.md.

Function Calling

Quick Start with Functions

// Define a simple function
var service = new OpenAIService(apiKey, httpClient)
    .WithFunction(
        "get_weather",
        "Gets the current weather for a location",
        ("location", "The city and country", required: true),
        (string location) => $"The weather in {location} is sunny, 22°C"
    );

// AI will automatically call the function when needed
var response = await service.GetCompletionAsync("What's the weather in Seoul?");
// Output: "The weather in Seoul is currently sunny with a temperature of 22°C."

Attribute-Based Function Registration

public class WeatherService
{
    [AiFunction("get_current_weather", "Gets the current weather for a location")]
    public string GetWeather(
        [AiParameter("The city name", required: true)] string city,
        [AiParameter("Temperature unit", required: false)] string unit = "celsius")
    {
        // Your implementation
        return $"Weather in {city}: 22°{unit[0]}";
    }
}

// Register all functions from a class
var weatherService = new WeatherService();
var service = new OpenAIService(apiKey, httpClient)
    .WithFunctions(weatherService);

Advanced Function Builder

var service = new OpenAIService(apiKey, httpClient)
    .WithFunction(FunctionBuilder.Create("calculate")
        .WithDescription("Performs mathematical calculations")
        .AddParameter("expression", "string", "The math expression", required: true)
        .AddParameter("precision", "integer", "Decimal places", required: false, defaultValue: 2)
        .WithHandler(async (args) => 
        {
            var expr = args["expression"].ToString();
            var precision = Convert.ToInt32(args.GetValueOrDefault("precision", 2));
            // Calculate and return result
            return await CalculateAsync(expr, precision);
        })
        .Build());

Multiple Functions with Different Types

var service = new OpenAIService(apiKey, httpClient)
    // Parameterless function
    .WithFunction(
        "get_time",
        "Gets the current time",
        () => DateTime.Now.ToString("HH:mm:ss")
    )
    // Two-parameter function
    .WithFunction(
        "add_numbers",
        "Adds two numbers",
        ("a", "First number", true),
        ("b", "Second number", true),
        (double a, double b) => $"The sum is {a + b}"
    )
    // Async function
    .WithFunctionAsync(
        "fetch_data",
        "Fetches data from API",
        ("endpoint", "API endpoint", true),
        async (string endpoint) => await httpClient.GetStringAsync(endpoint)
    );

// The AI will automatically use the appropriate functions
var response = await service.GetCompletionAsync(
    "What time is it? Also, what's 15 plus 27?"
);

Function Calling Policies

// Pre-defined policies
service.DefaultPolicy = FunctionCallingPolicy.Fast;     // 30s timeout, 10 rounds
service.DefaultPolicy = FunctionCallingPolicy.Complex;   // 300s timeout, 50 rounds
service.DefaultPolicy = FunctionCallingPolicy.Vision;    // 200s timeout, for image analysis

// Custom policy
service.DefaultPolicy = new FunctionCallingPolicy
{
    MaxRounds = 25,
    TimeoutSeconds = 120,
    ExecutionMode = FunctionExecutionMode.Parallel,
    MaxConcurrency = 5,
    EnableLogging = true  // Enable debug output
};

// Per-request policy override
var fastResponse = await service
    .WithPolicy(FunctionCallingPolicy.Fast)
    .GetCompletionAsync("Complex task requiring functions");

// Inline policy configuration
var configuredResponse = await service
    .BeginMessage()
    .AddText("Analyze this data")
    .WithMaxRounds(5)
    .WithTimeout(60)
    .SendAsync();

Sequential is the default for ordinary handler scheduling and preserves one-at-a-time execution. The provider response and conversation history still retain the complete multi-call batch introduced in v7. Parallel runs calls from the same provider response concurrently up to MaxConcurrency, then returns their results in the original provider order. Use parallel execution only for independent, thread-safe handlers.

TimeoutSeconds controls provider requests and the surrounding round loop. Cancellation-aware tools receive the execution token. Calls waiting to start are skipped with cancellation results; started functions are awaited to keep call/result history consistent, including legacy functions that cannot stop cooperatively. Streaming uses one timeout for the complete round loop, including response headers and the SSE body. Policy expiry raises AIServiceException; cancelling the token passed to StreamAsync remains an OperationCanceledException with the caller token.

Async Tool Calling

A slow lookup can leave time for useful work that does not depend on its result. For example, the model can explain general packing advice while a weather tool runs, then incorporate the forecast once it arrives. Allow this overlap for the selected handler:

using System.Threading.Tasks;
using Mythosia.AI.Builders;
using Mythosia.AI.Extensions;
using Mythosia.AI.Models;

service.ChangeModel(AIModels.OpenAI.Gpt6Astra);
var weatherTool = FunctionBuilder.Create("get_demo_weather")
    .WithDescription("Returns a demo weather snapshot for Seoul")
    .WithAsync()
    .WithHandler(async _ =>
    {
        await Task.Delay(5000);
        return "Demo weather in Seoul: clear, 24 C";
    })
    .Build();
service.WithFunction(weatherTool);

var answer = await service.GetCompletionAsync(
    "Check the demo Seoul weather. While waiting, list three packing essentials.");

WithAsync() sets FunctionDefinition.AllowAsync = true; its default is false, and WithAsync(false) disables the option. Attribute-based registration also accepts [AiFunction("lookup", "Look up data", AllowAsync = true)]. This permission is independent of WithFunctionAsync and FunctionExecutionMode.Parallel: those control .NET handlers, while AllowAsync allows the model to continue before a result arrives. Enable it only when overlapping model work is appropriate for that function.

Mythosia enables the API option for GPT-6.1 Sol / GPT-6 Astra / Sol / Luna through Responses. Other models and APIs omit the option and execute the same handler with the existing wait-for-result behavior, without modifying AllowAsync. The provider must also mark the actual call as async (FunctionCall.IsAsync); permission alone does not guarantee async execution. See the official async tool calling guide.

FunctionExecutionMode still controls ordinary calls. Opted-in async jobs can overlap even in Sequential mode and share a separate pending-job limit set by MaxConcurrency.

Completion calls, existing input-taking streams, and StartRunAsync each manage their pending tool jobs within the originating execution and deliver outputs with their original call IDs. A successful completion or run Result waits for pending results to be processed. Cancellation-aware handlers receive the execution token; cleanup still waits for started handlers that ignore cancellation. Calls waiting to start receive cancellation results without executing. Exceptions are failed results, and cancellation is recorded with both IsError and IsCancelled. Disposing the existing service.StreamAsync iterator also cleans up execution. Ending a run.StreamAsync() reader only stops observation; use run.Cancel() or dispose the run to stop execution.

When async tools are used, GetCompletionAsync returns the intermediate independent text and the final text accumulated in order, after the request finishes. StreamAsync emits text as it arrives across those rounds.

Streaming starts handlers after complete function calls and a valid response boundary have been received, then continues the next model round while async jobs run. It does not dispatch handlers from incomplete call events. When the model returns no new calls while jobs remain pending, the library waits for results before resuming another round. Pending calls also prevent automatic context-overflow summarization retries, which could otherwise omit unfinished calls from history.

Function Calling with Streaming

// Stream with function calling support
await foreach (var content in service.StreamAsync(
    "What's the weather in Seoul and calculate 15% tip on $85",
    StreamOptions.WithFunctions))
{
    if (content.Type == StreamingContentType.FunctionCall && content.FunctionCall is { } call)
    {
        Console.WriteLine($"Calling function: {call.Name}");
    }
    else if (content.Type == StreamingContentType.FunctionResult && content.FunctionResult is { } result)
    {
        Console.WriteLine($"Function completed: {result.Call.Name}; error={result.IsError}");
    }
    else if (content.Type == StreamingContentType.Text)
    {
        Console.Write(content.Content);
    }
}

Tool tasks through the common run

// Collect the run result without observing events
await using var run = await service
    .CreateRequest("Find the weather in Seoul and explain what to wear today.")
    .WithMaxRounds(10)
    .StartRunAsync(
);
var answer = (await run.Result).Text;

// Observe a second task after the first task has finished
await using var streamedRun = await service
    .CreateRequest("Find the weather in Seoul and explain what to wear today.")
    .WithMaxRounds(10)
    .StartRunAsync();
await foreach (var content in streamedRun.StreamAsync())
{
    if (content.Type == StreamingContentType.FunctionCall && content.FunctionCall is { } call)
    {
        Console.WriteLine($"Calling: {call.Name}");
    }
    else if (content.Type == StreamingContentType.FunctionResult)
    {
        Console.WriteLine($"Tool result: {content.Content}");
    }
    else if (content.Type == StreamingContentType.Text)
    {
        Console.Write(content.Content);
    }
}

Normal completion and run execution already share the multi-round function loop. RunAgentAsync and RunAgentStreamAsync are obsolete compatibility helpers, retaining their legacy 10-round default and max-step exception behavior. WithMaxRounds(10) preserves the limit when migrating; new run failures use the common error contract.

Disabling Functions Temporarily

// Disable functions for a single request
var response = await service
    .WithoutFunctions()
    .GetCompletionAsync("Don't use any functions for this");

// Or use the async helper
var response = await service.AskWithoutFunctionsAsync(
    "Process this without calling functions"
);

Structured Output

Deserialize LLM responses directly into C# POCOs with automatic JSON recovery.

Basic Usage

// Define your POCO
public class WeatherResponse
{
    public string City { get; set; }
    public double Temperature { get; set; }
    public string Condition { get; set; }
}

// Get typed result — schema is auto-generated and sent to the LLM
var result = await service.GetCompletionAsync<WeatherResponse>(
    "What's the weather in Seoul?");
Console.WriteLine($"{result.City}: {result.Temperature}°C, {result.Condition}");

Auto-Recovery Retry

When the LLM returns invalid JSON, a correction prompt is automatically sent asking the model to fix its output. This is not a network retry — it's an output quality/format correction loop.

// Configure service-level retry count (default: 2)
service.StructuredOutputMaxRetries = 3;

// On final failure, StructuredOutputException is thrown with rich diagnostics:
// - FirstRawResponse, LastRawResponse
// - ParseError, AttemptCount, SchemaJson, TargetTypeName

The repair budget excludes the initial response. StructuredOutputMaxRetries and MaxRepairAttempts treat negative values as zero and accept up to int.MaxValue - 1; int.MaxValue raises ArgumentOutOfRangeException before provider work so the total attempt count cannot overflow. This applies to typed completion and BeginStream(...).As<T>().

Per-Call Structured Output Policy

Override retry behavior for a single request without changing service defaults:

// Custom policy — applies only to this call, then auto-cleared
var result = await service
    .WithStructuredOutputPolicy(new StructuredOutputPolicy { MaxRepairAttempts = 5 })
    .GetCompletionAsync<MyDto>(prompt);

// Preset: no retry (1 attempt only)
var result = await service
    .WithNoRetryStructuredOutput()
    .GetCompletionAsync<MyDto>(prompt);

// Preset: strict mode (up to 3 retries = 4 total attempts)
var result = await service
    .WithStrictStructuredOutput()
    .GetCompletionAsync<MyDto>(prompt);
Preset MaxRepairAttempts Description
Default null (service default) Uses StructuredOutputMaxRetries
NoRetry 0 Single attempt, no retry
Strict 3 Up to 3 correction retries

Streaming Structured Output

Stream text chunks in real-time to the UI while getting a final deserialized object with auto-repair:

var run = service.BeginStream(prompt)
    .WithStructuredOutput(new StructuredOutputPolicy { MaxRepairAttempts = 2 })
    .As<MyDto>();

// Optional: observe chunks in real-time
await foreach (var chunk in run.Stream(cancellationToken))
{
    Console.Write(chunk); // UI display
}

// Final deserialized result (waits for stream + parse/repair)
MyDto dto = await run.Result;
  • Result works without Stream() — just await run.Result internally consumes the stream and parses
  • Stream() is single-use — second call throws InvalidOperationException
  • Result waits for stream completion — even if awaited mid-stream, it won't resolve early
  • Repair retries are non-streaming — correction prompts use GetCompletionAsync() for efficiency

Collection Support (List<T>, T[])

Both GetCompletionAsync<T>() and streaming support collection types — no wrapper DTO needed:

// Non-streaming: get a list directly
var items = await service.GetCompletionAsync<List<ItemDto>>(
    "Extract all entities from this document...");

// Streaming: observe chunks + get list result
var run = service.BeginStream(prompt).As<List<ItemDto>>();
await foreach (var chunk in run.Stream()) Console.Write(chunk);
List<ItemDto> items = await run.Result;

List<T>, T[], IReadOnlyList<T> are all supported. JSON array schema is auto-generated from the element type.

Conversation Summary Policy

Automatically summarize old conversation messages when the conversation exceeds a configured threshold. The summary is stored and injected into the system message on subsequent stateful LLM requests. Stateless requests omit the stored summary without changing it.

Configuration

// Token-based: summarize when total tokens exceed 3000, keep recent ~1000 tokens
service.ConversationPolicy = SummaryConversationPolicy.ByToken(
    triggerTokens: 3000,
    keepRecentTokens: 1000
);

// Message-count-based: summarize when messages exceed 20, keep last 5
service.ConversationPolicy = SummaryConversationPolicy.ByMessage(
    triggerCount: 20,
    keepRecentCount: 5
);

// Combined (OR condition): triggers when either threshold is exceeded
service.ConversationPolicy = SummaryConversationPolicy.ByBoth(
    triggerTokens: 3000,
    triggerCount: 20
);

Usage

// Just use as normal — summarization happens automatically
service.ConversationPolicy = SummaryConversationPolicy.ByMessage(triggerCount: 20, keepRecentCount: 5);

var response = await service.GetCompletionAsync("Continue our conversation...");
// When message count exceeds 20, old messages are summarized automatically

Session Persistence

// Save summary for later
string saved = service.ConversationPolicy.CurrentSummary;

// Restore in a new session
policy.LoadSummary(saved);

Key Design Decisions

  • StatelessMode protection — Summary LLM calls use StatelessMode = true to prevent polluting the main conversation history
  • Explicit management — ConversationPolicy defaults to null; in v7 that means the full active conversation history is sent without a hidden message-count window
  • Provider-agnostic — Works with all providers (OpenAI, Claude, Gemini, Grok, DeepSeek, Perplexity)
  • Incremental summarization — When re-summarizing, existing summary is included as context for the new summary

Context-Overflow Recovery

(Since v6.8.0) When the server rejects a request for exceeding the model's context window, the conversation is compacted and the request is sent again — automatically. The limit belongs to the server, so being told "that did not fit" is the authoritative signal, more reliable than a client-side token estimate and always in step with the deployment's real limit.

// On by default (ContextRecoveryMaxRetries = 1). Nothing to configure — a request that
// overflows the window is compacted and retried once instead of failing outright.
service.ConversationPolicy = SummaryConversationPolicy.ByToken(triggerTokens: 3000);
var response = await service.GetCompletionAsync("…a very long conversation…");

// Opt out (pre-6.8.0 behavior — the rejection propagates unchanged):
service.ContextRecoveryMaxRetries = 0;
  • Server-driven — Detection reads the provider's actual 400/413 rejection (OpenAI, vLLM, Anthropic, Google), never a guessed token count. A rate limit or a server error is never mistaken for an overflow.
  • Costs nothing when it cannot help — If there is nothing left to compact, recovery gives up before issuing a summary call or deleting any message; it never spends a summary or destroys history to arrive at the same rejection.
  • Streaming recovers per round — An overflow mid-run recompacts and replays only the round that overflowed, keeping the tool results earlier rounds produced.
  • Diagnostics — When recovery cannot save the request, the thrown ContextLengthExceededException carries RecoverySkipReason (no-policy, nothing-to-cut, tool-side-effects, retries-exhausted, …) and the server-reported MaxContextTokens / RequestedTokens when available.

See the release notes for provider limits. DeepSeek uses the shared streaming recovery loop, but blocks automatic compaction when tools require preserved native reasoning history. Perplexity preserves native Agent API output for subsequent requests; see its execution and history limits.

Enhanced Streaming

Stream Options

// Text only - fastest, no overhead
await foreach (var chunk in service.StreamAsync("Hello", StreamOptions.TextOnlyOptions))
{
    Console.Write(chunk.Content);
}

// With metadata - includes model info, timestamps, etc.
await foreach (var content in service.StreamAsync("Hello", StreamOptions.FullOptions))
{
    if (content.Metadata != null)
    {
        Console.WriteLine($"Model: {content.Metadata["model"]}");
    }
    Console.Write(content.Content);
}

// Custom options
var options = new StreamOptions()
    .WithMetadata(true)
    .WithFunctionCalls(true)
    .AsTextOnly(false);

await foreach (var content in service.StreamAsync("Query", options))
{
    // Process based on content.Type
    switch (content.Type)
    {
        case StreamingContentType.Text:
            Console.Write(content.Content);
            break;
        case StreamingContentType.FunctionCall:
            Console.WriteLine($"Calling: {content.Metadata["function_name"]}");
            break;
        case StreamingContentType.Completion:
            Console.WriteLine($"Total length: {content.Metadata["total_length"]}");
            break;
    }
}

Streaming Diagnostics

When an SSE stream dies mid-flight against a self-hosted backend (vLLM, ollama, internal proxy), you usually need to know exactly where it died. Register diagnostic hooks once on the service — every subsequent StreamAsync call picks them up automatically. Same fluent builder pattern as WithRag.

using Mythosia.AI.Extensions;

service.WithStreamDiagnostics(d => d
    .OnRawLine(line => logger.LogDebug("SSE: {Line}", line))
    .OnComplete(diag => logger.LogInformation("Stream finished: {Diag}", diag)));

await foreach (var chunk in service.StreamAsync(message))
    Console.Write(chunk.Content);

Each On* method is independent — register only what you need:

// Raw line trace only
service.WithStreamDiagnostics(d => d.OnRawLine(line => logger.LogDebug("SSE: {Line}", line)));

// Clear all hooks
service.WithStreamDiagnostics(_ => { });

When SSE reading throws, the library wraps the exception in StreamReadException with a StreamDiagnostics snapshot taken at the moment of failure. This works regardless of whether WithStreamDiagnostics was registered:

try
{
    await foreach (var chunk in service.StreamAsync(message))
        Console.Write(chunk.Content);
}
catch (StreamReadException ex)
{
    logger.LogError(ex,
        "Stream died after {Lines} lines, {Chars} chars. Last raw line: {Line}",
        ex.Diagnostics.LinesRead,
        ex.Diagnostics.AccumulatedTextLength,
        ex.Diagnostics.LastRawLine);

    // ex.InnerException carries the original exception (IOException, etc.)
}

StreamDiagnostics exposes LinesRead, DataLinesProcessed, ParseFailures, AccumulatedTextLength, LastRawLine, and Elapsed. Hooks are propagated through CopyFrom, so cross-provider switches in a multi-provider chat UI keep the registered diagnostics without re-registration.

Available in Mythosia.AI v6.4.0+. Full guide: docs/streaming.md.

Token Usage

Streaming exposes token usage in two different places, with different meanings:

  • StreamingContentType.RoundUsage: usage for one LLM round only.
  • StreamingContentType.Completion: cumulative usage for the whole streaming run.

For a single LLM call, the final RoundUsage.Usage and Completion.Usage should describe the same one-round request. For an agent or function-calling run, each LLM round emits its own RoundUsage, while the final Completion.Usage remains the sum of all rounds.

This distinction is important for UI context meters. If you want to show "how many tokens the current conversation state used when it entered the latest LLM call", use the latest RoundUsage.Usage.InputTokens. If you want cost or diagnostics for the full agent run, use Completion.Usage.TotalTokens.

RoundUsage events also include:

  • RoundIndex: 1-based LLM round number.
  • IsFinalRound: true when this is the last LLM round in the stream.
await foreach (var content in service.StreamAsync(message, StreamOptions.FullOptions))
{
    if (content.Type == StreamingContentType.Text)
        Console.Write(content.Content);

    if (content.Type == StreamingContentType.RoundUsage && content.Usage != null)
    {
        Console.WriteLine($"Round: {content.RoundIndex}");
        Console.WriteLine($"Round total: {content.Usage.TotalTokens}");
        Console.WriteLine($"Final round: {content.IsFinalRound}");
    }

    if (content.Type == StreamingContentType.Completion && content.Usage != null)
    {
        Console.WriteLine($"Input tokens: {content.Usage.InputTokens}");
        Console.WriteLine($"Output tokens: {content.Usage.OutputTokens}");
        Console.WriteLine($"Cached tokens: {content.Usage.CachedInputTokens}");
        Console.WriteLine($"Reasoning tokens: {content.Usage.ReasoningTokens}");
        Console.WriteLine($"Cache hit ratio: {content.Usage.CacheHitRatio:P1}");
    }
}

Agent Token Meter Example

int? contextTokenMeter = null;
TokenUsage? cumulativeRunUsage = null;

await using var streamedRun = await service
    .CreateRequest("Find the weather in Seoul and answer briefly.")
    .WithMaxRounds(10)
    .StartRunAsync();
await foreach (var content in streamedRun.StreamAsync())
{
    if (content.Type == StreamingContentType.RoundUsage && content.Usage != null)
    {
        // Best value for a UI context/token meter.
        contextTokenMeter = content.Usage.InputTokens;

        Console.WriteLine(
            $"Round {content.RoundIndex}: input={content.Usage.InputTokens}, total={content.Usage.TotalTokens} tokens");

        if (content.IsFinalRound)
        {
            Console.WriteLine($"Final context meter value: {contextTokenMeter}");
        }

        continue;
    }

    if (content.Type == StreamingContentType.Completion)
    {
        // Cumulative usage across the whole agent run.
        cumulativeRunUsage = content.Usage;
        continue;
    }

    if (content.Type == StreamingContentType.Text)
        Console.Write(content.Content);
}

Token Usage Contract

  • RoundUsage.Usage is never an accumulated run total. It represents that one LLM round.
  • RoundUsage.Usage.TotalTokens preserves an explicit provider total, even when the input/output breakdown is incomplete. The adapter calculates InputTokens + OutputTokens only when the provider omits its total.
  • Completion.Usage keeps the existing cumulative meaning for the full stream or agent run.
  • In function-calling streams, non-final rounds have IsFinalRound = false; the last round has IsFinalRound = true.
  • Token usage collection does not depend on IncludeMetadata. Usage can still be emitted when metadata is disabled.
  • Providers may attach official usage to different stream chunks internally. Consumers should read the normalized RoundUsage and Completion events rather than provider-specific chunk metadata.
  • Gemini streams are drained after function calls so late usageMetadata chunks can still become RoundUsage.

The Token test category contains provider-level tests for this contract. If those tests pass for a provider/model, Mythosia.AI considers round-level usage and final cumulative usage supported for that provider/model. If a provider/model does not return official usage, these tests should fail or be treated as unsupported for token usage.

TokenUsage fields:

Field Description Providers
InputTokens Input/prompt tokens All
OutputTokens Output/completion tokens All
TotalTokens Total tokens used All
CachedInputTokens Tokens served from cache OpenAI, Claude, DeepSeek, Gemini
CacheCreationTokens Tokens written to cache OpenAI, Claude
ReasoningTokens Internal reasoning tokens OpenAI, Gemini

Computed properties: NonCachedInputTokens, CacheHitRatio, HasCacheActivity, VisibleOutputTokens.

Reasoning Streaming

GPT-6.1 Sol / GPT-6 Astra / Sol / Luna, supported GPT-5.1–5.6 models, Claude, Gemini, Grok, and DeepSeek Flash expose provider-returned reasoning through streaming events. Enable DeepSeek thinking explicitly, then observe with StreamOptions.WithReasoning(); observing reasoning does not turn it on.

await foreach (var content in service.StreamAsync(message, new StreamOptions().WithReasoning()))
{
    if (content.Type == StreamingContentType.Reasoning)
        Console.WriteLine($"[Thinking] {content.Content}");
    else if (content.Type == StreamingContentType.Text)
        Console.Write(content.Content);
}

Service Support

Service Function Calling Streaming Reasoning Notes
OpenAI GPT-6 Sol / Luna ✅ ✅ ✅ Responses API, opt-in async function calls, optional reasoning (None–Max, excluding Minimal), Standard/Pro, streaming steering, paid Fast
OpenAI GPT-6 Astra ✅ ✅ ✅ Responses API, opt-in async function calls, mandatory reasoning through Max, verbosity, summaries, optional Pro reasoning mode
OpenAI GPT-5.6 Sol / Terra / Luna ✅ ✅ ✅ Max effort, verbosity, summaries, optional Pro reasoning mode
OpenAI GPT-5.5 / 5.5 Pro ✅ ✅ ✅ Per-model reasoning enums + verbosity
OpenAI GPT-5.4 / 5.4 Mini / 5.4 Nano / 5.4 Pro ✅ ✅ ✅ Per-model reasoning enums + verbosity
OpenAI GPT-5.3 Codex ✅ ✅ ✅ Per-model reasoning enums + verbosity
OpenAI GPT-5.2 / 5.2 Pro ✅ ✅ ✅ Per-model reasoning enums + verbosity
OpenAI GPT-5.1 ✅ ✅ ✅ Reasoning + verbosity control
OpenAI GPT-4.1 / 4.1 Mini / GPT-4o / 4o Mini ✅ ✅ — Full function support
Claude Sonnet 5.5 ✅ ✅ ✅ Since v8.2.0; adaptive/high default, explicit between_tools, retained signed thinking and tool progress; forced tools unsupported
Claude Opus 5.5 ✅ ✅ ✅ Since v8.1.0; always-on adaptive thinking, medium/omitted default, explicit updates, preserved-thinking controls; forced tools unsupported
Claude Fable 5.1 ✅ ✅ ✅ Progress updates, per-message effort, turn instructions, binding diagnostics; forced tool choice unsupported
Claude Mythos 5.1 ✅ ✅ ✅ Invitation only; same 5.1 controls without Fable's prefix check; forced tool choice unsupported
Claude Fable 5 ✅ ✅ ✅ Adaptive thinking + tool use
Claude Mythos 5 ✅ ✅ ✅ Limited availability; always-on adaptive thinking + tool use
Claude Opus 5 / Sonnet 5 ✅ ✅ ✅ Adaptive thinking + signed tool continuation
Claude Opus 4.8 / 4.7 / 4.6 / 4.5 ✅ ✅ ✅ Extended thinking + tool use
Claude Sonnet 4.6 / 4.5 ✅ ✅ ✅ Extended thinking + tool use
Claude Haiku 4.5 ✅ ✅ ✅ Extended thinking + tool use
Gemini 3.8 Flash / 3.7 Flash / 3.6 Flash / 3.5 Flash / 3.5 Flash-Lite / current Gemini 3 previews ✅ ✅ ✅ ThinkingLevel + thought signatures
Gemini 2.5 Pro / Flash / Flash-Lite ✅ ✅ ✅ ThinkingBudget control
xAI Grok 4.7 / 4.6 / 4.5 / 4.3 / 4.20 / Build ✅ ✅ ✅ Model-specific GrokReasoning; common effort on 4.7 / 4.6; optional reasoning summaries
DeepSeek Flash (V4.1 Flash) ✅ ✅ ✅ User image input, Low/High/Max thinking, native reasoning history, local tools; Chat Completions rejects forced tool choice while thinking; opt-in Responses allows named functions
Perplexity ✅ ✅ Model-dependent Agent API, hosted tools, local functions, and citations

Complete Examples

Building a Weather Assistant

public class WeatherAssistant
{
    private readonly OpenAIService _service;
    private readonly HttpClient _httpClient;

    public WeatherAssistant(string apiKey)
    {
        _httpClient = new HttpClient();
        _service = new OpenAIService(apiKey, _httpClient)
            .WithSystemMessage("You are a helpful weather assistant.")
            .WithFunction(
                "get_weather",
                "Gets current weather for a city",
                ("city", "City name", true),
                GetWeatherData
            )
            .WithFunction(
                "get_forecast",
                "Gets weather forecast",
                ("city", "City name", true),
                ("days", "Number of days", false),
                GetForecast
            );
        
        // Configure function calling behavior
        _service.DefaultPolicy = new FunctionCallingPolicy
        {
            MaxRounds = 10,
            TimeoutSeconds = 30,
            EnableLogging = true
        };
    }

    private string GetWeatherData(string city)
    {
        // In real implementation, call weather API
        return $"{{\"city\":\"{city}\",\"temp\":22,\"condition\":\"sunny\"}}";
    }

    private string GetForecast(string city, int days = 3)
    {
        // In real implementation, call forecast API
        return $"{{\"city\":\"{city}\",\"forecast\":\"{days} days of sun\"}}";
    }

    public async Task<string> AskAsync(string question)
    {
        return await _service.GetCompletionAsync(question);
    }

    public async IAsyncEnumerable<string> StreamAsync(string question)
    {
        await foreach (var content in _service.StreamAsync(question))
        {
            if (content.Type == StreamingContentType.Text && content.Content != null)
            {
                yield return content.Content;
            }
        }
    }
}

// Usage
var assistant = new WeatherAssistant(apiKey);

// Functions are called automatically
var response = await assistant.AskAsync("What's the weather in Tokyo?");
// AI calls get_weather("Tokyo") and responds naturally

// Streaming also supports functions
await foreach (var chunk in assistant.StreamAsync(
    "Compare weather in Seoul and Tokyo for the next 5 days"))
{
    Console.Write(chunk);
}

Math Tutor with Step-by-Step Solutions

var mathTutor = new OpenAIService(apiKey, httpClient)
    .WithSystemMessage("You are a math tutor. Always explain your reasoning.")
    .WithFunction(
        "calculate",
        "Performs calculations",
        ("expression", "Math expression", true),
        (string expr) => {
            // Using a math expression evaluator
            var result = EvaluateExpression(expr);
            return $"Result: {result}";
        }
    )
    .WithFunction(
        "solve_equation",
        "Solves equations step by step",
        ("equation", "Equation to solve", true),
        (string equation) => {
            var steps = SolveWithSteps(equation);
            return JsonSerializer.Serialize(steps);
        }
    );

// The AI will use functions and explain the process
var response = await mathTutor.GetCompletionAsync(
    "Solve the equation 2x + 5 = 13 and verify the answer"
);
// Output includes step-by-step solution with verification

Best Practices

  1. Function Design: Keep functions focused and simple. Complex logic should be broken into multiple functions.

  2. Error Handling: Throw for execution failures so the executor records failed tool results. A deliberately returned string, even error text, remains successful tool content.

  3. Performance: Use appropriate policies for your use case (Fast for simple tasks, Complex for detailed analysis).

  4. Streaming: Use TextOnlyOptions for best performance when metadata isn't needed.

  5. Testing: Test function calling with various prompts to ensure robust behavior.

Troubleshooting

Q: Functions aren't being called when expected?

  • Ensure functions are registered with clear, descriptive names and descriptions
  • Check that EnableFunctions is true on the service
  • Verify the model supports function calling (see Service Support table above)

Q: Function calling is too slow?

  • Adjust the policy timeout: service.DefaultPolicy.TimeoutSeconds = 30
  • Use FunctionCallingPolicy.Fast for simple operations
  • Consider using streaming for better perceived performance

Q: How to debug function execution?

  • Enable logging: service.DefaultPolicy.EnableLogging = true
  • Check the console output for round-by-round execution details
  • Use StreamOptions.FullOptions to see function call metadata

Q: Can I use functions with streaming?

  • Yes! Functions work seamlessly with streaming
  • Use StreamOptions.WithFunctions to see function execution in real-time

Validate processing speed against real providers

In a source checkout, from the repository root, run:

./build/test-inference-speed-live.ps1

The paid suite uses the existing test Key Vault setup and synthetic prompts. It checks Anthropic Opus 5.5, OpenAI GPT-6 Astra, Gemini 3.8 Flash and Grok 4.6 across ProviderDefault/Standard/Fast and completion/Run paths: 24 cases. Account access errors, absent applied-mode reporting and server downgrades do not count as successful Fast validation; every case must pass without skips. Reports go to artifacts/test-results/inference-speed-live. Use -NoBuild only after building the current Release tests. This command documents how to run the suite, not a claim that the current account has passed it.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Mythosia.AI:

Package Downloads
Mythosia.AI.Providers.Alibaba

Call Qwen chat endpoints on DashScope, vLLM or Ollama while using the shared Mythosia.AI conversation and tool APIs. QwenService supports provider-specific thinking controls, custom deployment names, streaming usage, immutable request configuration, cancellable local tools, controllable runs and local model-capability inspection. Requires Mythosia.AI 8.2.0. What's New in v3.0.2: Completion uses the prepared request message snapshot for request context and history consistency. Public APIs, endpoint defaults and thinking controls remain unchanged. Native steering, hosted search and image generation are outside this adapter.

Mythosia.AI.Mcp

Connect application AI workflows to existing MCP tools without writing a separate function adapter for each server. Mythosia.AI.Mcp discovers tools through a built-in stdio or custom IMcpTransport connection and registers FunctionDefinitions on supporting providers. Includes cooperative cancellation, explicit tool failures and coordinated connection cleanup. This preview requires Mythosia.AI 8.1.0.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
8.2.0 132 10/3/2026
8.1.0 178 9/24/2026
8.0.0 152 9/13/2026
7.1.0 145 9/8/2026
7.0.0 210 8/8/2026
6.8.0 197 7/23/2026
6.7.0 172 7/13/2026
6.6.0 159 6/10/2026
6.5.0 240 5/30/2026
6.4.0 543 4/28/2026
6.4.0-preview1 203 4/25/2026
6.3.0 241 4/20/2026
6.2.0 194 4/16/2026
6.1.0 284 4/10/2026
6.0.0 235 4/3/2026
5.3.0 167 4/2/2026
5.2.0 187 3/29/2026
5.1.0 226 3/28/2026
5.0.1 206 3/24/2026
5.0.0 342 3/15/2026
Loading failed

v8.2.0 adds Claude Sonnet 5.5 across completion, streaming, structured output, images, tools, web search and Run. Untouched settings use adaptive/high thinking with readable thinking omitted. ClaudeThinkingMode and WithBetweenToolsThinking add explicit up-front thinking control; None and disabled legacy budgets use high-effort between_tools, which still permits tool progress. Between-tools accepts Auto/Low/Medium/High, omits display/budget/binding fields and rejects per-message effort changes and cache-preserving overrides. Signed empty thinking and progress metadata survive turns and tools; stored assistant edits fail locally, while earlier prefix changes follow provider binding policy; adaptive mode can request Error or DropBlock explicitly. Forced tools, assistant prefills, Minimal, Fast, native async tools and Run steering are unsupported for Sonnet 5.5. Existing model defaults remain. Claude reasoning and binding are resolved together per request. Stateless auxiliary profiles with DisableReasoning enabled and a non-default purpose omit inherited binding; Opus 5.5/Fable 5.1/Mythos 5.1 use Low without readable thinking, and Sonnet 5.5 uses high-effort between_tools. Parent settings/history and public APIs remain unchanged; explicitly stateful requests retain binding and normal validation. Completion, streaming, structured output and Run share one request preparation boundary: capture and install options, execute real virtual profile hooks once, then validate effective common and native settings before automatic summaries, input-history changes or transport startup. Application calls own logical requests even with reused profiles or messages. Framework virtual adapters and their first matching base entry preserve prepared settings when input is replaced; default callback streaming also continues the original request. Ordinary nested context/tool callback calls remain independent. Built-in input snapshots prevent later context and turn instructions from rewriting accepted history; custom content stays owner-managed and shared-conversation concurrency is not enabled. Perplexity validation and serialization share one effective plan, excluding suppressed parent reasoning and tools from auxiliary Sonar validation. Claude summary guards inspect retained canonical wire history, including native, typed, legacy and AdditionalMessages signed thinking. Default binding protects that prefix; explicit DropBlock permits compaction where supported, subject to other conversation constraints. Custom Run sessions retain captured options across initial progress events, and compatibility agent round limits remain local. Known summary cuts preserve surviving Claude occurrences; standalone prompt counting ignores unrelated history. The additive protected ResolveRequestMessage helper supports provider input ownership without changing existing execution signatures. Protected BeginIndependentRequestScope separates an unrelated helper called by an override at the same base entry before forwarding. Await or fully enumerate the helper inside the scope; disposal restores outer settings, features, context and delegation. It does not isolate history or permit concurrency; use a stateless helper profile. Each Run owns a copy of its effective settings after StartRunAsync returns, so caller profile restoration cannot alter the active Run; execution profile hooks still run once. Stateless auxiliary requests isolate parent history, summaries, observations, output schemas and user options without skipping native OpenAI, Perplexity or other provider validation. Stateful requests retain conversation and binding checks. Claude validates the complete effective thinking plan, including consumed manual budgets and undefined effort, before side effects; valid profile/common reasoning overrides retain precedence. Ordinary and preserved Claude history and token counting share one projection. Equivalent defined enum, integer, string and JSON FunctionSource values do not duplicate imported parallel batches or accepted tool calls; signatures, order and provider fields remain. Ordinary requests retain native historical calls/results after functions are disabled or removed. Parameterless token counting includes enabled definitions and tool_choice while retaining historical tool turns when tools are disabled. Standalone prompt counting and count-specific restrictions remain. v8.2.0 also adds GPT-6.1 Sol through existing completion, streaming, structured-output, image-input, tools and Run APIs. Supports Low/Medium/High/XHigh/Max reasoning with Medium default; None/Minimal are rejected. Includes native async tools in Standard/Pro, Standard-only steering, Standard single-agent cache-preserving reasoning updates, Pro and paid Fast processing. Pro Run and functions remain supported; local steering rejection does not abort the run. Run resolves responses against HttpClient.BaseAddress, maps HTTP(S) to WS(S), and rejects invalid bases without endpoint fallback; its dedicated socket and protected transport override remain. Cancelling steering while waiting to send preserves the Run; in-flight send cancellation or failure may abort it. Cancellation while awaiting acknowledgement does not retract sent input. Accepted steering retains its continuation-response position in history when output processing lags. Nonrecoverable Run transport failures cancel cooperative tools and preserve the original failure after cleanup; tools that ignore cancellation are still awaited. Fully received final responses survive normal WebSocket closure when a peer Close frame arrives during completion of their initial or tool-result send, without replay or suppression of caller cancellation or unconfirmed send failures. A received terminal API error retains its original reason across a later close while local tools run. Existing service defaults and GPT-6 Sol/Luna None behavior remain. Requires Mythosia.AI.Abstractions 4.2.0. Full notes: https://github.com/AJ-comp/Mythosia.AI/blob/main/src/core/Mythosia.AI/RELEASE_NOTES.md#v820 Forwarded profile changes replace prior profile flags against captured settings before automatic summary; callback streaming cancels and drains its producer, retains observer and cleanup failures, and uses captured recovery budgets; Claude protects Mythos bound thinking and retained override/tool dependencies, groups additional legacy batches once; stateless OpenAI helpers preserve parent history guards. Profile replacement preserves later tool/policy edits and explicit same-value setting assignments. Allowed Claude compaction retains persistent conversation instructions separately from summaries, in order and with system authority; turn-only scope and failure/cancellation rollback remain. Claude tool schemas use lowercase JSON Schema keywords and recursively map array items, preserving parameter names and default object keys while omitting unset optional fields. Public parameter APIs are unchanged. Anthropic streaming and Run interrupt stalled HTTP error-body reads on cancellation or request-policy timeout, allowing cleanup and later Run reuse. Caller cancellation and policy timeout retain their distinct exception behavior; public APIs are unchanged. Common streaming rounds classify identifiable HttpClient timeouts (TaskCanceledException with an inner TimeoutException) as AIServiceException with the original transport exception retained, so Run.Result faults with its timeout cause. This applies when neither caller cancellation nor the request policy timeout has fired; their behavior and other transport cancellation remain unchanged. Claude streaming and Run await asynchronous cleanup of acquired HTTP response bodies. Subsequent response/content disposal failures do not replace successful completion, the original read error or cancellation; disposal of the original response/content is still attempted. Known limitations in v8.2.0: Sonnet 5.5/Opus 5.5 pause_turn responses ending in a pending server_tool_use can be rejected as assistant prefill before continuation; completion, streaming and Run are affected, while completed-server-result pauses still continue. Custom buffering HttpContent can hold successful SSE body acquisition past caller cancellation or a request-policy timeout, retaining the active-Run guard. Ordinary SocketsHttpHandler streaming and the corrected HTTP error-body path passed local checks. Both cases remain unfixed. These latest lifecycle fixes were validated with deterministic tests and local HTTP connections, not live provider API calls. Details: https://github.com/AJ-comp/Mythosia.AI/blob/main/src/core/Mythosia.AI/RELEASE_NOTES.md#known-limitations