synor/sdk/go/synor.go
Gulshan Yadav a808bb37a6 feat(sdk): add consumer SDKs for JavaScript, Python, and Go
Add complete SDK implementations for accessing Synor Compute:

JavaScript/TypeScript SDK (sdk/js/):
- Full async/await API with TypeScript types
- Tensor operations: matmul, conv2d, attention
- Model inference with streaming support
- WebSocket-based job monitoring
- Browser and Node.js compatible

Python SDK (sdk/python/):
- Async/await with aiohttp
- NumPy integration for tensors
- Context managers for cleanup
- Type hints throughout
- PyPI-ready package structure

Go SDK (sdk/go/):
- Idiomatic Go with context support
- Efficient binary tensor serialization
- HTTP client with configurable timeouts
- Zero external dependencies (stdlib only)

All SDKs support:
- Matrix multiplication (FP64 to INT4 precision)
- Convolution operations (2D, 3D)
- Flash attention
- LLM inference
- Spot pricing queries
- Job polling and cancellation
- Heterogeneous compute targeting (CPU/GPU/TPU/NPU/LPU)
2026-01-11 14:11:58 +05:30

352 lines
8.5 KiB
Go

// Package synor provides a Go SDK for Synor Compute.
//
// Access distributed heterogeneous compute resources (CPU, GPU, TPU, NPU, LPU)
// for AI/ML workloads at 90% cost reduction compared to traditional cloud.
//
// Example:
//
// client := synor.NewClient("your-api-key")
// result, err := client.MatMul(ctx, a, b, synor.FP16)
package synor
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
// Version of the SDK.
const Version = "0.1.0"
// DefaultEndpoint is the default API endpoint.
const DefaultEndpoint = "https://compute.synor.cc/api/v1"
// ProcessorType represents supported processor types.
type ProcessorType string
const (
CPU ProcessorType = "cpu"
GPU ProcessorType = "gpu"
TPU ProcessorType = "tpu"
NPU ProcessorType = "npu"
LPU ProcessorType = "lpu"
FPGA ProcessorType = "fpga"
WASM ProcessorType = "wasm"
WebGPU ProcessorType = "webgpu"
)
// Precision represents computation precision levels.
type Precision string
const (
FP64 Precision = "fp64"
FP32 Precision = "fp32"
FP16 Precision = "fp16"
BF16 Precision = "bf16"
INT8 Precision = "int8"
INT4 Precision = "int4"
)
// BalancingStrategy represents job scheduling strategies.
type BalancingStrategy string
const (
Speed BalancingStrategy = "speed"
Cost BalancingStrategy = "cost"
Energy BalancingStrategy = "energy"
Latency BalancingStrategy = "latency"
Balanced BalancingStrategy = "balanced"
)
// JobStatus represents job states.
type JobStatus string
const (
Pending JobStatus = "pending"
Queued JobStatus = "queued"
Running JobStatus = "running"
Completed JobStatus = "completed"
Failed JobStatus = "failed"
Cancelled JobStatus = "cancelled"
)
// Config holds client configuration.
type Config struct {
APIKey string
Endpoint string
Strategy BalancingStrategy
Precision Precision
Timeout time.Duration
Debug bool
}
// DefaultConfig returns a default configuration.
func DefaultConfig(apiKey string) Config {
return Config{
APIKey: apiKey,
Endpoint: DefaultEndpoint,
Strategy: Balanced,
Precision: FP32,
Timeout: 30 * time.Second,
Debug: false,
}
}
// Client is the main Synor Compute client.
type Client struct {
config Config
httpClient *http.Client
}
// NewClient creates a new client with the given API key.
func NewClient(apiKey string) *Client {
return NewClientWithConfig(DefaultConfig(apiKey))
}
// NewClientWithConfig creates a new client with custom configuration.
func NewClientWithConfig(config Config) *Client {
return &Client{
config: config,
httpClient: &http.Client{
Timeout: config.Timeout,
},
}
}
// JobResult represents the result of a job.
type JobResult struct {
JobID string `json:"job_id"`
Status JobStatus `json:"status"`
Data interface{} `json:"data,omitempty"`
Error string `json:"error,omitempty"`
Metrics *JobMetrics `json:"metrics,omitempty"`
}
// JobMetrics contains execution metrics.
type JobMetrics struct {
ExecutionTimeMs float64 `json:"execution_time_ms"`
QueueTimeMs float64 `json:"queue_time_ms"`
ProcessorType ProcessorType `json:"processor_type"`
ProcessorID string `json:"processor_id"`
FLOPS float64 `json:"flops"`
MemoryBytes int64 `json:"memory_bytes"`
CostMicro int64 `json:"cost_micro"`
EnergyMJ float64 `json:"energy_mj"`
}
// Tensor represents a multi-dimensional array.
type Tensor struct {
Data []float32 `json:"data"`
Shape []int `json:"shape"`
DType Precision `json:"dtype"`
}
// NewTensor creates a new tensor from data and shape.
func NewTensor(data []float32, shape []int, dtype Precision) *Tensor {
return &Tensor{
Data: data,
Shape: shape,
DType: dtype,
}
}
// Zeros creates a tensor of zeros.
func Zeros(shape []int, dtype Precision) *Tensor {
size := 1
for _, dim := range shape {
size *= dim
}
return &Tensor{
Data: make([]float32, size),
Shape: shape,
DType: dtype,
}
}
// Serialize converts tensor to transmission format.
func (t *Tensor) Serialize() map[string]interface{} {
buf := new(bytes.Buffer)
for _, v := range t.Data {
var b [4]byte
*(*float32)((&b[0])) = v
buf.Write(b[:])
}
return map[string]interface{}{
"data": base64.StdEncoding.EncodeToString(buf.Bytes()),
"shape": t.Shape,
"dtype": t.DType,
}
}
// MatMul performs matrix multiplication.
func (c *Client) MatMul(ctx context.Context, a, b *Tensor, precision Precision) (*JobResult, error) {
job, err := c.SubmitJob(ctx, "matmul", map[string]interface{}{
"a": a.Serialize(),
"b": b.Serialize(),
"precision": precision,
})
if err != nil {
return nil, err
}
return job.Wait(ctx)
}
// Inference runs model inference.
func (c *Client) Inference(ctx context.Context, model, input string, maxTokens int) (*JobResult, error) {
job, err := c.SubmitJob(ctx, "inference", map[string]interface{}{
"model": model,
"input": input,
"max_tokens": maxTokens,
"strategy": c.config.Strategy,
})
if err != nil {
return nil, err
}
return job.Wait(ctx)
}
// Job represents a submitted compute job.
type Job struct {
ID string
client *Client
status JobStatus
}
// SubmitJob submits a new compute job.
func (c *Client) SubmitJob(ctx context.Context, operation string, params map[string]interface{}) (*Job, error) {
body := map[string]interface{}{
"operation": operation,
"params": params,
"strategy": c.config.Strategy,
}
var resp struct {
JobID string `json:"job_id"`
}
if err := c.request(ctx, "POST", "/jobs", body, &resp); err != nil {
return nil, err
}
return &Job{
ID: resp.JobID,
client: c,
status: Pending,
}, nil
}
// Wait waits for the job to complete.
func (j *Job) Wait(ctx context.Context) (*JobResult, error) {
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-ticker.C:
result, err := j.client.GetJobStatus(ctx, j.ID)
if err != nil {
return nil, err
}
if result.Status == Completed || result.Status == Failed || result.Status == Cancelled {
return result, nil
}
}
}
}
// GetJobStatus gets the status of a job.
func (c *Client) GetJobStatus(ctx context.Context, jobID string) (*JobResult, error) {
var result JobResult
if err := c.request(ctx, "GET", "/jobs/"+jobID, nil, &result); err != nil {
return nil, err
}
return &result, nil
}
// CancelJob cancels a running job.
func (c *Client) CancelJob(ctx context.Context, jobID string) error {
return c.request(ctx, "DELETE", "/jobs/"+jobID, nil, nil)
}
// PricingInfo contains pricing information.
type PricingInfo struct {
ProcessorType ProcessorType `json:"processor_type"`
SpotPrice float64 `json:"spot_price"`
AvgPrice24h float64 `json:"avg_price_24h"`
AWSEquivalent float64 `json:"aws_equivalent"`
SavingsPercent float64 `json:"savings_percent"`
}
// GetPricing gets current pricing for all processor types.
func (c *Client) GetPricing(ctx context.Context) ([]PricingInfo, error) {
var resp struct {
Pricing []PricingInfo `json:"pricing"`
}
if err := c.request(ctx, "GET", "/pricing", nil, &resp); err != nil {
return nil, err
}
return resp.Pricing, nil
}
func (c *Client) request(ctx context.Context, method, path string, body interface{}, result interface{}) error {
url := c.config.Endpoint + path
var bodyReader io.Reader
if body != nil {
bodyBytes, err := json.Marshal(body)
if err != nil {
return fmt.Errorf("failed to marshal request: %w", err)
}
bodyReader = bytes.NewReader(bodyBytes)
}
req, err := http.NewRequestWithContext(ctx, method, url, bodyReader)
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+c.config.APIKey)
req.Header.Set("Content-Type", "application/json")
resp, err := c.httpClient.Do(req)
if err != nil {
return fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
var errResp struct {
Message string `json:"message"`
}
json.NewDecoder(resp.Body).Decode(&errResp)
return &SynorError{
Message: errResp.Message,
StatusCode: resp.StatusCode,
}
}
if result != nil {
if err := json.NewDecoder(resp.Body).Decode(result); err != nil {
return fmt.Errorf("failed to decode response: %w", err)
}
}
return nil
}
// SynorError represents an API error.
type SynorError struct {
Message string
StatusCode int
}
func (e *SynorError) Error() string {
return fmt.Sprintf("synor: %s (status %d)", e.Message, e.StatusCode)
}