Quick Start¶
This guide assumes you have installed liter-llm and set your API key via environment variable.
Basic Chat Completion¶
Send a message and get a response:
import asyncio
import os
from liter_llm import LlmClient
async def main() -> None:
client = LlmClient(api_key=os.environ["OPENAI_API_KEY"])
response = await client.chat(
model="openai/gpt-4o",
messages=[{"role": "user", "content": "Hello!"}],
)
print(response.choices[0].message.content)
asyncio.run(main())
use liter_llm::{
ChatCompletionRequest, ClientConfigBuilder, DefaultClient, LlmClient,
Message, UserContent, UserMessage,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ClientConfigBuilder::new(std::env::var("OPENAI_API_KEY")?)
.build();
let client = DefaultClient::new(config, Some("openai/gpt-4o"))?;
let request = ChatCompletionRequest {
model: "openai/gpt-4o".into(),
messages: vec![Message::User(UserMessage {
content: UserContent::Text("Hello!".into()),
name: None,
})],
..Default::default()
};
let response = client.chat(request).await?;
if let Some(choice) = response.choices.first() {
println!("{}", choice.message.content.as_deref().unwrap_or(""));
}
Ok(())
}
package main
import (
"context"
"fmt"
"os"
llm "github.com/kreuzberg-dev/liter-llm/packages/go"
)
func main() {
client := llm.NewClient(llm.WithAPIKey(os.Getenv("OPENAI_API_KEY")))
resp, err := client.Chat(context.Background(), &llm.ChatCompletionRequest{
Model: "openai/gpt-4o",
Messages: []llm.Message{
llm.NewTextMessage(llm.RoleUser, "Hello!"),
},
})
if err != nil {
panic(err)
}
if len(resp.Choices) > 0 && resp.Choices[0].Message.Content != nil {
fmt.Println(*resp.Choices[0].Message.Content)
}
}
import dev.kreuzberg.literllm.LlmClient;
import dev.kreuzberg.literllm.Types.*;
import java.util.List;
public class Main {
public static void main(String[] args) throws Exception {
try (var client = LlmClient.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.build()) {
var response = client.chat(new ChatCompletionRequest(
"openai/gpt-4o",
List.of(new UserMessage("Hello!"))
));
System.out.println(response.choices().getFirst().message().content());
}
}
}
# frozen_string_literal: true
require "liter_llm"
require "json"
client = LiterLlm::LlmClient.new(ENV.fetch("OPENAI_API_KEY"), {})
response = JSON.parse(client.chat(JSON.generate(
model: "openai/gpt-4o",
messages: [{ role: "user", content: "Hello!" }]
)))
puts response.dig("choices", 0, "message", "content")
<?php
declare(strict_types=1);
use LiterLlm\LlmClient;
$client = new LlmClient(apiKey: getenv('OPENAI_API_KEY') ?: '');
$response = json_decode($client->chat(json_encode([
'model' => 'openai/gpt-4o',
'messages' => [
['role' => 'user', 'content' => 'Hello!'],
],
])), true);
echo $response['choices'][0]['message']['content'] . PHP_EOL;
using LiterLlm;
await using var client = new LlmClient(
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")!);
var response = await client.ChatAsync(new ChatCompletionRequest(
Model: "openai/gpt-4o",
Messages: [new UserMessage("Hello!")]
));
Console.WriteLine(response.Choices[0].Message.Content);
Streaming Responses¶
Stream tokens as they arrive instead of waiting for the full response:
import asyncio
import os
from liter_llm import LlmClient
async def main() -> None:
client = LlmClient(api_key=os.environ["OPENAI_API_KEY"])
async for chunk in await client.chat_stream(
model="openai/gpt-4o",
messages=[{"role": "user", "content": "Tell me a story"}],
):
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
print()
asyncio.run(main())
import { LlmClient } from "@kreuzberg/liter-llm";
const client = new LlmClient({ apiKey: process.env.OPENAI_API_KEY! });
const chunks = await client.chatStream({
model: "openai/gpt-4o",
messages: [{ role: "user", content: "Tell me a story" }],
});
for (const chunk of chunks) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
console.log();
use futures::StreamExt;
use liter_llm::{
ChatCompletionRequest, ClientConfigBuilder, DefaultClient, LlmClient,
Message, UserContent, UserMessage,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ClientConfigBuilder::new(std::env::var("OPENAI_API_KEY")?)
.build();
let client = DefaultClient::new(config, Some("openai/gpt-4o"))?;
let request = ChatCompletionRequest {
model: "openai/gpt-4o".into(),
messages: vec![Message::User(UserMessage {
content: UserContent::Text("Tell me a story".into()),
name: None,
})],
..Default::default()
};
let mut stream = client.chat_stream(request).await?;
while let Some(chunk) = stream.next().await {
let chunk = chunk?;
if let Some(choice) = chunk.choices.first() {
if let Some(content) = &choice.delta.content {
print!("{content}");
}
}
}
println!();
Ok(())
}
package main
import (
"context"
"fmt"
"os"
llm "github.com/kreuzberg-dev/liter-llm/packages/go"
)
func main() {
client := llm.NewClient(llm.WithAPIKey(os.Getenv("OPENAI_API_KEY")))
err := client.ChatStream(
context.Background(),
&llm.ChatCompletionRequest{
Model: "openai/gpt-4o",
Messages: []llm.Message{
llm.NewTextMessage(llm.RoleUser, "Tell me a story"),
},
},
func(chunk *llm.ChatCompletionChunk) error {
if len(chunk.Choices) > 0 && chunk.Choices[0].Delta.Content != nil {
fmt.Print(*chunk.Choices[0].Delta.Content)
}
return nil
},
)
if err != nil {
panic(err)
}
fmt.Println()
}
import dev.kreuzberg.literllm.LlmClient;
import dev.kreuzberg.literllm.Types.*;
import java.util.List;
public class Main {
public static void main(String[] args) throws Exception {
// Note: The Java client does not yet support streaming.
// Use the non-streaming chat method instead.
try (var client = LlmClient.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.build()) {
var response = client.chat(new ChatCompletionRequest(
"openai/gpt-4o",
List.of(new UserMessage("Tell me a story"))
));
System.out.println(response.choices().getFirst().message().content());
}
}
}
# frozen_string_literal: true
require "liter_llm"
require "json"
# Note: The Ruby client does not yet support streaming.
# Use the non-streaming chat method instead.
client = LiterLlm::LlmClient.new(ENV.fetch("OPENAI_API_KEY"), {})
response = JSON.parse(client.chat(JSON.generate(
model: "openai/gpt-4o",
messages: [{ role: "user", content: "Tell me a story" }]
)))
puts response.dig("choices", 0, "message", "content")
<?php
declare(strict_types=1);
use LiterLlm\LlmClient;
$client = new LlmClient(apiKey: getenv('OPENAI_API_KEY') ?: '');
$chunksJson = $client->chatStream(json_encode([
'model' => 'openai/gpt-4o',
'messages' => [
['role' => 'user', 'content' => 'Tell me a story'],
],
]));
$chunks = json_decode($chunksJson, true);
foreach ($chunks as $chunk) {
echo $chunk['choices'][0]['delta']['content'] ?? '';
}
echo PHP_EOL;
using LiterLlm;
// Note: The C# client does not yet support streaming.
// Use the non-streaming ChatAsync method instead.
await using var client = new LlmClient(
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")!);
var response = await client.ChatAsync(new ChatCompletionRequest(
Model: "openai/gpt-4o",
Messages: [new UserMessage("Tell me a story")]
));
Console.WriteLine(response.Choices[0].Message.Content);
# Note: The Elixir client does not yet support streaming.
# Use the non-streaming chat function instead.
{:ok, response} =
LiterLlm.chat(
%{
model: "openai/gpt-4o",
messages: [%{role: "user", content: "Tell me a story"}]
},
api_key: System.fetch_env!("OPENAI_API_KEY")
)
IO.puts(hd(response["choices"])["message"]["content"])
import init, { LlmClient } from "@kreuzberg/liter-llm-wasm";
await init();
// Note: chatStream is not yet supported in the WASM binding.
// Use the non-streaming chat method instead.
const client = new LlmClient({ apiKey: "sk-..." });
const response = await client.chat({
model: "openai/gpt-4o",
messages: [{ role: "user", content: "Tell me a story" }],
});
console.log(response.choices[0].message.content);
Provider Routing¶
liter-llm uses a provider/model prefix convention to route requests to the correct provider. The provider prefix determines which API endpoint, auth header, and parameter mappings to use.
openai/gpt-4o -> OpenAI
anthropic/claude-sonnet-4-20250514 -> Anthropic
groq/llama3-70b -> Groq
google/gemini-2.0-flash -> Google AI
mistral/mistral-large -> Mistral
bedrock/anthropic.claude-v2 -> AWS Bedrock
Switch providers by changing the model string -- no other code changes needed:
import asyncio
import os
from liter_llm import LlmClient
async def main() -> None:
messages = [{"role": "user", "content": "What is the capital of France?"}]
# OpenAI
client_openai = LlmClient(api_key=os.environ["OPENAI_API_KEY"])
r1 = await client_openai.chat(model="openai/gpt-4o", messages=messages)
print(f"OpenAI: {r1.choices[0].message.content}")
# Anthropic
client_anthropic = LlmClient(api_key=os.environ["ANTHROPIC_API_KEY"])
r2 = await client_anthropic.chat(model="anthropic/claude-sonnet-4-20250514", messages=messages)
print(f"Anthropic: {r2.choices[0].message.content}")
# Groq
client_groq = LlmClient(api_key=os.environ["GROQ_API_KEY"])
r3 = await client_groq.chat(model="groq/llama3-70b", messages=messages)
print(f"Groq: {r3.choices[0].message.content}")
asyncio.run(main())
import { LlmClient } from "@kreuzberg/liter-llm";
const messages = [{ role: "user", content: "What is the capital of France?" }];
// OpenAI
const openai = new LlmClient({ apiKey: process.env.OPENAI_API_KEY! });
const r1 = await openai.chat({ model: "openai/gpt-4o", messages });
console.log(`OpenAI: ${r1.choices[0].message.content}`);
// Anthropic
const anthropic = new LlmClient({ apiKey: process.env.ANTHROPIC_API_KEY! });
const r2 = await anthropic.chat({ model: "anthropic/claude-sonnet-4-20250514", messages });
console.log(`Anthropic: ${r2.choices[0].message.content}`);
// Groq
const groq = new LlmClient({ apiKey: process.env.GROQ_API_KEY! });
const r3 = await groq.chat({ model: "groq/llama3-70b", messages });
console.log(`Groq: ${r3.choices[0].message.content}`);
API keys must be set for each provider you call
The examples above require OPENAI_API_KEY, ANTHROPIC_API_KEY, and GROQ_API_KEY to be set. See Installation for details.
Next Steps¶
- Chat Guide -- Multi-turn conversations, system prompts, parameters
- Streaming Guide -- Backpressure, error handling, cancellation
- Tool Calling -- Function calling with JSON schema validation
- Configuration -- Timeouts, retries, base URL overrides
- Provider Registry -- Browse all 142 supported providers
- API Reference -- Full API documentation for all languages