edge-completions
An ergonomic, typed Rust SDK and optional command-line client for
OpenAI-compatible chat completions through Cloudflare. The first supported
convenience model is moonshotai/kimi-k3.
This is an independent open-source project. It is not affiliated with, endorsed by, or sponsored by Cloudflare, Inc. Cloudflare is a trademark of Cloudflare, Inc.
Why this crate
- A small, object-safe
ChatCompletionstrait for application boundaries. - A sealed typestate request builder that rejects illegal construction sequences at compile time.
- Validated newtypes for account IDs, API tokens, models, URLs, timeouts, and limits.
- Typed tool schemas, arguments, and results without a public untyped JSON escape hatch.
thiserrorerrors for configuration, transport, provider, response, and tool failures.- Redacted token debug output and bounded response-body decoding.
- An opt-in
edge-completionscommand that prints typed assistant text and never prints provider envelopes. - No autonomous tool loop: your application retains authorization and execution control.
Install
Add the library:
Install the optional command-line client:
The cli feature is intentionally disabled for library consumers, so SDK-only
builds do not compile command-line dependencies. The minimum supported Rust
version is 1.86.
Applications using the asynchronous examples also need Tokio. Typed tool definitions use Schemars and Serde:
Configuration
Create a scoped Cloudflare API token and set:
Client::from_env() also accepts the original
KIMI3_ON_CLOUDFLARE_API_KEY variable as a compatibility fallback. New
applications should use CLOUDFLARE_API_TOKEN.
You can find the account ID in the Cloudflare dashboard after selecting your account, or follow Cloudflare's account and zone ID guide. Never commit either value.
Command line
Validate configuration without making an API request:
Expected output:
configuration is valid
Send a prompt and print only the assistant's text:
Prompts can also come from standard input:
|
Use edge-completions help chat for all chat options. The command returns typed,
sanitized errors on standard error. It does not expose raw request or response
envelopes and does not execute model-proposed tools. See the complete
CLI reference,
including exit codes and the base-URL security contract.
Library quickstart
use ;
async
Run the complete example:
Expected result: the example prints the model identifier followed by validated assistant text. It never prints the provider response envelope.
Typed tool calls
A tool contract ties together its generated JSON Schema, validated input type, and serializable output type:
use ToolDefinition;
use JsonSchema;
use ;
;
Decode a provider proposal only through the matching contract:
let call = completion.first_choice?.message.first_tool_call?;
let validated_call = call.?;
// The application authorizes and executes the action here.
let report = get_weather;
let result_message = validated_call.result?;
Run the complete two-turn example:
The example returns deterministic sample weather; it does not call a weather service or claim to provide a live forecast.
Output follows this shape. The final sentence depends on the model:
Tool requested: get_weather
Validated city: San Francisco
Final answer: ...
Compile-time composition
The typestate builder represents valid request states as types. Adding a message or tool consumes one state and returns the next, while trait bounds control which operations exist:
# use ;
# use JsonSchema;
# use ;
# ;
#
#
#
#
Calling build before message, or tool_choice before tool, does not
compile. AssistantOutput models response alternatives as an exhaustive sum
type, so callers must handle text, tool calls, text and tool calls, and empty
output.
| Guarantee | Enforced by | Failure point |
|---|---|---|
| A request has at least one message | ChatRequestBuilder typestate |
Compilation |
| Tool choice follows at least one tool | WithTools trait bound |
Compilation |
| A tool result matches its tool contract | ValidatedToolCall<T> |
Compilation |
| Every supported assistant outcome is handled | Exhaustive AssistantOutput match |
Compilation |
| Provider data matches the declared contract | Typed deserialization and validation | Runtime |
The same design has a small categorical interpretation: request states are objects, legal transitions are composable morphisms, and assistant output is a coproduct with a product branch. External network and model data still require typed runtime validation.
See the type-system guide for the complete state graph, compile-fail examples, and the boundary between static guarantees and runtime checks.
Migrating from 0.2
Version 0.3 is additive. Existing ChatRequest::new, ChatRequest::kimi_k3,
with_tool, with_tools, and ToolCall::arguments_for calls remain available.
New code should prefer these replacements:
| 0.2 API | Preferred 0.3 API | Benefit |
|---|---|---|
ChatRequest::kimi_k3(messages) |
ChatRequest::kimi_k3_builder().message(...).build() |
Non-empty messages are proven at compile time |
request.with_tools(tools, choice) |
.tool(...).tool_choice(choice) |
Tool choice cannot precede a tool |
call.arguments_for::<T>() |
call.validate::<T>() |
The validation proof remains available for result encoding |
message.content() plus tool_calls() |
message.output() |
All supported output combinations are handled together |
No automatic migration is required. Adopt the new API when compile-time guarantees are useful at the call site.
Trait boundary
Application services can depend on behavior instead of the concrete HTTP client:
async
Keep the environment-based credential lookup while customizing the transport:
use ;
Features
| Feature | Default | Purpose |
|---|---|---|
cli |
No | Builds the installable edge-completions command. |
Error and security model
- Every library failure is typed with
thiserror; production code contains nounwrap,expect, or panic path. - Local request cardinality and tool-choice sequencing are enforced by typestate; provider responses and model-produced tool calls are checked at runtime.
ValidatedToolCall<T>is a proof-carrying value that binds decoded arguments and encoded output to the sameToolDefinitionat compile time.- API tokens use redacted
Debugand are never included in errors. - Success and failure bodies are size-bounded. Unknown error bodies are not exposed.
- Only HTTPS endpoints are accepted, except loopback HTTP for local tests.
- Tool names and arguments are model-controlled and remain untrusted until
validate::<T>()produces aValidatedToolCall<T>witness. The compatibility helperarguments_for::<T>()performs the same validation and returns the arguments. - Tool execution is intentionally outside this crate.
A 402 Payment Required with Cloudflare code 2021 means the account or AI
Gateway lacks usable Workers AI balance/provider billing. Add the required
balance or configure BYOK before retrying the live examples.
Validation
RUSTDOCFLAGS="-D warnings -D missing_docs"
Tests cover exact request serialization, public trait use, typed tool-call round trips, HTTP authentication, response validation, provider errors, response limits, TLS policy, and secret redaction against local servers. Tests do not make live provider calls.
Scope and limitations
- Supported now: non-streaming chat completions, typed function tools, Kimi K3 convenience construction, optional AI Gateway ID, timeout and body-size policy.
- Not supported yet: streaming, multimodal content, embeddings, Responses API, provider-specific raw extension maps, or autonomous tool execution.
- Provider contract drift remains possible. Contract changes should land with a versioned test before expanding the public API.
See the architecture, type-system guide, contributing guide, CLI reference, security policy, and release process.
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.