Skip to main content

tszclient-go – Go client for TSZ (Thyris Safe Zone)

tszclient-go is a lightweight Go client for interacting with a TSZ (Thyris Safe Zone) deployment.

It provides:

  • A typed interface for the /detect endpoint (PII detection & guardrails)
  • A helper for the chat-completions-compatible LLM gateway (/v1/chat/completions)
  • Simple configuration via Config and a single Client type

There is also an official Python client (tszclient_py, distributed as the tszclient-py package) for Python services. See:

  • pkg/tszclient_py/
  • examples/python-sdk-demo/main.py

Note: tszclient-go is published as a Go module with the path source.example/thyris/safe-zone/pkg/tszclient-go. This module lives inside the thyris-sz monorepo, but can be consumed independently via go get.

Installation​

From inside this repository (when importing via local module name), you can import it as:

import "thyris-sz/pkg/tszclient-go"

From an external project, use the public Go module path:

import tszclient "source.example/thyris/safe-zone/pkg/tszclient-go"

Then add it to your go.mod via:

go get source.example/thyris/safe-zone/pkg/tszclient-go@v0.1.0

See examples/go-sdk-demo in this repository for a complete, runnable example that:

  • Uses the source repository import path
  • Has its own go.mod demonstrating how to depend on source.example/thyris/safe-zone
  • Calls both /detect and the /v1/chat/completions gateway from a single program

Configuration​

type Config struct {
BaseURL string
APIKey string // Optional auth token (sent as Bearer + X-ADMIN-KEY for compatibility)
HTTPClient *http.Client
}

client, err := tszclient.New(tszclient.Config{
BaseURL: "http://localhost:8080",
APIKey: "token_admin", // Use when TSZ auth is enabled
})
if err != nil {
log.Fatalf("failed to create tsz client: %v", err)
}
  • BaseURL should point to your TSZ instance (gateway or direct).
  • APIKey is optional. If set, the client sends:
    • Authorization: Bearer <APIKey>
    • X-ADMIN-KEY: <APIKey> (legacy compatibility)
  • HTTPClient is optional; if nil, a default client with 60 second timeout is used.

Using /detect via the client​

Contexts, timeouts, and cancellation​

The client methods accept a context.Context, so you can control deadlines and cancellation per call. By default, if you don't provide a custom HTTPClient in Config, the client uses an http.Client with a 60 second timeout; you can also apply shorter/longer timeouts via context:

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

You can reuse the same client with different contexts depending on the requirements of each call.

Request & Response Types​

type DetectRequest struct {
Text string `json:"text"`
RID string `json:"rid,omitempty"`
ExpectedFormat string `json:"expected_format,omitempty"`
Guardrails []string `json:"guardrails,omitempty"`
}

type DetectResponse struct {
RedactedText string `json:"redacted_text,omitempty"`
Detections []DetectionResult `json:"detections,omitempty"`
ValidatorResults []ValidatorResult `json:"validator_results,omitempty"`
Breakdown map[string]int `json:"breakdown"`
Blocked bool `json:"blocked"`
ContainsPII bool `json:"contains_pii"`
OverallConfidence string `json:"overall_confidence"`
Message string `json:"message,omitempty"`
}

Example (basic Detect)​

package main

import (
"context"
"log"
"time"

tszclient "source.example/thyris/safe-zone/pkg/tszclient-go"
)

func main() {
// Per-call timeout via context
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

client, err := tszclient.New(tszclient.Config{
BaseURL: "http://localhost:8080",
})
if err != nil {
log.Fatalf("failed to create tsz client: %v", err)
}

resp, err := client.Detect(ctx, tszclient.DetectRequest{
Text: "Contact me at user@example.com",
RID: "RID-GO-001",
Guardrails: []string{"TOXIC_LANGUAGE"},
})
if err != nil {
log.Fatalf("detect failed: %v", err)
}

if resp.Blocked {
log.Printf("request blocked by TSZ: %s", resp.Message)
return
}

log.Printf("Redacted: %s", resp.RedactedText)
}

Optional convenience helpers​

For common detect flows, you can use small helper functions to keep your call sites concise. The client exposes a DetectText wrapper and functional options such as WithGuardrails, WithRID, and WithExpectedFormat:

resp, err := client.DetectText(
ctx,
"Contact me at user@example.com",
tszclient.WithRID("RID-GO-002"),
tszclient.WithGuardrails("TOXIC_LANGUAGE", "FINANCIAL_DATA"),
)
if err != nil {
log.Fatalf("detect failed: %v", err)
}

These helpers are completely optional; you can always construct a full DetectRequest and call Detect directly if you prefer explicit request structs.

Using the LLM Gateway (/v1/chat/completions)​

The client also makes it easy to call the chat-completions-compatible TSZ LLM gateway.

Types​

type ChatCompletionRequest struct {
Model string `json:"model"`
Messages []map[string]interface{} `json:"messages"`
Stream bool `json:"stream,omitempty"`
Extra map[string]interface{} `json:"-"`
}

type ChatCompletionResponse map[string]interface{}

Example (non‑streaming)​

package main

import (
"context"
"fmt"
"log"

tszclient "source.example/thyris/safe-zone/pkg/tszclient-go"
)

func main() {
ctx := context.Background()

client, err := tszclient.New(tszclient.Config{
BaseURL: "http://localhost:8080",
})
if err != nil {
log.Fatalf("failed to create tsz client: %v", err)
}

resp, err := client.ChatCompletions(ctx, tszclient.ChatCompletionRequest{
Model: "llama3.1:8b",
Messages: []map[string]interface{}{
{"role": "user", "content": "Hello via TSZ gateway"},
},
Stream: false,
}, map[string]string{
"X-TSZ-RID": "RID-GW-GO-001",
"X-TSZ-Guardrails": "TOXIC_LANGUAGE",
})
if err != nil {
log.Fatalf("chat completions failed: %v", err)
}

choices, ok := resp["choices"].([]interface{})
if !ok || len(choices) == 0 {
log.Println("no choices in response")
return
}

first, _ := choices[0].(map[string]interface{})
msg, _ := first["message"].(map[string]interface{})
content, _ := msg["content"].(string)

fmt.Println("LLM response via TSZ:")
fmt.Println(content)
}

In this call:

  • TSZ first scans the user message with /detect (PII + guardrails) and masks or blocks according to your policies.
  • If the input is safe, TSZ forwards only the redacted prompt to the upstream LLM.
  • When the LLM responds, TSZ applies the same guardrail set on the assistant output before returning it to the client.

Management API​

The client supports all management operations for Patterns, Allowlists, Blocklists, and Validators.

Managing Patterns​

// List patterns
patterns, err := client.ListPatterns(ctx)

// Create a new pattern
p := tszclient.Pattern{
Name: "CUSTOM_ID",
Regex: "ID-\\d{5}",
IsActive: true,
Category: "PII",
}
newPattern, err := client.CreatePattern(ctx, p)

// Delete a pattern
err := client.DeletePattern(ctx, newPattern.ID)

Managing Lists​

// Allowlist
client.CreateAllowlistItem(ctx, tszclient.AllowlistItem{Value: "admin@example.com"})
items, _ := client.ListAllowlist(ctx)

// Blocklist
client.CreateBlocklistItem(ctx, tszclient.BlacklistItem{Value: "forbidden_term"})

Importing Templates​

You can import full guardrail templates (JSON packs) directly:

template := tszclient.TemplateDefinition{
Name: "My Policy Pack",
Patterns: []tszclient.Pattern{...},
}
err := client.ImportTemplate(ctx, template)

Error handling​

When TSZ returns a non‑2xx HTTP response, the client returns an APIError:

type APIError struct {
StatusCode int
Body []byte
}

This allows you to inspect the raw JSON error body, including TSZ‑specific error codes such as tsz_content_blocked or tsz_output_blocked.