Human language meets Python. Write AI-powered scripts in plain English.
Project description
Lamia - AI Native language
Write AI-powered scripts in plain English.
Lamia extends Python with human-readable syntax for AI commands, web automation, and file operations. Write what you want in plain English - Lamia handles the LLM calls, validates the output, and returns structured data.
How it guarantees results: every command runs through a built-in validator. If the output doesn't match the expected format or schema, Lamia retries automatically across a configurable model chain — escalating to the next model until it passes or the chain is exhausted. You define the contract once; Lamia enforces it on every run.
- Get your expected results in HTML, JSON, CSV, XML, YAML Markdown formats back
- Web automation with automatic data extraction into Pydantic models
- Multi-model support: OpenAI, Anthropic, Ollama (and extensible)
- Model evaluation to find the cheapest model that still passes validation
Installation
pip install lamia-lang
Quick Start
Create a .lm file and run it with lamia your_script.lm:
# Ask AI and create a login from using our model
page = "Create a login form" -> HTML[LoginForm]
# Read a local file as typed JSON
config = "./config.json" -> JSON[OnlyTheConfigsWeNeed]
# Scrape a website into a Pydantic model
quote = "https://finance.yahoo.com/quote/AAPL" -> HTML[StockQuote]
A minimal real-world example - extract stock quotes from Yahoo Finance into a CSV:
class StockQuote(BaseModel):
ticker: str = Field(description="Stock ticker symbol, e.g. AAPL")
open: float = Field(description="Open price from the Quote Summary section")
bid: str = Field(description="Bid price from the Quote Summary section")
ask: str = Field(description="Ask price from the Quote Summary section")
bid_size: int = Field(description="Bid size (number of lots) from the Quote Summary")
ask_size: int = Field(description="Ask size (number of lots) from the Quote Summary")
for ticker in ["QQQ", "VOO", "VGT"]:
"extract the stock quote data from https://finance.yahoo.com/quote/{ticker}" -> File(CSV[StockQuote], "stocks.csv", append=True)
For more real-world examples, you can check the Lamia Examples repository.
Running from Python
Lamia can be used as a Python library as well.
from lamia import Lamia
lamia = Lamia()
ai_response = lamia.run(
"Create a login form",
"openai:gpt4o",
"anthropic:claude",
return_type=HTML[LoginForm]
)
Using Lamia Claude Pro or Max Subscription
Currently, Lamia supports only 3 LLM providers: OpenAI, Anthropic, and Ollama (local models). But you can easily extend it to support other providers by creating a new adapter by extending the BaseLLMAdapter class and placing it in the extensions/adapters directory in the root of the project.
For more information see the Implementing a new Adapter section of the Lamia LLM Adapters documentation.
Here is a ready to use adapter for Claude Pro or Max subscriptions. Just place it in the extensions/adapters/llm directory in the root of your Lamia project.
IMPORTANT: Using this llm adapter might result your account being banned by Anthropic. This is just an example showing how you can have your own LLM adapter (not supported by Lamia).
and add the following to your config.yaml file:
model_chain:
- name: "claude-max:claude-sonnet-4"
max_retries: 3
"""
Adapter for anthropic-max-router local proxy.
Routes requests through anthropic-max-router
(https://github.com/nsxdavid/anthropic-max-router) — an OpenAI-compatible
endpoint backed by Anthropic's Claude API via OAuth.
Works with Claude Pro ($20/mo) and Max ($100/$200/mo) subscriptions
for flat-rate billing instead of pay-per-token.
The router stores its OAuth tokens in .oauth-tokens.json relative to the
working directory, so all commands below use ~ as a stable anchor.
"""
import logging
from typing import Optional, Type
import aiohttp
from pydantic import BaseModel
from lamia.adapters.llm.base import BaseLLMAdapter, LLMResponse, make_strict_schema
from lamia import LLMModel
logger = logging.getLogger(__name__)
DEFAULT_BASE_URL = "http://127.0.0.1:3000"
class ClaudeMaxAdapter(BaseLLMAdapter):
"""Adapter for anthropic-max-router using the native Anthropic endpoint."""
@classmethod
def name(cls) -> str:
return "claude-max"
@classmethod
def env_var_names(cls) -> list[str]:
return [] # No env variables like API key names needed
@classmethod
def is_remote(cls) -> bool:
return False
@property
def supports_structured_output(self) -> bool:
return True
def __init__(self, base_url: str = DEFAULT_BASE_URL):
self.base_url = base_url.rstrip("/")
self.session: Optional[aiohttp.ClientSession] = None
async def async_initialize(self) -> None:
if self.session is None:
self.session = aiohttp.ClientSession(
headers={"Content-Type": "application/json"},
timeout=aiohttp.ClientTimeout(total=600),
)
async def generate(
self,
prompt: str,
model: LLMModel,
response_model: Optional[Type[BaseModel]] = None,
) -> LLMResponse:
if self.session is None:
await self.async_initialize()
assert self.session is not None
model_name = model.get_model_name_without_provider() or "claude-sonnet-4"
payload: dict = {
"model": model_name,
"messages": [{"role": "user", "content": prompt}],
"max_tokens": model.max_tokens or 64000,
"temperature": model.temperature or 0.7,
}
if model.top_p is not None:
payload["top_p"] = model.top_p
if response_model is not None:
payload["output_config"] = {
"format": {
"type": "json_schema",
"schema": make_strict_schema(response_model),
}
}
url = f"{self.base_url}/v1/messages"
logger.debug("Requesting %s with model=%s", url, model_name)
async with self.session.post(url, json=payload) as response:
if response.status != 200:
error_text = await response.text()
raise RuntimeError(
f"claude-max-api error (status {response.status}): {error_text}"
)
data = await response.json()
content = data.get("content", [])
text = ""
for block in content:
if block.get("type") == "text":
text = block["text"]
break
usage_data = data.get("usage", {})
return LLMResponse(
text=text,
raw_response=data,
usage={
"input_tokens": usage_data.get("input_tokens", 0),
"output_tokens": usage_data.get("output_tokens", 0),
"total_tokens": (
usage_data.get("input_tokens", 0)
+ usage_data.get("output_tokens", 0)
),
},
model=model_name,
)
async def close(self) -> None:
if self.session:
await self.session.close()
self.session = None
Module Documentation
| Module | Description |
|---|---|
| Hybrid Syntax | .lm file syntax: LLM commands, file operations, web actions, sessions, -> File(...) write syntax |
| Validation | Validators for HTML, JSON, YAML, XML, Markdown, CSV, Pydantic models |
| Web Adapters | Browser automation (Selenium, Playwright) and HTTP clients |
| LLM Adapters | Implementing new LLM provider adapters |
| Engine | Core engine, LLM manager, configuration |
| Selector Resolution | CSS/XPath and AI-powered natural language selectors |
| Evaluation | Model evaluation to find cost-effective models |
Documentation
Full documentation: lamia-lang.github.io/lamia
Development
See CONTRIBUTING.md for development setup, doc building, and code style guidelines.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lamia_lang-0.1.6.tar.gz.
File metadata
- Download URL: lamia_lang-0.1.6.tar.gz
- Upload date:
- Size: 6.6 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6c6f0125b4ba3ee3e0fa9b8bf2be3e08e9cfd9b84cfb4688dc4eda0141ff5b82
|
|
| MD5 |
9c91aced1614114d88d514925d0c0d5a
|
|
| BLAKE2b-256 |
e4dbdf518419cbb4262aa4891322bfccebd66500a88c91498bfb6c3b94a83ff1
|
Provenance
The following attestation bundles were made for lamia_lang-0.1.6.tar.gz:
Publisher:
publish.yml on lamia-lang/lamia
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lamia_lang-0.1.6.tar.gz -
Subject digest:
6c6f0125b4ba3ee3e0fa9b8bf2be3e08e9cfd9b84cfb4688dc4eda0141ff5b82 - Sigstore transparency entry: 1394601519
- Sigstore integration time:
-
Permalink:
lamia-lang/lamia@e3362916882360343f6ae6b6dafbe1b9770ed5fa -
Branch / Tag:
refs/tags/v0.1.6 - Owner: https://github.com/lamia-lang
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e3362916882360343f6ae6b6dafbe1b9770ed5fa -
Trigger Event:
push
-
Statement type:
File details
Details for the file lamia_lang-0.1.6-py3-none-any.whl.
File metadata
- Download URL: lamia_lang-0.1.6-py3-none-any.whl
- Upload date:
- Size: 416.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a32a31f74112435d7a4bf1afba424d4b2fbdbd80299ddf64ef95d521b10752a7
|
|
| MD5 |
2cbcb7ac505da61d047d5bdadd6b6037
|
|
| BLAKE2b-256 |
1905ca2d89b037ece5152cea00648826b96229c7b3fa8be1ec6722050cc6938f
|
Provenance
The following attestation bundles were made for lamia_lang-0.1.6-py3-none-any.whl:
Publisher:
publish.yml on lamia-lang/lamia
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lamia_lang-0.1.6-py3-none-any.whl -
Subject digest:
a32a31f74112435d7a4bf1afba424d4b2fbdbd80299ddf64ef95d521b10752a7 - Sigstore transparency entry: 1394601644
- Sigstore integration time:
-
Permalink:
lamia-lang/lamia@e3362916882360343f6ae6b6dafbe1b9770ed5fa -
Branch / Tag:
refs/tags/v0.1.6 - Owner: https://github.com/lamia-lang
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e3362916882360343f6ae6b6dafbe1b9770ed5fa -
Trigger Event:
push
-
Statement type: