All notable changes to this package are listed here. The format follows Keep a Changelog, and versions follow Semantic Versioning.
The first release of 0.4 (0.4.0 is never published). Implements version 1.4 of the SDK
specification (SPEC_VERSION is "1.4"), knowledge, and passes conformance suite 1.27.
Suite 1.22's Segment Vision cases record an opening's offset from the centre of the
match's box; the readings keep the offset as sent.
- Knowledge bases, collections of type
text:collections.create(name, type="text", scope_key=None), the scope key checked before sending (^[a-z0-9_]{1,40}$). A knowledge base's object (scope_key,chunks,tokens,version,status_counts,pinned) and a purging delete ({"id", "status": "purging"}) are returned as sent. Document: Markdown or plain text, a file from a path, bytes or a binary stream (read to its end before anything is sent), or an earlier upload byupload_id. A file's type comes fromcontent_type, its file name's extension, or its first bytes (PDF, PNG, JPEG, TIFF, WebP).collections.add_documentandcollections.replace_document(title,reference,pinned,pin_rank,label,metadata,wait), each with anIdempotency-Key. A text, and a UTF-8 text or Markdown file under the inline threshold (titled by its file name's stem), go inline; every other file is uploaded with the knowledge purpose (POST /v1/uploadswithpurposeandfilename, then the storagePUT, 120 s an attempt unlessupload_timeoutis set) and named byupload_id. An upload of the client's own that the API reports stale is uploaded again once and the item sent again with a newIdempotency-Key;upload_purpose_mismatchis raised at once.wait=Truesends?wait=trueand waits 20 s an attempt unless a timeout is set.upload_documents(documents): 1 to 16 documents uploaded ahead, their upload ids in order. Knowledge uploads are never cached;upload(images)is unchanged and sends nopurpose.collections.list_itemstakes a knowledge base'sq(1 to 200 characters) andsort(newest,oldestortitle), each sent only when given and checked before sending.collections.update_itemtakes a document'spinned,pin_rankandtitle;collections.get_item(withinclude_text),collections.list_chunks,collections.delete_failedandcollections.search, which returns aKnowledgeReading.collections.wait_until_ready(collection_id, item_id=None, timeout=300, poll_interval=1): reads a document until it isreadyorfailed(a failed document is returned), or a knowledge base until nothing is queued or processing; the wait grows 1.5 times a read up to 10 s, andTimeoutErroris raised pasttimeout.knowledge(collection, ...), theknowledgebooster with its options, each sent only when given, andrequire.KnowledgeReading(collection,collection_name,corpus,query,budget,scope,warnings,passages,omitted,pending_items,search,truncated, its objects as sent) with eachKnowledgePassage. A reading or a passage with a required field missing or of the wrong type is anUnknownReading.- Checked before sending, with the API's 422 entries: a title (1 to 200 characters once
trimmed, required with a text), an empty text,
format, and a document'sreference,pin_rank(0 to 1,000),label,metadataandpinned; a file that is empty, over 50 MB, or not of a knowledge type is the presign'sapi_usage_error, and a file whose type cannot be told is aValueError.
- A collection
typeother thanfaceortextis refused with "Input should be 'face' or 'text'". - A 529 whose
details.reasonisingestion_delayedis raised at once, never retried.
Passes conformance suite 1.21.
DepthReading, a reading of thebynn_depthbooster (Depth Vision):ahead,directions,ground,grid_rows,grid_cols,grid,depth_min,depth_max,depth_median,widthandheight, in meters and pixels as sent.SegmentReading, a reading of thebynn_segmentbooster (Segment Vision):width,height,prompts,instances(each aSegmentInstancewithlabel,score,box,polygon,area,centroid,openingas aSegmentOpening,depthas aSegmentDepthandsize_mas aSegmentSizewith itsSegmentExtent),depth_sourceandassumed_hfov_deg. An absentahead,depth_source,assumed_hfov_deg,opening,depth,size_m(or a distance insidedepth, or the size'sopening) reads asNone. A reading with a required field missing or of the wrong type,directionsother than 7 entries, or a negative or fractional grid size or picture side stays anUnknownReading.- The README lists every booster the API serves,
face_detection,face_recognition,ocr,bynn_depthandbynn_segmentbesideageandai_generated, with their options and readings.
Implements version 1.3 of the SDK specification (SPEC_VERSION is "1.3") and passes
conformance suite 1.20.
- Device keys, as
dn.keys, for a Manage device keys key:create(a Decisions-only key and its secret, with anIdempotency-Key: the caller'sidempotency_key, else a new one per call, the same on every attempt; retried also while the first attempt runs),listandall(by labels,device_idandstatus, a page of 1 to 200),update(only the fields given;Noneclearslabels,limits,daily_spend_caporexpires_at),revokeandrevoke_by_labels(withdevice_idsanddry_run). Each returns the API's object as sent. Checked before sending, as the API answers them: a key's name (1 to 48 characters once trimmed),device_id, labels (at most 16), the shape oflimitsand adaily_spend_capfrom 0.01 to 1,000 in whole cents (each the API's 422 entry, the first refused in the body's order), an update with nothing to change, a malformedidempotency_key,list's filters andlimit, and a bulk revoke without a label or with over 1,000device_ids. - Per-key usage, as
dn.usage:key(GET /v1/keys/{key_id}/usage),list(GET /v1/usage, one page) andall(every page,next_cursorfollowed until it isNone). Each returns the API's object as sent and retries as any read. The query's rules (granularity,group_by, labels,device_id, key ids and at most 100 of them,scope,status,include_empty,limit) are checked before sending with the API's messages. FaceRecognitionReading.collection_name, and on eachFaceMatchitscollection_nameand the item'smetadata(an object as sent), eachNonewhen absent, never{}. Acollection_namethat is not a text or ametadatathat is not an object makes the reading anUnknownReading.- An item's
metadata:enrolltakes it, an object of up to 20 keys (1 to 40 ofa-z,0-9and_) whose values are texts of up to 500 characters, numbers or booleans, checked before sending as the API checks it. collections.update_item(collection_id, item_id, reference, label, metadata): aPATCHsending only the fields given (label=Noneclears the label,metadatareplaces the whole object), with anIdempotency-Keyand the retries ofenroll; with no field it raises the API'sValidationErrorbefore sending.collections.list_itemstakesreferenceorreference_prefix(not both), each checked as the API checks it.DecisionNodeError.details: the error body'sdetail.detailswhen it is an object, elseNone. A 529 whosedetails.reasonisenrollment_unpricedis never retried.ModelInfo.price_per_million_input,price_per_million_output,price_per_million_image,price_per_million_videoandbatch_price_factor: the entry's prices as sent, eachNonewhen it states none (a fine-tuned model states nobatch_price_factor).limitsandrawkeep them too.
- A 429
key_spend_cap_reached(a key past its daily spend cap) is raised at once and never retried: the cap resets at 00:00 UTC. It stays aRateLimitErrorwithdetailsresets_at,capandspent. - The README documents the key kinds, a publishable key's
403origin_not_allowedandmodel_not_allowed(withdetails["allowed"]), and its429s withdetails["reason"]client_rateandin_flight, retried as every 429.
Batch.price_factor. The API does not send it: the batch price is each model'sbatch_price_factorinmodels()(none on a fine-tuned model), and each line's charge is in its usage.
Implements version 1.3 of the SDK specification (SPEC_VERSION is "1.3"). First
published as 0.3.1 the same day.
- Several places in one answer:
point()andbox()takemax(2 to 16), and the answer is aPointsAnswerorBoxesAnswer(count,present,complete,pointsofPointPlaceorboxesofBoxPlace,image,width,height), told by its own list, in the order and numbers sent.max(a whole number from 2 to 16) is checked before sending; whether a model version answers several places is the API's to say. - Boosters:
boostersondecide,check,classify,score,number,gate,decide_manyrequests, batch requests andcheck_request, as booster names,Booster(name, images, require, options)objects (sent as{"name": ...}with each setting only when given, or as the bare name when none is) or wire-form mappings, sent in the order given. Left out or empty, the request has noboostersfield. The client checks nothing about boosters itself; the API answers. Sessions take no boosters. Decision.evidence: booster name toEvidence(version,status,latency_ms,billed_calls,results), withAgeReading,AiGeneratedReading(itsTopGenerator,Noneunless the reading says likely generated,c2pa_signer, andc2pa,untrustedfor Content Credentials that validate but are not trusted) andUnknownReadingfor a reading of a booster this version does not know or with a field missing or of the wrong type.Nonewhen the request named no boosters.Decision.usage.booster_tokensandDecision.usage.boosters(Nonewhen absent).- The readings of the
ocr,face_detectionandface_recognitionboosters:OcrReading(text,truncated),FaceDetectionReading(faces,detectionsofFaceDetectionwith aFaceBoxand aconfidence) andFaceRecognitionReading(faces,matchesofFaceMatchwith aFaceBox, ascore, thecollectionsearched, the item'sreference, andnameorlabel,Nonewhen absent). Box corners are kept as received. A malformed reading is anUnknownReading. - Collections of faces, as
dn.collections:create,list,get,delete,enroll(one face from one image, sent as a decision's image is),list_itemsanddelete_item, each returning the API's object as sent.createandenrollsend anIdempotency-Key, a new UUID per call and the same on every attempt of it, so the API creates, enrolls and bills once; they retry like any call, and also while the API reports the first attempt still running (a 409idempotency_in_progress, after itsRetry-After). A 409idempotency_key_reusedis raised and never retried. A collection's type and name, an item's reference and label and the items list'soffsetandlimitare checked before anything is sent; of the picture only a URL's rules are, every other fault is the API's 422image_unreadable. - A decision whose boosters name
ocrwaits 30 seconds per attempt unless atimeoutwas set on the client, and is never retried after a timeout, since the server finishes that attempt and may bill it; its other retries stay. Usage.cost: what the answer cost in USD, aCost(usd,model_usd,boosters_usd,free_tier) read as sent, on decisions, batch result lines, a session's open and frame replies;Nonewhen absent, never zeros.DecisionNodeError.boosters: thex-decisionnode-boostersheader as booster name toBoosterStatus(status,latency_ms), on every error; empty when the header is absent.- Fine-tuned models:
ModelInfo.fine_tuned,base,adapter_versionandstatus(Noneon the catalogue's own entries). A fine-tuned model's name is sent as given, andDecision.modelis the pinned name that answered. RequestTooLargeErrorfor the API's 25 MiB request body limit: raised for every 413, never retried, and before anything is sent or uploaded when a request, adecide_manyrequest or a batch line is over 26,214,400 bytes as it will be sent (images to upload counted by theirupload_id).check_requestmeasures it too.- The fallback:
fallback(TrueorFalse) ondecide, the helpers,decide_manyrequests, batch requests andcheck_request, sent only when given.Decision.fallbackis aFallback(served_model,requested_model) on an answer from another model than the one asked for, read fromx-decisionnode-fallbackandx-decisionnode-requested-model;Noneotherwise.
- The default model is
decisionnode-moe(it wasdecisionnode-latest), and the docs use the family idsdecisionnode-moeanddecisionnode-flash, which always mean the current release; a pinned id isdecisionnode-<family>-<version>, asmodels()lists it. The older ids are still accepted by the API and sent as given. PermissionDeniedErroris documented as every 403 that is not a refusal, a booster turned off for the workspace included;OverloadedErroras also raised, and retried, for a required booster that failed (booster_unavailable).- An error whose body is not JSON (an HTML or text page, or no body) is classed by its
status alone, with
error_typeNone,request_idandreasonfrom the headers and the client's own message,HTTP <status> <text>(HTTP 413 Content Too Large), with the request id when sent. The page is never read:bodyisNone. - An image id of more than 64 characters is refused before anything is sent:
BadRequestErrorapi_usage_error, "Image : its id is longer than 64 characters."
Implements version 1.2 of the SDK specification (SPEC_VERSION is "1.2") and passes
conformance suite 1.10.
- Videos sent as frames:
Video(id, frames, fps, start, end)andVideoFrame(t, data, media_type), taken bydecide,check,classify,score,number,gate,decide_manyrequests, batch requests andcheck_requestasvideos. A frame's data is bytes (its type read from the first bytes) or base64 text with its media type. Frames always go inline. - Before anything is sent: ids unique across images and videos, frame times of at least 0
and strictly increasing, JPEG, PNG or WebP frames,
fpsabove 0 and at most 4, andendafterstart, each raisingBadRequestErrorwith the API's message. More than 4 videos raises the API'sValidationError(atoo_longentry on the videos). Frame counts are the API's. Decision.usage.video_frames,Decision.usage.video_seconds(Nonewhen absent) andDecision.warnings(an empty list when absent).- The
pointandboxquestion types: thepoint(instructions, image, units)andbox(instructions, image, units)builders, andPointAnswer(x,y) andBoxAnswer(x1,y1,x2,y2), each withpresent,confidence,image,widthandheight. A request with one needs an image, andimagenames one of the request's images (required with two or more); withunitsthese are checked before anything is sent and raiseValidationErrorwith the API's entries.
systemone().POST /v1/systemoneis not part of the API; every decision is sent toPOST /v1/decide.
- A request holds up to 16 images (4 before); a 17th raises the API's
ValidationErrorbefore anything is sent: atoo_longentry on the images, listed with the request's other entries, a local image in itsinputwithout its bytes. Up to 16 images are uploaded through one presign;upload()of more raisesBadRequestError(files: 1 to 16 files, got 17.) before anything is sent.
First version. Implements version 1.1 of the specification shared by every official
DecisionNode SDK, exported as SPEC_VERSION, and passes conformance suite 1.6.
-
The
numberquestion type: thenumberbuilder, thenumberhelper andNumberAnswer(number,expected,confidence,probabilitieskeyed by the value as the API writes it). -
systemoneforPOST /v1/systemone, the strict path. -
Truth answers read as
TruthAnswerunder either wire name the API uses for the type. -
Images by
upload_id, andupload(images). Local images whose bytes totalDEFAULT_INLINE_THRESHOLD(512 KiB) or more in a request are uploaded first (one presign, parallelPUTs with exactly the signed headers) and sent byupload_id; smaller ones go inline. The client optioninline_thresholdchanges the threshold. Uploads are remembered by the SHA-256 of their bytes and reused until 5 minutes before they expire; a stale one is uploaded again and the request sent once more. -
Every fixed rule the API documents is checked before any request, in the API's order, raising its own error class,
error_type, message and 422 entries: the 128-level nesting limit; question types and shapes, option, level and truth rules and the number grid (numberis refused onsystemone); the state's type; image count, ids, format by magic bytes, size, pixel dimensions read from the JPEG, PNG and WebP headers and an upload reference's media type; image URLs (https, at most 2,048 characters, no user information, port 443, no literal private address); sessionwindowandttl_seconds; frames refused bySession.sendwith nothing written and noseqused; batchcustom_idrules, an empty add andbatches.listpaging. Limits a model publishes are checked from the values held after amodels()call, found by the model's name or by the alias or pinned version an entry states. -
check_request(method, **arguments)andSession.check_frame(...)run every local check a call would run, raise the same error, and send and upload nothing. -
upload_timeout(default 60 seconds): each storagePUTattempt of an image has its own deadline, apart fromtimeout. -
A session's stream goes through the HTTP client's proxy and uses its TLS settings.
-
Batch results are read from the network line by line, the whole file.
-
A storage
PUTanswered403after its URL's lifetime is presigned again once. -
Sessions:
open_session,SessionandAsyncSession(connect,send,replies,next_event,end,close),Reply,FrameError,SessionClosed, andsupervise.SessionLimitError, aRateLimitError, for a key at its open-session limit. -
The Batch API:
batches.create,add,finalize,get,list,resultsandcancel, andrun_batch;Batch,BatchPage,BatchResultandBatchRun. -
Connections kept open between calls (up to 100, 30 seconds idle), and an
http2option with thehttp2extra. -
DecisionNodeandAsyncDecisionNodeclients withdecide,decide_many,check,classify,score,gate,modelsanddecide_raw.check,classify,scoreandgatetakeimagesandmodellikedecide. -
gatewith fixed wording, the same in every official SDK. -
ModelInfofor each entry ofmodels(), andDecisionResultfor each result ofdecide_many. -
Question builders
truth,choiceandscore; wire-form mappings are accepted too. -
Typed answers:
TruthAnswer,ChoiceAnswer,ScoreAnswer, andUnknownAnswerfor answer types this version does not know. -
Images from a file path, bytes (or base64 text) or an
httpsURL. The media type of a file, and of bytes given without one, is read from the first bytes (JPEG, PNG or WebP); a file that cannot be read raisesValueErrorwhen theImageis made. -
An answer of a known type with a missing or wrongly typed field comes back as
UnknownAnswerwith its raw fields; the rest of the decision parses as usual. -
Retries on 408, 429, 5xx, timeouts and connection errors, honouring
retry-after-msandRetry-After(seconds or an HTTP date), capped at 60 seconds. -
A 10 second timeout that covers the whole attempt, from sending the request to the last byte of the response body.
timeoutmust be above 0 andmax_retries0 or more. -
Exception classes for the documented statuses, all derived from
DecisionNodeError, includingPermissionDeniedErrorfor a 403 that is not a refusal. A redirect and any other status raiseDecisionNodeErroritself with itsstatus; a 2xx whose body is not JSON raisesServerError.str(error)is the server's message,""when it sent none. -
Argument problems found before sending (an empty question map, a value that cannot be sent as JSON, an unreadable file, a bad
base_url,timeoutormax_retries) raise Python's ownValueError, never aDecisionNodeError.decide_manychecks every request before sending any. -
Redirects are never followed, also on an
http_clientpassed in that is set to follow them. -
The base URL must be
https; plainhttpis accepted only forlocalhost,127.0.0.1and::1. -
Decision.safety, parsed from thex-decisionnode-safetyresponse header. The README shows how to sendx-decisionnode-safety-body: trueto also get the result in the response body. -
Settings from
DECISIONNODE_API_KEY,DECISIONNODE_BASE_URLandDECISIONNODE_MODEL; an empty argument counts as not given. -
decide_manyrefuses aconcurrencybelow 1 withValueErrorbefore sending. -
check,classifyandscoreraiseServerError(status200,reasonfromx-decisionnode-error) when the answer to their question is missing or malformed;decidereturns such an answer asUnknownAnswer. -
A caller's
http_clientis checked when the client is made: it must be anhttpx.Client(httpx.AsyncClient) itself, with httpx's own transports, or it is refused withValueError, since another type may follow redirects. For anhttpsbase URL it may not turn off certificate or hostname checks. -
DecisionNodeErroris exported by that name and is the base of every exception. -
CI pins every action to a commit SHA, sets
persist-credentials: falseon every checkout, installs a pinned uv and checks out the conformance suite at a pinned commit (suite 1.6).