Custom scores and model lifecycle
FinicClient supports all six documented Finic REST operations. Score remains
an ordered question for the decision API; custom model scoring uses
ScoringRequest and ScoringResponse.
from langchain_finic import FinicClient, ScoringRequest
# Use a ready model you created in Finic. Each score_id must be unique in that model.
def score_payment(model_id: str, score_id: str):
with FinicClient() as client:
return client.score(ScoringRequest(
model_id=model_id,
score_id=score_id,
data={"amount": 200, "new_device": True},
))
score/ascore return a ScoringResponse whose score field is an integer from
0 to 1000. The client checks both model and record identity in the response.
skip_schema_validation and update_schema
are explicit flags: the latter changes the remote model's schema. Default
validation against the stored schema remains Finic's responsibility.
Creating, discovering, and calibrating
create_score(CreateScoreRequest(data=examples, description=...))starts model creation and returns model, job, and request IDs immediately. Supply at least 100 labeled examples, with at least 20 of each outcome. Labels are booleans, integer 0/1, or case-insensitive text true/false/1/0/yes/no in either JSON or CSV. The original label representation is preserved in the request. Each example needs another field. Finic samples more than 2,000 rows; the client never silently truncates your data.list_scores()returns the newest 500 models and their status/count metadata. Finic does not document pagination for this endpoint.calibrate(CalibrationRequest(model_id=..., data=outcomes))starts a calibration job. Outcomes contain a previously submittedscore_idandis_bad; a row's optionalmodel_idmust match. Repeated outcomes replace existing labels.get_job_status(job_id)returnscomplete, an optional typederror, and a derivedsucceededproperty. Complete does not imply success.
All five methods have native async equivalents prefixed with a. Use
async with FinicClient() for async work. Injected httpx clients remain
caller-owned; close them separately. These methods use the same FINIC_API_KEY
configuration as decisions.
JSON and CSV
Scoring accepts an object, a one-object list, or CSV with a header and one row.
Creation and calibration accept lists of objects or CSV strings. CSV travels as
text in the JSON data field; file uploads/multipart are not implemented.
The client validates CSV row widths, unique headers, labels, and required fields,
but leaves CSV type coercion and dotted-field expansion to Finic. This preserves
provider semantics (including ZIP codes with leading zeros).
JSON records are checked against the 60,000-byte compact JSON limit, excluding
is_bad. CSV record-size checks after provider coercion happen on the server.
All request envelopes must fit the 2 MB limit. Descriptions are at most 4,000
characters and score IDs at most 200 characters. Validation rejects nonfinite
JSON numbers. Remote errors include FinicError.error_type, .param, .status,
and .request_id; default messages omit the provider body.
Retries and waiting
Reads and decisions use bounded retries. Scoring, creation, and calibration
make one attempt, even when max_attempts is larger. A timeout may mean the
write succeeded: the client reports failure and never fabricates a result or
replaces your ID. A repeated successful score ID yields duplicate_score_id,
without the original score. Handle that explicitly. Automatic retries around
these methods or tools (including Runnable with_retry) can replay writes.
wait_for_job(job_id, timeout=900, poll_interval=30) returns a successful job or
raises on job failure/deadline. Sync cancellation uses an optional
threading.Event; it interrupts sleeps and prevents later polls, but cannot
interrupt an in-flight synchronous HTTP request. Its deadline bounds poll
starts and HTTP phase timeouts, not a hard wall-clock limit for a slow response.
await_job has a hard overall asyncio deadline and propagates task cancellation.
Both helpers use one attempt per poll and surface transient HTTP errors to the
caller. Waiting never restarts creation or calibration.
Finic documents no model deletion endpoint. Creation and calibration change server-side state. Keep job/model IDs in your application, and enable those operations only where your application intends to perform them.
See the Finic API reference for provider limits and API types for method signatures.
Native LangChain scoring
FinicScorer(model_id=...) implements the typed Runnable interface using
ScoringInput and ScoringResponse. It supports invoke, ainvoke, LCEL,
batch, abatch, callbacks, configurable fields, and as_tool().
from langchain_finic import FinicScorer
async def score_records(model_id: str, records: list[dict]):
async with FinicScorer(model_id=model_id) as scorer:
# Each input contains its own unique score_id and data.
return await scorer.abatch(records, {"max_concurrency": 2})
Direct scorer batches default to five concurrent calls; max_concurrency
overrides this. Other LangChain compositions follow their own batching defaults,
so pass an explicit concurrency limit to the outer chain. Concurrency is not a
requests-per-second limiter: Finic's organization rate and quotas still apply.
Batch results keep input order. return_exceptions=True preserves per-record
failures; a batch is not atomic and cancellation cannot retract accepted writes.
Streaming yields one complete result. Do not put automatic retry wrappers around
write operations without considering duplicate IDs and ambiguous outcomes.
The scorer uses the classifier's ownership rules: invocation-scoped owned pools
unless used as a context manager; injected clients remain caller-owned. Default
serialization contains a secret reference rather than an API key. Injected
transports/clients are not serializable. The bound model_id is configurable;
individual record inputs cannot override it.
Callbacks and LangSmith traces include the operation and model ID; successful outputs retain request and score IDs. Tracing is opt-in and record contents are not automatically redacted. The scorer does not fabricate token usage metadata for an endpoint that does not return it.
Selected lifecycle tools
from langchain_finic import FinicClient, FinicToolkit
# Keep this client open for the entire lifetime of the returned tools.
def lifecycle_tools(client: FinicClient):
read_tools = FinicToolkit(client=client).get_tools()
creation_tool = FinicToolkit(
client=client, operations=("create_score",),
).get_tools()[0]
return read_tools, creation_tool
The defaults are finic_list_scores and finic_get_job_status. Select
create_score or calibrate explicitly when your application authorizes those
writes. All tools have Pydantic argument schemas and native async implementations.
Creation/calibration return job handles immediately. FinicError propagates with
its structured metadata; an application can decide how to handle it. Job-status
tool results include the provider's failure details, so apply your application's
usual trace-access controls.
The base install includes the scorer and toolkit. Install the middleware extra
for the LangChain agent and LangGraph examples:
The graph separates each write from its following wait and retains job/model IDs
in checkpoints. Resume a failed wait using the same thread and a None input;
do not start the graph over. Use durable checkpoints for process restarts. Even
with checkpoints, a write accepted remotely before a connection failure may need
manual reconciliation. The graph intentionally has no automatic write retries.
Scoring tool capabilities and bindings
scorer.as_tool() exposes record scoring only. Schema mutation and validation
bypass are rejected at runtime as well as omitted from the default argument schema.
To deliberately grant either capability, construct the tool with
allow_schema_update=True or allow_schema_bypass=True. Direct FinicClient
and FinicScorer.invoke inputs still support both provider flags. Existing agents
that used those flags must opt in when upgrading. Custom argument schemas do not
bypass the capability check.
with_config(...).as_tool(), bind().as_tool(), and configurable-field tools retain
the finic_score name and JSON output. Explicit names/descriptions are supported.
Choose capabilities when converting the runnable into a tool; configuring that
tool afterwards retains its capability policy.
batch_as_completed and abatch_as_completed use the same default concurrency
of five as ordinary scorer batches and return each original input index with its
result. An explicit max_concurrency overrides the default. Cancellation cannot
retract writes already accepted by Finic. For organization-wide request pacing,
coordinate all workers through a shared rate limiter or queue before invoking
scoring; a semaphore alone does not enforce requests per second. Keep one attempt
per write and reconcile ambiguous failures rather than automatically replaying them.