inference-link
Trusted, application-scoped connections to self-hosted AI services.
inference-link is a small client-side connection layer for tools that share a
private LLM, speech-to-text, or text-to-speech server. It lets a user verify a
server once, keeps tokens and model choices separate between applications, and
can expose the trusted connection as a temporary OpenAI-compatible localhost
endpoint.
The project does not run models and is not a provider-neutral AI SDK. It owns connection onboarding, server identity, local state, and lightweight sync clients for a documented HTTP contract.
Install
python -m pip install inference-link
Python 3.9 or newer is required.
Why use it?
- Trust on first use. HTTPS servers may use a self-signed certificate. On first contact, the user verifies its SHA-256 fingerprint; later connections are pinned to that certificate.
- Application scopes. Each integrating tool can inherit the default connection or keep its own server, token, and model selection.
- No hand-written connection file. Onboarding and model selection are exposed as CLI actions that applications can embed.
- OpenAI-compatible bridge. Existing programs can run through a temporary
authenticated relay on
127.0.0.1without receiving the upstream token. - Small synchronous clients. Python callers can use built-in LLM, STT, and TTS clients, including backend health checks and failover for profile-based configurations.
Connect from the command line
A bare address means HTTPS on port 8090:
inference-link connect 192.168.0.90
inference-link set-token TOKEN # only when the server requires authentication
inference-link list-models
inference-link set-model my-model
inference-link status
The first HTTPS connection prints the server certificate fingerprint and asks for confirmation. Verify that fingerprint through a separate trusted channel before accepting it.
Plain HTTP must be requested explicitly:
inference-link connect http://127.0.0.1:8090
HTTP has no authenticated server identity, so the command displays a warning.
Run an OpenAI-compatible application
run starts a relay on a random loopback port and exports OPENAI_API_KEY,
OPENAI_BASE_URL, and OPENAI_MODEL to the child process:
inference-link run -- my-openai-cli
inference-link --app code-review run -- my-openai-cli --model-from-env
The relay accepts only its generated bearer token, forwards requests to the trusted upstream connection, and stops when the child exits. The child sees the relay token, not the upstream token.
Embed connection actions in another CLI
Applications can expose the same onboarding flow under their own command:
import argparse
import inference_link as nl
parser = argparse.ArgumentParser()
nl.add_connection_arguments(parser)
args = parser.parse_args()
if nl.handle_connection_action(args, command="my-tool"):
raise SystemExit(0)
with nl.llm(tool="my-tool") as client:
response = client.chat([
{"role": "user", "content": "Hello"},
])
print(response["choices"][0]["message"]["content"])
The application inherits the default connection until the user connects that application to another server or changes one of its settings.
Python clients
The connection-state API provides synchronous clients:
import inference_link as nl
with nl.llm(tool="my-tool") as client:
response = client.chat([{"role": "user", "content": "Hello"}])
with nl.stt(tool="voice-input") as client:
transcript = client.transcribe(open("audio.raw", "rb").read())
with nl.tts(tool="voice-output") as client:
audio = client.synthesize("Hello")
Existing profile configurations remain available through the name= and
config= arguments. Profiles can contain an ordered backend list and are the
API to use when client-side health probing and failover are required.
Migrating from nemor-link
The distribution, import package, and primary executable were renamed in version 0.2.0:
nemor-link -> inference-link
nemor_link -> inference_link
The old executable and Python import remain as deprecated compatibility
aliases. Existing connection state under ~/.config/nemor-link/state.json is
read when the new state file does not exist; the next state-changing command
writes to ~/.config/inference-link/state.json. Legacy profile configuration
at ~/.config/llm.json is also used as a fallback for the new
~/.config/inference-link/profiles.json path.
Server contract
The current clients expect a server with these endpoints:
GET /v1/capabilitiesfor public connection capabilities; older servers may expose onlyGET /health;GET /v1/modelsfor model selection;POST /v1/chat/completionsfor OpenAI-compatible chat completions;POST /sttfor raw audio transcription;POST /tts/generatefor speech synthesis.
Authentication uses Authorization: Bearer .... A server may additionally
advertise whether authentication is required through its capabilities
response. STT runtimes and prompts currently use dedicated request headers.
STT initial_prompt uses the following version-1 header contract:
- printable ASCII is sent unchanged as
X-Initial-Promptfor compatibility; - other Unicode text is UTF-8 encoded, then standard-Base64 encoded, and sent
as
X-Initial-Prompt-Encoded: v1:<base64>; - a request containing both prompt headers, an unknown version, invalid Base64, or invalid UTF-8 is malformed and should receive a 400 response.
Servers can use the bundled strict decoder before passing the result to their speech runtime:
from inference_link import InitialPromptEncodingError, decode_initial_prompt
try:
initial_prompt = decode_initial_prompt(request.headers)
except InitialPromptEncodingError:
return {"error": "invalid initial prompt"}, 400
Security model
- HTTPS identity uses trust on first use, not a public certificate authority. The first fingerprint confirmation is therefore security-sensitive.
- A changed certificate is rejected until the user explicitly reconnects and trusts the new identity.
- Connection state is stored atomically under the platform configuration
directory (
~/.config/inference-link/state.jsonon typical Linux systems). On POSIX, newly written state files use mode0600and their directory is created with mode0700. - Tokens are stored in that state file; they are not placed in an operating system keychain. Avoid passing long-lived tokens on a shared machine where process arguments or shell history are observable.
- Explicit HTTP connections provide transport but no server authentication or confidentiality.
Current limitations
- The API is synchronous.
- The server HTTP contract above is currently required; arbitrary model runtimes are not discovered or started by the client.
Releasing
Build and validate distributions locally:
./tools/make.sh
The publishing helper mirrors the release tools used by the other projects in this workspace and accepts either package index:
./tools/publish.sh testpypi
./tools/publish.sh pypi
License
MIT. See LICENSE.
Metadata
Release files for inference-link 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| inference_link-0.2.0.tar.gz | 31.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| inference_link-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 59.6 kB
Release files / inference_link-0.2.0.tar.gz
| Download URL | inference_link-0.2.0.tar.gz |
|---|---|
| Size | 31.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
eab11a094442dc00d04816c4668567c1d5e7c7278622ea2d7203aa651cdfb5b2
|
|
BLAKE2b-256 checksum How to use checksums |
f30bd08a1976450e55f006987d280461c894253d12ea3645c06150b255d92db1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.19
|
Release files / inference_link-0.2.0-py3-none-any.whl
| Download URL | inference_link-0.2.0-py3-none-any.whl |
|---|---|
| Size | 28.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
58f89e30787416060fcd74e7c5186f3333510aaf198298ecd3db4419a553375f
|
|
BLAKE2b-256 checksum How to use checksums |
512ef22248f966a2c00caa8b5712e25bde2427f0b99d27f8aa588f1babd1851e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.19
|