Public Integration SDKs (Go / Python / Node)¶
The sdks/ directory provides public integration SDKs in three languages (all Apache-2.0 licensed, separate from the platform's BiuMind Community License):
| Language | Package | Source | Requirements |
|---|---|---|---|
| Go | github.com/biumind/biumind/sdks/go (package name biumind) |
sdks/go/ |
Standard library only, Go 1.25+ |
| Python | biumind (0.1.0) |
sdks/python/ |
Standard library only, Python 3.9+ |
| Node | @biumind/sdk (0.1.0) |
sdks/node/ |
Built-in fetch only, Node 18+ |
The three SDKs cover two groups of capabilities:
- Model calls: via model-relay's Anthropic Messages-compatible endpoint (
POST /v1/messages), streaming and non-streaming. - Memory storage: via the Brain memory service (the
/v1/memoryfamily).
Authentication uniformly uses a Bearer JWT. Generate a PAT under "Settings → Virtual API Key" in the client and fill it into the Token field; see Developers overview.
When to use the SDKs, and when not to¶
- Use the SDKs: your code is already Go / Python / Node and you call model or memory APIs repeatedly — the SDKs handle the auth headers, timeouts, SSE stream parsing, and error type mapping for you.
- Use REST: other languages, or a one-off curl call — every endpoint is plain HTTP + JSON; just make requests directly. See API reference for endpoint details.
- Use MCP: you want an AI agent inside Claude Code or another MCP client to read and write your knowledge base and memory directly, with zero code. See API reference: MCP.
[!NOTE] The three SDKs deliberately keep their API surfaces isomorphic (one client mental model), but names and environment variables are not word-for-word identical — Go uses
RelayClient, Python / Node useHubClient; the environment variable prefixes also differ (see each language's section below). When mixing languages, consult the environment variable cross-reference.
Go SDK¶
Installation¶
Configuration¶
The Config struct (sdks/go/biumind.go):
type Config struct {
RelayURL string // model-relay base URL, required
BrainURL string // Brain base URL; defaults to RelayURL when empty
Token string // Bearer JWT (PAT)
Timeout time.Duration // defaults to 30 seconds when zero
// HTTPClient can inject a custom http.Client; when nil the SDK builds one per the rules above
HTTPClient *http.Client
}
Construct from environment variables:
Reads BIUMIND_MODEL_RELAY_URL, BIUMIND_TOKEN, BIUMIND_BRAIN_URL (optional). Missing either of the first two is an error. Timeout can only be set via the struct field; there is no corresponding environment variable.
Or construct explicitly:
cfg := biumind.Config{
RelayURL: "https://biumind.ai",
Token: "bm_pat_...",
Timeout: 60 * time.Second,
}
[!WARNING] The Go SDK's model-gateway environment variable is
BIUMIND_MODEL_RELAY_URL; the Python / Node SDKs useBIUMIND_HUB_URL— both point to the same model-relay service, but the names differ. Don't copy them blindly when porting scripts.
RelayClient (model calls)¶
Request body types:
type Message struct {
Role string `json:"role"`
Content string `json:"content"`
}
type MessagesRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
System string `json:"system,omitempty"`
MaxTokens int `json:"max_tokens,omitempty"`
Stream bool `json:"stream,omitempty"` // set by the two methods themselves; no need to fill in
Extra map[string]interface{} `json:"-"` // extra vendor-specific fields, merged into the JSON last
}
Non-streaming: Messages¶
Sends a non-streaming request and returns the raw JSON bytes — whether the upstream is Anthropic- or OpenAI-shaped is decided by server-side routing; the caller deserializes it.
relay := biumind.NewRelayClient(cfg)
raw, err := relay.Messages(ctx, biumind.MessagesRequest{
Model: "claude-sonnet-4-6",
MaxTokens: 1024,
Messages: []biumind.Message{{Role: "user", Content: "Why is the sky blue?"}},
})
if err != nil {
log.Fatal(err)
}
var resp struct {
Content []struct{ Text string `json:"text"` } `json:"content"`
}
_ = json.Unmarshal(raw, &resp)
fmt.Println(resp.Content[0].Text)
Streaming: MessagesStream¶
func (h *RelayClient) MessagesStream(ctx context.Context, req MessagesRequest) (<-chan string, <-chan error)
Returns two channels: the data channel emits only the text deltas of Anthropic-style content_block_delta / text_delta events; errors that occur mid-stream go to the error channel, and the data channel is closed when the stream ends. Cancelling ctx interrupts the stream.
chunks, errs := relay.MessagesStream(ctx, biumind.MessagesRequest{
Model: "claude-sonnet-4-6",
Messages: []biumind.Message{{Role: "user", Content: "Why is the sky blue?"}},
})
for chunk := range chunks {
fmt.Print(chunk)
}
if err := <-errs; err != nil {
log.Fatal(err)
}
MemoryClient (memory storage)¶
Data types:
type Memory struct { // one memory record; Score is populated only in recall results
ID string
ProjectID string
Kind string // "recall" | "preference" | "skill"
Content string
Salience float64 // salience, 0~1
CreatedAt time.Time
LastAccessedAt time.Time
Score *float64
}
type RecallResult struct {
Memories []Memory
Mode string // "hybrid" | "lexical" | "unknown"
Query string
}
Store — write one memory¶
func (m *MemoryClient) Store(ctx context.Context, projectID, content string, opts StoreOptions) (*Memory, error)
StoreOptions{ Kind string; Salience *float64 }. Kind defaults to "recall" when empty (the value must be recall / preference / skill, otherwise a local error is returned); when Salience is nil the server applies its default (currently 0.5).
mem := biumind.NewMemoryClient(cfg)
saved, err := mem.Store(ctx, "proj_x", "The user prefers dark mode", biumind.StoreOptions{
Kind: "preference",
Salience: ptr(0.8), // *float64
})
List — list recent memories¶
func (m *MemoryClient) List(ctx context.Context, projectID string, opts ListOptions) ([]Memory, error)
ListOptions{ Kind string; Limit int }; Limit defaults to 100 when <= 0.
Recall — hybrid retrieval¶
func (m *MemoryClient) Recall(ctx context.Context, projectID, query string, opts RecallOptions) (*RecallResult, error)
Hybrid lexical + semantic retrieval over the project's memories. RecallOptions{ Kind string; Limit int }; Limit defaults to 10 when <= 0; an empty query is an error.
r, _ := mem.Recall(ctx, "proj_x", "UI preferences", biumind.RecallOptions{})
for _, m := range r.Memories {
fmt.Println(*m.Score, m.Content)
}
Delete — delete by id¶
Error handling¶
The Go SDK uses a single *biumind.Error type for all HTTP failures (non-HTTP failures such as network errors are plain errors):
type Error struct {
Status int // HTTP status code
Body string // response body
RetryAfter time.Duration // the server's Retry-After hint; 0 when absent
}
func (e *Error) IsAuth() bool // 401 / 403
func (e *Error) IsRateLimit() bool // 429
func (e *Error) IsNotFound() bool // 404
var be *biumind.Error
if errors.As(err, &be) {
switch {
case be.IsRateLimit():
time.Sleep(be.RetryAfter)
case be.IsAuth():
// token expired or insufficient permissions; refresh credentials
case be.IsNotFound():
// resource does not exist
}
}
Python SDK¶
Installation¶
Zero third-party runtime dependencies (stdlib-only); can be installed directly in air-gapped environments.
Configuration¶
@dataclass(frozen=True)
class BiuMindConfig:
hub_url: str # required, model-relay base URL
token: str # required, Bearer JWT (PAT)
brain_url: str = "" # defaults to hub_url when empty
timeout: float = 30.0 # seconds
Construct from environment variables:
Reads BIUMIND_HUB_URL, BIUMIND_TOKEN, BIUMIND_BRAIN_URL (optional), BIUMIND_TIMEOUT (optional, seconds, default 30). Missing either of the first two raises ValueError.
from biumind import BiuMindConfig
cfg = BiuMindConfig.from_env()
# or construct explicitly:
cfg = BiuMindConfig(hub_url="https://biumind.ai", token="bm_pat_...", timeout=60.0)
HubClient (model calls)¶
The server routes upstream based on the model field in the request (claude-* to Anthropic, gpt-* to OpenAI, etc.); the caller does not specify the vendor.
Non-streaming: messages¶
def messages(
self,
*,
model: str,
messages: List[Dict[str, Any]],
system: Optional[str] = None,
max_tokens: int = 1024,
extra: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]
Sends a non-streaming request and returns the parsed full response dict. Key/value pairs in extra are merged into the request body (for passing vendor-specific parameters).
resp = hub.messages(
model="claude-sonnet-4-6",
messages=[{"role": "user", "content": "Why is the sky blue?"}],
)
print(resp["content"][0]["text"])
Streaming (text deltas only): messages_stream¶
def messages_stream(
self,
*,
model: str,
messages: List[Dict[str, Any]],
system: Optional[str] = None,
max_tokens: int = 1024,
extra: Optional[Dict[str, Any]] = None,
) -> Iterator[str]
A generator that yields the text deltas of content_block_delta / text_delta events one by one; other SSE event types are discarded.
for chunk in hub.messages_stream(
model="claude-sonnet-4-6",
messages=[{"role": "user", "content": "Why is the sky blue?"}],
):
print(chunk, end="", flush=True)
Streaming (full events): raw_stream¶
def raw_stream(
self,
*,
model: str,
messages: List[Dict[str, Any]],
system: Optional[str] = None,
max_tokens: int = 1024,
extra: Optional[Dict[str, Any]] = None,
) -> Iterator[Dict[str, Any]]
Same parameters as messages_stream, but yields every parsed full SSE event dict (including message_start, error events, etc.) — suited to callers who need usage statistics or tool-call events.
for event in hub.raw_stream(model="claude-sonnet-4-6",
messages=[{"role": "user", "content": "hi"}]):
if event.get("type") == "message_start":
print(event)
MemoryClient (memory storage)¶
Data types: Memory (fields id / project_id / kind / content / salience / created_at / last_accessed_at / score; times are parsed as datetime; score is populated only in recall results) and RecallResult (memories / mode / query).
store — write one memory¶
def store(
self,
*,
project_id: str,
content: str,
kind: str = "recall",
salience: Optional[float] = None,
) -> Memory
kind must be recall / preference / skill, otherwise ValueError is raised; when salience is None the server applies its default (currently 0.5).
list — list recent memories¶
recall — hybrid retrieval¶
def recall(
self,
*,
project_id: str,
q: str,
kind: Optional[str] = None,
limit: int = 10,
) -> RecallResult
Hybrid lexical + semantic retrieval; a blank q raises ValueError.
r = mem.recall(project_id="proj_x", q="UI preferences")
for m in r.memories:
print(m.score, m.content)
delete — delete by id¶
Error handling¶
A typed exception hierarchy, all inheriting from BiuMindError (carrying status and body attributes):
| Exception | Triggered by | Extra attributes |
|---|---|---|
AuthError |
401 / 403 | — |
RateLimitError |
429 | retry_after (seconds, float; 0.0 when the server gives no hint) |
NotFoundError |
404 | — |
BiuMindError |
other 4xx / 5xx | — |
import time
from biumind import RateLimitError, AuthError
try:
hub.messages(model="claude-sonnet-4-6", messages=[{"role": "user", "content": "hi"}])
except RateLimitError as e:
time.sleep(e.retry_after or 1)
except AuthError:
... # refresh the token
Node SDK¶
Installation¶
Zero runtime dependencies; uses Node 18+'s built-in fetch. ESM ("type": "module").
Configuration¶
export class BiuMindConfig {
constructor({ hubUrl, token, brainUrl = "", timeoutMs = 30_000 })
static fromEnv(env = process.env)
}
fromEnv() reads BIUMIND_HUB_URL, BIUMIND_TOKEN, BIUMIND_BRAIN_URL (optional), BIUMIND_TIMEOUT_MS (optional, milliseconds, default 30000). The constructor throws when hubUrl / token are missing.
import { BiuMindConfig, HubClient, MemoryClient } from "@biumind/sdk";
const cfg = BiuMindConfig.fromEnv();
// or construct explicitly:
const cfg = new BiuMindConfig({ hubUrl: "https://biumind.ai", token: "bm_pat_..." });
HubClient (model calls)¶
Non-streaming: messages¶
Returns the parsed full response object. Key/value pairs in extra are merged into the request body.
const resp = await hub.messages({
model: "claude-sonnet-4-6",
messages: [{ role: "user", content: "Why is the sky blue?" }],
});
console.log(resp.content[0].text);
Streaming (text deltas only): messagesStream¶
An async generator that yields only the content_block_delta / text_delta text deltas.
for await (const chunk of hub.messagesStream({
model: "claude-sonnet-4-6",
messages: [{ role: "user", content: "Why is the sky blue?" }],
})) {
process.stdout.write(chunk);
}
Streaming (full events): rawStream¶
Yields every parsed full SSE event object.
for await (const event of hub.rawStream({
model: "claude-sonnet-4-6",
messages: [{ role: "user", content: "hi" }],
})) {
if (event.type === "message_start") console.log(event);
}
MemoryClient (memory storage)¶
[!NOTE] The Node SDK's
Memoryobject converts the server's snake_case fields to camelCase (projectId/createdAt/lastAccessedAt), unlike the Go / Python SDKs which keep the original field names. Times areDateobjects (nullwhen absent).
store — write one memory¶
kind must be recall / preference / skill, otherwise an error is thrown.
const saved = await mem.store({ projectId: "proj_x", content: "The user prefers dark mode", kind: "preference" });
list — list recent memories¶
recall — hybrid retrieval¶
Returns { memories, mode, query }; a blank q throws.
const r = await mem.recall({ projectId: "proj_x", q: "UI preferences" });
for (const m of r.memories) console.log(m.score, m.content);
delete — delete by id¶
Error handling¶
A typed hierarchy isomorphic to the Python SDK's, all inheriting from BiuMindError (carrying status and body properties): AuthError (401 / 403), RateLimitError (429; retryAfter is in seconds), NotFoundError (404).
import { RateLimitError, AuthError } from "@biumind/sdk";
try {
await hub.messages({ model: "claude-sonnet-4-6", messages: [{ role: "user", content: "hi" }] });
} catch (e) {
if (e instanceof RateLimitError) {
await new Promise((r) => setTimeout(r, (e.retryAfter || 1) * 1000));
} else if (e instanceof AuthError) {
// refresh the token
} else {
throw e;
}
}
Environment variable cross-reference¶
| Purpose | Go | Python | Node |
|---|---|---|---|
| Model gateway address | BIUMIND_MODEL_RELAY_URL |
BIUMIND_HUB_URL |
BIUMIND_HUB_URL |
| Brain address (optional) | BIUMIND_BRAIN_URL |
BIUMIND_BRAIN_URL |
BIUMIND_BRAIN_URL |
| Credential | BIUMIND_TOKEN |
BIUMIND_TOKEN |
BIUMIND_TOKEN |
| Timeout (optional) | none (constructor field only) | BIUMIND_TIMEOUT (seconds) |
BIUMIND_TIMEOUT_MS (milliseconds) |
Behavioral differences across the three languages¶
| Dimension | Go | Python | Node |
|---|---|---|---|
| Client naming | RelayClient |
HubClient |
HubClient |
| Default timeout | 30 seconds | 30 seconds | 30000 milliseconds |
| Timeout semantics (non-streaming) | Covers the whole request (including reading the response body) | Covers each blocking socket operation | Covers only up to response-header arrival; reading the response body is unbounded |
| Timeout semantics (streaming) | No timeout (internally forces Timeout = 0; SSE is open-ended) |
Covers each socket read (effectively an idle timeout) | Covers only up to response-header arrival; reading the stream is unbounded |
| Automatic retry | None | None | None |
| Non-streaming return | Raw []byte; the caller deserializes |
Parsed dict |
Parsed object |
| Full SSE event stream | Not available | raw_stream |
rawStream |
max_tokens default |
Field not sent (server decides) | 1024 | 1024 |
| Memory field naming | snake_case (project_id, etc.) |
snake_case | camelCase (projectId, etc.) |
| Memory time type | time.Time |
datetime (falls back to epoch on parse failure) |
Date (null when absent) |
Error on invalid kind |
Returns an error | Raises ValueError |
Throws a plain Error |
[!WARNING] None of the three SDKs retries automatically: on a 429 rate limit, wait yourself (based on the error object's
RetryAfter/retry_after/retryAfter, all in seconds) and resend.Go's streaming calls have no timeout protection — a long-hanging stream relies on the caller's
context.Contextfor cancellation; in production, consider settingcfg.HTTPClientor usingcontext.WithTimeoutas a backstop.
Licensing and boundaries¶
sdks/is Apache-2.0 and freely usable in commercial integrations; the platform itself (apps/,services/,workers/,packages/) is under the BiuMind Community License — see Developers overview.- The SDKs cover only the two API groups: model calls and memory storage. Knowledge base, Wiki, graph, and the other capabilities go through the REST API — see API reference.