Skip to main content

Python client for the LayerScale inference server

Project description

layerscale

Python client for the LayerScale inference server.

Install

pip install layerscale

Get a license key

You need a LayerScale license key to authenticate. Grab a free one at layerscale.ai/get-license — it takes about 10 seconds.

Usage

from layerscale import LayerScale

client = LayerScale(
    "http://localhost:8080",
    api_key="LS-...",  # or set LAYERSCALE_LICENSE_KEY env var
)

Complete example

End-to-end: create a session, push a few OHLCV candles, wait for them to be processed, and ask the model about the data.

import os
from layerscale import LayerScale

client = LayerScale(
    "http://localhost:8080",
    api_key=os.environ.get("LAYERSCALE_LICENSE_KEY"),
)

# 1. Create a session with a system prompt and freeze it in the cache
#    so it is not reprocessed on every incoming data update.
session = client.sessions.create(
    type="ohlcv",
    prompt=(
        "You are a real-time market analyst. You receive live OHLCV candles "
        "and answer questions about market direction in a single word when possible."
    ),
    context_size=4096,
    mark_prefix=True,
)

# 2. Push a few OHLCV candles and wait for them to be decoded into the cache.
#    For a reactive alternative, iterate `client.sessions.events()` or open a
#    WebSocket via `client.sessions.stream()` instead of using `wait=True`.
candles = [
    {"o": 150.20, "h": 150.80, "l": 150.10, "c": 150.70, "v": 1_200_000, "timestamp": 1_733_000_000, "sym": "AAPL"},
    {"o": 150.70, "h": 151.10, "l": 150.60, "c": 150.95, "v":   900_000, "timestamp": 1_733_000_060, "sym": "AAPL"},
    {"o": 150.95, "h": 151.40, "l": 150.90, "c": 151.30, "v": 1_500_000, "timestamp": 1_733_000_120, "sym": "AAPL"},
    {"o": 151.30, "h": 151.80, "l": 151.20, "c": 151.75, "v": 1_100_000, "timestamp": 1_733_000_180, "sym": "AAPL"},
    {"o": 151.75, "h": 152.10, "l": 151.60, "c": 152.00, "v": 1_300_000, "timestamp": 1_733_000_240, "sym": "AAPL"},
]
client.sessions.push(session.session_id, candles, wait=True)

# 3. Query the model about the data we just ingested.
answer = client.sessions.query(
    session.session_id,
    prompt="Is the market trending up or down?",
    max_tokens=8,
    fast_answer=True,
)
print("Answer:", answer.text.strip())

client.sessions.delete(session.session_id)

Async client

import asyncio
from layerscale import AsyncLayerScale

async def main():
    async with AsyncLayerScale("http://localhost:8080") as client:
        session = await client.sessions.create(type="ohlcv", prompt="...", mark_prefix=True)
        await client.sessions.push(session.session_id, candles, wait=True)
        answer = await client.sessions.query(session.session_id, prompt="Trend?")
        print(answer.text)
        await client.sessions.delete(session.session_id)

asyncio.run(main())

OpenAI-compatible chat

chat = client.chat.create(
    messages=[{"role": "user", "content": "Hello"}],
)
print(chat.choices[0].message.content)

Anthropic-compatible messages

msg = client.messages.create(
    messages=[{"role": "user", "content": "Hello"}],
    max_tokens=256,
)
for block in msg.content:
    if block.type == "text":
        print(block.text)

Streaming

# Chat completions
with client.chat.stream(messages=[{"role": "user", "content": "Tell me a joke"}]) as stream:
    for chunk in stream:
        delta = chunk.choices[0].delta.content
        if delta:
            print(delta, end="", flush=True)

# Anthropic-style messages
with client.messages.stream(
    messages=[{"role": "user", "content": "Tell me a joke"}],
    max_tokens=256,
) as stream:
    for event in stream:
        if event.type == "content_block_delta" and event.delta.type == "text_delta":
            print(event.delta.text, end="", flush=True)

Sessions

session = client.sessions.create(
    type="ohlcv",
    prompt="You are a trading analyst.",
    flash=[{"query": "Is the trend bullish?"}, {"query": "Is volume increasing?"}],
    mark_prefix=True,
)

answer = client.sessions.query(
    session.session_id,
    prompt="What is the trend?",
    max_tokens=256,
)

# Stream generation token-by-token
with client.sessions.query_stream(session.session_id, max_tokens=256) as stream:
    for chunk in stream:
        if not chunk.done:
            print(chunk.token, end="", flush=True)

Session management

sessions = client.sessions.list()
state = client.sessions.get(session.session_id)
client.sessions.delete(session.session_id)

Continuous streaming

Push OHLCV data into the lock-free ring buffer for background processing:

client.sessions.push(session.session_id, [
    {"o": 150.5, "h": 151.0, "l": 150.0, "c": 150.75, "v": 1_000_000, "timestamp": 1704067200, "sym": "AAPL"},
])

status = client.sessions.stream_status(session.session_id)
stats  = client.sessions.stats(session.session_id)

Flash queries

Register questions that are automatically evaluated in the background after each data update. Answers are cached for near-instant retrieval:

registered = client.sessions.flash(session.session_id, "Is the trend bullish?")
# optional `max_tokens` caps the answer length (default 32):
client.sessions.flash(session.session_id, "Summarise the trend", max_tokens=64)

queries = client.sessions.list_flash(session.session_id)
client.sessions.unflash(session.session_id, registered.id)

Session events (SSE)

with client.sessions.events(session.session_id) as events:
    for event in events:
        if event.type == "flash_ready":
            print(event.query, event.value, event.confidence)
        elif event.type == "data_updated":
            print("new data version:", event.data_version)

WebSocket streaming

A WebSocket combines data-push and event subscription on one connection. It does not auto-reconnect — wrap it in your own backoff loop for long-lived consumers:

from layerscale import WsFlashReady, WsDataUpdated

with client.sessions.stream(session.session_id) as socket:
    socket.push([candle])
    for event in socket:
        if isinstance(event, WsFlashReady):
            print(event.data.query, event.data.value)
        elif isinstance(event, WsDataUpdated):
            print("version:", event.data.data_version)

Cancellation and timeouts

The default request timeout is 10 minutes. Override it on the client or per-request:

client = LayerScale("http://localhost:8080", timeout=30.0)  # 30s default

# Per-request override
answer = client.sessions.query(session_id, prompt="...")  # uses client default

Error handling

from layerscale import APIStatusError, LayerScaleError

try:
    client.sessions.query(session_id, max_tokens=256)
except APIStatusError as exc:
    print(exc.status_code, exc.message)
except LayerScaleError as exc:
    print(exc)

API

Method Endpoint Description
health.check() GET /v1/health Check whether the server is ready.
models.list() GET /v1/models List loaded models.
chat.create(**params) POST /v1/chat/completions OpenAI-compatible chat completion.
chat.stream(**params) POST /v1/chat/completions (SSE) Stream chat chunks.
completions.create(**params) POST /v1/completions OpenAI-compatible legacy completion.
messages.create(**params) POST /v1/messages Anthropic-compatible messages API.
messages.stream(**params) POST /v1/messages (SSE) Stream messages events.
sessions.create(**params) POST /v1/sessions/init Create a streaming session.
sessions.list() GET /v1/sessions List all active sessions.
sessions.get(id) GET /v1/sessions/:id/state Inspect a session's state.
sessions.delete(id) DELETE /v1/sessions/:id Delete a session.
sessions.append(id, text) POST /v1/sessions/:id/append Append raw text to context.
sessions.slots(id) GET /v1/sessions/:id/slots List cache slots.
sessions.query(id, **params) POST /v1/sessions/:id/generate Run a generation.
sessions.query_stream(id, **params) POST /v1/sessions/:id/generate (SSE) Stream generation token-by-token.
sessions.mark_prefix(id) POST /v1/sessions/:id/mark_prefix Freeze cache prefix.
sessions.push(id, data, wait=?) POST /v1/sessions/:id/stream/push Push streaming data.
sessions.stream_status(id) GET /v1/sessions/:id/stream/status Processor status.
sessions.stats(id) GET /v1/sessions/:id/stats Computed statistics.
sessions.flash(id, query, max_tokens=?) POST /v1/sessions/:id/flash Register a flash query.
sessions.list_flash(id) GET /v1/sessions/:id/flash List flash queries.
sessions.unflash(id, query_id) DELETE /v1/sessions/:id/flash/:query_id Remove a flash query.
sessions.events(id) GET /v1/sessions/:id/events (SSE) Subscribe to session events.
sessions.stream(id) WS /v1/sessions/:id/ws Bidirectional WebSocket.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

layerscale-0.2.0.tar.gz (18.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

layerscale-0.2.0-py3-none-any.whl (27.9 kB view details)

Uploaded Python 3

File details

Details for the file layerscale-0.2.0.tar.gz.

File metadata

  • Download URL: layerscale-0.2.0.tar.gz
  • Upload date:
  • Size: 18.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for layerscale-0.2.0.tar.gz
Algorithm Hash digest
SHA256 1d14de9f60c769b144f79e1b44cf454541a960bc427bd6d02610624a6281b487
MD5 cfdc8baf795ddeec899ca3f302c6ed19
BLAKE2b-256 b110509270a33042788fb3b465d3e7187c34771d0f11c75c4cb1ac7e971c24d1

See more details on using hashes here.

File details

Details for the file layerscale-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: layerscale-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 27.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for layerscale-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6348d3bb49a127761e677701f4fb252d3aeb3892473feada6a48ad8317e6d9ad
MD5 5ee26cc95a07cd2a812747448e37b450
BLAKE2b-256 bf8084d5e74f19ad134d3e383492dfbe65b6d26bcca3f4671921e6a1eff7fa6b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page