Skip to main content

AML LLM OpenAI Server

An OpenAI-compatible API server for Amlogic LLM devices, with built-in tool-calling evaluation and Agent capability benchmarking tools.

  • Server: OpenAI-compatible /v1/chat/completions endpoint (ADLA & llama.cpp backends)
  • Tools Evaluation: Measure tool-calling accuracy of local vs cloud models
  • Agent Benchmark: Evaluate LLM agent capabilities on MMLU-Pro, TAU2-Bench, and BFCL

Prerequisites

This project is using uv please use following command or follow the official document to install uv

curl -LsSf https://astral.sh/uv/install.sh | sh

Install the wheel

uv venv --python 3.11
uv pip install amlllm-openai-server -i <index url>

Bootstrap

Use a bootstrap tool

You need Internet connection to use bootstrap scripts to download the models or you can manually put files in folder

  1. List the available model
uv run -m amlllm_openai_server.bootstrap list

You will see the available models as following table

# Name Type Thinking Multimodal Context Tools
0 Qwen3-1.7B Qwen3 Yes No 8192 Yes
1 Qwen3-4B-Instruct-2507 Qwen3 No No 8192 Yes
2 Qwen3-4B Qwen3 Yes No 4096 Yes
  1. Download the model

Replace the model name as your expectation. Here use Qwen3-4B-Instruct-2507 for example.

uv run -m amlllm_openai_server.bootstrap download Qwen3-4B-Instruct-2507

Server Configuration

config/server.yaml

server:
  host: "0.0.0.0"
  port: 8000
  api_key: ""          # optional, set for authentication
  log_level: "info"
  title: "AMLLLM OneAPI Proxy"
  version: "0.2.0"

models:
  root_dir: "../models"
  enabled:
    - default           # model directories under root_dir

# Optional: upstream proxy to a cloud API
# upstream:
#   base_url: "https://api.openai.com"
#   api_key: "sk-..."
#   model: "gpt-4o"
#   force_upstream: false
#   skill_injection: false

models/default/model.json

Example for Qwen3 Seriers:

{
  "id": "amlogic-default",
  "weights": "Qwen3-1.7B-Q4AM_PB32.adla",
  "model_type": "qwen",
  "sampling_mode": "top_p",
  "sampler_params": {"temp": 0.7, "top_p": 0.85, "top_k": 40, "penalty_repeat": 1.15},
  "system_prompt": "You are a helpful assistant.",
  "prompt_prefix": "",
  "prompt_postfix": "",
  "retain_history": true,
  "loglevel": "ERROR",
  "metadata": {
    "family": "default",
    "device": "amlogic"
  },
  "chat_format": "{%- if tools %}\n    ... (Qwen3 tool-aware Jinja2 template) ...\n"
}

The example above uses legacy top-level sampling keys for brevity. The preferred form is a single sampler_params dict, e.g. "sampler_params": {"temp": 0.7, "top_p": 0.85, "top_k": 40, "penalty_repeat": 1.15}.

Model Configuration Parameters

Parameter Type Required Default Description
id string Yes — Unique model identifier, used as model in OpenAI API requests
weights string Yes — Path to the model weights file (.adla for ADLA, .gguf for llama.cpp)
backend string No "adla" Runtime backend: "adla" (Amlogic ADLA, AARCH64 only) or "llama_cpp"
model_type string Yes — Model architecture type: "qwen", "llama", "gemma", etc. Determines chat template and tokenization
sampling_mode string No "chain_sampler" Sampling strategy: "arg_max" (a.k.a. "max" / greedy), "top_p", "top_k", or "chain_sampler" (a.k.a. "chain", configurable via sampler_params)
sampler_params dict No {} Preferred sampler configuration dict (e.g. {"temp": 0.7, "top_p": 0.85, "top_k": 40, "penalty_repeat": 1.15}). Serialized to JSON and passed as init_extend.sampler_params_json; per-run overrides are merged at request time. See Sampler parameters
top_k int No 40 Legacy Top-K (auto-converted to sampler_params.top_k when sampler_params is absent)
top_p float No 0.85 Legacy Top-P (auto-converted to sampler_params.top_p when sampler_params is absent)
temperature float No 0.7 Legacy temperature (auto-converted to sampler_params.temp when sampler_params is absent)
repeat_penalty float No 1.15 Legacy repeat penalty (auto-converted to sampler_params.penalty_repeat)
presence_penalty float No 0 Legacy presence penalty (auto-converted to sampler_params.penalty_present)
system_prompt string No "" Default system prompt prepended to every conversation
prompt_prefix string No "" Custom text prepended before the prompt
prompt_postfix string No "" Custom text appended after the prompt
stop list[string] No [] Stop sequences that halt generation
retain_history bool No true Whether to keep conversation history across turns (enables KV-cache continuation)
loglevel string No "ERROR" ADLA runtime log level: "DEBUG", "INFO", "WARNING", "ERROR"
context_size int No 4096 Model context window size (tokens)
metadata dict No {} Arbitrary key-value metadata exposed in /v1/models response
token_pair dict No {} Special token ID mapping, e.g. {"151657": 151658} maps <tool_call> → </tool_call>, the token MUST appear in pair, as 151657 ->xxx -> 151658, otherwise, the generation will be stopped.
chat_format string No "" Jinja2 chat template string or path to tokenizer_config.json. When set, the template is rendered in Python (disable_chat_template=1). When empty, the raw OpenAI messages JSON is handed to the SDK (AML_LLM_INPUT_MESSAGES, disable_chat_template=0) and the SDK renders its built-in template. See Jinja2 Chat Templates
template_kwargs dict No {} Extra parameters passed to the Jinja2 chat template (e.g. {"enable_thinking": true})
mmproj string No "" Path to the mmproj (vision/projector) model for VLM (multimodal) support
image_pad string No "<|image_pad|>" Placeholder in the prompt marking where image content is inserted

Sampler parameters

The sampler_params dict is the preferred way to configure sampling. It is serialized to JSON and passed to the SDK as init_extend.sampler_params_json; per-request overrides (temperature, top_p, and other recognized keys) are merged into a per-run sampler_params_json with sampling_config_valid=1. Recognized keys include (among others): temp, top_p, top_k, min_p, typ_p, penalty_repeat, penalty_present, penalty_freq, penalty_last_n, mirostat, mirostat_tau, mirostat_eta, seed, samplers, grammar_gbnf. Legacy top-level keys (top_k, top_p, temperature, repeat_penalty, presence_penalty) are auto-converted into this dict only when sampler_params is absent.

Jinja2 Chat Templates

The chat_format field uses Jinja2 syntax or a tokenizer config to define how messages are formatted into the model's native prompt structure. Two rendering modes are supported, selected automatically from chat_format:

  • chat_format set (Jinja template string or path to tokenizer_config.json): the template is rendered in Python (disable_chat_template=1). Tool schemas are merged into the template by the renderer, and only the rendered prompt is sent to the SDK. KV-cache continuation sends only the delta (new) messages.
  • chat_format empty: the raw OpenAI messages JSON is passed to the SDK with AML_LLM_INPUT_MESSAGES and disable_chat_template=0; the SDK renders its built-in Jinja template. Tool schemas are passed separately as run_extend.tools_schemas.
  1. Prepare the model tokenizer_config.json
  2. For Qwen series, it is possible to put the Jinja template string directly
  3. For other series, put the path to tokenizer_config.json

Tool calling

Tool calls are detected by the SDK's PEG tool-call parser and delivered through the on_tool_call callback (registered automatically at init, mirroring on_token). Parsed calls are the primary source; if the SDK yields none, the server falls back to parsing tool-call markup from the generated text. No extra configuration is required beyond passing tools in the OpenAI request — the schemas are forwarded to the SDK as run_extend.tools_schemas.


Running the Server

# Default: reads config/server.yaml
uv run python -m amlllm_openai_server

# Custom config path
uv run python -m amlllm_openai_server --config /path/to/server.yaml

# Override host/port/log-level
uv run python -m amlllm_openai_server --host 0.0.0.0 --port 8000 --log-level debug

Test the endpoint:

curl http://localhost:8000/v1/models
curl http://localhost:8000/healthz

If you want to install PicoClaw

dpkg -i picoclaw_modified_aarch64.deb

Run demo with PicoClaw

  1. Start the AMLLLM server (see above)
  2. Start PicoClaw:
    picoclaw-launcher &
    
  3. Configure PicoClaw model:
    • Provider: openai
    • Model identifier: amlogic-default
    • API Base URL: http://localhost:8000/v1

Tips: The agent work folder is under $HOME/.picoclaw/workspace. If you are using local models with context limitations, it's better to remove AGENTS.md and SOUL.md.


Chat Completion API Usage Examples

Python (using the openai package)

from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:8000/v1",
    api_key="not-needed",  # optional, set only if configured in server.yaml
)

# Basic text completion
response = client.chat.completions.create(
    model="amlogic-default",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Hello! What is the capital of France?"},
    ],
    temperature=0.7,
    max_tokens=512,
)
print(response.choices[0].message.content)


# Streaming completion
stream = client.chat.completions.create(
    model="amlogic-default",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Write a short poem about AI."},
    ],
    temperature=0.8,
    max_tokens=256,
    stream=True,
)
for chunk in stream:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
print()


# Tool / function calling
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get the current weather for a city",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "City name, e.g. 'Beijing'",
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                    },
                },
                "required": ["location"],
            },
        },
    }
]

response = client.chat.completions.create(
    model="amlogic-default",
    messages=[
        {"role": "user", "content": "What is the weather like in Shanghai?"},
    ],
    tools=tools,
    temperature=0.1,
    max_tokens=512,
)
message = response.choices[0].message
if message.tool_calls:
    for tool_call in message.tool_calls:
        print(f"Call: {tool_call.function.name}({tool_call.function.arguments})")
else:
    print(message.content)

JavaScript / TypeScript (using the openai npm package)

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "http://localhost:8000/v1",
  apiKey: "not-needed",
});

// Basic text completion
async function basicCompletion() {
  const response = await client.chat.completions.create({
    model: "amlogic-default",
    messages: [
      { role: "system", content: "You are a helpful assistant." },
      { role: "user", content: "Hello! What is the capital of France?" },
    ],
    temperature: 0.7,
    max_tokens: 512,
  });
  console.log(response.choices[0].message.content);
}

// Streaming completion
async function streamCompletion() {
  const stream = await client.chat.completions.create({
    model: "amlogic-default",
    messages: [
      { role: "system", content: "You are a helpful assistant." },
      { role: "user", content: "Write a short poem about AI." },
    ],
    temperature: 0.8,
    max_tokens: 256,
    stream: true,
  });
  for await (const chunk of stream) {
    process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
  }
  console.log();
}

// Tool / function calling
async function toolCalling() {
  const response = await client.chat.completions.create({
    model: "amlogic-default",
    messages: [
      { role: "user", content: "What is the weather like in Shanghai?" },
    ],
    tools: [
      {
        type: "function",
        function: {
          name: "get_weather",
          description: "Get the current weather for a city",
          parameters: {
            type: "object",
            properties: {
              location: {
                type: "string",
                description: "City name, e.g. 'Beijing'",
              },
              unit: {
                type: "string",
                enum: ["celsius", "fahrenheit"],
              },
            },
            required: ["location"],
          },
        },
      },
    ],
    temperature: 0.1,
    max_tokens: 512,
  });
  const message = response.choices[0].message;
  if (message.tool_calls) {
    for (const toolCall of message.tool_calls) {
      console.log(
        `Call: ${toolCall.function.name}(${toolCall.function.arguments})`,
      );
    }
  } else {
    console.log(message.content);
  }
}

// Run all examples
basicCompletion();
streamCompletion();
toolCalling();

Metadata

Release files for amlllm-openai-server 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for amlllm-openai-server 0.2.0
File
amlllm_openai_server-0.2.0-cp314-cp314-manylinux_2_28_x86_64.whl CPython 3.14 CPython 3.14 Linux glibc 2.28+ x86-64 Details
amlllm_openai_server-0.2.0-cp313-cp313-manylinux_2_34_aarch64.whl CPython 3.13 CPython 3.13 Linux glibc 2.34+ ARM64 Details
amlllm_openai_server-0.2.0-cp313-cp313-manylinux_2_28_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.28+ x86-64 Details
amlllm_openai_server-0.2.0-cp312-cp312-manylinux_2_34_aarch64.whl CPython 3.12 CPython 3.12 Linux glibc 2.34+ ARM64 Details
amlllm_openai_server-0.2.0-cp312-cp312-manylinux_2_28_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.28+ x86-64 Details
amlllm_openai_server-0.2.0-cp311-cp311-manylinux_2_34_aarch64.whl CPython 3.11 CPython 3.11 Linux glibc 2.34+ ARM64 Details
amlllm_openai_server-0.2.0-cp311-cp311-manylinux_2_28_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.28+ x86-64 Details
amlllm_openai_server-0.2.0-cp310-cp310-manylinux_2_28_x86_64.whl CPython 3.10 CPython 3.10 Linux glibc 2.28+ x86-64 Details

Total release size: 15.9 MB

Release files / amlllm_openai_server-0.2.0-cp314-cp314-manylinux_2_28_x86_64.whl

Download URL amlllm_openai_server-0.2.0-cp314-cp314-manylinux_2_28_x86_64.whl
Size 2.2 MB
Tags CPython 3.14 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
a2e8e81d01ee2edd6c3c2602b8ef08350f2fe7a2ee06f28b40cd9c0a1ef41583
BLAKE2b-256 checksum
How to use checksums
16c0364033a76108a5d77434301a12b2fb7d451835d39adc2447bda75fb7dd17
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / amlllm_openai_server-0.2.0-cp313-cp313-manylinux_2_34_aarch64.whl

Download URL amlllm_openai_server-0.2.0-cp313-cp313-manylinux_2_34_aarch64.whl
Size 1.6 MB
Tags CPython 3.13 Linux glibc 2.34+ ARM64
SHA-256 checksum
How to use checksums
6352550a6519cf3667558d5830d5f2c7c2f308769a7133fac77911497a07afce
BLAKE2b-256 checksum
How to use checksums
920539808818bb6f1691b457579e50638b0722344a020f0528b5373deec24496
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / amlllm_openai_server-0.2.0-cp313-cp313-manylinux_2_28_x86_64.whl

Download URL amlllm_openai_server-0.2.0-cp313-cp313-manylinux_2_28_x86_64.whl
Size 2.2 MB
Tags CPython 3.13 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
0f79c0df795214f5ef8dd1ed62e860a8f9e7efc68ba31b02b6b525b0e2ff7de5
BLAKE2b-256 checksum
How to use checksums
f44efca7fb27f121d162cf32f2e63a80ee1d39b53515e75ca6c492d3ae775c6d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / amlllm_openai_server-0.2.0-cp312-cp312-manylinux_2_34_aarch64.whl

Download URL amlllm_openai_server-0.2.0-cp312-cp312-manylinux_2_34_aarch64.whl
Size 1.6 MB
Tags CPython 3.12 Linux glibc 2.34+ ARM64
SHA-256 checksum
How to use checksums
3992795d67a06b9ac54021db1f367e99b6a94339c98a4492053ed4ebd067e802
BLAKE2b-256 checksum
How to use checksums
acf5385bb298a8cc2c92ec435bc6483369a83d0d459ff5f23f8d88c4309fe5cc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / amlllm_openai_server-0.2.0-cp312-cp312-manylinux_2_28_x86_64.whl

Download URL amlllm_openai_server-0.2.0-cp312-cp312-manylinux_2_28_x86_64.whl
Size 2.2 MB
Tags CPython 3.12 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
73059c6105e7f8658aca16e3301ffaedefc4db7bfb30b6fe03dc0b34b8c3f75e
BLAKE2b-256 checksum
How to use checksums
d975922ba3a3dd4e444d25a2a6d61422b4cbca914d83f36cdb95bbda5e136fc0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / amlllm_openai_server-0.2.0-cp311-cp311-manylinux_2_34_aarch64.whl

Download URL amlllm_openai_server-0.2.0-cp311-cp311-manylinux_2_34_aarch64.whl
Size 1.6 MB
Tags CPython 3.11 Linux glibc 2.34+ ARM64
SHA-256 checksum
How to use checksums
c5c9b5ee27f31b04a69d468b81f97d00ae23c45e467cebc4d8643b4821003700
BLAKE2b-256 checksum
How to use checksums
af1db169888db1f0eab2e575f0171a749367e8c9bfa8bb65b14b4f0257546611
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / amlllm_openai_server-0.2.0-cp311-cp311-manylinux_2_28_x86_64.whl

Download URL amlllm_openai_server-0.2.0-cp311-cp311-manylinux_2_28_x86_64.whl
Size 2.2 MB
Tags CPython 3.11 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
d70c9145baaa7ca9549f08746d8680cbf7344d75f0f8b5266ced7edeb2293c8c
BLAKE2b-256 checksum
How to use checksums
0d80cc5cad9888025545694a48fb95726f832987fd1ead0aa1737bf08efb0b98
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / amlllm_openai_server-0.2.0-cp310-cp310-manylinux_2_28_x86_64.whl

Download URL amlllm_openai_server-0.2.0-cp310-cp310-manylinux_2_28_x86_64.whl
Size 2.2 MB
Tags CPython 3.10 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
83b60966609e04e50eafa560311ddb20b3c23278430ee68eccd8f3ae7a5c09e3
BLAKE2b-256 checksum
How to use checksums
df70f51ea636dd35bda65e8fd7fd235f4aebb55a1716e8d0905b2f30e706ed4e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.2.1

8 release files

This release

0.2.0 This release

8 release files

0.1.1

7 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page