@ostronaut/sdk
v0.3.0-alpha.0
Published
TypeScript client and React hooks for the Ostronaut platform (scoring + multi-turn practice)
Readme
@ostronaut/sdk
Version:
0.1.0-alpha.1— alpha. Pin to the exact version. Do not use^until v1.0.0.
TypeScript client, server-side functions, and React hooks for the Ostronaut scoring API. No styling, no components — just data and behavior.
Install
npm install @ostronaut/sdk@alpha
# or
yarn add @ostronaut/sdk@alpha
# or
pnpm add @ostronaut/sdk@alphaReact is an optional peer dependency. You only need it if you use the hooks. For server-side use (evaluate(), OstronautClient) React is not required.
Server-side usage (Next.js API routes, Node.js, edge functions)
Use the standalone evaluate() function — no React, no hooks, no context.
// app/api/brief/score/route.ts
import { evaluate } from "@ostronaut/sdk";
import type { EvaluateRequest } from "@ostronaut/sdk";
const config = {
apiKey: process.env.OSTRONAUT_API_KEY!,
baseUrl: process.env.OSTRONAUT_API_URL, // host only: https://ostronaut-backend.fly.dev
};
export async function POST(req: Request) {
const body: EvaluateRequest = await req.json();
const result = await evaluate(body, config);
return Response.json(result);
}The baseUrl takes the host only — no path suffix. The SDK appends /api/v1/scoring internally.
PL's exact call shape
PL's /brief flow sends inline criteria (no framework_slug):
import { evaluate, OstronautAuthError, OstronautRateLimitError, OstronautError } from "@ostronaut/sdk";
import type { CriterionDef } from "@ostronaut/sdk";
const criteria: CriterionDef[] = [
{
name: "Specificity",
description: "Does the feedback name a concrete behaviour?",
evidence_good: ["names specific event", "quotes exact words"],
evidence_bad: ["vague adjectives", "personality-based"],
evaluator_looks_for: "Look for a specific observable behaviour, not general praise.",
},
// ...
];
try {
const result = await evaluate(
{ submission_text, criteria, feedback_voice: "coach" },
{ apiKey: process.env.OSTRONAUT_API_KEY!, baseUrl: process.env.OSTRONAUT_API_URL },
);
return result; // EvaluateResponse
} catch (err) {
if (err instanceof OstronautAuthError) {
// 401 — bad key. Log it, return 503 to the user.
} else if (err instanceof OstronautRateLimitError) {
// 429 — err.retryAfter is the seconds to wait (or null if header absent)
} else if (err instanceof OstronautError) {
// Other API errors — err.status, err.message, err.body
}
}React hooks (client components)
import { useEvaluate } from "@ostronaut/sdk";
const { evaluate, loading, data, error, reset } = useEvaluate({
apiKey: process.env.NEXT_PUBLIC_OSTRONAUT_API_KEY!, // only safe if you proxy through your own route
baseUrl: process.env.NEXT_PUBLIC_OSTRONAUT_API_URL,
});Next.js note: Don't expose
apiKeyto the browser. Callevaluate()from a server route and proxy from client components — keep the key inprocess.env.OSTRONAUT_API_KEY(noNEXT_PUBLIC_).
Types
All types are exported from the package root.
import type {
EvaluateRequest,
EvaluateResponse,
ScoredCriterion,
CriterionDef,
FeedbackVoice,
EvaluatePathRequest,
EvaluatePathResponse,
PathChoice,
StepFeedback,
HistoryParams,
ScoreHistoryEvent,
ScoreHistoryResponse,
ProgressRequest,
ProgressResponse,
CompetencyLevel,
PracticeRecommendation,
SpacedReview,
ScoreRecord,
CompetencyModel,
} from "@ostronaut/sdk";CriterionDef (matches PL's existing shape)
interface CriterionDef {
name: string;
description?: string;
evidence_good?: string[];
evidence_bad?: string[];
evaluator_looks_for?: string;
}This is the canonical type. PL should import it from the SDK rather than maintaining a local copy.
EvaluateRequest
interface EvaluateRequest {
submission_text: string; // required
framework_slug?: string; // one of framework_slug or criteria required
criteria?: CriterionDef[]; // one of framework_slug or criteria required
learner_ref?: string; // opaque non-PII id; enables persistence
exercise_ref?: string;
context_chunks?: string[];
feedback_voice?: "executive" | "coach" | "peer";
}Errors
| Class | Status | When |
|-------|--------|------|
| OstronautAuthError | 401 | Bad or missing API key |
| OstronautRateLimitError | 429 | Rate limit hit; check .retryAfter (seconds) |
| OstronautError | any other | Base class for all Ostronaut errors; has .status, .message, .body |
All three are subclasses of Error. Catch OstronautAuthError and OstronautRateLimitError before OstronautError (subclass-first).
OstronautClient (lower-level)
If you want to reuse a single configured instance across multiple calls:
import { OstronautClient } from "@ostronaut/sdk";
const client = new OstronautClient({
apiKey: process.env.OSTRONAUT_API_KEY!,
baseUrl: process.env.OSTRONAUT_API_URL, // host only
});
const result = await client.evaluate({ submission_text, criteria, feedback_voice });Methods: evaluate(), evaluatePath(), history(), progress().
Environment variables
| Variable | Value | Notes |
|----------|-------|-------|
| OSTRONAUT_API_KEY | osk_... | Server-only. Never expose as NEXT_PUBLIC_*. |
| OSTRONAUT_API_URL | https://ostronaut-backend.fly.dev | Host only — no trailing slash, no /api/v1. |
Changelog
See CHANGELOG.md.
