DocsReference
SDK reference
Every export of the TypeScript SDK: the client and its methods, the types they return, the instruction builders, the decoders, the address helpers and the constants.
Import
@curta/sdk is an ES module for Node 20 and later and for the browser. It is written against @solana/web3.js 1.x and takes its Connection, PublicKey, Keypair and TransactionInstruction types. The package carries the WebAssembly build of curta-core inside its own module, so importing it takes no file read, no WASM URL and no bundler plugin. Quickstart installs it.
import { readFileSync } from 'node:fs'
import { Connection, Keypair, PublicKey } from '@solana/web3.js'
import { Curta } from '@curta/sdk'
const secret = JSON.parse(readFileSync(process.env.CURTA_KEYPAIR!, 'utf8')) as number[]
const wallet = Keypair.fromSecretKey(Uint8Array.from(secret))
export const curta = new Curta({
connection: new Connection(process.env.CURTA_RPC_URL!, 'confirmed'),
wallet,
})
export const model = new PublicKey(process.argv[2])Every example on this page starts from this file: a client named curta and the address of a sealed model named model. All amounts are bigint lamports. All addresses are PublicKey.
The package has one entry point, @curta/sdk, with every export on this page. A second export, @curta/sdk/wasm, is the raw curta_core.wasm file.
The Curta client
new Curta(options: CurtaOptions)
interface CurtaOptions {
connection: Connection
wallet?: Wallet | Keypair
programId?: PublicKey
wasm?: CoreWasm
}
interface Wallet {
publicKey: PublicKey
signTransaction(transaction: Transaction): Promise<Transaction>
}connectionConnection- The RPC connection every read and every transaction goes through. Reads use the
confirmedcommitment. walletWallet | Keypairoptional- The signer and fee payer of every transaction the client sends. A
Keypairsigns in process. AWalletis any object with apublicKeyand asignTransactionmethod, which is the shape a browser wallet adapter has. A client without a wallet reads, quotes and replays; a method that sends throwsThis operation requires a wallet. programIdPublicKeydefault DEFAULT_PROGRAM_ID- The address of the
curtaprogram. Every account read checks that the account is owned by this program. wasmCoreWasmoptional- A core instance to use for quotes, turn planning and replay. Without it the client loads the packaged core on first use and keeps it.
The instance exposes three read-only properties: connection, programId and wallet.
| Group | Methods |
|---|---|
| Runs | quote, openRun, run, turn, fund, cancel, settle, close |
| Reads | getConfig, getModel, getRun, getRunHead, getWeights |
| Replay | replay |
| Models | publish, seal, setRoyalty, claim |
| Transactions | send |
Run methods
Four of these take the same parameters, RunParameters:
interface RunParameters {
model: PublicKey
prompt: string
maxTokens?: number
temperature?: number
seed?: bigint
}modelPublicKey- The address of a sealed model.
promptstring- The text the model continues. At most 1,024 bytes of UTF-8.
maxTokensnumberdefault 32- The most tokens the run produces, from 1 to the model’s context length.
temperaturenumberdefault 0- From 0 to 16. The client converts it once to fixed point with 16 fractional bits through
temperatureQ16. seedbigintdefault 1n- The 64-bit seed of the sampler.
quote
quote(params: RunParameters): Promise<Quote>
interface Quote {
turns: bigint
turnRewards: bigint
royalties: bigint
protocolShare: bigint
totalBudget: bigint
runAccountSize: number
runAccountRentLamports: bigint
}Computes what a run costs before it opens. It reads the model, the Config account and the model’s section zero weight account, then counts turns with the packaged core for this exact prompt. It sends nothing and takes no wallet. It throws Model is not sealed for a model that is still uploading.
turnsbigint- The turns the run takes when it produces
maxTokenstokens, tokenizing the prompt included. turnRewardsbigint- The turns times the turn reward in
Config. royaltiesbigintmaxTokenstimes the model’s royalty per token.protocolSharebigint- The protocol share on the two lines above: their sum times the protocol share in basis points, divided by 10,000 and rounded down.
totalBudgetbigint- The sum of the three: the budget that carries the run to its last token.
runAccountSizenumber- The run account’s size in bytes: the 256-byte header plus the core’s working state for this prompt length and
maxTokens. Onstories260Kwith the prompt “Once upon a time” and 12 tokens it is 48,304. runAccountRentLamportsbigint- The rent-exempt minimum for an account of that size, read from the cluster. It returns when the run closes.
import { curta, model } from './setup'
const q = await curta.quote({ model, prompt: 'Once upon a time', maxTokens: 12 })
console.log(q.turns, q.totalBudget, q.runAccountSize, q.runAccountRentLamports)Fees explains each line of the budget.
openRun
openRun(
params: RunParameters & { budgetLamports?: bigint; runKeypair?: Keypair },
): Promise<{ address: PublicKey; signature: string; quote: Quote }>Opens a run and returns as soon as the opening transaction confirms. The transaction holds two instructions: a System createAccount that creates the run account at runAccountSize bytes, owned by the program and holding the rent plus the budget, and open_run. The wallet and the run’s keypair both sign.
budgetLamportsbigintdefault quote.totalBudget- The budget deposited in the run account above its rent.
runKeypairKeypairdefault a new keypair- The keypair whose public key becomes the run’s address.
addressPublicKey- The run account.
signaturestring- The signature of the opening transaction.
quoteQuote- The quote the run was opened on.
import { curta, model } from './setup'
const { address, signature, quote } = await curta.openRun({
model,
prompt: 'Once upon a time',
maxTokens: 12,
temperature: 0,
seed: 42n,
})
console.log(address.toBase58(), signature, quote.turns)run
run(
params: RunParameters & {
onTurn?: (run: RunAccount) => void | Promise<void>
signal?: AbortSignal
},
): Promise<{ text: string; address: PublicKey; tokens: number[] }>Opens a run with openRun, then waits while crankers turn it. It reads the run’s head every 200 milliseconds and resolves when the state is complete. When the run becomes starved or cancelled it throws Run is starved or Run is cancelled, followed by the run’s address.
onTurn(run: RunAccount) => void | Promise<void>optional- Called with the whole run account on every read while the run is running, so a caller can draw text as it appears.
signalAbortSignaloptional- Ends the wait. The run stays open on chain.
textstring- The model’s output, read from the run account.
addressPublicKey- The run account.
tokensnumber[]- The ids of the tokens the model produced.
import { curta, model } from './setup'
const { text, address, tokens } = await curta.run({
model,
prompt: 'Once upon a time',
maxTokens: 12,
onTurn: (run) => process.stdout.write('\r' + run.text),
})
console.log('\n' + address.toBase58(), tokens.length, text)turn
turn(address: PublicKey): Promise<string>Lands one turn on a run and returns the transaction’s signature. It reads the run’s head, asks the core’s planner which weight accounts the next turn reads, and sends turn with the run’s current turn count as expectedTurn. The wallet pays the fee and receives the turn reward. It throws Run is starved, Run is complete or Run is cancelled for a run that is not running.
import { PublicKey } from '@solana/web3.js'
import { curta } from './setup'
const run = new PublicKey(process.argv[3])
for (;;) {
const head = await curta.getRunHead(run)
if (head.status !== 'running') break
console.log(await curta.turn(run))
}fund, cancel, settle, close
fund(run: PublicKey, amountLamports: bigint): Promise<string>
cancel(run: PublicKey): Promise<string>
settle(address: PublicKey): Promise<string>
close(run: PublicKey): Promise<string>Each sends one instruction and returns the signature.
fundany wallet- Transfers
amountLamportsfrom the wallet into the run’s budget. A starved run returns to running when its budget reachesnextTurnCost, so the top-up that resumes it isnextTurnCost - budgetRemainingor more. cancelthe opener- Stops a running or starved run. What it has accrued stays owed and moves at settlement.
settleany wallet- Settles a complete or cancelled run: the accrued royalties move to the model’s vault and the accrued protocol share to the fee account. The method reads the run to find its model.
closethe opener- Closes a settled run. Every lamport left in the account, unspent budget and rent, returns to the opener.
import { PublicKey } from '@solana/web3.js'
import { curta } from './setup'
const run = new PublicKey(process.argv[3])
const head = await curta.getRunHead(run)
if (head.status === 'starved') await curta.fund(run, head.nextTurnCost - head.budgetRemaining)
if ((head.status === 'complete' || head.status === 'cancelled') && !head.settled) await curta.settle(run)
if ((await curta.getRunHead(run)).settled) await curta.close(run)Read methods
Each read fetches one account at the confirmed commitment, checks its owner and its eight-byte tag, and decodes it. A missing account throws Account not found; an account owned by another program throws Unexpected account owner.
getConfig(): Promise<ConfigAccount>
getModel(address: PublicKey): Promise<ModelAccount>
getRun(address: PublicKey): Promise<RunAccount>
getRunHead(address: PublicKey): Promise<{
model: PublicKey
status: string
settled: boolean
turns: bigint
turnReward: bigint
rewardsPaid: bigint
budgetRemaining: bigint
nextTurnCost: bigint
core: Uint8Array
}>
getWeights(model: ModelAccount): Promise<Uint8Array[]>getConfigConfigAccount- The program’s
Configaccount, found atconfigAddress(programId). getModelModelAccount- A model: publisher, configuration, royalty, weights hash, sealing progress and weight accounts.
getRunRunAccount- A whole run account: the header, the working state, and the prompt, token ids and text decoded from it.
getRunHeadobject- The first 384 bytes of a run account: enough for the state, the budget and the next turn’s plan. A cranker reads this between turns.
getWeightsUint8Array[]- The payload of every weight account of a model, in section order. The result is kept per model for the life of the client.
The fields of the object getRunHead returns:
modelPublicKey- The model the run is on.
statusstringrunning,starved,completeorcancelled.settledboolean- Whether the run has settled.
turnsbigint- The turns landed. The next turn names this number as
expectedTurn. turnRewardbigint- The turn reward the run pays, fixed when it opened.
rewardsPaidbigint- The turn rewards paid so far.
budgetRemainingbigint- The budget left.
nextTurnCostbigint- What the next turn reserves from the budget before it computes.
coreUint8Array- The 128-byte header of the working state, which
CoreWasm.planTurnreads.
import { PublicKey } from '@solana/web3.js'
import { curta, model } from './setup'
const config = await curta.getConfig()
const m = await curta.getModel(model)
console.log(config.turnReward, config.protocolShareBps, m.name, m.sealed, m.royalty, m.sections.length)
const run = await curta.getRun(new PublicKey(process.argv[3]))
console.log(run.status, run.turns, run.budgetRemaining, run.prompt, run.tokens, run.text)replay
replay(address: PublicKey): AsyncGenerator<ReplayToken>
interface ReplayToken {
index: number
token: number
chainToken: number | undefined
matches: boolean
text: string
}Recomputes a run on your machine. It reads the run, the model and every weight account, starts a core session with the run’s prompt, maxTokens, temperature and seed, and turns it. It yields one ReplayToken per token as the session produces it. It takes no wallet.
indexnumber- The token’s position in the output, from zero.
tokennumber- The token id the replay computed.
chainTokennumber | undefined- The token id the run account holds at that position.
matchesboolean- Whether the two are equal.
textstring- The replay’s text up to the turn that produced this token.
import { PublicKey } from '@solana/web3.js'
import { curta } from './setup'
for await (const t of curta.replay(new PublicKey(process.argv[3]))) {
console.log(t.index, t.token, t.chainToken, t.matches, t.text)
}Replay and determinism covers why the two always agree.
Model methods
publish
publish(options: PublishOptions): Promise<PublishResult>
interface PublishOptions {
artifact: Uint8Array
name: string
royaltyLamports?: bigint
resume?: PublishState
onState?: (state: PublishState) => void | Promise<void>
onProgress?: (written: number, total: number) => void
}
interface PublishState {
version: 1
modelKey: number[]
weightKeys: number[][]
hash: string
}
interface PublishResult {
model: PublicKey
state: PublishState
weightBytes: number
rentLamports: bigint
transactions: number
elapsedMs: number
}Creates a model and its weight accounts and writes the weights. It decodes the artifact, computes its weights hash, creates the Model account with create_model, creates one weight account per section, and sends write_weights in chunks of 800 bytes, 24 at a time. Each weight account keeps a bitmap of the chunks it holds, so a second call with the same state reads the bitmap and writes only what is absent.
artifactUint8Array- A converted weight file, as
curta convertorCoreWasm.convertwrites it. namestring- The model’s name, at most 64 bytes of UTF-8.
royaltyLamportsbigintdefault 0n- The royalty per token.
resumePublishStateoptional- The state of an earlier call for the same artifact. A state whose hash differs throws
Resume state belongs to another artifact. onState(state) => void | Promise<void>optional- Called with the publish state before the first account is created and again when writing ends. The state holds the secret keys of the model and weight accounts: save it to a private file.
onProgress(written, total) => voidoptional- Called after each chunk with the bytes written and the artifact’s size.
modelPublicKey- The model’s address.
statePublishState- The state to pass as
resume. weightBytesnumber- The artifact’s size in bytes.
rentLamportsbigint- The rent deposited by this call in the accounts it created.
transactionsnumber- The transactions this call sent.
elapsedMsnumber- The time the call took, in milliseconds.
seal, setRoyalty, claim
seal(address: PublicKey): Promise<number>
setRoyalty(model: PublicKey, royaltyLamports: bigint): Promise<string>
claim(model: PublicKey, amountLamports?: bigint): Promise<string>sealthe publisher- Sends
seal_modeluntil the model is sealed, each transaction checking the next 65,536 bytes of weights against the hash. Returns the count of transactions it sent. setRoyaltythe publisher- Sets the royalty per token in lamports. Runs opened afterwards pay it. Returns the signature.
claimthe publisher- Moves royalties from the model’s vault to the publisher’s wallet.
amountLamportsdefaults to0n, which claims everything above the vault’s rent. Returns the signature.
import { readFileSync, writeFileSync } from 'node:fs'
import { curta } from './setup'
const result = await curta.publish({
artifact: new Uint8Array(readFileSync('stories260K.curta')),
name: 'stories260K',
royaltyLamports: 100n,
onState: (state) => writeFileSync('stories260K.upload.json', JSON.stringify(state)),
onProgress: (written, total) => process.stderr.write(written + '/' + total + '\n'),
})
console.log(result.model.toBase58(), await curta.seal(result.model))Publish a model walks through the whole job and Weight format describes the artifact.
send
send(instructions: TransactionInstruction[], signers?: Signer[]): Promise<string>Signs, sends and confirms one transaction, and returns its signature. Every method that writes goes through it, and it is public so your own instructions travel the same path. The transaction opens with a compute unit limit of 1,400,000, names the wallet as fee payer and uses the latest blockhash. The wallet signs first, then each entry of signers.
After sending, it reads the signature’s status every 100 milliseconds and sends the same bytes again every 2 seconds until the status is confirmed or finalized. A transaction the chain refuses throws Transaction … failed with the chain’s error; one whose blockhash runs out throws Transaction expired.
import { PublicKey } from '@solana/web3.js'
import { fundInstruction } from '@curta/sdk'
import { curta } from './setup'
const run = new PublicKey(process.argv[3])
const signature = await curta.send([
fundInstruction({ funder: curta.wallet!.publicKey, run, amountLamports: 50_000n, programId: curta.programId }),
])Functions
crank
crank(
client: Curta,
options?: {
signal?: AbortSignal
once?: boolean
onProgress?: (progress: CrankProgress) => void | Promise<void>
},
): Promise<{ turnsLanded: number; lamportsEarned: bigint }>
interface CrankProgress {
kind: 'turn' | 'settle'
run: PublicKey
signature: string
turnsLanded: number
lamportsEarned: bigint
}The cranker’s loop: it turns every running run in the order the runs opened and settles complete and cancelled ones. Run a cranker describes each pass.
signalAbortSignaloptional- Stops the loop. The call rejects with the signal’s reason.
oncebooleandefault false- One pass through the queue, then the call resolves with its totals.
onProgress(progress: CrankProgress) => void | Promise<void>optional- Called after every transaction the cranker lands, with the totals since the call began.
The seven named exports
run(client, params) // client.run(params)
openRun(client, params) // client.openRun(params)
turn(client, address) // client.turn(address)
quote(client, params) // client.quote(params)
getRun(client, address) // client.getRun(address)
getModel(client, address) // client.getModel(address)
replay(client, address) // client.replay(address)Each takes the client first and calls the method of the same name.
temperatureQ16 and weightsCommitment
temperatureQ16(temperature: number): number
weightsCommitment(sections: Uint8Array[]): Promise<Uint8Array>temperatureQ16number- Converts a temperature to the fixed-point value the program stores: the temperature times 65,536, rounded.
1becomes65536. A value outside 0 to 16 throws aRangeError. weightsCommitmentPromise<Uint8Array>- The 32-byte weights hash of an artifact’s sections, the value
create_modeldeclares and sealing verifies. It starts from 32 zero bytes and, for each 65,536-byte chunk of each section in order, becomes the SHA-256 of the previous value followed by the chunk.
import { readFileSync } from 'node:fs'
import { decodeArtifact, weightsCommitment } from '@curta/sdk'
const artifact = decodeArtifact(new Uint8Array(readFileSync('stories260K.curta')))
const hash = await weightsCommitment(artifact.sections)
console.log(Buffer.from(hash).toString('hex'))Account types
The decoded form of each account. Accounts gives the byte layout behind every field. address is present when the decoder was given one, which every client read does.
ConfigAccount
addressPublicKeyoptional- The
Configaccount. authorityPublicKey- The wallet that sets the turn reward and the protocol share.
feeVaultPublicKey- The fee account, where the protocol share arrives.
turnRewardbigint- The turn reward in lamports that a run opened now pays per turn.
protocolShareBpsnumber- The protocol share in basis points, from 0 to 10,000.
ModelAccount, ModelConfig and WeightSection
addressPublicKeyoptional- The
Modelaccount. publisherPublicKey- The wallet that published the model.
vaultPublicKey- The model’s vault, where its royalties arrive.
configModelConfig- The architecture:
dim,hiddenDim,layers,heads,kvHeads,vocabSizeandcontextLength, each a number. sealedboolean- Whether the model is sealed. Runs open on sealed models.
sealingboolean- Whether sealing has begun. From then on the weights take no more writes.
sealSectionnumber- The section the next
seal_modelreads. sealOffsetnumber- The byte offset in that section where the next
seal_modelstarts. sealStepbigint- The count of sealing steps done. The next
seal_modelnames it asexpectedStep. royaltybigint- The royalty per token in lamports.
expectedHashUint8Array- The 32-byte weights hash declared when the model was created.
runningHashUint8Array- The hash sealing has computed so far.
weightBytesnumber- The total bytes of weights across all sections.
runsbigint- The runs opened on the model.
tokensbigint- The tokens the model has produced in settled runs.
earnedbigint- The royalties settled to the model’s vault over its life.
createdSlotbigint- The slot the model was created in.
namestring- The model’s name.
sectionsWeightSection[]- The weight accounts in order. Each has
address,byteLength(the section’s size) andwritten(the bytes uploaded). Section zero holds the tokenizer and tables; the others hold matrices.
RunAccount and RunStatus
RunStatus is 'running' | 'starved' | 'complete' | 'cancelled'.
addressPublicKeyoptional- The run account.
openerPublicKey- The wallet or program address that opened the run.
modelPublicKey- The model the run is on.
configPublicKey- The
Configaccount the run opened under. statusRunStatus- The run’s state.
settledboolean- Whether the run has settled.
protocolShareBpsnumber- The protocol share in basis points, fixed when the run opened.
maxTokensnumber- The most tokens the run produces.
temperatureQ16number- The temperature in fixed point: divide by 65,536.
promptLengthnumber- The prompt’s length in bytes.
seedbigint- The seed the run opened with.
turnRewardbigint- The turn reward, fixed when the run opened.
royaltybigint- The royalty per token, fixed when the run opened.
fundedbigint- The budget deposited over the run’s life: the opening budget plus every
fund. budgetRemainingbigint- The budget left after rewards paid and amounts accrued.
rewardsPaidbigint- The turn rewards paid to crankers.
royaltiesAccruedbigint- The royalties owed to the model’s vault, moved at settlement.
protocolAccruedbigint- The protocol share owed to the fee account, moved at settlement.
turnsbigint- The turns landed.
generatednumber- The tokens produced.
coreLengthnumber- The size of the working state in bytes.
openedSlotbigint- The slot the run opened in. The cranker’s queue orders by it.
lastSlotbigint- The slot of the run’s latest turn.
nextTurnCostbigint- What the next turn reserves from the budget. Zero on a complete run.
coreUint8Array- The working state: the forward pass in progress, the attention cache, the prompt and the output.
promptstring- The prompt, decoded from the working state.
tokensnumber[]- The ids of the tokens produced so far.
textstring- The output text so far.
Instruction builders
One builder per instruction of the program. Each takes one object and returns a TransactionInstruction. Each accepts an optional programId, which defaults to DEFAULT_PROGRAM_ID, derives the program’s own addresses (Config, the fee account, the model’s vault) itself, and appends the event authority and the program account after the accounts listed. Instructions has every account with its signer and writable flags and every argument’s encoding.
| Builder | Tag | Parameters | Accounts it sets, in order |
|---|---|---|---|
| initConfigInstruction | 0 | authority: PublicKey, turnRewardLamports: bigint, protocolShareBps: number | authority, config, feeVault, systemProgram, programData |
| setConfigInstruction | 1 | authority: PublicKey, turnRewardLamports: bigint, protocolShareBps: number | authority, config |
| createModelInstruction | 2 | publisher: PublicKey, model: PublicKey, config: ModelConfig, name: string, royaltyLamports: bigint, expectedHash: Uint8Array, sections: { address: PublicKey; byteLength: number }[] | publisher, model, modelVault, systemProgram |
| writeWeightsInstruction | 3 | publisher: PublicKey, model: PublicKey, weights: PublicKey, section: number, offset: number, bytes: Uint8Array | publisher, model, weights |
| sealModelInstruction | 4 | publisher: PublicKey, model: PublicKey, weights: PublicKey, expectedStep: bigint | publisher, model, weights |
| setRoyaltyInstruction | 5 | publisher: PublicKey, model: PublicKey, royaltyLamports: bigint | publisher, model |
| claimRoyaltiesInstruction | 6 | publisher: PublicKey, model: PublicKey, amountLamports?: bigint | publisher, model, modelVault |
| openRunInstruction | 7 | opener: PublicKey, model: PublicKey, run: PublicKey, metadata: PublicKey, prompt: string, maxTokens: number, temperatureQ16: number, seed: bigint, budgetLamports: bigint | opener, model, run, config, metadata, systemProgram |
| turnInstruction | 8 | cranker: PublicKey, run: PublicKey, model: PublicKey, metadata: PublicKey, weights: PublicKey, additionalWeights?: PublicKey[], expectedTurn: bigint, nonce?: bigint | cranker, run, model, config, metadata, weights, then additionalWeights when given |
| fundInstruction | 9 | funder: PublicKey, run: PublicKey, amountLamports: bigint | funder, run, systemProgram |
| cancelInstruction | 10 | opener: PublicKey, run: PublicKey | opener, run |
| settleInstruction | 11 | caller: PublicKey, run: PublicKey, model: PublicKey | caller, run, model, modelVault, feeVault, config |
| closeRunInstruction | 12 | opener: PublicKey, run: PublicKey | opener, run |
createModelInstructionname, expectedHash- Throws
Invalid model name or hashwhen the name is longer than 64 bytes of UTF-8 or the hash is not 32 bytes. claimRoyaltiesInstructionamountLamports- Defaults to
0n, which claims everything above the vault’s rent. openRunInstructionmetadata, temperatureQ16- The model’s section zero weight account,
model.sections[0].address, and the temperature already converted withtemperatureQ16. The run account exists before this instruction: a SystemcreateAccountearlier in the same transaction creates it. turnInstructionweights, additionalWeights, nonce- The first matrix section the turn reads and, when the turn reads two, the second as the single entry of
additionalWeights. More than one entry throwsA turn can name at most two matrix sections.CoreWasm.planTurnnames the sections; a turn that reads no matrix passes the metadata account again asweights.noncedefaults to a fresh number, which makes each turn transaction distinct.
import { PublicKey } from '@solana/web3.js'
import { loadCore, turnInstruction } from '@curta/sdk'
import { curta } from './setup'
const address = new PublicKey(process.argv[3])
const head = await curta.getRunHead(address)
const m = await curta.getModel(head.model)
const plan = (await loadCore()).planTurn(m.config, head.core)
const ix = turnInstruction({
cranker: curta.wallet!.publicKey,
run: address,
model: head.model,
metadata: m.sections[0].address,
weights: m.sections[plan.sections[0] ?? 0].address,
additionalWeights: plan.sections.slice(1).map((s) => m.sections[s].address),
expectedTurn: head.turns,
programId: curta.programId,
})
console.log(await curta.send([ix]))This is the transaction curta.turn builds.
Decoders
Accounts
decodeConfig(data: Uint8Array, address?: PublicKey): ConfigAccount
decodeModel(data: Uint8Array, address?: PublicKey): ModelAccount
decodeRun(data: Uint8Array, address?: PublicKey): RunAccount
decodeWeights(data: Uint8Array, model?: PublicKey, section?: number): Uint8Array
decodeFeeVault(data: Uint8Array): { kind: 'feeVault' }
decodeModelVault(data: Uint8Array): { kind: 'modelVault' }
decodeArtifact(bytes: Uint8Array): Artifact
decodeCoreOutput(core: Uint8Array): { prompt: string; tokens: number[]; text: string }Each account decoder takes an account’s raw data, checks its eight-byte tag and its length, and throws Invalid … account otherwise.
decodeWeightsUint8Array- Returns a weight account’s payload, the bytes after its 2,048-byte header. With
modelorsectiongiven, it checks them against the header and throwsWeight section does not belong to modelon a difference. decodeArtifactArtifact- Reads a converted weight file into
{ config: ModelConfig; sections: Uint8Array[] }. The sections are views into the file, in order. decodeCoreOutput{ prompt, tokens, text }- Reads the prompt, the token ids and the output text from a run’s working state.
decodeRuncalls it for the same three fields. decodeFeeVault, decodeModelVault{ kind }- Check that the data is the fee account or a model’s vault. A vault holds only its tag; its balance is the account’s lamports.
import { Connection, PublicKey } from '@solana/web3.js'
import { decodeRun } from '@curta/sdk'
const connection = new Connection(process.env.CURTA_RPC_URL!, 'confirmed')
const address = new PublicKey(process.argv[2])
const account = await connection.getAccountInfo(address)
const run = decodeRun(account!.data, address)
console.log(run.status, run.text)Instructions
decodeInstruction(bytes: Uint8Array): DecodedInstruction | null
interface DecodedInstruction {
name: InstructionName
args: Fields
accounts: string[]
}Decodes the data of one instruction sent to the program. name is one of instructionNames, accounts names each account of the instruction in order, ending with eventAuthority and program, and args holds the arguments. 64-bit values are decimal strings. It returns null for empty data, for an event’s inner instruction and for an unknown tag.
| name | Keys of args |
|---|---|
| init_config | turnReward, protocolShareBps, configBump, feeBump |
| set_config | turnReward, protocolShareBps |
| create_model | royalty, config, name, expectedHash, sections (address and byteLength each) |
| write_weights | section, offset, byteLength |
| seal_model | expectedStep |
| set_royalty | amount |
| claim_royalties | amount |
| open_run | maxTokens, temperatureQ16, seed, budget, prompt |
| turn | expectedTurn, nonce, weightAccountCount |
| fund | amount |
| cancel | none |
| settle | none |
| close_run | none |
Events
decodeEvent(bytes: Uint8Array): CurtaEvent | null
interface CurtaEvent {
name: InstructionName
primary: string
actor: string
related: string
values: string[]
tokens: number[]
data: Fields
}Decodes the body of an event, with or without its eight-byte tag in front. Every state change emits one: the instruction’s name, three addresses, six 64-bit values as decimal strings, and the ids of the tokens the instruction completed. data repeats those five fields and adds named ones for the instruction:
| name | Named keys of data |
|---|---|
| create_model | model, publisher, royalty, weightBytes, sectionCount |
| write_weights | model, section, offset, byteLength, written |
| open_run | run, opener, model, budget, maxTokens, temperatureQ16, seed, status |
| turn | run, cranker, model, turn, reward, royalty, protocolShare, budgetRemaining, status |
| fund | run, amount, budgetRemaining, status |
| settle | run, royalty, protocolShare, rewards, generated |
| close_run | run, refund |
status is the run’s state as a number: 0 running, 1 starved, 2 complete, 3 cancelled. Events gives the wire layout and the meaning of every value.
curtaProgramDecoder(programId?: PublicKey): {
programId: string
eventAuthority: string
instruction: (bytes: Uint8Array) => DecodedInstruction | null
event: (bytes: Uint8Array) => { name: InstructionName; data: Fields } | null
}curtaProgramDecoder bundles both decoders with the program’s address and its event authority, the shape an indexer takes. Fields is Record<string, JsonValue> and JsonValue is any JSON value.
Errors
decodeProgramError(code: number): string | nullReturns the name of a program error from its number, or null for a number outside the table. The numbers start at 6000 in the order of PROGRAM_ERRORS. Errors says what raises each.
| Code | Name |
|---|---|
| 6000 | InvalidInstruction |
| 6001 | InvalidAccount |
| 6002 | MissingSignature |
| 6003 | InvalidOwner |
| 6004 | InvalidSize |
| 6005 | AlreadyInitialized |
| 6006 | InvalidAuthority |
| 6007 | InvalidState |
| 6008 | InvalidConfig |
| 6009 | InvalidModel |
| 6010 | InvalidWeights |
| 6011 | InvalidOffset |
| 6012 | HashMismatch |
| 6013 | ArithmeticOverflow |
| 6014 | InsufficientBudget |
| 6015 | StaleTurn |
| 6016 | CoreError |
| 6017 | NotSettled |
| 6018 | InvalidEvent |
| 6019 | InsufficientFunds |
Address helpers
configAddress(programId?: PublicKey): [PublicKey, number]
feeVaultAddress(programId?: PublicKey): [PublicKey, number]
modelVaultAddress(model: PublicKey, programId?: PublicKey): [PublicKey, number]
eventAuthorityAddress(programId?: PublicKey): [PublicKey, number]
programDataAddress(programId?: PublicKey): PublicKeyThe first four return a program-derived address and its bump. programId defaults to DEFAULT_PROGRAM_ID.
| Helper | Seeds | The account |
|---|---|---|
| configAddress | "config" | The Config account. |
| feeVaultAddress | "fee" | The fee account. |
| modelVaultAddress | "model-vault", model | A model’s royalty vault. |
| eventAuthorityAddress | "__event_authority" | The address that signs the program’s events. Every instruction takes it. |
| programDataAddress | program id | The program’s data account under the upgradeable loader, which init_config takes. |
import { PublicKey } from '@solana/web3.js'
import { configAddress, feeVaultAddress, modelVaultAddress } from '@curta/sdk'
const [config] = configAddress()
const [feeVault] = feeVaultAddress()
const [vault, bump] = modelVaultAddress(new PublicKey(process.argv[2]))
console.log(config.toBase58(), feeVault.toBase58(), vault.toBase58(), bump)Constants
DEFAULT_PROGRAM_IDPublicKey- The
curtaprogram:De7awjrzJXdgcozSjQshCJej2xzfDasPRBzTrw7PegFn. CONFIG_SIZE128- The size of the
Configaccount in bytes. MODEL_SIZE4096- The size of a
Modelaccount in bytes. RUN_HEADER_SIZE256- The size of a run account’s header. The working state follows it.
WEIGHTS_HEADER_SIZE2048- The size of a weight account’s header. The payload follows it.
instructionNamesreadonly string[]- The thirteen instruction names, indexed by tag: init_config, set_config, create_model, write_weights, seal_model, set_royalty, claim_royalties, open_run, turn, fund, cancel, settle, close_run.
InstructionNameis their union. instructionAccountsreadonly string[][]- The account names of each instruction, indexed by tag, without the two every instruction ends with.
EVENT_CPI_TAGUint8Array- The eight bytes that open an event’s inner instruction:
e445a52e51cb9a1d. PROGRAM_ERRORSreadonly string[]- The twenty error names, indexed from code 6000.
The core in WebAssembly
The package’s copy of curta-core, the code the program runs, compiled to WebAssembly. The client uses it for quotes, turn plans and replay, and it is exported for the same uses in your code.
loadCore(bytes?: BufferSource): Promise<CoreWasm>
class CoreWasm {
static load(bytes?: BufferSource): Promise<CoreWasm>
stateSize(config: ModelConfig, promptLength: number, maxTokens: number): number
quote(config: ModelConfig, metadata: Uint8Array, prompt: string, maxTokens: number, temperatureQ16?: number): bigint
planTurn(config: ModelConfig, state: Uint8Array): TurnPlan
requiredSection(config: ModelConfig, state: Uint8Array): number
createSession(config: ModelConfig, sections: Uint8Array[], prompt: string, maxTokens: number, temperatureQ16: number, seed: bigint): CoreSession
convert(checkpoint: Uint8Array, tokenizer: Uint8Array): Uint8Array
}
interface TurnPlan {
sections: number[]
estimatedCu: number
maxGenerated: number
}
class CoreSession {
turn(): Uint8Array
state(): Uint8Array
close(): void
}loadCorePromise<CoreWasm>- Without an argument, instantiates the packaged core once and returns the same instance to every caller. With the bytes of a core build, instantiates those.
stateSizenumber- The size in bytes of a run’s working state for a prompt of
promptLengthbytes andmaxTokenstokens. A run account isRUN_HEADER_SIZEplus this. It throwsInvalid run parametersfor a prompt over 1,024 bytes or amaxTokensoutside 1 to the context length. quotebigint- The turns a run takes.
metadatais the payload of the model’s section zero. planTurnTurnPlan- The next turn of a run, from its working state or the first 128 bytes of it.
sectionsholds the indexes of the matrix sections the turn reads, at most two;estimatedCuis the planner’s estimate of the turn’s compute units;maxGeneratedis the most tokens the turn can complete. requiredSectionnumber- The index of the first section the next turn reads.
createSessionCoreSession- A run held in memory, with all of a model’s sections loaded.
turn()advances it by one turn and returns the working state up to the end of the output,state()returns the whole working state, andclose()frees it.decodeCoreOutputreads either. convertUint8Array- Converts a llama2.c checkpoint and its tokenizer file into a weight artifact. It throws
Checkpoint conversion failedfor a file it cannot read.
import { decodeCoreOutput, loadCore } from '@curta/sdk'
import { curta, model } from './setup'
const m = await curta.getModel(model)
const sections = await curta.getWeights(m)
const core = await loadCore()
const session = core.createSession(m.config, sections, 'Once upon a time', 12, 0, 42n)
let state = session.turn()
while (new DataView(state.buffer).getUint32(8, true) !== 20) state = session.turn()
console.log(decodeCoreOutput(state).text)
session.close()The loop runs the model to its last token on your machine with the weights the chain holds: the working state’s phase, the 32-bit value at byte 8, reads 20 once the run is complete. Inference core describes the working state and the phases of a turn.