HyphenHyphen
HomeConsole
  • Get Started
  • Connect your coding agent
  • Build with Hyphen
  • Chat UIs & automation
  • Recipes
  • Guides
  • API Reference
OverviewLangChain & LangGraphLlamaIndexPydantic AICrewAIInstructorDSPyVercel AI SDKMastraOpenAI Node SDK
powered by Zudoku
Build with Hyphen

Instructor

Instructor does one thing: you hand it a Pydantic model, it hands you back a validated instance. It wraps an OpenAI client, so pointing it at Hyphen is the same base URL change as everywhere else.

TerminalCode
pip install "instructor>=1.15.4" openai pydantic export HYPHEN_API_KEY="sk-..."

Pin instructor>=1.15.4

Before 1.15.4, from_provider(...) silently dropped base_url and sent your traffic to api.openai.com with a Hyphen key. It fails as a confusing auth error rather than a routing error. Either pin the version, or use the from_openai form further down, which was never affected.

Setup guide: this config comes from Instructor's official documentation and has not been run end to end against the gateway. Corrections to support@hyphen-solution.com.

Minimal

Code
import os import instructor from instructor import Mode from pydantic import BaseModel class Person(BaseModel): name: str age: int occupation: str client = instructor.from_provider( "openai/minimax-m3", base_url="https://api.hyphen-solution.com/v1", api_key=os.environ["HYPHEN_API_KEY"], mode=Mode.TOOLS, ) person = client.create( response_model=Person, messages=[ {"role": "user", "content": "Extract: John is a 30-year-old software engineer"} ], max_tokens=4000, max_retries=3, ) print(person) # Person(name='John', age=30, occupation='software engineer')

Use Mode.TOOLS

This is the setting that matters. Instructor can extract structured data several ways, and only one of them works against the M-series.

ModeMechanismExpected on Hyphen
Mode.TOOLSTool callingYes. Use this.
Mode.MD_JSONPrompt only, parses fenced JSONYes, fallback
Mode.JSONNeeds response_format: json_objectNo
Mode.JSON_SCHEMANeeds response_format: json_schemaNo
Mode.TOOLS_STRICTNeeds strict schema supportNo

Mode.TOOLS is already the default for the openai provider, so you can omit it. Setting it explicitly documents the intent and stops a future default change from breaking you.

If tool calling ever misbehaves on a prompt, Mode.MD_JSON is the safe fallback because it needs no provider features at all:

Code
client = instructor.from_provider( "openai/minimax-m3", base_url="https://api.hyphen-solution.com/v1", api_key=os.environ["HYPHEN_API_KEY"], mode=Mode.MD_JSON, )

The classic form

Wrapping an OpenAI client yourself. Slightly more verbose, stable across versions, and makes the base URL impossible to lose.

Code
import os import instructor from instructor import Mode from openai import OpenAI from pydantic import BaseModel class Person(BaseModel): name: str age: int occupation: str client = instructor.from_openai( OpenAI( base_url="https://api.hyphen-solution.com/v1", api_key=os.environ["HYPHEN_API_KEY"], ), mode=Mode.TOOLS, ) person = client.chat.completions.create( model="minimax-m3", response_model=Person, messages=[ {"role": "user", "content": "Extract: John is a 30-year-old software engineer"} ], max_tokens=4000, ) print(person)

Note the model name has no openai/ prefix in this form. The prefix only exists in from_provider's combined provider/model string.

Validation and retries

This is why Instructor is worth using. max_retries feeds validation errors back to the model so it can repair its own output.

Code
from pydantic import BaseModel, Field, field_validator class Issue(BaseModel): title: str = Field(max_length=80) severity: str @field_validator("severity") @classmethod def known_severity(cls, v: str) -> str: allowed = {"low", "medium", "high", "critical"} if v not in allowed: raise ValueError(f"severity must be one of {allowed}") return v issue = client.create( response_model=Issue, messages=[{"role": "user", "content": "CSV exporter drops the last row. Blocking."}], max_tokens=4000, max_retries=3, )

Each retry costs tokens against your monthly cap, so keep max_retries small. Three is plenty.

Lists and nested models

Code
from typing import List class Team(BaseModel): name: str members: List[Person] team = client.create( response_model=Team, messages=[{"role": "user", "content": "The Platform team is Ana (32, SRE) and Bo (28, dev)."}], max_tokens=4000, )

Gotchas

  • max_tokens of 4000. Reasoning runs before the tool call carrying your object is emitted. Too low and you get a retry loop that never succeeds. See Choosing a model.
  • Never Mode.JSON or Mode.JSON_SCHEMA. The M-series ignore response_format.
  • Pin instructor>=1.15.4 if you use from_provider with base_url.
  • 429 means the monthly budget is spent. Note that retries make this arrive sooner. See Handling the 429 cap.

Related

  • Instructor docs
  • Mode comparison
  • Structured JSON output: the same job without a library.
  • Pydantic AI: if you want agents as well as extraction.
Last modified on July 28, 2026
CrewAIDSPy
On this page
  • Minimal
  • Use Mode.TOOLS
  • The classic form
  • Validation and retries
  • Lists and nested models
  • Gotchas
  • Related