Skip to content

Client libraries

Matricxon speaks plain HTTP and JSON, so any HTTP client works. The examples below are small, ready-to-copy clients for Python, Node.js and Go. All endpoints and options are in the API reference.

Every example reads the server address from MATRICXON_URL, or uses http://localhost:8420.

Needs Python 3.10 or newer and requests:

Terminal window
pip install requests
import json
import os
import requests
class Matricxon:
"""A small client for the Matricxon API."""
def __init__(self, url: str | None = None) -> None:
self.url = (url or os.environ.get("MATRICXON_URL", "http://localhost:8420")).rstrip("/")
def chat(self, model: str, messages: list[dict], **options):
"""Yields the reply piece by piece."""
body = {"model": model, "messages": messages, "options": options}
with requests.post(f"{self.url}/api/chat", json=body, stream=True, timeout=600) as r:
r.raise_for_status()
for line in r.iter_lines():
if line:
yield json.loads(line)["message"]["content"]
def embed(self, model: str, texts: list[str]) -> list[list[float]]:
r = requests.post(f"{self.url}/api/embed", json={"model": model, "input": texts}, timeout=600)
r.raise_for_status()
return r.json()["embeddings"]
mx = Matricxon()
model = "hf.co/unsloth/Llama-3.2-3B-Instruct-GGUF:Llama-3.2-3B-Instruct-Q3_K_M"
for piece in mx.chat(model, [{"role": "user", "content": "What is a GGUF file?"}], temperature=0.7):
print(piece, end="", flush=True)

Needs Node.js 18 or newer, which has fetch built in. No packages to install. Save it as matricxon.mjs:

export class Matricxon {
constructor(url = process.env.MATRICXON_URL ?? 'http://localhost:8420') {
this.url = url.replace(/\/$/, '');
}
// Yields the reply piece by piece.
async *chat(model, messages, options = {}) {
const res = await fetch(`${this.url}/api/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ model, messages, options }),
});
if (!res.ok) throw new Error(`Matricxon: ${res.status} ${await res.text()}`);
const decoder = new TextDecoder();
let buffer = '';
for await (const chunk of res.body) {
buffer += decoder.decode(chunk, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop();
for (const line of lines) if (line.trim()) yield JSON.parse(line).message.content;
}
}
async embed(model, texts) {
const res = await fetch(`${this.url}/api/embed`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ model, input: texts }),
});
if (!res.ok) throw new Error(`Matricxon: ${res.status} ${await res.text()}`);
return (await res.json()).embeddings;
}
}
import { Matricxon } from './matricxon.mjs';
const mx = new Matricxon();
const model = 'hf.co/unsloth/Llama-3.2-3B-Instruct-GGUF:Llama-3.2-3B-Instruct-Q3_K_M';
for await (const piece of mx.chat(model, [{ role: 'user', content: 'What is a GGUF file?' }], { temperature: 0.7 })) {
process.stdout.write(piece);
}

Needs Go 1.23 or newer (for range-over-func iterators). Only the standard library, no packages to install. Save it as matricxon/matricxon.go inside your module:

// Package matricxon is a small client for the Matricxon API.
package matricxon
import (
"bufio"
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"iter"
"net/http"
"os"
"strings"
)
type Message struct {
Role string `json:"role"`
Content string `json:"content"`
}
type Client struct {
URL string
HTTP *http.Client
}
// New reads the server address from MATRICXON_URL, or uses http://localhost:8420.
func New() *Client {
url := os.Getenv("MATRICXON_URL")
if url == "" {
url = "http://localhost:8420"
}
return &Client{URL: strings.TrimRight(url, "/"), HTTP: http.DefaultClient}
}
func (c *Client) post(ctx context.Context, path string, body any) (*http.Response, error) {
data, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.URL+path, bytes.NewReader(data))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
res, err := c.HTTP.Do(req)
if err != nil {
return nil, err
}
if res.StatusCode != http.StatusOK {
defer res.Body.Close()
text, _ := io.ReadAll(res.Body)
return nil, fmt.Errorf("matricxon: %s %s", res.Status, text)
}
return res, nil
}
// Chat yields the reply piece by piece.
func (c *Client) Chat(ctx context.Context, model string, messages []Message, options map[string]any) iter.Seq2[string, error] {
return func(yield func(string, error) bool) {
res, err := c.post(ctx, "/api/chat", map[string]any{"model": model, "messages": messages, "options": options})
if err != nil {
yield("", err)
return
}
defer res.Body.Close()
lines := bufio.NewScanner(res.Body)
lines.Buffer(make([]byte, 64*1024), 4*1024*1024) // the last line carries the timings
for lines.Scan() {
if len(bytes.TrimSpace(lines.Bytes())) == 0 {
continue
}
var part struct {
Message Message `json:"message"`
}
if err := json.Unmarshal(lines.Bytes(), &part); err != nil {
yield("", err)
return
}
if !yield(part.Message.Content, nil) {
return
}
}
if err := lines.Err(); err != nil {
yield("", err)
}
}
}
func (c *Client) Embed(ctx context.Context, model string, texts []string) ([][]float64, error) {
res, err := c.post(ctx, "/api/embed", map[string]any{"model": model, "input": texts})
if err != nil {
return nil, err
}
defer res.Body.Close()
var out struct {
Embeddings [][]float64 `json:"embeddings"`
}
return out.Embeddings, json.NewDecoder(res.Body).Decode(&out)
}

Then call it from your program. Change example.com/app to your own module path:

package main
import (
"context"
"fmt"
"log"
"example.com/app/matricxon"
)
func main() {
mx := matricxon.New()
model := "hf.co/unsloth/Llama-3.2-3B-Instruct-GGUF:Llama-3.2-3B-Instruct-Q3_K_M"
messages := []matricxon.Message{{Role: "user", Content: "What is a GGUF file?"}}
for piece, err := range mx.Chat(context.Background(), model, messages, map[string]any{"temperature": 0.7}) {
if err != nil {
log.Fatal(err)
}
fmt.Print(piece)
}
fmt.Println()
}
  • Replies always stream, one JSON object per line. The last line has "done": true and the timings.
  • A model that is not loaded yet loads on the first call, which can take a while on a CPU. Keep the timeout long.
  • Admin routes need a signed request. See Admin routes and the admin secret.