Skip to main content

Language Model Development Kit.

Project description

Language Model Development Kit

What it offers:

  • Simplest interface to call different Language Model APIs
  • Minimal dependencies: HTTP requests only, no third party packages
  • Streaming
  • Comfy structured outputs via Pydantic models, only if the provider / model supports it natively
  • Parallel completions
  • Unified HTTP error handling
  • Easy location config (for providers with multiple datacenters like AWS Bedrock, GCP Vertex and Azure)
  • Model fallbacks
  • Bring Your Own Key (for each provider)

What it does NOT offer:

  • Tools / function calling / MCP
  • Agents
  • Multimodality (only text-in, text-out)
  • Shady under-the-hood prompt modification (e.g. to force structured output)
  • API gateways

If you are looking for a more constrained but out-of-the-box agent interface, I'd recommend pydantic-ai or haystack-ai. If you are looking to keep granular control but extend on tools or multimodality, I'd recommend litellm or leveraging the OpenAI-compatible endpoints that providers normally set up. If you want a unified a token for all providers and are willing to give away telemetry data, check Gateways like openrouter.

Install

uv add lmdk

Usage

from lmdk import complete

model = "mistral:mistral-small-2603"
# supports locations as in "vertex:gemini-2.5-flash@europe-west4"
Single prompt
response = complete(model=model, prompt="Tell me a joke")
Multi-turn conversation
messages = [
    UserMessage("My name is Alice."),
    AssistantMessage("Nice to meet you, Alice!"),
    UserMessage("What is my name?"),
]
response = complete(model=model, prompt=messages)
System prompt and generation kwargs
response = complete(
    model=model,
    prompt="Hi!",
    system_instruction="Talk like a pirate",
    generation_kwargs={"temperature": 0.9, "max_tokens": 10}
)
Streaming
token_iter = complete(model=model, prompt="Count from 1 to 5.", stream=True)
Model fallbacks
response = complete(model=["mistral:nonexistent-model", model], prompt="Hi")
# first request will raise NotFoundError bc model does not exist, second will work
Structured output
class Ingredient(BaseModel):
    name: str
    quantity: int
    unit: str = ""

class Recipe(BaseModel):
    ingredients: list[Ingredient]

response = complete(model=model, prompt="How do I make cheescake?", output_schema=Recipe)
# response.parsed will have a Recipe instance
Parallel calls
from lmdk import complete_batch

results = complete_batch(model=model, prompt_list=["Greet in english", "Saluda en espanyol."])
# results will be al list of CompletionResult
Template Rendering
from lmdk import render_template

# Render a template string with variables
result = render_template(
    template="Hello, {{ name }}!",
    name="World"
)
# Output: "Hello, World!"

# Render a template from a jinja file
result = render_template(
    path="path/to/template.jinja2",
    name="World"
)

Development

Structure

src/lmdk/
├── core.py         # Entry points: complete, complete_batch
├── datatypes.py    # Common message and response schemas
├── provider.py     # Base Provider class and registry
├── providers/      # Concrete implementations (Mistral, Vertex, etc.)
├── errors.py       # Unified HTTP and API error handling
└── utils.py        # Shared helper functions

Tooling

We use just for development tasks. Use:

  • just sync: Updates lockfile and syncs environment.
  • just format: Lints and formats with ruff.
  • just check-types: Static analysis with ty.
  • just analyze-complexity: Cyclomatic complexity checks with complexipy.
  • just test: Runs pytest with 90% coverage threshold.

Contribute

  1. Hooks: Install pre-commit hooks via just install-hooks. PRs will fail CI if linting/formatting is not applied.
  2. Issues: Open an issue first using the default template.
  3. PRs: Link your PR to the relevant issue using the PR template.

You can use just validate <model> (runs example.py) to verify which features run properly and which do not for a new provider / model. Not all of them have to pass to open a PR: some providers do not even support native structured output. Do at least the normal non-structured, non-streamed completion. The rest can raise NotImplementedError.

License

MIT

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

lmdk-1.3.0.tar.gz (32.2 kB view details)

Uploaded Source

Built Distribution

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

lmdk-1.3.0-py3-none-any.whl (18.4 kB view details)

Uploaded Python 3

File details

Details for the file lmdk-1.3.0.tar.gz.

File metadata

  • Download URL: lmdk-1.3.0.tar.gz
  • Upload date:
  • Size: 32.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.0 {"installer":{"name":"uv","version":"0.11.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for lmdk-1.3.0.tar.gz
Algorithm Hash digest
SHA256 89578f95fba44b9bfc9c4a8f57ebfbffc96662d17a0c037141e4d4edf766e50d
MD5 d50660fad828e28ca07d0317f8788af1
BLAKE2b-256 e41c87de4b511c4ee030cf930a9b6f6745fbf7a8ef01ede8018c947c8892aabf

See more details on using hashes here.

File details

Details for the file lmdk-1.3.0-py3-none-any.whl.

File metadata

  • Download URL: lmdk-1.3.0-py3-none-any.whl
  • Upload date:
  • Size: 18.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.0 {"installer":{"name":"uv","version":"0.11.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for lmdk-1.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 25205d59e0143342a8d5ab83177c04dba336ce85188d3c9298f05b79ae1f6ce5
MD5 5944fa9ccc9074db935e68e96369a1f1
BLAKE2b-256 e5004c7dc52ee257ebfd7dfe06b45772d209d09770f735ca90e31bd79b0dba69

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