Skip to main content

LLMterface

LLMterface is under active development. APIs may change and the README may lag slightly behind implementation. Until v1.0, minor releases may introduce breaking changes.


A small, opinionated, provider-agnostic interface for working with LLMs in Python.

LLMterface focuses on one thing: submitting prompts to AI providers and getting back structured, validated responses.

No agents. No chains. No workflows. No hidden control flow.

If you want a thin abstraction layer you can understand in one sitting, this is for you.

Why LLMterface?

LLMterface exists to solve a narrow problem: sending prompts to LLMs and getting validated responses while avoiding vendor lock-in and minimizing complexity.

It intentionally avoids orchestration, memory, agents, and workflow abstractions so those concerns remain explicit and application-owned. This makes LLMterface easy to reason about, debug, and integrate alongside other tools rather than replacing them.

Goals

  • Minimal: A small, readable interface with very few moving parts.
  • Generic: Universal configuration primitives that map cleanly to provider defaults, with optional provider-specific overrides.
  • Unobtrusive: Designed to coexist with other LLM libraries without forcing architectural decisions.
  • Extensible: Providers can be added via entry points without modifying core code.

Non-Goals

If you want any of the following, build them on top of LLMterface or use a different library:

  • Agent orchestration
  • Tool calling frameworks
  • Prompt chains or workflow systems
  • Long-term memory systems

Installation

pip install llmterface

Provider Specific installation

pip install llmterface[gemini]
pip install llmterface[openai]  # TODO
pip install llmterface[openai,anthropic]  # TODO
# or to install all currently supported providers
pip install llmterface[all]

Basic Usage

import llmterface as llm

handler = llm.LLMterface(
    config=llm.GenericConfig(
        api_key="<YOUR GEMINI API KEY>",
        provider="gemini",
    )
)
res = handler.ask(
    "how many LLMs does it take to screw in a lightbulb? Explain your reasoning."
)
print(res)
# -> “Depends. One to do it, five to argue about alignment, and twelve to hallucinate that the room is already bright.”

Basic Configuration

configuration of the handler is done through the GenericConfig class which can be supplied at three levels:

  1. Handler
  2. Chat
  3. Question

Overrides apply in that order, with the most specific configuration winning.

import llmterface as llm
import llmterface_gemini as gemini
from functools import partial

gemini_config = partial(
    llm.GenericConfig,
    provider=gemini.GeminiConfig.PROVIDER,
    api_key="<YOUR GEMINI API KEY>",
)
handler_config = gemini_config(response_model=int)
chat_config = gemini_config(response_model=float)
handler = llm.LLMterface(config=handler_config)
chat_id = handler.create_chat(chat_config.provider, config=chat_config)

Q = "What is the airspeed velocity of an unladen swallow?"
question = llm.Question(
    question=Q,
    config=gemini_config() # response_model defaults to str
)
int_res = handler.ask(Q)
print(int_res, type(int_res))
# 42 <class 'int'>

float_res = handler.ask(Q, chat_id=chat_id)
print(float_res, type(float_res))
# 42.0 <class 'float'>

str_res = handler.ask(question, chat_id=chat_id)
print(str_res, type(str_res))
# african or european swallow? <class 'str'>

Provider-specific overrides

If you need access to vendor-specific features, you can supply provider overrides explicitly.

import llmterface as llm
import llmterface_gemini as gemini

gemini_override = gemini.GeminiConfig(
    api_key="<YOUR GEMINI API KEY>",
    model=gemini.GeminiTextModelType.CHAT_2_0_FLASH_LITE,
)

config = llm.GenericConfig(
    provider=gemini.GeminiConfig.PROVIDER,
    provider_overrides={
        gemini.GeminiConfig.PROVIDER: gemini_override
    },
)

handler = llm.LLMterface(config=config)

res = handler.ask("How many LLMs does it take to screw in a lightbulb?")
print(res)
# -> 6

Structured Responses

LLMterface is designed to work naturally with Pydantic models.

from pydantic import BaseModel, Field
import llmterface as llm

class WeatherResponse(BaseModel):
    temperature_c: float = Field(..., description="Temperature in Celsius")
    condition: str = Field(
        ..., description="Weather described in a silly way"
    )

question = llm.Question(
    question="What is the current weather in Paris?",
    config=llm.GenericConfig(
        api_key="<YOUR GEMINI API KEY>",
        provider="gemini",
        response_model=WeatherResponse,
    ),
)

res = llm.LLMterface().ask(question)

assert isinstance(res, WeatherResponse)
print(res.temperature_c)
# -> 12.0
print(res.condition)
# -> 'Sunny with a chance of croissants'

Key Objects

LLMterface is built around a small set of core objects.


Question[TRes: AllowedResponseTypes](BaseModel):

A Question represents a single prompt submission to an LLM, along with optional configuration, retry behavior, and response typing.

At its simplest, a Question is just text:

import llmterface as llm
question = llm.Question(
    question="What is the answer to life, the universe, and everything?"
)

But Question is also generic over the expected response type:

import llmterface as llm
question = llm.Question[llm.simple_answers.SimpleInteger](
    question="What is 6 * 7?",
    config=llm.GenericConfig(
        provider="gemini",
        api_key="<YOUR GEMINI API KEY>",
        response_model=int)
)

This allows LLMterface to validate and return structured responses automatically.


Prompt normalization

Before submission, the question text is normalized via get_question():

  • Dedented
  • Stripped of leading/trailing whitespace

This makes multiline prompts predictable and easy to format. You can override this behavior by subclassing Question.


Retry behavior

Question defines how retries are handled, not the client.

Retries occur when:

  • The provider errors

  • The response fails schema validation

The on_retry() method can be overridden to implement custom retry behavior, including modifying the prompt, incorporating the previous response, or stopping retries entirely.

By default, schema validation failures cause the prompt to be augmented with a strict formatting reminder and the previous response content.


GenericConfig[TRes: AllowedResponseTypes = str](BaseModel)

GenericConfig is the provider-agnostic configuration model used throughout LLMterface.

It defines a common set of fields that map cleanly onto most LLM providers. The goal is that your application code can stay stable even if you switch providers, because you configure intent (model tier, temperature, response model), not vendor-specific knobs.

If a field is not supported by a given provider, it is simply ignored by that provider integration.

If vendor-specific configuration is required, it can be supplied via provider_overrides.


Example

import llmterface as llm
import llmterface_gemini as gemini

config = llm.GenericConfig(
    provider="gemini",
    api_key="<YOUR API KEY>",
    model=llm.GenericModelType.text_lite,
    temperature=0.2,
    response_model=float,
    provider_overrides={
        "gemini": gemini.GeminiConfig(
            api_key="<YOUR GEMINI API KEY>",
            # provider-specific fields here
        )
    },
)

Override behavior

Configurations in LLMterface are not merged field-by-field.

When one configuration takes precedence over another, it fully replaces the lower-precedence configuration at that level.

For example:

  • A question-level config completely overrides a chat-level config
  • A chat-level config completely overrides a handler-level config

This keeps configuration resolution simple, explicit, and predictable.


Provider-specific configs

Vendor-specific configuration objects inherit from:

llmterface.providers.provider_config.ProviderConfig

Provider configs may expose additional fields beyond GenericConfig. These are only interpreted by the corresponding provider integration and are ignored elsewhere.

This design allows provider integrations to evolve independently without leaking provider-specific concerns into application code.

Metadata

Release files for llmterface-gemini 0.2.1

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

Source distribution (sdist)

Source distribution for llmterface-gemini 0.2.1
File Size Uploaded Provenance
llmterface_gemini-0.2.1.tar.gz 8.9 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for llmterface-gemini 0.2.1
File Interpreter ABI Platform
llmterface_gemini-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 18.3 kB

Release files / llmterface_gemini-0.2.1.tar.gz

Download URL llmterface_gemini-0.2.1.tar.gz
Size 8.9 kB
Tags Source
SHA-256 checksum
How to use checksums
cbaa092378a52b7c4619fc2fa8b25a12ad811e2f3614aac27439dc134a0d2f21
BLAKE2b-256 checksum
How to use checksums
ea3d55fa78e7b6d690878203494246c5f9490fc8c0f4551816a7ca98f450418d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 18, 2026.

Transparency log

Release files / llmterface_gemini-0.2.1-py3-none-any.whl

Download URL llmterface_gemini-0.2.1-py3-none-any.whl
Size 9.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4e5ee872db60253afd80382b2bc37a1191f84b9150cec2cd96a077667376e453
BLAKE2b-256 checksum
How to use checksums
89c34a7b33ec3d5448457a123edd99f0897e6ac17fdc5192cd643df9aa0489f3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 18, 2026.

Transparency log

Security for llmterface-gemini 0.2.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for llmterface_gemini-0.2.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 18, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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