com.softinio.verdict4s

Members list

Grouped members

Errors

sealed abstract class Verdict4sError(message: String, cause: Option[Throwable] = ...) extends Exception

Failures a verdict4s call can produce.

Failures a verdict4s call can produce.

Lives in the effect-free core so that callers can pattern match on it without taking a dependency on cats-effect or http4s.

Attributes

Companion
object
Supertypes
class Exception
class Throwable
trait Serializable
class Object
trait Matchable
class Any
Show all
Known subtypes
class Api
class Decoding
class Invalid
class Refused
class Transport
class Validation
Show all

Questions

final class Ask[Q <: Tuple]

A set of questions asked together, answered at exactly the right types.

A set of questions asked together, answered at exactly the right types.

One request carrying several questions is both cheaper and faster than one request each, and it is the shape the service is designed around. Ask keeps that batching while preserving each question's own answer type:

val (urgent, dept, mood) = client.ask(
 Ask(
   Question.noul("Does this convey urgency?"),
   Question.choice[Dept]("Which team should handle this?"),
   Question.score("How frustrated?", Seq("Calm", "Cross", "Livid"))
 ),
 state
)

urgent is a NoulAnswer, dept a ChoiceAnswer[Dept] and mood a ScoreAnswer -- destructured positionally, with no casts and no lookups by string. The map-based algebra.Evaluation remains available for cases where the questions are not known statically.

Question keys are generated positionally, so callers never name them. Use withKeys if you need them to match something else.

Attributes

Companion
object
Supertypes
class Object
trait Matchable
class Any

Configuration

trait Jitter[F[_]]

Where a retry delay's randomness comes from.

Where a retry delay's randomness comes from.

A two-method trait rather than a direct call to a random source, for two reasons. Tests get a fully deterministic schedule from Jitter.constant without a mocking library. And the obvious alternatives are both worse here: cats.effect.std.Random is only transitively on the classpath, and deriving randomness from Temporal.monotonic yields a constant on Scala.js, where the clock is millisecond-granular.

This lives in the client, not in algebra, because drawing a sample is an effect and the core must stay effect-free.

Attributes

Companion
object
Supertypes
class Object
trait Matchable
class Any
object Verdict4sEnv

Reading configuration from the environment. JVM only.

Reading configuration from the environment. JVM only.

A separate object rather than methods on Verdict4s, and not by preference: a Scala object cannot be split across src/ and src-jvm/, so adding these there would force jev and openAI to be duplicated into both platform directories — exactly the drift that keeping one signature on both platforms is meant to prevent.

Scala.js has no environment to read, so this simply does not exist there. The JVM artifact having a slightly larger surface than the JS one is normal and does not affect Verdict4s.

Variable names match each vendor's official SDKs, so a project can share them:

Variable Meaning
VERDICT4S_VENDOR jev (the default) or openai, for default
TYPESAFE_API_KEY Jev's API key
TYPESAFE_BASE_URL Jev's address
TYPESAFE_DEFAULT_MODEL Jev's default model
OPENAI_API_KEY OpenAI's API key
OPENAI_BASE_URL OpenAI's address, including /v1

Attributes

Supertypes
class Object
trait Matchable
class Any
Self type

Client

final class JevClient[F[_]] extends Verdict4sClient[F], ModelCatalog[F]

A client for TypeSafe AI's Jev: the shared surface, plus ModelCatalog.

A client for TypeSafe AI's Jev: the shared surface, plus ModelCatalog.

Built with Verdict4sClient.jev. Its copy methods return a JevClient, so withConfig never loses the model catalog.

Attributes

Supertypes
trait ModelCatalog[F]
trait Verdict4sClient[F]
class Object
trait Matchable
class Any
trait ModelCatalog[F[_]]

A vendor that lists the models available to the account. Jev does.

A vendor that lists the models available to the account. Jev does.

Attributes

Supertypes
class Object
trait Matchable
class Any
Known subtypes
class JevClient[F]
final class OpenAIClient[F[_]] extends Verdict4sClient[F], SafetyIdentity[F]

A client for OpenAI Decisions: the shared surface, plus SafetyIdentity.

A client for OpenAI Decisions: the shared surface, plus SafetyIdentity.

Built with Verdict4sClient.openAI. Its copy methods return an OpenAIClient, so withConfig never loses the safety identifier.

Attributes

Supertypes
trait SafetyIdentity[F]
trait Verdict4sClient[F]
class Object
trait Matchable
class Any
trait SafetyIdentity[F[_]]

A vendor that accepts an identifier for your end user, to help it detect abuse. OpenAI Decisions does, as safety_identifier.

A vendor that accepts an identifier for your end user, to help it detect abuse. OpenAI Decisions does, as safety_identifier.

Attributes

Supertypes
class Object
trait Matchable
class Any
Known subtypes
class OpenAIClient[F]
object Verdict4s

Batteries-included entry point.

Batteries-included entry point.

Picks a sensible transport for whichever platform you are on — Ember on the JVM, the browser and Node Fetch API on Scala.js — so getting a working client is a one-liner, for either vendor:

Verdict4s.jev[IO](typesafeKey).use { client =>
 client.ask(Ask(Question.noul("Is this urgent?")), ticket)
}

Verdict4s.openAI[IO](openAIKey).use { client =>
 client.ask(Ask(Question.noul("Is this urgent?")), ticket)
}

If you already have an http4s Client[F], or want a backend other than the default, depend on verdict4s-client and construct a Verdict4sClient directly.

Attributes

Supertypes
class Object
trait Matchable
class Any
Self type
Verdict4s.type
trait Verdict4sClient[F[_]]

A client for a decision API: Jev, OpenAI Decisions, or any other Vendor.

A client for a decision API: Jev, OpenAI Decisions, or any other Vendor.

Code written against this trait works with every vendor. The questions, Ask and the typed answers are the same whichever one answers them:

def triage[F[_]](
   client: Verdict4sClient[F]
): F[(NoulAnswer, ChoiceAnswer[Dept])] =
 client.ask(
   Ask(
     Question.noul("Does this convey urgency?"),
     Question.choice[Dept]("Which team should handle this?")
   ),
   "Help! My payouts have been failing for 3 days."
 )

triage(Verdict4sClient.jev[IO](httpClient, typesafeKey))
triage(Verdict4sClient.openAI[IO](httpClient, openAIKey))

What only one vendor offers is a separate capability trait, mixed into that vendor's client: ModelCatalog on JevClient, SafetyIdentity on OpenAIClient. Ask for exactly what you use, and the compiler stops a call the chosen vendor cannot serve:

def report[F[_]](using c: Verdict4sClient[F] & ModelCatalog[F]) = c.models

Takes an http4s Client[F] rather than building one. That single decision buys every backend and every platform without this library owning any of them — Ember, Fetch, Blaze, Netty, the JDK client — and it is why the test suite needs no network: Client.fromHttpApp serves routes in memory, so the same tests run on the JVM and on Node.

Retries are on by default, matching the official SDKs; see algebra.RetryPolicy to tune or disable them.

Attributes

Companion
object
Supertypes
class Object
trait Matchable
class Any
Known subtypes
class JevClient[F]
class OpenAIClient[F]

Constructing a client over a transport you supply.

Constructing a client over a transport you supply.

Attributes

Companion
trait
Supertypes
class Object
trait Matchable
class Any
Self type
object syntax

Optional sugar for the call site.

Optional sugar for the call site.

The documented form is client.ask(questions, state), which needs nothing in implicit scope and infers F from the client. This import adds a reading-order alternative for those who prefer it:

import com.softinio.verdict4s.syntax.*

given Verdict4sClient[IO] = client
val (urgent, dept) = Ask(q1, q2).run("payouts failing")

run needs the client as a given for F to be inferable, which is an unusual idiom; runWith is the same thing with the client passed explicitly.

Attributes

Supertypes
class Object
trait Matchable
class Any
Self type
syntax.type

Protocol

trait Vendor

How to speak to one vendor's decision API.

How to speak to one vendor's decision API.

Jev and OpenAI Decisions answer the same three kinds of question with the same kinds of answer, and differ only in how they spell them on the wire. The questions, Ask and the typed answers are therefore shared, and a Vendor is everything that is not: where to send a request, how to encode it, how to read the reply, and which requests the vendor accepts.

A value rather than a typeclass, so that the vendor can be chosen at runtime, from configuration, without a type parameter on every signature.

It is also the way to drive a vendor with no effect system at all. Render a request, send it with whatever HTTP client you already have, and parse the reply back into typed answers:

val vendor = Vendor.OpenAI
val body = vendor.renderAsk(ask, state) // Either[Verdict4sError, String]
val text = myHttpClient.post(
 vendor.evaluateUrl,
 vendor.headers(key),
 body
) // your code
val answers = vendor.parseAsk(ask, text)

Attributes

Companion
object
Supertypes
class Object
trait Matchable
class Any
Known subtypes
object JevVendor
class OpenAIVendor

Type members

Classlikes

object Ask

Building an Ask from one or more questions, and the type that computes its answers.

Building an Ask from one or more questions, and the type that computes its answers.

Attributes

Companion
class
Supertypes
class Object
trait Matchable
class Any
Self type
Ask.type
object Jitter

Ready-made sources of jitter: a random one, and a constant one for tests.

Ready-made sources of jitter: a random one, and a constant one for tests.

Attributes

Companion
trait
Supertypes
class Object
trait Matchable
class Any
Self type
Jitter.type
object Vendor

The supported vendors.

The supported vendors.

Attributes

Companion
trait
Supertypes
class Object
trait Matchable
class Any
Self type
Vendor.type

The cases of Verdict4sError, and a constructor for API errors built from a raw response.

The cases of Verdict4sError, and a constructor for API errors built from a raw response.

Attributes

Companion
class
Supertypes
trait Sum
trait Mirror
class Object
trait Matchable
class Any
Self type