Universal.MoonshotAI.Client
1.0.0
dotnet add package Universal.MoonshotAI.Client --version 1.0.0
NuGet\Install-Package Universal.MoonshotAI.Client -Version 1.0.0
<PackageReference Include="Universal.MoonshotAI.Client" Version="1.0.0" />
<PackageVersion Include="Universal.MoonshotAI.Client" Version="1.0.0" />
<PackageReference Include="Universal.MoonshotAI.Client" />
paket add Universal.MoonshotAI.Client --version 1.0.0
#r "nuget: Universal.MoonshotAI.Client, 1.0.0"
#:package Universal.MoonshotAI.Client@1.0.0
#addin nuget:?package=Universal.MoonshotAI.Client&version=1.0.0
#tool nuget:?package=Universal.MoonshotAI.Client&version=1.0.0
Universal.MoonshotAI.Client
A simple .NET client library for Moonshot AI's Chat Completions API (Kimi models), supporting streaming, tools, and structured outputs.
Quick Start
using Universal.MoonshotAI.Client;
using Universal.MoonshotAI.Client.V1.Chat;
using var client = new MoonshotAIClient("your-api-key");
var response = await client.V1.Chat.CreateCompletionAsync(new CreateCompletionRequest
{
Model = Models.KimiK3,
Messages = [
new SystemMessage("You are a helpful assistant."),
new UserMessage("Hello, how are you?")
]
});
Console.WriteLine(response.Choices[0].Message.Content);
Calling api.moonshot.cn (mainland China) instead of the default api.moonshot.ai:
using var client = new MoonshotAIClient("https", "api.moonshot.cn", "your-api-key");
Streaming Responses
await foreach (var chunk in client.V1.Chat.CreateCompletionStreamingAsync(new CreateCompletionRequest
{
Model = Models.KimiK3,
Messages = [new UserMessage("Write a short story.")],
Stream = true
}))
{
if (chunk.Choices?[0]?.Delta?.Content != null)
{
Console.Write(chunk.Choices[0].Delta.Content);
}
}
Function Calling & Tools
var response = await client.V1.Chat.CreateCompletionAsync(new CreateCompletionRequest
{
Model = Models.KimiK3,
Messages = [new UserMessage("What's the weather like in San Francisco?")],
Tools = [
new Tool
{
Type = ToolTypes.Function,
Function = new Function
{
Name = "get_weather",
Description = "Get current weather for a location",
Parameters = new JsonSchema
{
Type = "object",
Properties = new Dictionary<string, JsonSchema>
{
["location"] = new JsonSchema { Type = "string", Description = "The city and state" }
},
Required = new[] { "location" }
}
}
}
]
});
if (response.Choices[0].Message is AssistantMessage assistantMsg && assistantMsg.ToolCalls?.Length > 0)
{
var toolCall = assistantMsg.ToolCalls[0];
var weatherData = GetWeather(toolCall.Function.Arguments); // Your implementation
var followUp = await client.V1.Chat.CreateCompletionAsync(new CreateCompletionRequest
{
Model = Models.KimiK3,
Messages = [
new UserMessage("What's the weather like in San Francisco?"),
assistantMsg,
new ToolMessage(weatherData, toolCall.Id)
]
});
}
Prefix Continuation (Partial Mode)
Moonshot-specific: append an assistant message with Partial = true to have the model continue from that content
rather than starting a fresh turn (e.g. to force a response to keep going after being cut off, or to seed the start
of a code block):
var response = await client.V1.Chat.CreateCompletionAsync(new CreateCompletionRequest
{
Model = Models.KimiK3,
Messages = [
new UserMessage("Write a Python function that reverses a string."),
new AssistantMessage("```python\n") { Partial = true }
]
});
When continuing a reasoning model's response this way (kimi-k3, or kimi-k2.6/kimi-k2.5 with thinking enabled),
echo the prior ReasoningContent back on the partial message too, and set MaxCompletionTokens generously —
truncating mid-thought can make the model discard its progress and restart from scratch instead of continuing:
var first = await client.V1.Chat.CreateCompletionAsync(new CreateCompletionRequest
{
Model = Models.KimiK3,
Messages = [new UserMessage("Write a Python function that reverses a string.")],
MaxCompletionTokens = 200
});
var assistantMsg = (AssistantMessage)first.Choices[0].Message;
var continued = await client.V1.Chat.CreateCompletionAsync(new CreateCompletionRequest
{
Model = Models.KimiK3,
Messages = [
new UserMessage("Write a Python function that reverses a string."),
new AssistantMessage(assistantMsg.Content)
{
Partial = true,
ReasoningContent = assistantMsg.ReasoningContent
}
],
MaxCompletionTokens = 2000
});
Extended Thinking
kimi-k2.6 and kimi-k2.5 support toggling extended thinking via Thinking; kimi-k3 uses ReasoningEffort
instead; kimi-k2.7-code always reasons and exposes no control for it. When enabled, the chain-of-thought is
returned on AssistantMessage.ReasoningContent (and streamed incrementally via CompletionChunkDelta.ReasoningContent) — separate from the final answer in Content:
var response = await client.V1.Chat.CreateCompletionAsync(new CreateCompletionRequest
{
Model = Models.KimiK26,
Messages = [new UserMessage("Prove that sqrt(2) is irrational.")],
Thinking = new ThinkingConfiguration { Type = ThinkingTypes.Enabled }
});
var assistantMsg = (AssistantMessage)response.Choices[0].Message;
Console.WriteLine(assistantMsg.ReasoningContent); // the chain of thought
Console.WriteLine(assistantMsg.Content); // the final answer
Web Search (Built-in Tool)
Moonshot-specific: declare Tool.WebSearch and, when the model calls it, echo the arguments straight back — content
and function name — in a ToolMessage rather than executing a search yourself; Moonshot runs the search
server-side and continues from there. Only meaningfully supported on kimi-k3 and kimi-k2.6; each search is billed
separately from token usage and can add substantially to context length:
var response = await client.V1.Chat.CreateCompletionAsync(new CreateCompletionRequest
{
Model = Models.KimiK3,
Messages = [new UserMessage("What's the latest news about Moonshot AI?")],
Tools = [Tool.WebSearch]
});
if (response.Choices[0].Message is AssistantMessage assistantMsg && assistantMsg.ToolCalls?.Length > 0)
{
var toolCall = assistantMsg.ToolCalls[0]; // toolCall.Function.Name == BuiltinFunctionNames.WebSearch
var followUp = await client.V1.Chat.CreateCompletionAsync(new CreateCompletionRequest
{
Model = Models.KimiK3,
Messages = [
new UserMessage("What's the latest news about Moonshot AI?"),
assistantMsg,
// name is required here — omitting it fails with an opaque "tokenization failed" error
new ToolMessage(toolCall.Function.Arguments, toolCall.Id, toolCall.Function.Name)
],
Tools = [Tool.WebSearch]
});
}
Caveat: as of 2026-07-29, this follow-up turn reliably 400s with
"tokenization failed"against the live API. This was chased down to the byte: a rawcurlrequest built to match Moonshot's own cookbook example field-for-field — bypassing this library entirely — reproduces the identical error, and the test account's balance/tier (enterprise-tier-3, active, funded) rules out a billing or account-gating explanation. This is an upstream issue in the$web_searchfeature itself, not in this library or your request. The shape implemented here is correct and should work once Moonshot's side is fixed.Universal.MoonshotAI.Client.Testsreflects this by marking its live web-search test inconclusive (not failing) on this specific error.
Context Caching
Automatic — there is no cache ID or TTL to manage. Any request whose prompt exceeds 256 tokens is eligible to have
its stable leading prefix (shared system instructions, documents, tool definitions) matched against a prior
request's; keep that prefix byte-for-byte identical and put large fixed context first in Messages to maximize
hit rate. Check Usage.CachedTokens on the response to confirm a hit:
var response = await client.V1.Chat.CreateCompletionAsync(request);
Console.WriteLine($"{response.Usage.CachedTokens} of {response.Usage.PromptTokens} prompt tokens were cached");
Structured JSON Output
The model only ever produces a JSON object — describe the desired shape (field names, an example) in the prompt
itself, since ResponseFormat alone doesn't communicate a schema to the model. Set MaxCompletionTokens
generously; a truncated response (Choice.FinishReason == "length") is invalid JSON.
var response = await client.V1.Chat.CreateCompletionAsync(new CreateCompletionRequest
{
Model = Models.KimiK3,
Messages = [new UserMessage("Analyze this text for sentiment. Respond as {\"sentiment\": string, \"confidence\": number}.")],
ResponseFormat = ResponseFormat.JsonObject
});
Error Handling
The library throws HttpException for API errors:
try
{
var response = await client.V1.Chat.CreateCompletionAsync(request);
}
catch (HttpException ex)
{
Console.WriteLine($"API Error: {ex.StatusCode} - {ex.Message}");
}
Disposal
The client implements IDisposable:
using var client = new MoonshotAIClient("your-api-key");
// Client will be automatically disposed
Advanced Usage
Raw Streaming Response
using var httpResponse = await client.V1.Chat.CreateCompletionStreamingRawAsync(request);
// Forward the raw HTTP response stream
Specific Clients
using var chatClient = new ChatClient(apiKey);
Known Gaps
This library only covers what Moonshot AI's Chat Completions API documents today — there is no Moonshot equivalent
of OpenAI's Embeddings, Images, Responses, or Realtime APIs, and Moonshot's own Files API (used for uploading
documents rather than for context caching, which is automatic — see above) isn't covered either, so all of those
are intentionally absent rather than stubbed out. Vision input (*-vision-preview models) is reachable by passing
a raw content-block array/object as Message.Content, since this library doesn't yet model typed content blocks
(image_url, etc.) the way Universal.OpenAI.Client does.
A handful of request-shape rules are documented by Moonshot but enforced only server-side, not by this library
(matching how the rest of this client defers to the API's own validation): Temperature must match the active
Thinking mode on kimi-k2.6/kimi-k2.5, Number > 1 conflicts with Temperature near 0, and ToolChoiceOptions.Required
isn't accepted by kimi-k2.7-code/kimi-k2.6. See the XML doc comments on the relevant properties for specifics —
passing an unsupported combination surfaces as an HttpException from the API, same as any other invalid request.
| Product | Versions 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 | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.Bcl.AsyncInterfaces (>= 10.0.10)
- Newtonsoft.Json (>= 13.0.4)
- Universal.Common.Json (>= 1.9.0)
- Universal.Common.Net.Http (>= 5.1.1)
- Universal.Common.Serialization (>= 2.4.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Universal.MoonshotAI.Client:
| Package | Downloads |
|---|---|
|
Universal.Operative.Sdk.Discrete.MoonshotAI
Moonshot AI (Kimi) extensions for the Universal.Operative.Sdk. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 149 | 7/29/2026 |
Initial release.