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
Configand a singleClienttype
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-gois published as a Go module with the pathsource.example/thyris/safe-zone/pkg/tszclient-go. This module lives inside thethyris-szmonorepo, but can be consumed independently viago 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.moddemonstrating how to depend onsource.example/thyris/safe-zone - Calls both
/detectand the/v1/chat/completionsgateway 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)
}
BaseURLshould point to your TSZ instance (gateway or direct).APIKeyis optional. If set, the client sends:Authorization: Bearer <APIKey>X-ADMIN-KEY: <APIKey>(legacy compatibility)
HTTPClientis 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.