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.

setup.ts
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

ts
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 confirmed commitment.
walletWallet | Keypairoptional
The signer and fee payer of every transaction the client sends. A Keypair signs in process. A Wallet is any object with a publicKey and a signTransaction method, which is the shape a browser wallet adapter has. A client without a wallet reads, quotes and replays; a method that sends throws This operation requires a wallet.
programIdPublicKeydefault DEFAULT_PROGRAM_ID
The address of the curta program. 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.

GroupMethods
Runsquote, openRun, run, turn, fund, cancel, settle, close
ReadsgetConfig, getModel, getRun, getRunHead, getWeights
Replayreplay
Modelspublish, seal, setRoyalty, claim
Transactionssend

Run methods

Four of these take the same parameters, RunParameters:

ts
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

ts
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 maxTokens tokens, tokenizing the prompt included.
turnRewardsbigint
The turns times the turn reward in Config.
royaltiesbigint
maxTokens times 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. On stories260K with 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.
quote.ts
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

ts
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.
open.ts
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

ts
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.
run.ts
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

ts
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.

turn.ts
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

ts
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 amountLamports from the wallet into the run’s budget. A starved run returns to running when its budget reaches nextTurnCost, so the top-up that resumes it is nextTurnCost - budgetRemaining or 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.
finish.ts
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.

ts
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 Config account, found at configAddress(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.
statusstring
running, starved, complete or cancelled.
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.planTurn reads.
read.ts
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

ts
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.
replay.ts
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

ts
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 convert or CoreWasm.convert writes 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

ts
seal(address: PublicKey): Promise<number>
setRoyalty(model: PublicKey, royaltyLamports: bigint): Promise<string>
claim(model: PublicKey, amountLamports?: bigint): Promise<string>
sealthe publisher
Sends seal_model until 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. amountLamports defaults to 0n, which claims everything above the vault’s rent. Returns the signature.
publish.ts
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

ts
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.

send.ts
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

ts
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

ts
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

ts
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. 1 becomes 65536. A value outside 0 to 16 throws a RangeError.
weightsCommitmentPromise<Uint8Array>
The 32-byte weights hash of an artifact’s sections, the value create_model declares 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.
hash.ts
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 Config account.
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 Model account.
publisherPublicKey
The wallet that published the model.
vaultPublicKey
The model’s vault, where its royalties arrive.
configModelConfig
The architecture: dim, hiddenDim, layers, heads, kvHeads, vocabSize and contextLength, 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_model reads.
sealOffsetnumber
The byte offset in that section where the next seal_model starts.
sealStepbigint
The count of sealing steps done. The next seal_model names it as expectedStep.
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) and written (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 Config account 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.

BuilderTagParametersAccounts it sets, in order
initConfigInstruction0authority: PublicKey, turnRewardLamports: bigint, protocolShareBps: numberauthority, config, feeVault, systemProgram, programData
setConfigInstruction1authority: PublicKey, turnRewardLamports: bigint, protocolShareBps: numberauthority, config
createModelInstruction2publisher: PublicKey, model: PublicKey, config: ModelConfig, name: string, royaltyLamports: bigint, expectedHash: Uint8Array, sections: { address: PublicKey; byteLength: number }[]publisher, model, modelVault, systemProgram
writeWeightsInstruction3publisher: PublicKey, model: PublicKey, weights: PublicKey, section: number, offset: number, bytes: Uint8Arraypublisher, model, weights
sealModelInstruction4publisher: PublicKey, model: PublicKey, weights: PublicKey, expectedStep: bigintpublisher, model, weights
setRoyaltyInstruction5publisher: PublicKey, model: PublicKey, royaltyLamports: bigintpublisher, model
claimRoyaltiesInstruction6publisher: PublicKey, model: PublicKey, amountLamports?: bigintpublisher, model, modelVault
openRunInstruction7opener: PublicKey, model: PublicKey, run: PublicKey, metadata: PublicKey, prompt: string, maxTokens: number, temperatureQ16: number, seed: bigint, budgetLamports: bigintopener, model, run, config, metadata, systemProgram
turnInstruction8cranker: PublicKey, run: PublicKey, model: PublicKey, metadata: PublicKey, weights: PublicKey, additionalWeights?: PublicKey[], expectedTurn: bigint, nonce?: bigintcranker, run, model, config, metadata, weights, then additionalWeights when given
fundInstruction9funder: PublicKey, run: PublicKey, amountLamports: bigintfunder, run, systemProgram
cancelInstruction10opener: PublicKey, run: PublicKeyopener, run
settleInstruction11caller: PublicKey, run: PublicKey, model: PublicKeycaller, run, model, modelVault, feeVault, config
closeRunInstruction12opener: PublicKey, run: PublicKeyopener, run
createModelInstructionname, expectedHash
Throws Invalid model name or hash when 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 with temperatureQ16. The run account exists before this instruction: a System createAccount earlier 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 throws A turn can name at most two matrix sections. CoreWasm.planTurn names the sections; a turn that reads no matrix passes the metadata account again as weights. nonce defaults to a fresh number, which makes each turn transaction distinct.
manual-turn.ts
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

ts
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 model or section given, it checks them against the header and throws Weight section does not belong to model on 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. decodeRun calls 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.
decode.ts
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

ts
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.

nameKeys of args
init_configturnReward, protocolShareBps, configBump, feeBump
set_configturnReward, protocolShareBps
create_modelroyalty, config, name, expectedHash, sections (address and byteLength each)
write_weightssection, offset, byteLength
seal_modelexpectedStep
set_royaltyamount
claim_royaltiesamount
open_runmaxTokens, temperatureQ16, seed, budget, prompt
turnexpectedTurn, nonce, weightAccountCount
fundamount
cancelnone
settlenone
close_runnone

Events

ts
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:

nameNamed keys of data
create_modelmodel, publisher, royalty, weightBytes, sectionCount
write_weightsmodel, section, offset, byteLength, written
open_runrun, opener, model, budget, maxTokens, temperatureQ16, seed, status
turnrun, cranker, model, turn, reward, royalty, protocolShare, budgetRemaining, status
fundrun, amount, budgetRemaining, status
settlerun, royalty, protocolShare, rewards, generated
close_runrun, 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.

ts
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

ts
decodeProgramError(code: number): string | null

Returns 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.

CodeName
6000InvalidInstruction
6001InvalidAccount
6002MissingSignature
6003InvalidOwner
6004InvalidSize
6005AlreadyInitialized
6006InvalidAuthority
6007InvalidState
6008InvalidConfig
6009InvalidModel
6010InvalidWeights
6011InvalidOffset
6012HashMismatch
6013ArithmeticOverflow
6014InsufficientBudget
6015StaleTurn
6016CoreError
6017NotSettled
6018InvalidEvent
6019InsufficientFunds

Address helpers

ts
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): PublicKey

The first four return a program-derived address and its bump. programId defaults to DEFAULT_PROGRAM_ID.

HelperSeedsThe account
configAddress"config"The Config account.
feeVaultAddress"fee"The fee account.
modelVaultAddress"model-vault", modelA model’s royalty vault.
eventAuthorityAddress"__event_authority"The address that signs the program’s events. Every instruction takes it.
programDataAddressprogram idThe program’s data account under the upgradeable loader, which init_config takes.
addresses.ts
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 curta program: De7awjrzJXdgcozSjQshCJej2xzfDasPRBzTrw7PegFn.
CONFIG_SIZE128
The size of the Config account in bytes.
MODEL_SIZE4096
The size of a Model account 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. InstructionName is 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.

ts
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 promptLength bytes and maxTokens tokens. A run account is RUN_HEADER_SIZE plus this. It throws Invalid run parameters for a prompt over 1,024 bytes or a maxTokens outside 1 to the context length.
quotebigint
The turns a run takes. metadata is 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. sections holds the indexes of the matrix sections the turn reads, at most two; estimatedCu is the planner’s estimate of the turn’s compute units; maxGenerated is 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, and close() frees it. decodeCoreOutput reads either.
convertUint8Array
Converts a llama2.c checkpoint and its tokenizer file into a weight artifact. It throws Checkpoint conversion failed for a file it cannot read.
local-run.ts
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.