instructor_dart 1.2.1
instructor_dart: ^1.2.1 copied to clipboard
Typed, validated structured outputs from LLMs. Build a JSON Schema in plain Dart, with validation and repair retries. Adapters for OpenAI, Anthropic and Gemini.
1.2.1 #
- The two examples that call a local model now check it is reachable first and exit with the command that starts it, instead of ending in an unhandled exception that reads like the package is broken. A missing server and a missing model are reported separately, since the fix differs.
- New
example/no_model_demo.dart: a scripted adapter whose first answer breaks the schema and whose second passes. No model, no network, and it shows the part people assume needs provider support -- the rejection is built here and quoted back on the retry.
1.2.0 #
- A response carrying both a tool call and text was resolved twice, and the two
answers disagreed. Validation read the tool call, while the text quoted back
to the model on a retry came from
text. So the retry showed the model one payload and complained about a different one, and the same mismatched pair reached callers throughExtractionException.attempts. The choice is made once now and both readers use it: the tool call wins and the text beside it is dropped, which is already what all three bundled adapters do on their own. LlmResponse.toolCall,LlmResponse.textandLlmResponse.emptyname the three things a response can be. The unnamed constructor is deprecated: it is the only one that can build a response carrying a tool call and text at once. It behaves exactly as before and will be removed in 2.0.0. An adapter outside this package keeps compiling and gets a warning naming its replacement.
1.1.0 #
- The README now answers, in its first screen, why to reach for this rather than the zero-dependency route or the package that already owns the category. Both answers carry the file and line, or the issue number, that a reader can check. A "reach for it when" list and a sentence on when to skip it follow, because a page that only argues for itself is not useful for deciding.
1.0.2 #
- The README leads with the recording of the package working. The file was already in the repository and the page never showed it, so a reader had to scroll past the prose to find out what the package does, or never found out.
1.0.1 #
- Fix the screenshot caption on pub.dev, which read "validated against the s chema". The caption was a folded YAML scalar with a line break in the middle of the word, and folding turns a line break into a space. It is now a single unbroken line.
- The Ollama e2e test now also skips when the server is running but
llama3.2:3bis not pulled. The guard only checked that the server answered, and a half-prepared machine failed the suite with a 404 instead of skipping. - Docs: tightened the README wording. Nothing under
lib/changed.
1.0.0 #
The API is stable. One freeze-cleanliness gap was found by adversarially testing the package rather than reading it, and it is fixed here.
LlmRequestnow copies itsmessagesandjsonSchema. They aliased the list and map you passed in, so mutating them afterwards changed a request an adapter was about to send. They are now copied to unmodifiable collections (matchingSchema.enumeration, which already copied its values). The constructor is no longerconst, which is only breaking for aconst LlmRequest(...)call — impossible in practice, since the messages and schema are runtime values.
Everything else was verified by execution and left unchanged: a 2xx response
with an unexpected nested shape becomes an AdapterException (not a raw
TypeError), a transport failure becomes AdapterException.transport, an
extraction that fails every retry throws ExtractionException carrying the
attempt history, close() is idempotent, and schema validation rejects a wrong
type, an out-of-range integer, a missing required field, and an unknown field.
LlmAdapter is a growable abstract base class so streaming can be added
later without breaking implementers; the only runtime dependency is http.
0.7.1 #
- Add
example/mock_extract.dartandexample/README.md. The Example tab was empty, and both existing examples need a live model. The new one uses a scriptedMockClient— the model returns an out-of-range value first, so it runs the schema-validation-then-retry loop, the package's core, with no network. Docs and example only.
0.7.0 #
Settles the adapter error contract before 1.0.0. Breaking because
AdapterException grew a field and its statusCode is now nullable; the
migration is small.
- Every adapter failure is now an
AdapterException. A 2xx response whose nested shape was not what a spec-compliant server sends used to escape as a rawTypeErrorthe caller could not catch by type — and an OpenAI-compatible server (Ollama, LM Studio, vLLM, OpenRouter) is exactly where odd shapes turn up. Reproduced across four shapes (choices as an object, choices[0] as a string, tool_calls[0] as a number, a content part's text as a number); all four now throwAdapterExceptionwith the originalTypeErroron.cause. - Transport failures are an
AdapterExceptiontoo. A dropped connection, a timeout, or a closed client used to surface asSocketException,TimeoutExceptionorhttp.ClientException. They now becomeAdapterException.transport, whosestatusCodeisnull(there was no response) and whose.causeis the underlying error. AdapterExceptiongained acauseand a nullablestatusCode. Both had to land before 1.0.0: adding a field or a "no status code" case to a frozen exception would be a breaking change. If you reade.statusCodeas non-nullable, handle the transportnull; the previous fields are unchanged.- Document
Schema.normalize's contract: it assumes a validated value and passes non-conforming input through rather than throwing (verified — it does not crash on unvalidated input), and it leaves an integer beyond 2^53 as adouble, matching the README.
0.6.0 #
Two pre-1.0.0 decisions, both about what the API promises rather than what it computes. Breaking for anyone who wrote their own adapter; the migration is one word.
-
LlmAdapteris now anabstract base classwith aclose()default, andInstructorhas aclose()that forwards to it. Two problems met here. An adapter constructed without anhttp.Clientcreates one and owns it, and the shape the README leads with passes the adapter inline and never keeps a reference — so nothing could reach that client, and a program following the README sat on an idle socket after its work was done.close()existed only on the three concrete adapters, not on the type callers hold. The second problem is that fixing this by adding a member to anabstract interface classbreaks every third-party implementer, and the roadmap has streaming on it, which would break them a second time. Abaseclass with concrete defaults can grow without breaking anyone, so that is what it is now.To migrate, change
implements LlmAdaptertoextends LlmAdapterand mark your classbase,finalorsealed. Overrideclose()only if your adapter owns something. -
Corrected the integer claim in the README. It said
json['age'] as intis always safe for anintegerfield. It is not, above 2^53:validateaccepts such a value, butnormalizedeliberately leaves it adoublerather than converting lossily, so the cast throws. Measured:1e16,1e17and1e300all validate and all throw onas int. The README now says so and suggests reading such a field asnum, or modelling it as a string.
0.5.0 #
The last things to settle before this can freeze at 1.0.0, all found by re-reviewing the public surface against what a permanent contract would fix.
- Stop
Schema.objectaliasing the caller's map. It stored the exact map passed in, soSchema.object(props)followed byprops['x'] = ...changed the schema afterwards, andschema.propertieswas itself writable, both bypassing every check the factory does and changing whatvalidatedemands. This is the same escape hatch 0.4.0 closed for the constructors, left open in one more place.Schema.objectnow copies into an unmodifiable map, matching whatSchema.enumerationalready documents and does for its list. Breaking only for code that mutated a schema through that aliasing, which was never intended to work. - Make
collectViolationsprivate. It was public with a note that it had to be, "so that schema types can recurse into each other". That was not true:Schemaissealedand every schema type lives in the one library, so the recursion works with it private, and Dart privacy is per-library. Public, it froze an internal accumulator hook, its out-parameter list and its seed-the-path convention, into the 1.0.0 contract.validateis the supported entry point and is unchanged. - Give
SchemaViolationandMessagevalue equality. Both are small immutable value types that callers naturally compare and put in sets: deduplicating the violations acrossExtractionException.attempts, or asserting onLlmRequest.messagesin an adapter test.Messagewas worse than missing equality, it was inconsistent: twoconstidentical messages compared equal through canonicalization while two runtime-built identical ones did not. Both now compare by value. Adding this after 1.0.0 would silently change how existing sets and maps of these types dedup, so it lands now.
0.4.0 #
- Make the concrete schema constructors library-private so the validating
Schema.*factories are the only way to build a schema. Breaking change:StringSchema(...),IntegerSchema(...),NumberSchema(...),BooleanSchema(...),EnumSchema(...),ListSchema(...)andObjectSchema(...)can no longer be called directly; useSchema.string,Schema.integer,Schema.number,Schema.boolean,Schema.enumeration,Schema.listandSchema.objectinstead. The concrete types stay exported for use in return types,switch, and field access, and.optional()still returns the same concrete type. This closes a construction path that skipped the factory checks:Schema.stringrejects an invalid regular expression andSchema.enumerationrejects an empty list and copies its values, and a direct constructor call bypassed both.
0.3.1 #
- Fix
.optional()rejecting an explicit JSONnullon the property it was applied to..optional()only removed the key from the JSON Schemarequiredlist, so a key present with valuenullstill fell through to the leaf schema's type check and failed as a type mismatch instead of being treated as absent. Forced tool calling on OpenAI, Anthropic and Gemini regularly fills in every declared parameter and represents "no value" asnullrather than omitting the key, so this broke.optional()for exactly the case it exists for. A required property givennullis still reported as a violation.
0.3.0 #
- Normalize numeric fields to the Dart type their schema promises instead of
coercing every integral double to
int. Anumberfield given a whole value like42now decodes to adouble(42.0), sojson['price'] as doubleinfromJsonno longer throws after validation reported success; anintegerfield still arrives asint. AddsSchema.normalize, called after validation to do this per node type.
0.2.3 #
- Shorten the screenshot description. pub.dev accepts up to 200 characters but scores only those under 160, so the previous release published cleanly and quietly gave up the documentation points it was meant to earn.
0.2.2 #
- Declare the diagram in
pubspec.yamlso pub.dev renders it on the package page. It was already in the repository and the README, but pub.dev shows only what thescreenshots:field points at.
0.2.1 #
- Shorten the pub.dev description back under the 180-character limit. The previous release grew it past that, which costs the "valid pubspec" points and truncates the text search engines show.
0.2.0 #
- Add
GeminiAdapterfor the Gemini API'sgenerateContent, completing the three major providers. The schema is sent as a function declaration and forced withfunctionCallingConfig.mode: "ANY". Two Gemini-specific shapes are handled:contentsonly accepts theuserandmodelroles, so an assistant message is sent asmodel, and system text goes in the top-levelsystemInstructionrather than being a message. The API key is sent in thex-goog-api-keyheader instead of thekeyquery parameter, so it stays out of URLs and logs.
0.1.2 #
- Docs: tightened the README wording and visuals.
0.1.1 #
- Expand the package description to name what the package does in the words people search for. No code changes.
0.1.0 #
Initial release.
- Plain-Dart schema builder rendering to JSON Schema: objects, strings, integers, numbers, booleans, enums, lists, nesting, optional properties, length/range/pattern constraints.
- Local validation with JSONPath-style violation reporting.
Instructor.extract/extractRawwith an automatic repair loop that feeds validation errors back to the model.OpenAIAdapterfor OpenAI and OpenAI-compatible servers (Ollama, LM Studio, vLLM, OpenRouter), using forced tool calls.AnthropicAdapterfor the Anthropic Messages API, using forced tool use.ExtractionExceptionwith full attempt history.
