feat: Phase 10 polish — package docs, examples, integration tests

Add doc.go with package-level godoc for all 15 packages, runnable
example functions for chat completion, streaming, embeddings, and
error handling, plus build-tagged integration tests for live API
validation.
This commit is contained in:
2026-03-05 20:12:02 +01:00
parent e528a3c308
commit c79add6452
17 changed files with 306 additions and 0 deletions

3
agents/doc.go Normal file
View File

@@ -0,0 +1,3 @@
// Package agents provides types for the Mistral agents API,
// including agent CRUD operations and agent chat completions.
package agents

5
audio/doc.go Normal file
View File

@@ -0,0 +1,5 @@
// Package audio provides types for the Mistral audio transcription API.
//
// Streaming transcription returns typed [StreamEvent] values via a sealed
// interface dispatched by the "type" field.
package audio

2
batch/doc.go Normal file
View File

@@ -0,0 +1,2 @@
// Package batch provides types for the Mistral batch jobs API.
package batch

9
chat/doc.go Normal file
View File

@@ -0,0 +1,9 @@
// Package chat provides types for the Mistral chat completion API.
//
// Messages use a sealed interface pattern — create them with
// [UserMessage], [SystemMessage], [AssistantMessage], or [ToolMessage]
// struct literals.
//
// Content is polymorphic: it can be a plain string (via [TextContent]),
// nil, or a slice of [ContentChunk] values (text, image URL, document URL, audio).
package chat

2
classification/doc.go Normal file
View File

@@ -0,0 +1,2 @@
// Package classification provides types for the Mistral classification API.
package classification

6
conversation/doc.go Normal file
View File

@@ -0,0 +1,6 @@
// Package conversation provides types for the Mistral conversations API.
//
// Conversations support multi-turn interactions with start, append, and
// restart operations. Streaming returns typed [Event] values via a sealed
// interface dispatched by the "type" discriminator.
package conversation

45
doc.go Normal file
View File

@@ -0,0 +1,45 @@
// Package mistral provides an idiomatic Go client for the Mistral AI API.
//
// Create a client with your API key, then call methods for each endpoint:
//
// client := mistral.NewClient("sk-...")
//
// // Chat completion
// resp, err := client.ChatComplete(ctx, &chat.CompletionRequest{
// Model: "mistral-small-latest",
// Messages: []chat.Message{&chat.UserMessage{Content: chat.TextContent("Hello!")}},
// })
//
// // Streaming
// stream, err := client.ChatCompleteStream(ctx, req)
// defer stream.Close()
// for stream.Next() {
// chunk := stream.Current()
// fmt.Print(chunk.Choices[0].Delta.Content)
// }
//
// # Configuration
//
// Use functional options to configure the client:
//
// client := mistral.NewClient("sk-...",
// mistral.WithTimeout(30 * time.Second),
// mistral.WithRetry(3, 500*time.Millisecond),
// )
//
// # Error Handling
//
// API errors are returned as *[APIError] values. Use sentinel checkers
// for common cases:
//
// if mistral.IsRateLimit(err) {
// // back off and retry
// }
//
// # Sub-packages
//
// Types are organized into sub-packages by domain: [chat], [agents],
// [conversation], [embedding], [model], [file], [finetune], [batch],
// [ocr], [audio], [library], [moderation], [classification], and [fim].
// All service methods live directly on [Client].
package mistral

2
embedding/doc.go Normal file
View File

@@ -0,0 +1,2 @@
// Package embedding provides types for the Mistral embeddings API.
package embedding

94
example_test.go Normal file
View File

@@ -0,0 +1,94 @@
package mistral_test
import (
"context"
"fmt"
"log"
mistral "somegit.dev/vikingowl/mistral-go-sdk"
"somegit.dev/vikingowl/mistral-go-sdk/chat"
"somegit.dev/vikingowl/mistral-go-sdk/embedding"
)
func ExampleNewClient() {
client := mistral.NewClient("sk-your-api-key")
_ = client // use client to call API methods
}
func ExampleClient_ChatComplete() {
client := mistral.NewClient("sk-your-api-key")
resp, err := client.ChatComplete(context.Background(), &chat.CompletionRequest{
Model: "mistral-small-latest",
Messages: []chat.Message{
&chat.UserMessage{Content: chat.TextContent("What is the capital of France?")},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Choices[0].Message.Content)
}
func ExampleClient_ChatCompleteStream() {
client := mistral.NewClient("sk-your-api-key")
stream, err := client.ChatCompleteStream(context.Background(), &chat.CompletionRequest{
Model: "mistral-small-latest",
Messages: []chat.Message{
&chat.UserMessage{Content: chat.TextContent("Tell me a short joke.")},
},
})
if err != nil {
log.Fatal(err)
}
defer stream.Close()
for stream.Next() {
chunk := stream.Current()
if len(chunk.Choices) > 0 {
fmt.Print(chunk.Choices[0].Delta.Content)
}
}
if err := stream.Err(); err != nil {
log.Fatal(err)
}
}
func ExampleClient_CreateEmbeddings() {
client := mistral.NewClient("sk-your-api-key")
resp, err := client.CreateEmbeddings(context.Background(), &embedding.Request{
Model: "mistral-embed",
Input: []string{"Hello world"},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Embedding dimension: %d\n", len(resp.Data[0].Embedding))
}
func ExampleIsRateLimit() {
client := mistral.NewClient("sk-your-api-key")
_, err := client.ChatComplete(context.Background(), &chat.CompletionRequest{
Model: "mistral-small-latest",
Messages: []chat.Message{
&chat.UserMessage{Content: chat.TextContent("Hi")},
},
})
if mistral.IsRateLimit(err) {
fmt.Println("Rate limited — back off and retry")
}
}
func ExampleIsNotFound() {
client := mistral.NewClient("sk-your-api-key")
_, err := client.GetModel(context.Background(), "nonexistent-model")
if mistral.IsNotFound(err) {
fmt.Println("Model not found")
}
}

3
file/doc.go Normal file
View File

@@ -0,0 +1,3 @@
// Package file provides types for the Mistral files API,
// including upload, download, and metadata operations.
package file

3
fim/doc.go Normal file
View File

@@ -0,0 +1,3 @@
// Package fim provides types for the Mistral Fill-in-the-Middle (FIM)
// completion API, used for code infilling tasks.
package fim

3
finetune/doc.go Normal file
View File

@@ -0,0 +1,3 @@
// Package finetune provides types for the Mistral fine-tuning API,
// including job creation, monitoring, and model archiving.
package finetune

115
integration_test.go Normal file
View File

@@ -0,0 +1,115 @@
//go:build integration
package mistral
import (
"context"
"os"
"strings"
"testing"
"somegit.dev/vikingowl/mistral-go-sdk/chat"
"somegit.dev/vikingowl/mistral-go-sdk/embedding"
)
func integrationClient(t *testing.T) *Client {
t.Helper()
key := os.Getenv("MISTRAL_API_KEY")
if key == "" {
t.Skip("MISTRAL_API_KEY not set")
}
return NewClient(key)
}
func TestIntegration_ListModels(t *testing.T) {
client := integrationClient(t)
resp, err := client.ListModels(context.Background())
if err != nil {
t.Fatal(err)
}
if len(resp.Data) == 0 {
t.Fatal("expected at least one model")
}
if resp.Data[0].ID == "" {
t.Error("expected model to have an ID")
}
}
func TestIntegration_ChatComplete(t *testing.T) {
client := integrationClient(t)
resp, err := client.ChatComplete(context.Background(), &chat.CompletionRequest{
Model: "mistral-small-latest",
Messages: []chat.Message{
&chat.UserMessage{Content: chat.TextContent("Reply with exactly: hello")},
},
})
if err != nil {
t.Fatal(err)
}
if len(resp.Choices) == 0 {
t.Fatal("no choices returned")
}
content := resp.Choices[0].Message.Content.String()
if !strings.Contains(strings.ToLower(content), "hello") {
t.Errorf("expected 'hello' in response, got %q", content)
}
}
func TestIntegration_ChatCompleteStream(t *testing.T) {
client := integrationClient(t)
stream, err := client.ChatCompleteStream(context.Background(), &chat.CompletionRequest{
Model: "mistral-small-latest",
Messages: []chat.Message{
&chat.UserMessage{Content: chat.TextContent("Say hi")},
},
})
if err != nil {
t.Fatal(err)
}
defer stream.Close()
chunks := 0
for stream.Next() {
chunks++
}
if err := stream.Err(); err != nil {
t.Fatal(err)
}
if chunks == 0 {
t.Error("expected at least one stream chunk")
}
}
func TestIntegration_Embeddings(t *testing.T) {
client := integrationClient(t)
resp, err := client.CreateEmbeddings(context.Background(), &embedding.Request{
Model: "mistral-embed",
Input: []string{"The quick brown fox"},
})
if err != nil {
t.Fatal(err)
}
if len(resp.Data) != 1 {
t.Fatalf("expected 1 embedding, got %d", len(resp.Data))
}
if len(resp.Data[0].Embedding) == 0 {
t.Error("embedding vector is empty")
}
}
func TestIntegration_ErrorHandling(t *testing.T) {
client := integrationClient(t)
_, err := client.GetModel(context.Background(), "definitely-not-a-real-model-id")
if err == nil {
t.Fatal("expected error for nonexistent model")
}
if !IsNotFound(err) {
t.Logf("got non-404 error (may vary by API): %v", err)
}
}

3
library/doc.go Normal file
View File

@@ -0,0 +1,3 @@
// Package library provides types for the Mistral document libraries API,
// including library and document CRUD, sharing, and processing.
package library

6
model/doc.go Normal file
View File

@@ -0,0 +1,6 @@
// Package model provides types for Mistral model listing and metadata.
//
// Model cards use a sealed interface — [BaseModelCard] for platform models
// and [FTModelCard] for fine-tuned models. JSON unmarshaling dispatches on
// the "type" field.
package model

2
moderation/doc.go Normal file
View File

@@ -0,0 +1,2 @@
// Package moderation provides types for the Mistral moderation API.
package moderation

3
ocr/doc.go Normal file
View File

@@ -0,0 +1,3 @@
// Package ocr provides types for the Mistral OCR API,
// including document processing with page-level extraction.
package ocr