Guides SDK
C# / .NET
Utilisez la bibliothèque officielle OpenAI .NET avec api14 : configuration, chat, streaming, appel d'outils, sorties structurées, embeddings et erreurs.
Utilisez la bibliothèque officielle OpenAI .NET avec api14. Définissez OpenAIClientOptions.Endpoint sur api14 et passez un identifiant de modèle tel que openai/gpt-5-mini à la création d'un client.
Installation
dotnet add package OpenAI
export API14_API_KEY="sk-api14-..."
Configuration
using System.ClientModel;
using OpenAI;
using OpenAI.Chat;
var credential = new ApiKeyCredential(Environment.GetEnvironmentVariable("API14_API_KEY")!);
var options = new OpenAIClientOptions { Endpoint = new Uri("https://api14.com/api/v1") };
var client = new ChatClient(model: "openai/gpt-5-mini", credential: credential, options: options);
Chaque ChatClient est lié à un seul modèle ; créez-en un par identifiant de modèle utilisé.
Lisez la clé depuis l'environnement, les secrets utilisateur ou votre gestionnaire de secrets, jamais depuis un appsettings.json versionné dans le contrôle de source. Dans ASP.NET Core, enregistrez un client en singleton ; il est thread-safe.
Chat
ChatCompletion completion = await client.CompleteChatAsync("Explain vector databases in two sentences.");
Console.WriteLine(completion.Content[0].Text);
Avec un prompt système :
ChatCompletion completion = await client.CompleteChatAsync(
[
new SystemChatMessage("You are terse."),
new UserChatMessage("What is RAG?"),
]);
Streaming
var claude = new ChatClient("anthropic/claude-sonnet-4.5", credential, options);
await foreach (StreamingChatCompletionUpdate update in claude.CompleteChatStreamingAsync("Write a haiku about APIs."))
{
foreach (ChatMessageContentPart part in update.ContentUpdate)
Console.Write(part.Text);
}
Voir Streaming.
Appel d'outils
using System.Text.Json;
ChatTool getWeather = ChatTool.CreateFunctionTool(
functionName: "get_weather",
functionDescription: "Current weather for a city",
functionParameters: BinaryData.FromString("""
{ "type": "object", "properties": { "city": { "type": "string" } }, "required": ["city"] }
"""));
var toolOptions = new ChatCompletionOptions { Tools = { getWeather } };
List<ChatMessage> messages = [new UserChatMessage("What's the weather in Istanbul?")];
ChatCompletion completion = await client.CompleteChatAsync(messages, toolOptions);
while (completion.FinishReason == ChatFinishReason.ToolCalls)
{
messages.Add(new AssistantChatMessage(completion));
foreach (ChatToolCall call in completion.ToolCalls)
{
using JsonDocument args = JsonDocument.Parse(call.FunctionArguments);
string city = args.RootElement.GetProperty("city").GetString()!;
messages.Add(new ToolChatMessage(call.Id, GetWeather(city))); // votre fonction
}
completion = await client.CompleteChatAsync(messages, toolOptions);
}
Console.WriteLine(completion.Content[0].Text);
Voir Appel d'outils.
Sortie structurée
var jsonOptions = new ChatCompletionOptions
{
ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat(
jsonSchemaFormatName: "person",
jsonSchema: BinaryData.FromString("""
{
"type": "object",
"properties": { "name": { "type": "string" }, "born": { "type": "integer" } },
"required": ["name", "born"],
"additionalProperties": false
}
"""),
jsonSchemaIsStrict: true),
};
ChatCompletion result = await client.CompleteChatAsync(
[new UserChatMessage("Extract: Ada Lovelace, born 1815.")], jsonOptions);
using JsonDocument person = JsonDocument.Parse(result.Content[0].Text);
Console.WriteLine(person.RootElement.GetProperty("born").GetInt32());
Voir Sorties structurées.
Image en entrée
var vision = new ChatClient("google/gemini-2.5-flash", credential, options);
ChatCompletion described = await vision.CompleteChatAsync(
[
new UserChatMessage(
ChatMessageContentPart.CreateTextPart("What is in this picture?"),
ChatMessageContentPart.CreateImagePart(new Uri("https://example.com/photo.jpg"))),
]);
Pour un fichier local, utilisez ChatMessageContentPart.CreateImagePart(BinaryData.FromBytes(File.ReadAllBytes("photo.png")), "image/png"). Voir Vision et fichiers.
Embeddings
using OpenAI.Embeddings;
var embeddings = new EmbeddingClient("openai/text-embedding-3-small", credential, options);
OpenAIEmbedding embedding = await embeddings.GenerateEmbeddingAsync("The quick brown fox");
Console.WriteLine(embedding.ToFloats().Length);
Erreurs
Les appels en échec lèvent une ClientResultException ; la réponse brute contient le statut et les en-têtes :
try
{
await client.CompleteChatAsync("Hi");
}
catch (ClientResultException ex) when (ex.Status == 402)
{
Console.Error.WriteLine("Balance too low, add funds (insufficient_balance).");
}
catch (ClientResultException ex) when (ex.Status == 429)
{
string? retryAfter = null;
ex.GetRawResponse()?.Headers.TryGetValue("Retry-After", out retryAfter);
Console.Error.WriteLine($"Rate limited, retry after {retryAfter ?? "60"}s.");
}
Voir Erreurs.
Délais d'attente et nouvelles tentatives
La bibliothèque réessaie automatiquement les erreurs 429 et 5xx avec un backoff exponentiel. Ajustez le nombre de tentatives et le délai réseau sur les options que vous passez à chaque client :
using System.ClientModel.Primitives;
var options = new OpenAIClientOptions
{
Endpoint = new Uri("https://api14.com/api/v1"),
NetworkTimeout = TimeSpan.FromMinutes(2),
RetryPolicy = new ClientRetryPolicy(maxRetries: 4),
};
Passez un CancellationToken à tout appel ...Async pour borner la durée totale, nouvelles tentatives comprises. Réessayer n'aide pas face à daily_limit_reached ou insufficient_balance. Voir Limites de débit.
Toutes les capacités
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api14.com/api/v1",
apiKey: process.env.API14_API_KEY,
});
const result = await client.chat.completions.create({
"model": "openai/gpt-5-mini",
"messages": [
{
"role": "user",
"content": "Explain vector databases in two sentences."
}
]
});
console.log(result.choices[0].message.content);