@arcproof/sdk-langchain
v0.1.0
Published
LangChain.js adapter for @arcproof/sdk -- turn any LangChain.js tool-calling agent into a claim-gathering step for the ArcProof trust layer.
Downloads
92
Maintainers
Readme
@arcproof/sdk-langchain
LangChain.js adapter for @arcproof/sdk: turn any LangChain.js tool-calling agent into a gatherClaims() function usable by runTrustedJob().
Install
npm install @arcproof/sdk @arcproof/sdk-langchain @langchain/core @langchain/langgraphUsage
import { createLangChainClaimGatherer, combineClaimGatherers } from "@arcproof/sdk-langchain";
import { runTrustedJob, VerifierRegistry, ARC_TESTNET } from "@arcproof/sdk";
import { ChatGroq } from "@langchain/groq";
import { tool } from "@langchain/core/tools";
import { z } from "zod";
const lookupTool = tool(
async ({ id }: { id: string }) => JSON.stringify(await lookUpSomething(id)),
{ name: "lookup", description: "...", schema: z.object({ id: z.string() }) }
);
const gatherClaims = createLangChainClaimGatherer({
agentId: "my-agent-v1",
model: new ChatGroq({ model: "llama-3.3-70b-versatile", apiKey: process.env.GROQ_API_KEY }),
tools: [lookupTool],
claimTypes: ["some_claim_type"], // optional -- omit for an open string
systemPrompt: "You are a ... specialist. Use your tools to gather claims, copy values verbatim.",
});
const result = await runTrustedJob(
{ network: ARC_TESTNET, contractAddress, verifiers: new VerifierRegistry() /* .register(...) your rules */ },
{
jobId: "job-1",
budgetAmount: 0.05,
requester, settler, // WalletCredential
providerAddresses: { "my-agent-v1": "0x..." },
gatherClaims,
context: { id: "loan-001" },
}
);Multiple specialists, always engaged: combineClaimGatherers([aprAgent, feeAgent, complianceAgent]).
Multiple specialists, an LLM decides which ones this specific request needs (the orchestrator layer -- matches the reference apps' orchestrator.ts/langchainPlanner.ts pattern, generalized):
import { createLangChainOrchestrator, type SpecialistDescriptor } from "@arcproof/sdk-langchain";
const specialists: SpecialistDescriptor[] = [
{ id: "apr-agent-v1", description: "Checks true APR and fees -- engage for any cost question.", gatherClaims: aprGatherer },
{ id: "eligibility-agent-v1", description: "Checks borrower region eligibility -- engage for any eligibility question.", gatherClaims: eligibilityGatherer },
];
const gatherClaims = createLangChainOrchestrator({ model, specialists });
// A pure "what's the APR" request engages only apr-agent-v1.
// A request that also asks about eligibility engages both.This is the full three-layer pattern, each with a clean separation of concerns:
orchestrator (createLangChainOrchestrator) -- decides which specialists this request needs
specialist (createLangChainClaimGatherer) -- checks and drafts claims
evaluator (@arcproof/sdk's VerifierRegistry) -- independently verifies, zero LLM callsIf the planning call itself fails outright (not "returned zero valid ids," which defaults to engaging every specialist -- an actual throw), it propagates up to runTrustedJob's existing refund path rather than silently falling back -- same "agent or loud failure" rule as everywhere else in this SDK.
Why claim_value/simulated are plain strings in the structured-output schema
Two real, independently-observed provider incompatibilities, not a stylistic choice:
- Gemini's function-calling schema translator rejects JSON Schema
anyOfunions nested inside array-of-object properties, at any branch count -- including the 2-branch[string, null]shape.nullable()produces. - Groq has been observed returning the JSON string
"false"for az.boolean()field ("expected boolean, but got string").
A plain string field with an explicit "true"/"false" convention, coerced back to a real boolean in JS, sidesteps both without depending on either provider fixing their schema translation. If you add new structured-output fields to a similar schema, keep this in mind.
Resilience
If the underlying LLM call fails (quota, outage, malformed generation), createLangChainClaimGatherer logs and returns zero claims rather than throwing. runTrustedJob's built-in hasCheckableClaims guard already handles "this provider contributed nothing" correctly (refund, not a false accept) -- a single flaky provider never has to crash the whole job.
License
MIT
