Go SDK

⚠️
Preview — this feature is being built; the design below is the spec.

syntic-go is the planned official Go client for the Syntic API. It is generated from the OpenAPI description, so every endpoint and field is available, with hand-written helpers for streaming, tool loops, pagination and batches. It is one of the first two clients after Python and TypeScript; see Other languages for the release order.

Today, call the API with net/http as shown in Quickstart: Go. That code keeps working after the SDK ships.

Install

go get github.com/syntic-ai/syntic-go
RequirementValue
Go1.22 or newer
Modulegithub.com/syntic-ai/syntic-go
DependenciesStandard library only

Create a client

import syntic "github.com/syntic-ai/syntic-go"
 
client := syntic.NewClient() // reads SYNTIC_API_KEY and SYNTIC_BASE_URL

Options are passed as functional options:

OptionDefaultNotes
syntic.WithAPIKey(key)$SYNTIC_API_KEYA private sk_live_ key or a workload identity sat_ token.
syntic.WithBaseURL(url)$SYNTIC_BASE_URL, else https://app.syntic.ai/v1
syntic.WithVersion(v)The version the release was built forSent as syntic-version.
syntic.WithBetas(flags...)noneSent as syntic-beta. Methods that need a flag add it themselves.
syntic.WithMaxRetries(n)2Retries rate_limit, overloaded, api_error and network errors with back-off, honouring retry-after.
syntic.WithHTTPClient(c)A client with a 10-minute timeoutBring your own transport, proxy or tracing.

Every method takes a context.Context first; cancel it to abort a request or a stream.

A first request

package main
 
import (
	"context"
	"fmt"
	"log"
 
	syntic "github.com/syntic-ai/syntic-go"
)
 
func main() {
	client := syntic.NewClient()
	reply, err := client.Chat.Completions.Create(context.Background(), syntic.ChatCompletionParams{
		Model:     "Amara S1.2",
		MaxTokens: syntic.Int(200),
		Messages: []syntic.Message{
			syntic.SystemMessage("Answer in one sentence."),
			syntic.UserMessage("Why is the sky blue?"),
		},
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(reply.Choices[0].Message.Content)
	fmt.Println(reply.Usage.TotalTokens, reply.RequestID)
}

The SDK always sends stream explicitly: Create returns one completion, Stream returns a stream.

Streaming

stream := client.Chat.Completions.Stream(ctx, syntic.ChatCompletionParams{
	Model:    "Amara Quick",
	Messages: []syntic.Message{syntic.UserMessage("Write a haiku about rain.")},
})
defer stream.Close()
 
for stream.Next() {
	fmt.Print(stream.Current().TextDelta())
}
if err := stream.Err(); err != nil { // includes a stream_error event, as a typed error
	log.Fatal(err)
}
final := stream.Completion() // assembled message, tool calls and usage
fmt.Println("\n", final.Usage.TotalTokens)

Tool loop

type OrderArgs struct {
	OrderID string `json:"order_id" jsonschema:"description=Order id such as 88123"`
}
 
getOrder := syntic.NewTool("get_order", "Look up an order by its id.",
	func(ctx context.Context, a OrderArgs) (any, error) {
		return map[string]string{"order_id": a.OrderID, "status": "shipped"}, nil
	})
 
result, err := client.Tools.Run(ctx, syntic.ToolRunParams{
	Model:     "Amara S1.2",
	Messages:  []syntic.Message{syntic.UserMessage("Where is order 88123?")},
	Tools:     []syntic.Tool{getOrder},
	MaxRounds: 8,
})
if err != nil {
	log.Fatal(err)
}
fmt.Println(result.FinalMessage.Content)

The JSON Schema is generated from the argument struct. A function that returns an error has the error sent back to the model as a tool result.

Errors

API errors are returned as *syntic.Error:

var apiErr *syntic.Error
if errors.As(err, &apiErr) {
	switch apiErr.Type {
	case syntic.ErrQuotaExhausted:
		fmt.Println("paused until", apiErr.Details["resets_at"])
	case syntic.ErrInvalidRequest:
		fmt.Println(apiErr.Code, apiErr.Param, apiErr.RequestID)
	}
}
FieldMeaning
StatusCodeHTTP status.
TypeOne of the eight envelope types, as typed constants.
Code, Message, Param, DetailsFrom the envelope.
RequestIDQuote it to support.

Network failures and timeouts are returned as ordinary Go errors wrapping context or net errors.

Pagination

iter := client.Agents.ListAutoPaging(ctx, syntic.AgentListParams{Limit: syntic.Int(50)})
for iter.Next() {
	a := iter.Current()
	fmt.Println(a.ID, a.Name)
}
if err := iter.Err(); err != nil {
	log.Fatal(err)
}

List (without AutoPaging) returns one page with HasMore, FirstID and LastID.

Files, batches, agents and admin

f, _ := client.Files.Upload(ctx, syntic.FileUploadParams{Path: "contract.pdf", Purpose: "document"})
 
batch, _ := client.Batches.Create(ctx, syntic.BatchCreateParams{InputFileID: syntic.String(f.ID)})
batch, _ = client.Batches.Wait(ctx, batch.ID, 30*time.Second)
 
session, _ := client.Sessions.Create(ctx, syntic.SessionCreateParams{AgentID: "agt_01JC4R8W6Q2N9X3T5V7Y"})
events := client.Sessions.Stream(ctx, session.ID)
for events.Next() {
	if e := events.Current(); e.Type == "message.completed" {
		fmt.Println(e.Content)
	}
}
 
admin := syntic.NewAdminClient() // reads SYNTIC_ADMIN_KEY
users := admin.Users.ListAutoPaging(ctx, syntic.UserListParams{})

Raw responses

reply, raw, err := client.Chat.Completions.CreateWithResponse(ctx, params)
fmt.Println(raw.Header.Get("x-syntic-billing-source"))

Versioning

The module follows semantic versioning with Go module major-version paths (/v2 and so on) for breaking changes, which come with a new default syntic-version. Each release lists the API version it was tested against.

Was this page helpful?