Guias de SDK
Rust
Use o crate async-openai com a api14: configure a base da API com OpenAIConfig, faça chat, streaming, tool calling, saída estruturada e trate erros em Rust.
Use o async-openai com a api14. Configure a base da API com OpenAIConfig::with_api_base e passe ids de modelo como openai/gpt-5-mini.
Instalação
cargo add async-openai futures serde_json
cargo add tokio --features full
export API14_API_KEY="sk-api14-..."
Configuração
use async_openai::{config::OpenAIConfig, Client};
let config = OpenAIConfig::new()
.with_api_base("https://api14.com/api/v1")
.with_api_key(std::env::var("API14_API_KEY")?);
let client = Client::with_config(config);
Leia a chave do ambiente, como acima, em vez de deixá-la fixa no código. Para alternar entre staging e produção, leia a URL base da mesma forma, por exemplo std::env::var("API14_BASE_URL").unwrap_or("https://api14.com/api/v1".into()).
Chat
use async_openai::{
config::OpenAIConfig,
types::{ChatCompletionRequestUserMessageArgs, CreateChatCompletionRequestArgs},
Client,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = OpenAIConfig::new()
.with_api_base("https://api14.com/api/v1")
.with_api_key(std::env::var("API14_API_KEY")?);
let client = Client::with_config(config);
let request = CreateChatCompletionRequestArgs::default()
.model("openai/gpt-5-mini")
.messages([ChatCompletionRequestUserMessageArgs::default()
.content("Explain vector databases in two sentences.")
.build()?
.into()])
.build()?;
let response = client.chat().create(request).await?;
if let Some(content) = &response.choices[0].message.content {
println!("{content}");
}
Ok(())
}
Streaming
use futures::StreamExt;
let request = CreateChatCompletionRequestArgs::default()
.model("anthropic/claude-sonnet-4.5")
.messages([ChatCompletionRequestUserMessageArgs::default()
.content("Write a haiku about APIs.")
.build()?
.into()])
.build()?;
let mut stream = client.chat().create_stream(request).await?;
while let Some(result) = stream.next().await {
match result {
Ok(chunk) => {
for choice in chunk.choices {
if let Some(text) = choice.delta.content {
print!("{text}");
}
}
}
Err(err) => eprintln!("stream error: {err}"),
}
}
Veja Streaming.
Tool calling
use async_openai::types::{
ChatCompletionRequestAssistantMessageArgs, ChatCompletionRequestMessage,
ChatCompletionRequestToolMessageArgs, ChatCompletionToolArgs, ChatCompletionToolType,
FunctionObjectArgs,
};
use serde_json::{json, Value};
let tools = vec![ChatCompletionToolArgs::default()
.r#type(ChatCompletionToolType::Function)
.function(FunctionObjectArgs::default()
.name("get_weather")
.description("Current weather for a city")
.parameters(json!({
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}))
.build()?)
.build()?];
let mut messages: Vec<ChatCompletionRequestMessage> = vec![ChatCompletionRequestUserMessageArgs::default()
.content("What's the weather in Istanbul?")
.build()?
.into()];
loop {
let request = CreateChatCompletionRequestArgs::default()
.model("openai/gpt-5-mini")
.messages(messages.clone())
.tools(tools.clone())
.build()?;
let message = client.chat().create(request).await?.choices.remove(0).message;
let Some(calls) = message.tool_calls else {
println!("{}", message.content.unwrap_or_default());
break;
};
messages.push(ChatCompletionRequestAssistantMessageArgs::default()
.tool_calls(calls.clone())
.build()?
.into());
for call in calls {
let args: Value = serde_json::from_str(&call.function.arguments)?;
let result = get_weather(args["city"].as_str().unwrap_or_default()); // sua função
messages.push(ChatCompletionRequestToolMessageArgs::default()
.tool_call_id(call.id)
.content(result)
.build()?
.into());
}
}
Veja Tool calling.
Saída estruturada
use async_openai::types::{ResponseFormat, ResponseFormatJsonSchema};
let request = CreateChatCompletionRequestArgs::default()
.model("openai/gpt-5-mini")
.messages([ChatCompletionRequestUserMessageArgs::default()
.content("Extract: Ada Lovelace, born 1815.")
.build()?
.into()])
.response_format(ResponseFormat::JsonSchema {
json_schema: ResponseFormatJsonSchema {
name: "person".into(),
description: None,
strict: Some(true),
schema: Some(json!({
"type": "object",
"properties": { "name": { "type": "string" }, "born": { "type": "integer" } },
"required": ["name", "born"],
"additionalProperties": false
})),
},
})
.build()?;
let response = client.chat().create(request).await?;
let person: Value = serde_json::from_str(response.choices[0].message.content.as_deref().unwrap_or("{}"))?;
Veja Saídas estruturadas.
Entrada de imagem
use async_openai::types::{
ChatCompletionRequestMessageContentPartImageArgs, ChatCompletionRequestMessageContentPartTextArgs,
ImageUrlArgs,
};
let message = ChatCompletionRequestUserMessageArgs::default()
.content(vec![
ChatCompletionRequestMessageContentPartTextArgs::default()
.text("What is in this picture?")
.build()?
.into(),
ChatCompletionRequestMessageContentPartImageArgs::default()
.image_url(ImageUrlArgs::default().url("https://example.com/photo.jpg").build()?)
.build()?
.into(),
])
.build()?;
Envie com google/gemini-2.5-flash ou outro modelo com entrada image. Veja Visão e arquivos.
Embeddings
use async_openai::types::CreateEmbeddingRequestArgs;
let request = CreateEmbeddingRequestArgs::default()
.model("openai/text-embedding-3-small")
.input(["The quick brown fox", "Lorem ipsum"])
.build()?;
let response = client.embeddings().create(request).await?;
println!("{}", response.data[0].embedding.len());
Endpoints para os quais o crate não tem tipo (jobs de vídeo, rerank) podem ser chamados com reqwest; veja os exemplos HTTP no bloco de código abaixo.
Erros
Os erros do gateway são interpretados como OpenAIError::ApiError; trate pelo code legível por máquina:
use async_openai::error::OpenAIError;
match client.chat().create(request).await {
Ok(response) => println!("{:?}", response.choices[0].message.content),
Err(OpenAIError::ApiError(err)) => match err.code.as_deref() {
Some("insufficient_balance") => eprintln!("Balance too low, add funds"),
Some("rate_limited") | Some("daily_limit_reached") => eprintln!("Slow down: {}", err.message),
_ => eprintln!("API error: {}", err.message),
},
Err(other) => eprintln!("{other}"),
}
Veja Erros.
Timeouts e novas tentativas
O async-openai já faz backoff e repete respostas 429 rate_limited. O backoff padrão continua tentando por até 15 minutos, o que é longo demais para a maioria dos apps e inútil para daily_limit_reached. Limite-o e dê um timeout ao cliente HTTP (cargo add backoff reqwest, nas versões que o async-openai usa):
use std::time::Duration;
let http = reqwest::Client::builder()
.timeout(Duration::from_secs(120))
.build()?;
let client = Client::with_config(config)
.with_http_client(http)
.with_backoff(backoff::ExponentialBackoffBuilder::new()
.with_max_elapsed_time(Some(Duration::from_secs(90)))
.build());
Veja Limites de taxa.
Todos os recursos
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);