Sawvant

SDKs

Official TypeScript, Python and Go clients for the Sawvant API.

Overview

There are official SDKs for TypeScript, Python and Go. They are open source, generated from the OpenAPI spec, and wrap the REST API. From any other language you call the REST API directly; the API reference has request samples for each endpoint.

LanguagePackageInstall
TypeScript@sawvant/sdknpm install @sawvant/sdk
Pythonsawvantpip install sawvant
Gosawvant-gogo get github.com/sawvant/sawvant-go

The TypeScript SDK needs Node.js 18 or later, the Python SDK Python 3.8 or later and the Go SDK Go 1.22 or later. The source of each SDK is on GitHub: sawvant-typescript, sawvant-python and sawvant-go.

Quick Start

Submit a job, then poll until it is done. Sizes are in mm.

TypeScript

import { Configuration, JobsApi, OptimizationApi } from '@sawvant/sdk';

const config = new Configuration({
  apiKey: 'sk_your_api_key',
  basePath: 'https://api.sawvant.com',
});

const optimization = new OptimizationApi(config);
const jobs = new JobsApi(config);

const job = await optimization.createOptimization({
  sheets: [
    { id: 'S1', length: 2440, width: 1220, quantity: 5, grain: 'none' },
  ],
  parts: [
    { id: 'P1', length: 600, width: 400, quantity: 10, grain: 'none' },
  ],
  machine: { bladeThickness: 4, maxLevels: 3, cutDirection: 'default' },
});

let result = await jobs.getJob(job.jobId);
while (result.status === 'pending' || result.status === 'running') {
  await new Promise(resolve => setTimeout(resolve, 500));
  result = await jobs.getJob(job.jobId);
}

if (result.status === 'completed') {
  console.log(result.result?.summary);
} else {
  console.error('Job failed:', result.error);
}

Python

import time

import sawvant
from sawvant import (
    CutDirection,
    GrainDirection,
    JobsApi,
    Machine,
    OptimizationApi,
    OptimizeRequest,
    Part,
    Sheet,
)

configuration = sawvant.Configuration(
    host='https://api.sawvant.com',
    api_key={'apiKey': 'sk_your_api_key'},
)

with sawvant.ApiClient(configuration) as client:
    optimization = OptimizationApi(client)
    jobs = JobsApi(client)

    request = OptimizeRequest(
        sheets=[Sheet(id='S1', length=2440, width=1220, quantity=5, grain=GrainDirection.NONE)],
        parts=[Part(id='P1', length=600, width=400, quantity=10, grain=GrainDirection.NONE)],
        machine=Machine(blade_thickness=4, max_levels=3, cut_direction=CutDirection.DEFAULT),
    )

    job = optimization.create_optimization(request)

    result = jobs.get_job(job.job_id)
    while result.status in ('pending', 'running'):
        time.sleep(0.5)
        result = jobs.get_job(job.job_id)

    if result.status == 'completed':
        print(result.result.summary)
    else:
        print('Job failed:', result.error)

Go

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	sawvant "github.com/sawvant/sawvant-go"
)

func main() {
	cfg := sawvant.NewConfiguration()
	cfg.Host = "api.sawvant.com"
	client := sawvant.NewAPIClient(cfg)

	ctx := context.WithValue(context.Background(), sawvant.ContextAPIKeys, map[string]sawvant.APIKey{
		"apiKey": {Key: "sk_your_api_key"},
	})

	req := sawvant.OptimizeRequest{
		Sheets: []sawvant.Sheet{
			{Id: "S1", Length: 2440, Width: 1220, Quantity: 5, Grain: sawvant.NONE},
		},
		Parts: []sawvant.Part{
			{Id: "P1", Length: 600, Width: 400, Quantity: 10, Grain: sawvant.NONE},
		},
		Machine: sawvant.Machine{BladeThickness: 4, MaxLevels: 3, CutDirection: sawvant.DEFAULT},
	}

	job, _, err := client.OptimizationAPI.CreateOptimization(ctx).OptimizeRequest(req).Execute()
	if err != nil {
		log.Fatalf("create optimization: %v", err)
	}

	for {
		res, _, err := client.JobsAPI.GetJob(ctx, job.GetJobId()).Execute()
		if err != nil {
			log.Fatalf("get job: %v", err)
		}
		switch res.GetStatus() {
		case "completed":
			fmt.Printf("%+v\n", res.GetResult().Summary)
			return
		case "failed":
			log.Fatalf("job failed: %s", res.GetError())
		}
		time.Sleep(500 * time.Millisecond)
	}
}

SSE Streaming

On the Pro plan you can follow a job through Server-Sent Events instead of polling. The server leaves the result out of stream events to keep them small. When a completed event arrives, each SDK fetches the job with GET /v1/jobs/{id} and puts the full result in the event.

TypeScript

import { streamJob } from '@sawvant/sdk/dist/streaming.js';

for await (const event of streamJob(job.jobId, { apiKey: 'sk_your_api_key' })) {
  if (event.type === 'progress') {
    console.log('Progress:', event.data.progress);
  } else if (event.type === 'completed') {
    console.log('Done:', event.data.result);
    break;
  } else if (event.type === 'failed') {
    console.error('Failed:', event.data.error);
    break;
  }
}

streamJob also takes baseUrl and an AbortSignal as signal.

Python

The Python helper uses httpx. Install the SDK with the streaming extra:

pip install "sawvant[streaming]"
from sawvant.streaming import stream_job

for event in stream_job(job.job_id, 'sk_your_api_key'):
    if event.type == 'progress':
        print('Progress:', event.data['progress'])
    elif event.type == 'completed':
        print('Done:', event.data['result'])
        break
    elif event.type == 'failed':
        print('Failed:', event.data.get('error'))
        break

stream_job also takes base_url and timeout in seconds. event.data is a plain dict with the job fields.

Go

StreamJob sends only the headers set on the configuration, so pass the key with AddDefaultHeader.

cfg := sawvant.NewConfiguration()
cfg.Host = "api.sawvant.com"
cfg.AddDefaultHeader("X-API-Key", "sk_your_api_key")
client := sawvant.NewAPIClient(cfg)

events, errs := client.StreamJob(ctx, jobID)

for {
	select {
	case event, ok := <-events:
		if !ok {
			return
		}
		switch event.Type {
		case "progress":
			fmt.Printf("Progress: %d%%\n", event.Data.GetProgress())
		case "completed":
			fmt.Printf("Done: %+v\n", event.Data.GetResult().Summary)
			return
		case "failed":
			log.Fatalf("Failed: %s", event.Data.GetError())
		}
	case err := <-errs:
		if err != nil {
			log.Fatal(err)
		}
	}
}

API Methods

EndpointTypeScriptPythonGo
POST /v1/optimizeOptimizationApi.createOptimizationOptimizationApi.create_optimizationOptimizationAPI.CreateOptimization
GET /v1/jobs/{id}JobsApi.getJobJobsApi.get_jobJobsAPI.GetJob
GET /v1/jobs/{id}/streamstreamJobstream_jobStreamJob
GET /healthSystemApi.getHealthSystemApi.get_healthSystemAPI.GetHealth

On this page