-
Notifications
You must be signed in to change notification settings - Fork 1
CLIENT_SDK_GUIDE
GitHub Actions edited this page Jan 2, 2026
·
1 revision
ThemisDB provides official client SDKs for Python, JavaScript/TypeScript, Go, and Rust, offering type-safe, idiomatic interfaces for LLM operations. All SDKs support both HTTP REST and gRPC binary protocols.
Authentication: All requests require Bearer Token authentication.
pip install themis-clientRequirements: Python 3.8+
npm install @themis/client
# or
yarn add @themis/clientRequirements: Node.js 16+
go get github.com/themis/go-clientRequirements: Go 1.19+
[dependencies]
themis-client = "0.1.0"Requirements: Rust 1.70+
from themis import ThemisClient
# Initialize client with Bearer Token
client = ThemisClient(
url="http://localhost:8080",
token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
)
# Or use gRPC for better performance
client = ThemisClient(
url="localhost:9090",
protocol="grpc",
token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
use_tls=True
)# Obtain token from login
from themis import authenticate
token = authenticate(
url="http://localhost:8080",
username="user",
password="password"
)
client = ThemisClient(url="http://localhost:8080", token=token)# Simple inference
response = client.llm.infer(
prompt="What is ThemisDB?",
model="mistral-7b",
lora="general-qa",
max_tokens=100,
temperature=0.7
)
print(response.text)
print(f"Tokens: {response.tokens_generated}")
print(f"Time: {response.inference_time_ms}ms")
print(f"Cache hit: {response.cache_hit}")# RAG with vector search
response = client.llm.rag(
query="What are the penalties for breach of contract?",
collection="legal_documents",
top_k=5,
similarity_threshold=0.8,
model="mistral-7b",
lora="legal-qa",
max_tokens=512
)
print(response.text)
print(f"Documents used: {response.documents_used}")
print(f"Retrieval time: {response.retrieval_time_ms}ms")# Stream tokens as generated
for token in client.llm.stream_infer(
prompt="Write a story about databases...",
model="mistral-7b",
max_tokens=500
):
print(token, end="", flush=True)
print() # Newline at endimport asyncio
async def main():
# Async client
client = ThemisClient(
url="http://localhost:8080",
token="your-token",
async_mode=True
)
# Concurrent requests
tasks = [
client.llm.infer(f"Query {i}", model="mistral-7b")
for i in range(10)
]
results = await asyncio.gather(*tasks)
for i, result in enumerate(results):
print(f"Result {i}: {result.text[:50]}...")
asyncio.run(main())# List models
models = client.llm.list_models()
for model in models:
print(f"{model.model_id}: {model.status} ({model.size_bytes / 1e9:.1f} GB)")
# Load model
client.llm.load_model(
model_id="mistral-7b",
path="/models/mistral-7b.gguf",
options={
"n_gpu_layers": 32,
"n_ctx": 4096
},
pin=False
)
# Unload model
client.llm.unload_model("mistral-7b")
# Get model info
info = client.llm.get_model_info("mistral-7b")
print(f"Status: {info.status}")
print(f"Memory: {info.memory_usage_mb} MB")
print(f"Usage: {info.usage_count} requests")# Upload model to blob storage
response = client.llm.ingest_model(
model_id="llama-3-8b",
source="/local/path/llama-3-8b.gguf",
version="v1.0",
replicate=True,
progress_callback=lambda pct: print(f"Upload: {pct}%")
)
print(f"URN: {response.urn}")
print(f"Checksum: {response.checksum}")
print(f"Replication: {response.shards_replicated}/{response.total_shards}")# List LoRAs
loras = client.llm.list_loras(base_model="mistral-7b")
for lora in loras:
print(f"{lora.lora_id}: {lora.status}")
# Load LoRA
client.llm.load_lora(
lora_id="legal-qa",
base_model="mistral-7b",
path="/loras/legal-qa.bin",
scale=1.0
)
# Unload LoRA
client.llm.unload_lora("legal-qa", base_model="mistral-7b")# Get LLM statistics
stats = client.llm.get_stats()
print(f"Throughput: {stats.throughput.requests_per_second:.1f} req/s")
print(f"Avg latency: {stats.latency.avg_ms:.1f}ms")
print(f"Active requests: {stats.active_requests}")
# Cache statistics
cache_stats = client.llm.get_cache_stats()
print(f"Response cache hit rate: {cache_stats.response_cache.hit_rate:.1%}")
print(f"Prefix cache hit rate: {cache_stats.prefix_cache.hit_rate:.1%}")
# Clear cache
client.llm.clear_cache("response") # or "prefix", "all"# Generate embedding
embedding = client.llm.embed(
text="Sample text for embedding",
model="mistral-7b",
normalize=True
)
print(f"Dimension: {len(embedding)}")
print(f"First values: {embedding[:5]}")from themis.exceptions import (
ModelNotFoundError,
InferenceError,
QueueFullError,
AuthenticationError
)
try:
response = client.llm.infer(
prompt="Test",
model="non-existent-model"
)
except ModelNotFoundError as e:
print(f"Model not found: {e}")
except InferenceError as e:
print(f"Inference failed: {e}")
except QueueFullError as e:
print(f"Queue full, retry after {e.retry_after_seconds}s")
except AuthenticationError as e:
print(f"Auth failed: {e}")import { ThemisClient } from '@themis/client';
// Initialize with Bearer Token
const client = new ThemisClient({
url: 'http://localhost:8080',
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
});
// Or use gRPC
const client = new ThemisClient({
url: 'localhost:9090',
protocol: 'grpc',
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
useTls: true
});import { authenticate } from '@themis/client';
// Obtain token
const token = await authenticate({
url: 'http://localhost:8080',
username: 'user',
password: 'password'
});
const client = new ThemisClient({ url: 'http://localhost:8080', token });// Simple inference
const response = await client.llm.infer({
prompt: 'What is ThemisDB?',
model: 'mistral-7b',
loraAdapter: 'general-qa',
maxTokens: 100,
temperature: 0.7
});
console.log(response.text);
console.log(`Tokens: ${response.tokensGenerated}`);
console.log(`Cache hit: ${response.cacheHit}`);const response = await client.llm.rag({
query: 'What are the contract provisions?',
collection: 'legal_documents',
topK: 5,
similarityThreshold: 0.8,
model: 'mistral-7b',
loraAdapter: 'legal-qa'
});
console.log(response.text);
console.log(`Documents: ${response.documentsUsed}`);// Stream tokens
const stream = client.llm.streamInfer({
prompt: 'Write a story...',
model: 'mistral-7b',
maxTokens: 500
});
for await (const token of stream) {
process.stdout.write(token);
}
console.log();// Process multiple requests in parallel
const prompts = ['Query 1', 'Query 2', 'Query 3'];
const results = await Promise.all(
prompts.map(prompt =>
client.llm.infer({
prompt,
model: 'mistral-7b'
})
)
);
results.forEach((result, i) => {
console.log(`Result ${i}: ${result.text}`);
});// List models
const models = await client.llm.listModels();
models.forEach(model => {
console.log(`${model.modelId}: ${model.status}`);
});
// Load model
await client.llm.loadModel({
modelId: 'mistral-7b',
path: '/models/mistral-7b.gguf',
options: {
nGpuLayers: 32,
nCtx: 4096
}
});
// Ingest model
const response = await client.llm.ingestModel({
modelId: 'llama-3-8b',
source: '/local/llama-3-8b.gguf',
version: 'v1.0',
replicate: true,
onProgress: (percent) => console.log(`Upload: ${percent}%`)
});import type {
InferenceRequest,
InferenceResponse,
RAGRequest,
ModelInfo,
LoRAInfo,
Statistics,
CacheStatistics
} from '@themis/client';
// Fully typed
const request: InferenceRequest = {
prompt: 'Test',
model: 'mistral-7b',
maxTokens: 100
};
const response: InferenceResponse = await client.llm.infer(request);package main
import (
"context"
"fmt"
"log"
themis "github.com/themis/go-client"
)
func main() {
// Initialize with Bearer Token
client, err := themis.NewClient(&themis.Config{
URL: "http://localhost:8080",
Token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
})
if err != nil {
log.Fatal(err)
}
defer client.Close()
// Or use gRPC
client, err := themis.NewClient(&themis.Config{
URL: "localhost:9090",
Protocol: themis.ProtocolGRPC,
Token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
UseTLS: true,
})
}// Obtain token
token, err := themis.Authenticate(context.Background(), &themis.AuthRequest{
URL: "http://localhost:8080",
Username: "user",
Password: "password",
})
if err != nil {
log.Fatal(err)
}
client, err := themis.NewClient(&themis.Config{
URL: "http://localhost:8080",
Token: token,
})ctx := context.Background()
response, err := client.LLM.Infer(ctx, &themis.InferenceRequest{
Prompt: "What is ThemisDB?",
Model: "mistral-7b",
LoraAdapter: "general-qa",
MaxTokens: 100,
Temperature: 0.7,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Response: %s\n", response.Text)
fmt.Printf("Tokens: %d\n", response.TokensGenerated)
fmt.Printf("Time: %dms\n", response.InferenceTimeMs)response, err := client.LLM.RAG(ctx, &themis.RAGRequest{
Query: "Contract provisions?",
Collection: "legal_documents",
TopK: 5,
SimilarityThreshold: 0.8,
Model: "mistral-7b",
LoraAdapter: "legal-qa",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Answer: %s\n", response.Text)
fmt.Printf("Documents used: %d\n", response.DocumentsUsed)stream, err := client.LLM.StreamInfer(ctx, &themis.InferenceRequest{
Prompt: "Write a story...",
Model: "mistral-7b",
MaxTokens: 500,
})
if err != nil {
log.Fatal(err)
}
for {
token, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
fmt.Print(token.Text)
}
fmt.Println()import "golang.org/x/sync/errgroup"
g, ctx := errgroup.WithContext(context.Background())
prompts := []string{"Query 1", "Query 2", "Query 3"}
results := make([]*themis.InferenceResponse, len(prompts))
for i, prompt := range prompts {
i, prompt := i, prompt // Capture loop vars
g.Go(func() error {
resp, err := client.LLM.Infer(ctx, &themis.InferenceRequest{
Prompt: prompt,
Model: "mistral-7b",
})
if err != nil {
return err
}
results[i] = resp
return nil
})
}
if err := g.Wait(); err != nil {
log.Fatal(err)
}
for i, result := range results {
fmt.Printf("Result %d: %s\n", i, result.Text)
}use themis_client::{ThemisClient, Config, Protocol};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize with Bearer Token
let client = ThemisClient::new(Config {
url: "http://localhost:8080".to_string(),
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...".to_string(),
..Default::default()
})?;
// Or use gRPC
let client = ThemisClient::new(Config {
url: "localhost:9090".to_string(),
protocol: Protocol::GRPC,
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...".to_string(),
use_tls: true,
..Default::default()
})?;
Ok(())
}use themis_client::InferenceRequest;
let response = client.llm().infer(InferenceRequest {
prompt: "What is ThemisDB?".to_string(),
model: "mistral-7b".to_string(),
lora_adapter: Some("general-qa".to_string()),
max_tokens: Some(100),
temperature: Some(0.7),
..Default::default()
}).await?;
println!("Response: {}", response.text);
println!("Tokens: {}", response.tokens_generated);
println!("Cache hit: {}", response.cache_hit);use themis_client::RAGRequest;
let response = client.llm().rag(RAGRequest {
query: "Contract provisions?".to_string(),
collection: "legal_documents".to_string(),
top_k: 5,
similarity_threshold: Some(0.8),
model: "mistral-7b".to_string(),
lora_adapter: Some("legal-qa".to_string()),
..Default::default()
}).await?;
println!("Answer: {}", response.text);
println!("Documents: {}", response.documents_used);use futures::StreamExt;
let mut stream = client.llm().stream_infer(InferenceRequest {
prompt: "Write a story...".to_string(),
model: "mistral-7b".to_string(),
max_tokens: Some(500),
..Default::default()
}).await?;
while let Some(token) = stream.next().await {
let token = token?;
print!("{}", token.text);
}
println!();use futures::future::join_all;
let prompts = vec!["Query 1", "Query 2", "Query 3"];
let futures: Vec<_> = prompts.iter().map(|&prompt| {
client.llm().infer(InferenceRequest {
prompt: prompt.to_string(),
model: "mistral-7b".to_string(),
..Default::default()
})
}).collect();
let results = join_all(futures).await;
for (i, result) in results.iter().enumerate() {
match result {
Ok(response) => println!("Result {}: {}", i, response.text),
Err(e) => eprintln!("Error {}: {}", i, e),
}
}# Refresh tokens automatically
from themis import ThemisClient, TokenRefreshError
client = ThemisClient(
url="http://localhost:8080",
token="initial-token",
auto_refresh=True,
refresh_callback=lambda: authenticate(url, user, pass)
)# Reuse client across requests
client = ThemisClient(url="...", token="...")
# Don't create new client per request
for i in range(1000):
response = client.llm.infer(...) # Good
# ThemisClient(...).llm.infer(...) # Badfrom themis import ThemisClient
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10)
)
def infer_with_retry(client, prompt):
return client.llm.infer(prompt=prompt, model="mistral-7b")
response = infer_with_retry(client, "What is ThemisDB?")import asyncio
async def process_batch(client, prompts):
tasks = [
client.llm.infer(prompt=p, model="mistral-7b")
for p in prompts
]
return await asyncio.gather(*tasks)
# Process 100 prompts concurrently
results = await process_batch(client, prompts)import time
def infer_with_metrics(client, prompt):
start = time.time()
try:
response = client.llm.infer(prompt=prompt, model="mistral-7b")
duration = time.time() - start
# Log metrics
print(f"Latency: {duration*1000:.1f}ms")
print(f"Tokens: {response.tokens_generated}")
print(f"Cache: {response.cache_hit}")
return response
except Exception as e:
duration = time.time() - start
print(f"Error after {duration*1000:.1f}ms: {e}")
raise| SDK | Protocol | Latency (p50) | Throughput | Notes |
|---|---|---|---|---|
| Python | HTTP | 25ms | 95 req/s | Good for scripts |
| Python | gRPC | 12ms | 180 req/s | 2x faster |
| JavaScript | HTTP | 28ms | 85 req/s | Node.js |
| JavaScript | gRPC | 14ms | 165 req/s | 2x faster |
| Go | HTTP | 18ms | 125 req/s | Compiled |
| Go | gRPC | 8ms | 245 req/s | 3x faster |
| Rust | HTTP | 15ms | 145 req/s | Compiled |
| Rust | gRPC | 7ms | 280 req/s | 4x faster |
Recommendation: Use gRPC for production, HTTP for development/scripts.
# Check token expiration
import jwt
token = "your-token"
payload = jwt.decode(token, options={"verify_signature": False})
print(f"Expires: {payload['exp']}")
# Refresh if expired
if time.time() > payload['exp']:
token = authenticate(...)
client = ThemisClient(url="...", token=token)# Test connection
try:
health = client.llm.get_health()
print(f"Status: {health.status}")
except ConnectionError as e:
print(f"Cannot connect: {e}")from themis.exceptions import RateLimitError
import time
try:
response = client.llm.infer(...)
except RateLimitError as e:
print(f"Rate limited, retry after {e.retry_after_seconds}s")
time.sleep(e.retry_after_seconds)
response = client.llm.infer(...) # RetryBefore (raw HTTP):
import requests
response = requests.post(
"http://localhost:8080/api/v1/llm/inference",
headers={"Authorization": "Bearer token"},
json={"prompt": "test", "model": "mistral-7b"}
).json()After (SDK):
from themis import ThemisClient
client = ThemisClient(url="http://localhost:8080", token="token")
response = client.llm.infer(prompt="test", model="mistral-7b")Before:
for prompt in prompts:
result = client.llm.infer(prompt=prompt, model="mistral-7b")
results.append(result)After:
async def main():
tasks = [client.llm.infer(prompt=p, model="mistral-7b") for p in prompts]
results = await asyncio.gather(*tasks)- Documentation: https://docs.themisdb.io/llm/sdk
- GitHub: https://github.com/themis/themis-client
- Issues: https://github.com/themis/themis-client/issues
- Discord: https://discord.gg/themisdb
- Architecture-ACCESS-MODEL-IMPLEMENTATION-SUMMARY
- Architecture-ADR-003-pg-dump-sql-parser
- Architecture-BASEENTITY-PRINCIPLE
- Architecture-CACHE-STORAGE-INTEGRATION
- Architecture-CMAKE-ARCHITECTURE
- Architecture-CMAKE-FLAGS-REFERENCE
- Architecture-CMAKE-MODULAR-ARCHITECTURE
- Architecture-CONCERNS-ARCHITECTURE-DIAGRAM
- Architecture-CONCERNS-IMPLEMENTATION-SUMMARY
- Architecture-CONTENT-MODEL
- Architecture-COPILOT-THEMISDB-GRAPH-RAG-BACKEND-ARCHITECTURE
- Architecture-CRYPTO-AND-KEYS
- Architecture-FEATURE-FLAGS-REFERENCE
- Architecture-GPU-ARCHITECTURE-REVIEW-TEMPLATE
- Architecture-HTTP-SHUTDOWN-HARDENING
- Architecture-MIGRATION-GUIDE-CONCERNS
- Architecture-MIGRATION-GUIDE-v13-v14
- Architecture-MODULARIZATION-GUIDE
- Architecture-MODULAR-ARCHITECTURE-ROADMAP
- Architecture-MODULE-ARCHITECTURE-INDEX
- Architecture-P1D01-ISSMPLUGIN-DESIGN-REVIEW
- Architecture-P1-D01-ISSMPLUGIN-DESIGN-REVIEW
- Architecture-P1-D08-MAMBA-GOVERNANCE-CONTRACT
- Architecture-P1-P2-IMPLEMENTATION-COMPLETION-INDEX
- Architecture-PHASE0-COMPLETION-ASSESSMENT
- Architecture-PHASE3-QUERYENGINE-DI-ARCHITECTURE
- Architecture-PHASE4-INDEX-MANAGER-DI
- Architecture-POSTGRESQL-WIRE-PROTOCOL
- Architecture-QUERYENGINE-IMPLEMENTATION-GUIDE
- Architecture-QUERY-SCHEDULING
- Architecture-RAFT-CONSENSUS-DESIGN
- Architecture-README
- Architecture-README-SSM-HYBRID-IMPLEMENTATION
- Architecture-REFACTORING-SUMMARY
- Architecture-RESOURCE-POOLING
- Architecture-SOURCE-DIRECTORY-GUIDE
- Architecture-THEMIS-CORE-GUIDE
- Architecture-UNIFIED-ACCESS-MODEL
- Architecture-WAL-GRPC-MTLS-CONFIGURATION
- Architecture-WIRE-PROTOCOL-RETRY
- Architecture-boltzmann-observability-draft
- Architecture-experimental-logarithmic-vector-storage
- Architecture-llm-wiki-mvp-adr
- Architecture-rewrite-engine-architecture
- Architecture-rope-api-architecture
- Architecture-ssm-gguf-mamba-status
- Architecture-ssm-hybrid-analysis
- Architecture-ssm-hybrid-rollout-plan
- Architecture-ssm-plugin-interface-design-review
- Architecture-transaction-coordinators
- Architecture-wiki-secondary-index
- Architecture-wire-protocol
- Governance-DISABLED-STUB-POLICY
- Governance-DOCS-PR-POLICY
- Governance-GA-PROMOTION-SIGN-OFF
- Governance-GITHUB-MILESTONES-SETUP
- Governance-MATURITY-CLAIM-VERIFICATION-CHECKLIST
- Governance-MATURITY-EVIDENCE-REGISTRY
- Governance-MERGE-GATE-BOT-CONFIG
- Governance-MERGE-GATE-STATUS-LIVE
- Governance-PHASE3-ENFORCEMENT-RUNBOOK
- Governance-PHASE-1-CLOSURE-REPORT
- Governance-PHASE-CLOSURE-POLICY
- Governance-PHASE-DEPENDENCY-GRAPH
- Governance-PLUGIN-SUBMODULE-ROLLBACK
- Governance-PRODUCTION-READY-2026-DELIVERY-PLAN
- Governance-PR-VERSION-TARGETING
- Governance-PR-VERSION-TARGETING-BACKFILL
- Governance-QUERY-MODULE-STATUS
- Governance-README
- Governance-RELEASE-PROMOTION-GATE-POLICY
- Governance-RELEASE-VALIDATION-CHECKLIST
- Governance-SECURITY-MODULE-5671-EVIDENCE-SUMMARY
- Governance-SHARDING-P6-RESIDUAL-RISK-ACCEPTANCE
- Governance-SOURCECODE-COMPLIANCE-GOVERNANCE
- Governance-UPDATES-DEVELOPMENT-STATUS-SIGN-OFF
- Governance-WAVE-C-IMPLEMENTATION-COMPLETE
- Module-acceleration-Roadmap
- Module-access-model-Roadmap
- Module-ai-Roadmap
- Module-analytics-Roadmap
- Module-api-Roadmap
- Module-aql-Roadmap
- Module-auth-Roadmap
- Module-base-Roadmap
- Module-cache-Roadmap
- Module-cdc-Roadmap
- Module-chaos-Roadmap
- Module-chimera-Roadmap
- Module-config-Roadmap
- Module-content-Roadmap
- Module-core-Roadmap
- Module-distributed-knowledge-Roadmap
- Module-distributed-tensor-Roadmap
- Module-document-Roadmap
- Module-ethics-ai-Roadmap
- Module-evaluation-Roadmap
- Module-execution-Roadmap
- Module-exporters-Roadmap
- Module-failover-Roadmap
- Module-geo-Roadmap
- Module-governance-Roadmap
- Module-gpu-Roadmap
- Module-graph-Roadmap
- Module-image-analysis-Roadmap
- Module-importers-Roadmap
- Module-index-Roadmap
- Module-ingestion-Roadmap
- Module-llama-cpp-Roadmap
- Module-llm-Roadmap
- Module-llm-streaming-Roadmap
- Module-llm-wiki-Roadmap
- Module-maintenance-Roadmap
- Module-metadata-Roadmap
- Module-network-Roadmap
- Module-observability-Roadmap
- Module-onnx-clip-Roadmap
- Module-performance-Roadmap
- Module-plugins-Roadmap
- Module-process-Roadmap
- Module-projects-Roadmap
- Module-prompt-engineering-Roadmap
- Module-query-Roadmap
- Module-rag-Roadmap
- Module-replication-Roadmap
- Module-retrieval-Roadmap
- Module-rpc-grpc-Roadmap
- Module-scheduler-Roadmap
- Module-scraper-Roadmap
- Module-search-Roadmap
- Module-security-Roadmap
- Module-server-Roadmap
- Module-sharding-Roadmap
- Module-stable-diffusion-Roadmap
- Module-storage-Roadmap
- Module-temporal-Roadmap
- Module-tensor-Roadmap
- Module-themis-Roadmap
- Module-timeseries-Roadmap
- Module-toolbox-Roadmap
- Module-training-Roadmap
- Module-transaction-Roadmap
- Module-updates-Roadmap
- Module-user-storage-encrypted-Roadmap
- Module-utils-Roadmap
- Module-vector-search-Roadmap
- Module-voice-Roadmap
- Module-whisper-Roadmap