Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Outlines: structured JSON/regex/Pydantic LLM generation.
.claude/skills/nousresearch-outlines/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-20 | ✗→✓ | ▲ Improved | 92% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 125% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 200% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 134% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 131% | 0% |
Use Outlines when you need to:
GitHub Stars: 12,000+ | From: dottxt.ai (formerly .txt)
> API note (Outlines 1.x): This skill targets the current v1 API. > The pre-1.0 helpers (outlines.models.transformers(...), > outlines.generate.json/choice/regex/...) have been removed. In v1 you > create a model with outlines.from_transformers(...) (or from_vllm, > from_llamacpp, from_openai) and then call the model directly with an > output type: model(prompt, output_type). JSON/Pydantic outputs are returned > as a JSON string — validate with YourModel.model_validate_json(result).
bash# Base installation pip install outlines # With specific backends pip install outlines transformers # Hugging Face models pip install outlines llama-cpp-python # llama.cpp pip install outlines vllm # vLLM for high-throughput
pythonimport outlines from typing import Literal from transformers import AutoModelForCausalLM, AutoTokenizer MODEL_NAME = "microsoft/Phi-3-mini-4k-instruct" # v1: wrap a Transformers model + tokenizer model = outlines.from_transformers( AutoModelForCausalLM.from_pretrained(MODEL_NAME, device_map="auto"), AutoTokenizer.from_pretrained(MODEL_NAME), ) # Call the model directly with an output type prompt = "Sentiment of 'This product is amazing!': " sentiment = model(prompt, Literal["positive", "negative", "neutral"]) print(sentiment) # "positive" (guaranteed one of these)
pythonfrom pydantic import BaseModel import outlines from transformers import AutoModelForCausalLM, AutoTokenizer class User(BaseModel): name: str age: int email: str MODEL_NAME = "microsoft/Phi-3-mini-4k-instruct" model = outlines.from_transformers( AutoModelForCausalLM.from_pretrained(MODEL_NAME, device_map="auto"), AutoTokenizer.from_pretrained(MODEL_NAME), ) # Generate structured output (returns a JSON string) prompt = "Extract user: John Doe, 30 years old, john@example.com" result = model(prompt, User, max_new_tokens=200) user = User.model_validate_json(result) # parse into the Pydantic model print(user.name) # "John Doe" print(user.age) # 30 print(user.email) # "john@example.com"
Outlines constrains token generation at the logit level using a compiled automaton derived from your output type.
How it works:
Literal) to a schema/grammarBenefits:
pythonimport outlines from pydantic import BaseModel from transformers import AutoModelForCausalLM, AutoTokenizer class Person(BaseModel): name: str age: int model = outlines.from_transformers( AutoModelForCausalLM.from_pretrained("microsoft/Phi-3-mini-4k-instruct", device_map="auto"), AutoTokenizer.from_pretrained("microsoft/Phi-3-mini-4k-instruct"), ) result = model("Generate person: Alice, 25", Person) person = Person.model_validate_json(result)
In v1 you pass the desired output type directly as the second argument.
Literal)pythonfrom typing import Literal sentiment = model("Review: This is great!", Literal["positive", "negative", "neutral"]) # Result: one of the three choices
pythonfrom pydantic import BaseModel class Product(BaseModel): name: str price: float in_stock: bool result = model("Extract: iPhone 15, $999, available", Product) product = Product.model_validate_json(result) # valid Product instance
python# Generate text matching a regex pattern phone = model("Generate phone number:", r"[0-9]{3}-[0-9]{3}-[0-9]{4}") # Result: "555-123-4567" (guaranteed to match the pattern)
python# Pass the Python type directly age = model("Person's age:", int) # guaranteed integer price = model("Product price:", float) # guaranteed float
Outlines supports multiple local and API-based backends via from_* factories.
pythonimport outlines from transformers import AutoModelForCausalLM, AutoTokenizer model = outlines.from_transformers( AutoModelForCausalLM.from_pretrained("microsoft/Phi-3-mini-4k-instruct", device_map="auto"), AutoTokenizer.from_pretrained("microsoft/Phi-3-mini-4k-instruct"), ) result = model(prompt, YourModel)
pythonimport outlines from llama_cpp import Llama llm = Llama("./models/llama-3.1-8b-instruct.Q4_K_M.gguf", n_gpu_layers=35, n_ctx=4096) model = outlines.from_llamacpp(llm) result = model(prompt, YourModel)
pythonimport outlines from vllm import LLM llm = LLM("meta-llama/Llama-3.1-8B-Instruct", tensor_parallel_size=2) model = outlines.from_vllm(llm) result = model(prompt, YourModel)
pythonimport outlines from openai import OpenAI client = OpenAI() model = outlines.from_openai(client, "gpt-4o-mini") # API backends support JSON-schema style structured output result = model(prompt, YourModel)
Outlines has first-class Pydantic support with automatic schema translation. Generation returns a JSON string; call model_validate_json to get an instance.
pythonfrom pydantic import BaseModel, Field class Article(BaseModel): title: str = Field(description="Article title") author: str = Field(description="Author name") word_count: int = Field(description="Number of words", gt=0) tags: list[str] = Field(description="List of tags") result = model("Generate article about AI", Article, max_new_tokens=300) article = Article.model_validate_json(result) print(article.title) print(article.word_count) # Guaranteed > 0
pythonclass Address(BaseModel): street: str city: str country: str class Person(BaseModel): name: str age: int address: Address # Nested model result = model("Generate person in New York", Person) person = Person.model_validate_json(result) print(person.address.city) # "New York"
pythonfrom enum import Enum from typing import Literal class Status(str, Enum): PENDING = "pending" APPROVED = "approved" REJECTED = "rejected" class Application(BaseModel): applicant: str status: Status # Must be one of enum values priority: Literal["low", "medium", "high"] # Must be one of literals result = model("Generate application", Application) app = Application.model_validate_json(result) print(app.status) # Status.PENDING (or APPROVED/REJECTED)
pythonfrom pydantic import BaseModel import outlines from transformers import AutoModelForCausalLM, AutoTokenizer class CompanyInfo(BaseModel): name: str founded_year: int industry: str employees: int model = outlines.from_transformers( AutoModelForCausalLM.from_pretrained("microsoft/Phi-3-mini-4k-instruct", device_map="auto"), AutoTokenizer.from_pretrained("microsoft/Phi-3-mini-4k-instruct"), ) text = """ Apple Inc. was founded in 1976 in the technology industry. The company employs approximately 164,000 people worldwide. """ prompt = f"Extract company information:\n{text}\n\nCompany:" company = CompanyInfo.model_validate_json(model(prompt, CompanyInfo, max_new_tokens=200)) print(f"Name: {company.name}") print(f"Founded: {company.founded_year}") print(f"Industry: {company.industry}") print(f"Employees: {company.employees}")
pythonfrom typing import Literal from pydantic import BaseModel # Binary classification result = model("Email: Buy now! 50% off!", Literal["spam", "not_spam"]) # Multi-class classification category = model( "Article: Apple announces new iPhone...", Literal["technology", "business", "sports", "entertainment"], ) # With confidence class Classification(BaseModel): label: Literal["positive", "negative", "neutral"] confidence: float out = model("Review: This product is okay, nothing special", Classification) result = Classification.model_validate_json(out)
pythonclass UserProfile(BaseModel): full_name: str age: int email: str phone: str country: str interests: list[str] prompt = """ Extract user profile from: Name: Alice Johnson Age: 28 Email: alice@example.com Phone: 555-0123 Country: USA Interests: hiking, photography, cooking """ profile = UserProfile.model_validate_json(model(prompt, UserProfile, max_new_tokens=250)) print(profile.full_name) print(profile.interests) # ["hiking", "photography", "cooking"]
pythonfrom typing import Literal class Entity(BaseModel): name: str type: Literal["PERSON", "ORGANIZATION", "LOCATION"] class DocumentEntities(BaseModel): entities: list[Entity] text = "Tim Cook met with Satya Nadella at Microsoft headquarters in Redmond." prompt = f"Extract entities from: {text}" result = DocumentEntities.model_validate_json(model(prompt, DocumentEntities, max_new_tokens=300)) for entity in result.entities: print(f"{entity.name} ({entity.type})")
pythonclass PythonFunction(BaseModel): function_name: str parameters: list[str] docstring: str body: str prompt = "Generate a Python function to calculate factorial" func = PythonFunction.model_validate_json(model(prompt, PythonFunction, max_new_tokens=300)) print(f"def {func.function_name}({', '.join(func.parameters)}):") print(f' """{func.docstring}"""') print(f" {func.body}")
pythonimport outlines from transformers import AutoModelForCausalLM, AutoTokenizer from pydantic import BaseModel class Person(BaseModel): name: str age: int model = outlines.from_transformers( AutoModelForCausalLM.from_pretrained("microsoft/Phi-3-mini-4k-instruct", device_map="auto"), AutoTokenizer.from_pretrained("microsoft/Phi-3-mini-4k-instruct"), ) texts = [ "John is 30 years old", "Alice is 25 years old", "Bob is 40 years old", ] # v1 accepts a list of prompts for batched generation prompts = [f"Extract from: {t}" for t in texts] outputs = model(prompts, Person, max_new_tokens=100) people = [Person.model_validate_json(o) for o in outputs] for person in people: print(f"{person.name}: {person.age}")
pythonimport outlines from transformers import AutoModelForCausalLM, AutoTokenizer MODEL_NAME = "microsoft/Phi-3-mini-4k-instruct" # Basic usage model = outlines.from_transformers( AutoModelForCausalLM.from_pretrained(MODEL_NAME, device_map="auto"), AutoTokenizer.from_pretrained(MODEL_NAME), ) # GPU + dtype configuration is set on the HF model itself import torch model = outlines.from_transformers( AutoModelForCausalLM.from_pretrained(MODEL_NAME, device_map="cuda", torch_dtype=torch.float16), AutoTokenizer.from_pretrained(MODEL_NAME), ) # Popular models for name in [ "meta-llama/Llama-3.1-8B-Instruct", "mistralai/Mistral-7B-Instruct-v0.3", "Qwen/Qwen2.5-7B-Instruct", ]: model = outlines.from_transformers( AutoModelForCausalLM.from_pretrained(name, device_map="auto"), AutoTokenizer.from_pretrained(name), )
pythonimport outlines from llama_cpp import Llama # Load GGUF model llm = Llama( "./models/llama-3.1-8b.Q4_K_M.gguf", n_ctx=4096, # Context window n_gpu_layers=35, # GPU layers n_threads=8, # CPU threads ) model = outlines.from_llamacpp(llm) # Full GPU offload: set n_gpu_layers=-1 on the Llama object
pythonimport outlines from vllm import LLM # Single GPU model = outlines.from_vllm(LLM("meta-llama/Llama-3.1-8B-Instruct")) # Multi-GPU model = outlines.from_vllm(LLM("meta-llama/Llama-3.1-70B-Instruct", tensor_parallel_size=4)) # With quantization model = outlines.from_vllm(LLM("meta-llama/Llama-3.1-8B-Instruct", quantization="awq"))
python# ✅ Good: Specific types class Product(BaseModel): name: str price: float # Not str quantity: int # Not str in_stock: bool # Not str # ❌ Bad: Everything as string class Product(BaseModel): name: str price: str # Should be float quantity: str # Should be int
pythonfrom pydantic import Field # ✅ Good: With constraints class User(BaseModel): name: str = Field(min_length=1, max_length=100) age: int = Field(ge=0, le=120) email: str = Field(pattern=r"^[\w\.-]+@[\w\.-]+\.\w+$") # ❌ Bad: No constraints class User(BaseModel): name: str age: int email: str
python# ✅ Good: Enum for fixed set class Priority(str, Enum): LOW = "low" MEDIUM = "medium" HIGH = "high" class Task(BaseModel): title: str priority: Priority # ❌ Bad: Free-form string class Task(BaseModel): title: str priority: str # Can be anything
python# ✅ Good: Clear context prompt = """ Extract product information from the following text. Text: iPhone 15 Pro costs $999 and is currently in stock. Product: """ # ❌ Bad: Minimal context prompt = "iPhone 15 Pro costs $999 and is currently in stock."
pythonfrom typing import Optional # ✅ Good: Optional fields for incomplete data class Article(BaseModel): title: str # Required author: Optional[str] = None # Optional date: Optional[str] = None # Optional tags: list[str] = [] # Default empty list # Can succeed even if author/date missing
python# v1 returns a JSON string for Pydantic/JSON output types. result = model(prompt, Article) # str article = Article.model_validate_json(result) # Article instance
| Feature | Outlines | Instructor | Guidance | LMQL | |---------|----------|------------|----------|------| | Pydantic Support | ✅ Native | ✅ Native | ✅ Yes | ❌ No | | JSON Schema | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | | Regex Constraints | ✅ Yes | ❌ No | ✅ Yes | ✅ Yes | | Local Models | ✅ Full | ⚠️ Limited | ✅ Full | ✅ Full | | API Models | ✅ Yes | ✅ Full | ✅ Yes | ✅ Full | | Zero Overhead | ✅ Yes | ❌ No | ⚠️ Partial | ✅ Yes | | Automatic Retrying | ❌ No | ✅ Yes | ❌ No | ❌ No | | Learning Curve | Low | Low | Low | High |
When to choose Outlines:
When to choose alternatives:
Speed:
Memory:
Accuracy:
references/json_generation.md - Comprehensive JSON and Pydantic patternsreferences/backends.md - Backend-specific configurationreferences/examples.md - Production-ready examples| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-20 | fail→pass | 15,023 | 7,154 | -52% | 1 | 1 | 0% | 3,320 | 6,376 | +92% | 0 | 0 | — |
case-21 | pass→pass | 9,082 | 4,147 | -54% | 1 | 1 | 0% | 1,917 | 5,831 | +204% | 0 | 0 | — |
case-01 | fail→pass | 13,501 | 7,328 | -46% | 1 | 1 | 0% | 2,861 | 6,430 | +125% | 0 | 0 | — |
case-02 | fail→pass | 10,940 | 8,658 | -21% | 1 | 1 | 0% | 2,206 | 6,612 | +200% | 0 | 0 | — |
case-03 | fail→pass | 13,382 | 6,272 | -53% | 1 | 1 | 0% | 2,625 | 6,139 | +134% | 0 | 0 | — |
case-04 | fail→pass | 15,706 | 10,702 | -32% | 1 | 1 | 0% | 3,011 | 6,964 | +131% | 0 | 0 | — |
case-05 | fail→pass | 18,913 | 16,464 | -13% | 1 | 1 | 0% | 3,010 | 7,807 | +159% | 0 | 0 | — |
case-06 | pass→pass | 14,921 | 10,684 | -28% | 1 | 1 | 0% | 2,428 | 6,837 | +182% | 0 | 0 | — |
case-07 | fail→pass | 8,465 | 5,711 | -33% | 1 | 1 | 0% | 1,683 | 5,995 | +256% | 0 | 0 | — |
case-08 | fail→pass | 5,505 | 5,752 | +4% | 1 | 1 | 0% | 1,176 | 6,052 | +415% | 0 | 0 | — |
case-09 | fail→pass | 8,757 | 5,679 | -35% | 1 | 1 | 0% | 1,794 | 5,952 | +232% | 0 | 0 | — |
case-10 | pass→pass | 12,500 | 5,657 | -55% | 1 | 1 | 0% | 2,302 | 5,912 | +157% | 0 | 0 | — |
case-11 | pass→pass | 9,591 | 7,246 | -24% | 1 | 1 | 0% | 1,796 | 6,316 | +252% | 0 | 0 | — |
case-12 | fail→pass | 9,429 | 6,787 | -28% | 1 | 1 | 0% | 2,017 | 6,391 | +217% | 0 | 0 | — |
case-13 | pass→pass | 8,851 | 5,126 | -42% | 1 | 1 | 0% | 1,746 | 5,908 | +238% | 0 | 0 | — |
case-19 | fail→pass | 8,497 | 4,155 | -51% | 1 | 1 | 0% | 2,006 | 5,849 | +192% | 0 | 0 | — |
case-14 | pass→pass | 9,582 | 7,261 | -24% | 1 | 1 | 0% | 1,928 | 6,215 | +222% | 0 | 0 | — |
case-15 | pass→pass | 10,827 | 6,979 | -36% | 1 | 1 | 0% | 2,018 | 6,157 | +205% | 0 | 0 | — |
case-16 | pass→pass | 9,102 | 6,821 | -25% | 1 | 1 | 0% | 1,833 | 6,291 | +243% | 0 | 0 | — |
case-17 | fail→pass | 5,667 | 4,356 | -23% | 1 | 1 | 0% | 1,297 | 5,876 | +353% | 0 | 0 | — |
case-18 | pass→pass | 9,479 | 5,280 | -44% | 1 | 1 | 0% | 2,153 | 6,159 | +186% | 0 | 0 | — |
case-22 | fail→pass | 8,519 | 5,771 | -32% | 1 | 1 | 0% | 2,028 | 6,174 | +204% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +59 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.