Skip to content

Installation

TypeCast runs on the JVM, JDK 17+, for Scala 3 (3.7+) and Java. The published artifacts are ordinary JVM jars: Scala consumers depend on them through sbt's %% operator; Java, Kotlin, and other JVM languages spell out the same coordinates from Gradle or Maven.

Pre-1.0 coordinates

Not yet on Maven Central. The coordinates below are the planned shape — organization io.typecast, version 0.1.0-SNAPSHOT until the first tag. Both the coordinates and the TypeCast name may change before launch (see the README status section).

Requirements

  • JDK 17 or newer. The build targets -java-output-version 17, and the Java facade relies on Java 17 records and sealed interfaces (it picks up Java 21 sealed pattern switches when present — see java.md).
  • Scala 3.7+ for Scala consumers. There is no Scala 2 support, by design.
  • A provider to extract against: an Anthropic or OpenAI API key, an OpenAI-compatible endpoint, or a local Ollama install. See Provider setup below.

Dependency coordinates

The core is zero-dependency (Scala stdlib only). You always add typecast-core, then one provider transport, then any optional modules you need (see the module table).

sbt

sbt's %% operator appends the Scala binary-compatibility suffix for you, so you write the bare module name:

libraryDependencies ++= Seq(
  "io.typecast" %% "typecast-core"      % "0.1.0-SNAPSHOT",
  "io.typecast" %% "typecast-anthropic" % "0.1.0-SNAPSHOT", // or typecast-openai
  "io.typecast" %% "typecast-iron"      % "0.1.0-SNAPSHOT"  // optional: refinement constraints
)

Gradle (Kotlin DSL)

dependencies {
    implementation("io.typecast:typecast-core_3:0.1.0-SNAPSHOT")
    implementation("io.typecast:typecast-anthropic_3:0.1.0-SNAPSHOT")
    implementation("io.typecast:typecast-java_3:0.1.0-SNAPSHOT") // Java/Kotlin facade
}

Gradle (Groovy DSL)

dependencies {
    implementation 'io.typecast:typecast-core_3:0.1.0-SNAPSHOT'
    implementation 'io.typecast:typecast-anthropic_3:0.1.0-SNAPSHOT'
    implementation 'io.typecast:typecast-java_3:0.1.0-SNAPSHOT' // Java/Kotlin facade
}

Maven

<dependency>
  <groupId>io.typecast</groupId>
  <artifactId>typecast-core_3</artifactId>
  <version>0.1.0-SNAPSHOT</version>
</dependency>
<dependency>
  <groupId>io.typecast</groupId>
  <artifactId>typecast-anthropic_3</artifactId>
  <version>0.1.0-SNAPSHOT</version>
</dependency>

The _3 suffix for non-sbt build tools

Outside sbt you write the _3 suffix explicitly: typecast-core_3, typecast-java_3, and so on. _3 is Scala's binary-compatibility suffix (Scala 3), baked into the artifactId by the publisher — the same convention as cats-core_3 or munit_3. sbt appends it automatically through %%; Gradle and Maven do not, so you spell it out.

The suffix says nothing about consumability. The jars are ordinary JVM jars, and the Java/Kotlin facade keeps Scala types out of its public signatures; the Scala standard library rides along as a regular transitive dependency, like any other jar. The standalone Kotlin/Gradle app in examples-realworld/kotlin-triage consumes typecast-java_3 and typecast-langchain4j_3 from a Gradle build to prove exactly this.

Modules

Add typecast-core plus one transport; reach for the rest as the feature you need requires.

Artifact Provides Add when
typecast-core The whole pipeline: JSON AST, tolerant parser, schema derivation, alignment, validation, the extraction state machine, report rendering. Zero external dependencies. Always.
typecast-derivation Additional derives-based derivation support that builds on core. Pulled in transitively by typecast-tools; add directly only if you need its surface without tools.
typecast-iron The typecast.iron.given bridge so Iron refinements (Int :| Positive, String :| ValidEmail) act as schema constraints, validators, and targeted-retry sources. Adds the iron dependency. You want compile-time refinement types inside derives LlmSchema.
typecast-anthropic The Anthropic transport (Anthropic.fromEnv(), AnthropicTransport(apiKey)), native structured outputs. Adds an HTTP client. Extracting against Anthropic / Claude.
typecast-openai The OpenAI transport and OpenAI-compatible factories (OpenAi(apiKey), Ollama(), OpenAiCompatible.jsonMode/promptOnly). Adds an HTTP client. Extracting against OpenAI, Ollama, vLLM, Groq, or any OpenAI-compatible endpoint.
typecast-runner-sync The synchronous runner. You want blocking, effect-free extraction (the simplest default).
typecast-runner-ce The cats-effect runner; carries fs2 streaming, so this is the module that provides StreamingExtract.stream[IO, A] (streaming.md). Adds cats-effect and fs2. You run on cats-effect / want streaming partial decode.
typecast-runner-zio The ZIO runner. Adds zio and zio-streams. You run on ZIO.
typecast-tools Typed tool dispatch: derives LlmTool, Tools(...), tc.withTools(...).run(...) (tools-and-mcp.md). Depends on typecast-derivation. You want the model to call typed tools.
typecast-mcp McpServer.from(tools).serveStdio() — export a tool registry as an MCP server. Depends on typecast-tools. You want to serve your tools over MCP.
typecast-java The Java/Kotlin facade: TypeCastClient, records, sealed interfaces, Jakarta Bean Validation annotations (java.md). Adds jakarta.validation-api and jackson-databind. You call TypeCast from Java, Kotlin, or another JVM language.
typecast-langchain4j Adapter that drives any LangChain4j ChatModel (20+ providers) through TypeCast. Depends on the Java facade plus both transports. Adds langchain4j. You want a provider only LangChain4j reaches.

Provider setup

The same user code runs on every provider; only the factory you pass to TypeCast(...) changes. The model identifier is required at construction — model names are provider-scoped and there is no sensible cross-provider default.

Anthropic

import typecast.anthropic.Anthropic
val tc = TypeCast(Anthropic.fromEnv(), model = "claude-haiku-4-5")

Anthropic.fromEnv() reads ANTHROPIC_API_KEY from the environment and returns an Either — a missing key is not thrown but deferred and reported as ExtractionError.TransportFailed on each extraction. Set it before running:

export ANTHROPIC_API_KEY="sk-ant-..."

You can also construct the transport directly with AnthropicTransport(apiKey).

OpenAI

import typecast.openai.OpenAi
val tc = TypeCast(OpenAi(sys.env("OPENAI_API_KEY")), model = "gpt-4o-mini")
export OPENAI_API_KEY="sk-..."

Ollama (local, no key)

import typecast.openai.Ollama
val tc = TypeCast(Ollama(), model = "llama3.2")

Ollama() targets the OpenAI-compatible endpoint at localhost:11434/v1 and needs no credentials. Install Ollama, then pull the default model used throughout the docs:

ollama pull llama3.2
ollama serve   # serves on localhost:11434

Other OpenAI-compatible endpoints

vLLM, Groq, and any other OpenAI-compatible server go through the compatible factories:

import typecast.openai.OpenAiCompatible
val groq = TypeCast(
  OpenAiCompatible.jsonMode("https://api.groq.com/openai/v1", apiKey = sys.env.get("GROQ_API_KEY")),
  model = "llama-3.3-70b-versatile"
)

For the per-provider enforcement model (native vs JSON mode vs prompt-only, and what gets validated post-hoc), see providers-and-lowering.md.

Smoke test

A minimal end-to-end check against a local Ollama (no API key needed):

import typecast.*
import typecast.openai.Ollama

case class City(name: String, country: String, population: Int) derives LlmSchema

@main def smokeTest(): Unit =
  val tc = TypeCast(Ollama(), model = "llama3.2")
  tc.extract[City]("Paris is the capital of France, home to about 2.1 million people.") match
    case Right(city) => println(s"${city.name}, ${city.country}: ${city.population}")
    case Left(err)   => println(err.render)

extract[A] returns Either[ExtractionError, A] — a validated value or a typed failure that renders human-readable. Once this runs, continue with getting-started.md for reports, retries, budgets, and provider swaps.