쿠키를 사용합니다. 쿠키와 브라우저 저장소는 로그인 상태를 유지하고 환경설정을 기억합니다. 동의하시면 사이트 이용 방식도 측정합니다. 언제든지 할 수 있습니다. 자세한 내용은 쿠키 정책을 참고하세요.

C# / .NET

SDK 가이드

C# / .NET

C#과 .NET에서 공식 OpenAI 패키지의 OpenAIClientOptions.Endpoint를 api14로 바꿔 쓰는 방법입니다. 채팅, 스트리밍, 도구 호출, 구조화된 출력, 임베딩, 재시도 코드를 담았습니다.

api14에서 공식 OpenAI .NET 라이브러리를 사용하세요. OpenAIClientOptions.Endpoint를 api14로 설정하고, 클라이언트를 만들 때 openai/gpt-5-mini 같은 모델 ID를 전달합니다.

설치

dotnet add package OpenAI
export API14_API_KEY="sk-api14-..."

설정

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);

각 ChatClient는 하나의 모델에 묶이므로, 사용하는 모델 ID마다 하나씩 만드세요.

키는 환경 변수, 사용자 비밀(user secrets) 또는 비밀 저장소에서 읽고, 소스 관리에 커밋되는 appsettings.json에는 넣지 마세요. ASP.NET Core에서는 클라이언트 하나를 싱글톤으로 등록하세요. 스레드 안전합니다.

채팅

ChatCompletion completion = await client.CompleteChatAsync("Explain vector databases in two sentences.");
Console.WriteLine(completion.Content[0].Text);

시스템 프롬프트를 함께 쓰는 경우:

ChatCompletion completion = await client.CompleteChatAsync(
[
    new SystemChatMessage("You are terse."),
    new UserChatMessage("What is RAG?"),
]);

스트리밍

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);
}

스트리밍을 참고하세요.

도구 호출

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))); // 직접 작성한 함수
    }
    completion = await client.CompleteChatAsync(messages, toolOptions);
}
Console.WriteLine(completion.Content[0].Text);

도구 호출을 참고하세요.

구조화된 출력

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());

구조화된 출력을 참고하세요.

이미지 입력

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"))),
]);

로컬 파일은 ChatMessageContentPart.CreateImagePart(BinaryData.FromBytes(File.ReadAllBytes("photo.png")), "image/png")를 사용하세요. 비전 및 파일을 참고하세요.

임베딩

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);

오류

실패한 호출은 ClientResultException을 던지며, 원시 응답에 상태와 헤더가 들어 있습니다.

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.");
}

오류를 참고하세요.

타임아웃과 재시도

라이브러리는 429와 5xx를 지수 백오프로 자동 재시도합니다. 모든 클라이언트에 전달하는 옵션에서 재시도 횟수와 네트워크 타임아웃을 조정하세요.

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),
};

모든 ...Async 호출에 CancellationToken을 전달하면 재시도를 포함한 전체 시간을 제한할 수 있습니다. daily_limit_reached나 insufficient_balance는 재시도해도 소용없습니다. 속도 제한을 참고하세요.

모든 기능

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);