From 1d2875695114b32c13461e802b8c288a07c25ff9 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 28 Jul 2026 08:09:48 +0530 Subject: [PATCH 1/7] chore: update README.md Updated the README to reflect the new project name and improved structure. Removed outdated sections and added new usage examples. --- README.md | 211 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 150 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 37531e8..8f2b7dc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# interfaze-js +# interfaze The official [Interfaze](https://interfaze.ai) SDK for TypeScript/JavaScript @@ -8,22 +8,7 @@ The official [Interfaze](https://interfaze.ai) SDK for TypeScript/JavaScript - **Multimodal inputs** - images, PDFs, audio, video, and CSV, by URL or base64. - **Universal** - Node 18+, browsers, and edge/workers; ESM + CommonJS; fully typed. -## Learn more - -- [interfaze.ai](https://interfaze.ai) - dashboard and API keys. -- [Python SDK](https://github.com/InterfazeAI/interfaze-python). - -## Capabilities - -| Category | Capabilities | -| ---------------- | ----------------------------------------------------------- | -| **Chat & text** | Chat completions, structured output, tools, reasoning | -| **Vision & OCR** | `tasks.ocr` - text and structured data from images and PDFs | -| **Web** | `tasks.webSearch`, `tasks.scrape` | -| **Audio** | `tasks.transcribe` - speech-to-text | -| **Detection** | `tasks.objectDetection`, `tasks.guiDetection` | -| **Translation** | `tasks.translate` | -| **Forecasting** | `tasks.forecast` - time-series prediction | +[Docs](https://interfaze.ai/docs) · [limits](https://interfaze.ai/docs/limits) · [pricing](https://interfaze.ai/pricing) · [dashboard](https://interfaze.ai) · [Python SDK](https://github.com/InterfazeAI/interfaze-python) ## Install @@ -34,96 +19,200 @@ npm install interfaze ## Setup -Get an API key from the [Interfaze dashboard](https://interfaze.ai), then: - ```ts -import { Interfaze } from "interfaze"; // or: import Interfaze from "interfaze" +import { Interfaze } from "interfaze"; const interfaze = new Interfaze({ apiKey: "sk_..." }); // or set INTERFAZE_API_KEY and call new Interfaze() ``` -CommonJS: `const { Interfaze } = require("interfaze");`. `model` defaults to `interfaze-beta`. - -## Usage - -Chat completion: +## Chat ```ts const res = await interfaze.chat.completions.create({ messages: [{ role: "user", content: "Write a haiku about deterministic AI." }], }); -console.log(res.choices[0].message.content); -console.log("cache hit:", res.vcache); // typed Interfaze extra + +res.choices[0]?.message.content; +res.vcache; // semantic-cache hit ``` -Task helpers - each returns the extracted result directly: +A standard `ChatCompletion`, plus `vcache`, and `precontext`/`reasoning` when they apply. + +### Streaming ```ts -await interfaze.tasks.ocr("https://example.com/receipt.jpg"); -await interfaze.tasks.webSearch("latest AI agent news"); -await interfaze.tasks.transcribe("https://example.com/audio.wav"); -await interfaze.tasks.scrape("https://example.com/product"); -await interfaze.tasks.translate("Hello", { to: "French" }); -await interfaze.tasks.objectDetection("https://example.com/photo.jpg"); -await interfaze.tasks.guiDetection("https://example.com/screenshot.png"); -await interfaze.tasks.forecast("https://example.com/timeseries.csv", { periods: 30 }); +const stream = interfaze.chat.completions.stream({ + messages: [{ role: "user", content: "Tell me a story." }], +}); + +for await (const text of stream.textDeltas()) process.stdout.write(text); + +const final = await stream.finalChatCompletion(); // .reasoning, .precontext ``` -Structured output: +`textDeltas()` yields display-ready text; ``/`` are stripped and returned structured on `finalChatCompletion()`. + +### Structured output ```ts import { responseFormat } from "interfaze"; const res = await interfaze.chat.completions.create({ - messages: [{ role: "user", content: "Weather in Tokyo?" }], + messages: [{ role: "user", content: "What is the current weather in Tokyo?" }], response_format: responseFormat({ type: "object", properties: { city: { type: "string" }, temp_c: { type: "number" } }, required: ["city", "temp_c"], }), }); -const data = JSON.parse(res.choices[0].message.content!); ``` -`responseFormat()` normalizes the schema (object-root wrap, `.optional()` → `.nullable()`); with zod v4 pass `responseFormat(z.toJSONSchema(schema))`. +With zod: `responseFormat(z.toJSONSchema(schema))`. -Streaming - `.stream()` yields typed events, with the inline ``/`` side-channels stripped: +### Reasoning ```ts -const stream = interfaze.chat.completions.stream({ - messages: [{ role: "user", content: "Tell me a story." }], +const res = await interfaze.chat.completions.create({ + reasoning_effort: "high", // also accepts Interfaze's on / off / auto + messages: [{ role: "user", content: "Why is renewable energy important?" }], }); -for await (const text of stream.textDeltas()) { - process.stdout.write(text); -} -const final = await stream.finalChatCompletion(); + +res.reasoning; ``` -> `stream.textDeltas()` yields clean visible text; iterating the stream directly gives the raw -> events, and `create({ stream: true })` gives the raw chunk iterator. +### Tools and function calling + +```ts +const res = await interfaze.chat.completions.create({ + messages: [{ role: "user", content: "What's the weather in Tokyo?" }], + tools: [{ type: "function", function: { name: "get_weather", parameters: schema } }], +}); + +for (const call of res.choices[0]?.message.tool_calls ?? []) { + if (call.type !== "function") continue; + const args = JSON.parse(call.function.arguments); +} +``` ## Inputs +By URL, dropped into a prompt: + ```ts import { inputs } from "interfaze"; -inputs.image("https://…/a.png"); // image_url part -inputs.file("https://…/doc.pdf"); // file part (pdf/csv/xml/json/txt/video…) -inputs.audio("https://…/a.wav"); // input_audio part -await inputs.dataUrl(bytes, "image/png"); // base64 data URI (Uint8Array/ArrayBuffer/Blob) -await inputs.fromPath("./doc.pdf"); // Node-only: read a local file +await interfaze.chat.completions.create({ + messages: [ + { + role: "user", + content: [ + { type: "text", text: "What's in this image, and summarize the PDF." }, + inputs.image("https://…/photo.png"), + inputs.file("https://…/report.pdf"), + ], + }, + ], +}); +``` + +From base64 / raw bytes URI: + +```ts +inputs.image(await inputs.dataUrl(pngBytes, "image/png")); +inputs.file(await inputs.dataUrl(pdfBytes, "application/pdf"), { filename: "report.pdf" }); +``` + +From a local file (Node) - `fromPath()` reads it into a `data:` URI: + +```ts +inputs.image(await inputs.fromPath("./photo.png")); +inputs.file(await inputs.fromPath("./report.pdf")); +``` + +Audio and video: + +```ts +inputs.audio("https://…/call.wav"); // input_audio part +inputs.video("https://…/clip.mp4"); // file part (no native video part) +inputs.video(await inputs.dataUrl(clipBytes, "video/mp4")); // video from base64 +``` + +`inputs.autoPart(src)` picks the part from the media type - image → `image_url`, audio → `input_audio`, else `file`. It's what the task helpers use. + +## Tasks + +Each helper forces one specialized tool - [faster and cheaper](https://interfaze.ai/docs/run-tasks) than a completion. All return `unknown`, so validate before use. + +```ts +await interfaze.tasks.ocr(url); +await interfaze.tasks.objectDetection(url); +await interfaze.tasks.guiDetection(url); +await interfaze.tasks.webSearch(query); +await interfaze.tasks.scrape(url); +await interfaze.tasks.transcribe(url); +await interfaze.tasks.translate(text, { to: "Spanish" }); +await interfaze.tasks.forecast(csvUrl, { periods: 30, unit: "days" }); +``` + +Most take an optional `{ prompt }` to steer. For multi-part messages, pass `task` directly: + +```ts +const res = await interfaze.chat.completions.create({ + task: "ocr", + messages: [{ role: "user", content: [{ type: "text", text: "Extract the total." }, inputs.image(url)] }], +}); +``` + +## Guardrails + +```ts +const res = await interfaze.chat.completions.create({ + guard: ["S1", "S10", "S12_IMAGE"], + messages: [{ role: "user", content: "..." }], +}); ``` -URLs and base64 both work; `image/gif` and `image/avif` are rejected client-side. +A match returns the plain string `unsafe S1` as the content. See the exported `GUARD_CODES` / `GUARD_LABELS`. ## Interfaze extras -- `res.precontext` - raw outputs of any internal tools that ran (OCR/web/scrape/STT/forecast/…). -- `res.reasoning` - reasoning text (with `reasoning_effort: "high"` and no schema). -- `res.vcache` - whether the semantic cache was hit. -- `reasoning_effort` also accepts `"on" | "off" | "auto"`. -- Guardrails: `create({ guard: ["S1", "S12_IMAGE"], … })`. -- Control options: `new Interfaze({ showAdditionalInfo, bypassMoe, bypassCache, adminKey })`. +Every response carries fields a plain OpenAI client drops: + +```ts +const res = await interfaze.chat.completions.create({ + messages: [{ role: "user", content: "Extract the total from this receipt." }], +}); + +res.vcache; // boolean - the semantic cache served this +res.reasoning; // string - present with reasoning_effort and no schema +res.debug; // admin-only payload (needs adminKey) + +for (const p of res.precontext ?? []) { + console.log(p.name, p.result); // ocr / web_search / scraper / stt / forecast / code_sandbox / … +} +``` + +Steer the router, cache, and streaming from the client: + +```ts +const interfaze = new Interfaze({ + showAdditionalInfo: true, // stream deltas as they're produced + bypassMoe: true, // skip the mixture-of-experts router + bypassCache: true, // skip the semantic cache +}); +``` + +## Errors + +```ts +import { BadRequestError, InterfazeError, RateLimitError } from "interfaze"; +``` + +`InterfazeError` is client-side (missing key, invalid guard code, stream misuse). Everything else extends the OpenAI `APIError` with `status` and `code` - `BadRequestError` (400), `AuthenticationError` (401), `RateLimitError` (429), and so on. + +## Examples + +Runnable snippets in [`examples/`](./examples). ## License From 0f5163e2d9ac88c4e7674e587be21745216c30e8 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 28 Jul 2026 08:16:04 +0530 Subject: [PATCH 2/7] Add capabilities section to README Added a capabilities section to outline various use cases and their corresponding entry points. --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index 8f2b7dc..a90cc96 100644 --- a/README.md +++ b/README.md @@ -210,6 +210,26 @@ import { BadRequestError, InterfazeError, RateLimitError } from "interfaze"; `InterfazeError` is client-side (missing key, invalid guard code, stream misuse). Everything else extends the OpenAI `APIError` with `status` and `code` - `BadRequestError` (400), `AuthenticationError` (401), `RateLimitError` (429), and so on. +## Capabilities + +| Use case | Entry point | +| -------------------------- | --------------------------------------------- | +| [Chat](#chat) | `chat.completions.create` | +| [Streaming](#streaming) | `chat.completions.stream` | +| [Structured output](#structured-output) | `responseFormat()` | +| [Reasoning](#reasoning) | `reasoning_effort` | +| [Tools](#tools-and-function-calling) | `tools` | +| [Multimodal inputs](#inputs) | `inputs.*` | +| [OCR](#tasks) | `tasks.ocr` | +| [Object and GUI detection](#tasks) | `tasks.objectDetection`, `tasks.guiDetection` | +| [Web search and scraping](#tasks) | `tasks.webSearch`, `tasks.scrape` | +| [Speech to text](#tasks) | `tasks.transcribe` | +| [Translation](#tasks) | `tasks.translate` | +| [Forecasting](#tasks) | `tasks.forecast` | +| Sandboxing | prompt-driven | +| [Guardrails](#guardrails) | `guard` | +| [Interfaze extras](#interfaze-extras) | `precontext`, `reasoning`, `vcache` | + ## Examples Runnable snippets in [`examples/`](./examples). From 5cf55b95f5ee48f4d810f227338f0cc7dd0bf6c9 Mon Sep 17 00:00:00 2001 From: Abhinavexist Date: Wed, 29 Jul 2026 12:48:49 +0530 Subject: [PATCH 3/7] chore(readme): improvise readme and examples --- README.md | 256 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 178 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index a90cc96..32c5284 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# interfaze +# Interfaze Typescript and Javascript SDK The official [Interfaze](https://interfaze.ai) SDK for TypeScript/JavaScript - **Familiar chat surface** - `chat.completions`, streaming, tools, and structured output. -- **Typed Interfaze extras** - `precontext` (internal tool output), `reasoning`, and `vcache` (semantic-cache hit) on every response. -- **One-line task helpers** - OCR, web search, scraping, speech-to-text, translation, object/GUI detection, forecasting. +- **Typed `precontext`** - the raw metadata Interfaze returns with a completion (bounding boxes, confidence scores, task results), fully typed. +- **[run_task](https://interfaze.ai/docs/run-tasks)** - run a single built-in task (OCR, web search, scraping, speech-to-text, translation, object/GUI detection, forecasting) without the full model. - **Multimodal inputs** - images, PDFs, audio, video, and CSV, by URL or base64. - **Universal** - Node 18+, browsers, and edge/workers; ESM + CommonJS; fully typed. @@ -25,123 +25,220 @@ import { Interfaze } from "interfaze"; const interfaze = new Interfaze({ apiKey: "sk_..." }); // or set INTERFAZE_API_KEY and call new Interfaze() ``` +## Your first request + +This guide will get you started with your first request to Interfaze, which follows the Chat Completions API standard. + +```ts +import { Interfaze, responseFormat } from "interfaze"; +import { z } from "zod"; + +const interfaze = new Interfaze(); + +const IdCard = z.object({ + first_name: z.string(), + last_name: z.string(), + dob: z.string().describe("Date of birth on the ID"), + licence_number: z.string(), +}); + +const res = await interfaze.chat.completions.create({ + messages: [ + { + role: "user", + content: [ + { type: "text", text: "Extract the details from this ID." }, + { type: "image_url", image_url: { url: "https://r2public.jigsawstack.com/interfaze/examples/id.jpg" } }, + ], + }, + ], + response_format: responseFormat(z.toJSONSchema(IdCard), "id_card"), +}); + +const idCard = JSON.parse(res.choices[0]?.message.content ?? "{}"); +console.log(idCard); // your IdCard schema +console.log("OCR result:", res.precontext?.[0]?.result); // the raw OCR that produced it +``` + +## precontext + +Alongside the answer, a response carries `precontext` - the raw metadata Interfaze produced while answering: + +```ts +for (const p of res.precontext ?? []) { + console.log(p.name, p.result); // e.g. "ocr" -> { text, boxes, confidence, … } +} +``` + ## Chat ```ts const res = await interfaze.chat.completions.create({ - messages: [{ role: "user", content: "Write a haiku about deterministic AI." }], + messages: [{ role: "user", content: "Which US public companies reported earnings today?" }], }); res.choices[0]?.message.content; -res.vcache; // semantic-cache hit ``` -A standard `ChatCompletion`, plus `vcache`, and `precontext`/`reasoning` when they apply. +The result is a standard `ChatCompletion` with `precontext`, `vcache`, and `reasoning` added (a web search backs the answer here). ### Streaming +Stream the reply as it's generated; the final completion still carries `precontext` and `reasoning`. + ```ts const stream = interfaze.chat.completions.stream({ - messages: [{ role: "user", content: "Tell me a story." }], + messages: [{ role: "user", content: "Summarize this week's top AI research and cite your sources." }], }); for await (const text of stream.textDeltas()) process.stdout.write(text); -const final = await stream.finalChatCompletion(); // .reasoning, .precontext +const final = await stream.finalChatCompletion(); // .precontext (the sources), .reasoning ``` -`textDeltas()` yields display-ready text; ``/`` are stripped and returned structured on `finalChatCompletion()`. +`textDeltas()` yields display-ready text - the inline ``/`` side-channels are stripped and returned structured on `finalChatCompletion()`. For the raw chunk iterator, use `create({ stream: true })`. ### Structured output +`responseFormat()` takes a JSON Schema - or a zod schema via `z.toJSONSchema()` - and normalizes it for Interfaze: + ```ts import { responseFormat } from "interfaze"; +import { z } from "zod"; + +const Invoice = z.object({ + vendor: z.string(), + currency: z.string(), + total: z.number(), + line_items: z.array(z.object({ description: z.string(), amount: z.number() })), +}); const res = await interfaze.chat.completions.create({ - messages: [{ role: "user", content: "What is the current weather in Tokyo?" }], - response_format: responseFormat({ - type: "object", - properties: { city: { type: "string" }, temp_c: { type: "number" } }, - required: ["city", "temp_c"], - }), + messages: [ + { + role: "user", + content: "Extract the invoice:\nAcme Corp\n3x Widget @ $30\n1x Shipping @ $10\nTotal: $100 USD", + }, + ], + response_format: responseFormat(z.toJSONSchema(Invoice), "invoice"), }); + +const invoice = JSON.parse(res.choices[0]?.message.content ?? "{}"); ``` -With zod: `responseFormat(z.toJSONSchema(schema))`. +Prefer a plain schema? +Pass one directly: +`responseFormat({ type: "object", properties: { … }, required: [ … ] })`. `message.content` comes back as a JSON string, so parse it - and keep the root an `object`, since a non-object root is wrapped under a `result` key. + +### Tools and function calling -### Reasoning +Interfaze supports tools - define `tools`, read `message.tool_calls`, run them, then pass the results back for the final answer. ```ts -const res = await interfaze.chat.completions.create({ - reasoning_effort: "high", // also accepts Interfaze's on / off / auto - messages: [{ role: "user", content: "Why is renewable energy important?" }], -}); +import type { ChatCompletionMessageParam, ChatCompletionTool } from "interfaze"; + +const tools: ChatCompletionTool[] = [ + { + type: "function", + function: { + name: "get_weather", + description: "Get the current weather for a city.", + parameters: { + type: "object", + properties: { city: { type: "string", description: "e.g. Tokyo" } }, + required: ["city"], + }, + }, + }, +]; + +const messages: ChatCompletionMessageParam[] = [{ role: "user", content: "What's the weather in Tokyo?" }]; +const res = await interfaze.chat.completions.create({ messages, tools, tool_choice: "auto" }); + +const message = res.choices[0]?.message; +if (message) messages.push(message); // the assistant turn, carrying any tool_calls + +for (const call of message?.tool_calls ?? []) { + if (call.type !== "function") continue; + const { city } = JSON.parse(call.function.arguments); + messages.push({ role: "tool", tool_call_id: call.id, content: await getWeather(city) }); +} -res.reasoning; +// send the tool results back for the final answer +const final = await interfaze.chat.completions.create({ messages, tools, tool_choice: "auto" }); ``` -### Tools and function calling +## Reasoning + +Ask for reasoning with `reasoning_effort`; the text comes back on `res.reasoning`. ```ts const res = await interfaze.chat.completions.create({ - messages: [{ role: "user", content: "What's the weather in Tokyo?" }], - tools: [{ type: "function", function: { name: "get_weather", parameters: schema } }], + reasoning_effort: "high", // also accepts Interfaze's "on" / "off" / "auto" + messages: [{ role: "user", content: "Which region should we launch in first, and why?" }], }); -for (const call of res.choices[0]?.message.tool_calls ?? []) { - if (call.type !== "function") continue; - const args = JSON.parse(call.function.arguments); -} +res.reasoning; // reasoning text - present with reasoning_effort and no schema ``` ## Inputs -By URL, dropped into a prompt: +Interfaze takes the content parts inline: ```ts -import { inputs } from "interfaze"; - await interfaze.chat.completions.create({ messages: [ { role: "user", content: [ - { type: "text", text: "What's in this image, and summarize the PDF." }, - inputs.image("https://…/photo.png"), - inputs.file("https://…/report.pdf"), + { type: "text", text: "What's in this image?" }, + { type: "image_url", image_url: { url: "https://…/photo.png" } }, ], }, ], }); ``` -From base64 / raw bytes URI: +`inputs.*` is a typed shortcut for the same parts - it picks the right part type and handles base64 and local files: ```ts -inputs.image(await inputs.dataUrl(pngBytes, "image/png")); -inputs.file(await inputs.dataUrl(pdfBytes, "application/pdf"), { filename: "report.pdf" }); +import { inputs } from "interfaze"; + +inputs.image("https://…/photo.png"); // image_url part +inputs.file("https://…/report.pdf"); // file part (pdf/csv/xml/json/txt) +inputs.audio("https://…/call.wav"); // input_audio part +inputs.video("https://…/clip.mp4"); // file part (no native video part) ``` -From a local file (Node) - `fromPath()` reads it into a `data:` URI: +From base64 / raw bytes, or a local file (Node): ```ts -inputs.image(await inputs.fromPath("./photo.png")); -inputs.file(await inputs.fromPath("./report.pdf")); +inputs.image(await inputs.dataUrl(pngBytes, "image/png")); +inputs.file(await inputs.dataUrl(pdfBytes, "application/pdf"), { filename: "report.pdf" }); +inputs.image(await inputs.fromPath("./photo.png")); // reads the file into a data: URI ``` -Audio and video: +Those build a `data:` URL for you - you can also write one inline yourself, the traditional and very common way: ```ts -inputs.audio("https://…/call.wav"); // input_audio part -inputs.video("https://…/clip.mp4"); // file part (no native video part) -inputs.video(await inputs.dataUrl(clipBytes, "video/mp4")); // video from base64 +await interfaze.chat.completions.create({ + messages: [ + { + role: "user", + content: [ + { type: "text", text: "What's in this image?" }, + { type: "image_url", image_url: { url: `data:image/png;base64,${imageBase64}` } }, + ], + }, + ], +}); ``` -`inputs.autoPart(src)` picks the part from the media type - image → `image_url`, audio → `input_audio`, else `file`. It's what the task helpers use. +`inputs.autoPart(src)` picks the part from the media type - image → `image_url`, audio → `input_audio`, else `file`. Interfaze rejects `image/gif` and `image/avif` client-side, and `inputs.fromPath` is Node-only. ## Tasks -Each helper forces one specialized tool - [faster and cheaper](https://interfaze.ai/docs/run-tasks) than a completion. All return `unknown`, so validate before use. +Run a single built-in task ([run_task](https://interfaze.ai/docs/run-tasks)) instead of a full completion. It runs one part of the model rather than the whole thing, so it's faster and cheaper - the downside is you get one task at a time, with a fixed output structure you can't customize: ```ts await interfaze.tasks.ocr(url); @@ -154,17 +251,22 @@ await interfaze.tasks.translate(text, { to: "Spanish" }); await interfaze.tasks.forecast(csvUrl, { periods: 30, unit: "days" }); ``` -Most take an optional `{ prompt }` to steer. For multi-part messages, pass `task` directly: +Each returns `unknown`, so validate before use. For a multi-part message, set `task` on a normal call instead: ```ts const res = await interfaze.chat.completions.create({ task: "ocr", messages: [{ role: "user", content: [{ type: "text", text: "Extract the total." }, inputs.image(url)] }], }); +const { result } = JSON.parse(res.choices[0]?.message.content ?? "{}"); // same output as tasks.ocr() ``` +Trade-offs versus a full completion: a task runs **one** built-in tool (not the full model), only one at a time, and its output is a **fixed structure you can't customize** - so `task` can't be combined with a custom `response_format`. Reach for a normal completion (like [your first request](#your-first-request)) when you need the full model or your own schema. + ## Guardrails +Enable safety categories with `guard`; a blocked request comes back as a normal completion, not an exception. + ```ts const res = await interfaze.chat.completions.create({ guard: ["S1", "S10", "S12_IMAGE"], @@ -172,27 +274,23 @@ const res = await interfaze.chat.completions.create({ }); ``` -A match returns the plain string `unsafe S1` as the content. See the exported `GUARD_CODES` / `GUARD_LABELS`. +A match returns the plain string `unsafe S1` as `message.content` - so check for it. See the exported `GUARD_CODES` / `GUARD_LABELS`. -## Interfaze extras +## Semantic cache -Every response carries fields a plain OpenAI client drops: +Interfaze serves semantically-similar requests from a cache. `res.vcache` reports whether a response was a cache hit: ```ts const res = await interfaze.chat.completions.create({ - messages: [{ role: "user", content: "Extract the total from this receipt." }], + messages: [{ role: "user", content: "..." }], }); -res.vcache; // boolean - the semantic cache served this -res.reasoning; // string - present with reasoning_effort and no schema -res.debug; // admin-only payload (needs adminKey) - -for (const p of res.precontext ?? []) { - console.log(p.name, p.result); // ocr / web_search / scraper / stt / forecast / code_sandbox / … -} +res.vcache; // boolean ``` -Steer the router, cache, and streaming from the client: +## Client options + +Set router, cache, and streaming behavior once on the client: ```ts const interfaze = new Interfaze({ @@ -204,31 +302,33 @@ const interfaze = new Interfaze({ ## Errors +Interfaze re-exports typed error classes to catch and narrow on: + ```ts import { BadRequestError, InterfazeError, RateLimitError } from "interfaze"; ``` -`InterfazeError` is client-side (missing key, invalid guard code, stream misuse). Everything else extends the OpenAI `APIError` with `status` and `code` - `BadRequestError` (400), `AuthenticationError` (401), `RateLimitError` (429), and so on. +`InterfazeError` is client-side (missing key, invalid guard code, stream misuse). Everything else is an `APIError` subclass carrying `status` and `code` - `BadRequestError` (400), `AuthenticationError` (401), `RateLimitError` (429), and so on. ## Capabilities -| Use case | Entry point | -| -------------------------- | --------------------------------------------- | -| [Chat](#chat) | `chat.completions.create` | -| [Streaming](#streaming) | `chat.completions.stream` | -| [Structured output](#structured-output) | `responseFormat()` | -| [Reasoning](#reasoning) | `reasoning_effort` | -| [Tools](#tools-and-function-calling) | `tools` | -| [Multimodal inputs](#inputs) | `inputs.*` | -| [OCR](#tasks) | `tasks.ocr` | -| [Object and GUI detection](#tasks) | `tasks.objectDetection`, `tasks.guiDetection` | -| [Web search and scraping](#tasks) | `tasks.webSearch`, `tasks.scrape` | -| [Speech to text](#tasks) | `tasks.transcribe` | -| [Translation](#tasks) | `tasks.translate` | -| [Forecasting](#tasks) | `tasks.forecast` | -| Sandboxing | prompt-driven | -| [Guardrails](#guardrails) | `guard` | -| [Interfaze extras](#interfaze-extras) | `precontext`, `reasoning`, `vcache` | +| Use case | Entry point | +| --------------------------------------- | --------------------------------------------- | +| [Chat](#chat) | `chat.completions.create` | +| [Streaming](#streaming) | `chat.completions.stream` | +| [Structured output](#structured-output) | `responseFormat()` | +| [Reasoning](#reasoning) | `reasoning_effort` | +| [Tools](#tools-and-function-calling) | `tools` | +| [Multimodal inputs](#inputs) | `inputs.*` | +| [OCR](#tasks) | `tasks.ocr` | +| [Object and GUI detection](#tasks) | `tasks.objectDetection`, `tasks.guiDetection` | +| [Web search and scraping](#tasks) | `tasks.webSearch`, `tasks.scrape` | +| [Speech to text](#tasks) | `tasks.transcribe` | +| [Translation](#tasks) | `tasks.translate` | +| [Forecasting](#tasks) | `tasks.forecast` | +| [Guardrails](#guardrails) | `guard` | +| [precontext](#precontext) | `res.precontext` | +| [Semantic cache](#semantic-cache) | `res.vcache` | ## Examples From b5025e5bea03296f0c8b204064600fd987087df7 Mon Sep 17 00:00:00 2001 From: Abhinavexist Date: Wed, 29 Jul 2026 21:33:37 +0530 Subject: [PATCH 4/7] chore(readme): add task example --- README.md | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 32c5284..1ad9bfe 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,6 @@ The official [Interfaze](https://interfaze.ai) SDK for TypeScript/JavaScript -- **Familiar chat surface** - `chat.completions`, streaming, tools, and structured output. -- **Typed `precontext`** - the raw metadata Interfaze returns with a completion (bounding boxes, confidence scores, task results), fully typed. -- **[run_task](https://interfaze.ai/docs/run-tasks)** - run a single built-in task (OCR, web search, scraping, speech-to-text, translation, object/GUI detection, forecasting) without the full model. -- **Multimodal inputs** - images, PDFs, audio, video, and CSV, by URL or base64. -- **Universal** - Node 18+, browsers, and edge/workers; ESM + CommonJS; fully typed. [Docs](https://interfaze.ai/docs) · [limits](https://interfaze.ai/docs/limits) · [pricing](https://interfaze.ai/pricing) · [dashboard](https://interfaze.ai) · [Python SDK](https://github.com/InterfazeAI/interfaze-python) @@ -103,27 +98,29 @@ const final = await stream.finalChatCompletion(); // .precontext (the sources), `responseFormat()` takes a JSON Schema - or a zod schema via `z.toJSONSchema()` - and normalizes it for Interfaze: ```ts -import { responseFormat } from "interfaze"; +import { responseFormat, inputs } from "interfaze"; import { z } from "zod"; -const Invoice = z.object({ - vendor: z.string(), - currency: z.string(), +const Receipt = z.object({ + merchant: z.string(), total: z.number(), - line_items: z.array(z.object({ description: z.string(), amount: z.number() })), + items: z.array(z.object({ name: z.string(), price: z.number() })), }); const res = await interfaze.chat.completions.create({ messages: [ { role: "user", - content: "Extract the invoice:\nAcme Corp\n3x Widget @ $30\n1x Shipping @ $10\nTotal: $100 USD", + content: [ + { type: "text", text: "Extract this receipt." }, + inputs.image("https://jigsawstack.com/preview/vocr-example.jpg"), + ], }, ], - response_format: responseFormat(z.toJSONSchema(Invoice), "invoice"), + response_format: responseFormat(z.toJSONSchema(Receipt), "receipt"), }); -const invoice = JSON.parse(res.choices[0]?.message.content ?? "{}"); +const receipt = JSON.parse(res.choices[0]?.message.content ?? "{}"); // { merchant, total, items: [...] } ``` Prefer a plain schema? @@ -238,7 +235,9 @@ await interfaze.chat.completions.create({ ## Tasks -Run a single built-in task ([run_task](https://interfaze.ai/docs/run-tasks)) instead of a full completion. It runs one part of the model rather than the whole thing, so it's faster and cheaper - the downside is you get one task at a time, with a fixed output structure you can't customize: +A task ([run_task](https://interfaze.ai/docs/run-tasks)) runs one built-in tool instead of the full model - faster and cheaper, but limited to that tool and its fixed output structure. So `task` can't be combined with a custom `response_format`; reach for a full completion (like [your first request](#your-first-request)) when you need the whole model or your own schema. + +The `tasks.*` helpers are the shortest way - each takes a source and returns the raw result (typed `unknown`, so validate before use): ```ts await interfaze.tasks.ocr(url); @@ -251,7 +250,7 @@ await interfaze.tasks.translate(text, { to: "Spanish" }); await interfaze.tasks.forecast(csvUrl, { periods: 30, unit: "days" }); ``` -Each returns `unknown`, so validate before use. For a multi-part message, set `task` on a normal call instead: +For a multi-part message (text plus an input), set `task` on a normal `create` call - the result comes back on `message.content`: ```ts const res = await interfaze.chat.completions.create({ @@ -261,7 +260,20 @@ const res = await interfaze.chat.completions.create({ const { result } = JSON.parse(res.choices[0]?.message.content ?? "{}"); // same output as tasks.ocr() ``` -Trade-offs versus a full completion: a task runs **one** built-in tool (not the full model), only one at a time, and its output is a **fixed structure you can't customize** - so `task` can't be combined with a custom `response_format`. Reach for a normal completion (like [your first request](#your-first-request)) when you need the full model or your own schema. +Or a `` system message plus an empty response schema: + +```ts +import { emptyTaskSchema } from "interfaze"; + +const res = await interfaze.chat.completions.create({ + messages: [ + { role: "system", content: "ocr" }, + { role: "user", content: [{ type: "text", text: "Extract the total." }, inputs.image(url)] }, + ], + response_format: emptyTaskSchema(), // an empty JSON schema +}); +const { result } = JSON.parse(res.choices[0]?.message.content ?? "{}"); +``` ## Guardrails From a19086ab24853a85cae7665a7b2fcb9477f57904 Mon Sep 17 00:00:00 2001 From: Abhinavexist Date: Wed, 29 Jul 2026 21:51:04 +0530 Subject: [PATCH 5/7] chore(readme): improve inputs example --- README.md | 51 +++++++++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 1ad9bfe..d7bebf2 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,15 @@ res.reasoning; // reasoning text - present with reasoning_effort and no schema ## Inputs -Interfaze takes the content parts inline: +Interfaze handles images, PDFs, audio, video, and CSV. The simplest way is to drop a public URL into the prompt - Interfaze fetches and reads it: + +```ts +await interfaze.chat.completions.create({ + messages: [{ role: "user", content: "Summarize this document: https://arxiv.org/pdf/1706.03762" }], +}); +``` + +Or attach it as a content part - a `file` part for documents, audio, and video; `image_url` for images: ```ts await interfaze.chat.completions.create({ @@ -188,50 +196,33 @@ await interfaze.chat.completions.create({ { role: "user", content: [ - { type: "text", text: "What's in this image?" }, - { type: "image_url", image_url: { url: "https://…/photo.png" } }, + { type: "text", text: "Summarize this document." }, + { type: "file", file: { filename: "paper.pdf", file_data: "https://arxiv.org/pdf/1706.03762" } }, ], }, ], }); ``` -`inputs.*` is a typed shortcut for the same parts - it picks the right part type and handles base64 and local files: +`file_data` also takes a base64 data URI: ```ts -import { inputs } from "interfaze"; - -inputs.image("https://…/photo.png"); // image_url part -inputs.file("https://…/report.pdf"); // file part (pdf/csv/xml/json/txt) -inputs.audio("https://…/call.wav"); // input_audio part -inputs.video("https://…/clip.mp4"); // file part (no native video part) +{ type: "file", file: { filename: "report.pdf", file_data: `data:application/pdf;base64,${base64}` } } ``` -From base64 / raw bytes, or a local file (Node): +`inputs.*` is a typed shortcut that builds these parts - and turns raw bytes or a local file into a data URI for you: ```ts -inputs.image(await inputs.dataUrl(pngBytes, "image/png")); -inputs.file(await inputs.dataUrl(pdfBytes, "application/pdf"), { filename: "report.pdf" }); -inputs.image(await inputs.fromPath("./photo.png")); // reads the file into a data: URI -``` - -Those build a `data:` URL for you - you can also write one inline yourself, the traditional and very common way: +import { inputs } from "interfaze"; -```ts -await interfaze.chat.completions.create({ - messages: [ - { - role: "user", - content: [ - { type: "text", text: "What's in this image?" }, - { type: "image_url", image_url: { url: `data:image/png;base64,${imageBase64}` } }, - ], - }, - ], -}); +inputs.image("https://…/photo.png"); // image_url part +inputs.file("https://…/report.pdf"); // file part +inputs.audio("https://…/call.wav"); // input_audio part +inputs.file(await inputs.dataUrl(pdfBytes, "application/pdf"), { filename: "report.pdf" }); // bytes / Blob +inputs.image(await inputs.fromPath("./photo.png")); // Node: read a local file ``` -`inputs.autoPart(src)` picks the part from the media type - image → `image_url`, audio → `input_audio`, else `file`. Interfaze rejects `image/gif` and `image/avif` client-side, and `inputs.fromPath` is Node-only. +Interfaze rejects `image/gif` and `image/avif` client-side, and `inputs.fromPath` is Node-only. ## Tasks From b451c8791d86228f8bc1f54c33fce6e4d3518ae0 Mon Sep 17 00:00:00 2001 From: Yoeven D Khemlani Date: Wed, 29 Jul 2026 11:40:03 -0700 Subject: [PATCH 6/7] update --- .prettierrc | 7 +++ CHANGELOG.md | 1 + README.md | 121 +++++++++++++++++++++++------------- biome.json | 23 ------- examples/file-inputs.ts | 5 +- package-lock.json | 25 +++++++- package.json | 6 +- scripts/capture-fixtures.ts | 12 ++-- scripts/qa-live.ts | 7 +-- src/chat.ts | 35 +++-------- src/client.ts | 4 +- src/constants.ts | 11 +--- src/index.ts | 9 +-- src/stream.ts | 7 +-- src/tasks.ts | 17 +---- src/types.ts | 14 +---- test/chat.test.ts | 18 ++---- test/errors.test.ts | 10 +-- test/guard.test.ts | 2 +- test/helpers.ts | 4 +- test/models.test.ts | 2 +- test/stream.test.ts | 18 ++---- test/tasks.test.ts | 16 ++--- tsup.config.ts | 1 - 24 files changed, 160 insertions(+), 215 deletions(-) create mode 100644 .prettierrc delete mode 100644 biome.json diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..e68cb8f --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "printWidth": 150, + "tabWidth": 2, + "singleQuote": false, + "bracketSameLine": false, + "trailingComma": "es5" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index f6537c8..93b67a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to `interfaze` are documented here. The format follows Initial release — a typed wrapper over the OpenAI SDK for the Interfaze API. ### Added + - `Interfaze` client (composition over `openai@6`) exposing `chat.completions`, `models`, and `tasks.*`. - `chat.completions.create()` returning `InterfazeChatCompletion` with typed `precontext`, `reasoning`, and `vcache`. - `chat.completions.stream()` — an Interfaze-tolerant streaming helper (handles role-less deltas; surfaces ``/``). diff --git a/README.md b/README.md index d7bebf2..593cf25 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ The official [Interfaze](https://interfaze.ai) SDK for TypeScript/JavaScript - [Docs](https://interfaze.ai/docs) · [limits](https://interfaze.ai/docs/limits) · [pricing](https://interfaze.ai/pricing) · [dashboard](https://interfaze.ai) · [Python SDK](https://github.com/InterfazeAI/interfaze-python) ## Install @@ -43,7 +42,12 @@ const res = await interfaze.chat.completions.create({ role: "user", content: [ { type: "text", text: "Extract the details from this ID." }, - { type: "image_url", image_url: { url: "https://r2public.jigsawstack.com/interfaze/examples/id.jpg" } }, + { + type: "image_url", + image_url: { + url: "https://r2public.jigsawstack.com/interfaze/examples/id.jpg", + }, + }, ], }, ], @@ -51,11 +55,11 @@ const res = await interfaze.chat.completions.create({ }); const idCard = JSON.parse(res.choices[0]?.message.content ?? "{}"); -console.log(idCard); // your IdCard schema -console.log("OCR result:", res.precontext?.[0]?.result); // the raw OCR that produced it +console.log(idCard); // your IdCard schema +console.log("OCR result:", res.precontext?.[0]?.result); // the raw OCR that produced it ``` -## precontext +## Precontext Alongside the answer, a response carries `precontext` - the raw metadata Interfaze produced while answering: @@ -69,13 +73,18 @@ for (const p of res.precontext ?? []) { ```ts const res = await interfaze.chat.completions.create({ - messages: [{ role: "user", content: "Which US public companies reported earnings today?" }], + messages: [ + { + role: "user", + content: "Which US public companies reported earnings today?", + }, + ], }); res.choices[0]?.message.content; ``` -The result is a standard `ChatCompletion` with `precontext`, `vcache`, and `reasoning` added (a web search backs the answer here). +The result is a standard `ChatCompletion` with `precontext`, and `reasoning` added (a web search backs the answer here). ### Streaming @@ -83,7 +92,12 @@ Stream the reply as it's generated; the final completion still carries `preconte ```ts const stream = interfaze.chat.completions.stream({ - messages: [{ role: "user", content: "Summarize this week's top AI research and cite your sources." }], + messages: [ + { + role: "user", + content: "Summarize this week's top AI research and cite your sources.", + }, + ], }); for await (const text of stream.textDeltas()) process.stdout.write(text); @@ -111,10 +125,7 @@ const res = await interfaze.chat.completions.create({ messages: [ { role: "user", - content: [ - { type: "text", text: "Extract this receipt." }, - inputs.image("https://jigsawstack.com/preview/vocr-example.jpg"), - ], + content: [{ type: "text", text: "Extract this receipt." }, inputs.image("https://jigsawstack.com/preview/vocr-example.jpg")], }, ], response_format: responseFormat(z.toJSONSchema(Receipt), "receipt"), @@ -150,7 +161,12 @@ const tools: ChatCompletionTool[] = [ ]; const messages: ChatCompletionMessageParam[] = [{ role: "user", content: "What's the weather in Tokyo?" }]; -const res = await interfaze.chat.completions.create({ messages, tools, tool_choice: "auto" }); + +const res = await interfaze.chat.completions.create({ + messages, + tools, + tool_choice: "auto", +}); const message = res.choices[0]?.message; if (message) messages.push(message); // the assistant turn, carrying any tool_calls @@ -158,11 +174,19 @@ if (message) messages.push(message); // the assistant turn, carrying any tool_ca for (const call of message?.tool_calls ?? []) { if (call.type !== "function") continue; const { city } = JSON.parse(call.function.arguments); - messages.push({ role: "tool", tool_call_id: call.id, content: await getWeather(city) }); + messages.push({ + role: "tool", + tool_call_id: call.id, + content: await getWeather(city), + }); } // send the tool results back for the final answer -const final = await interfaze.chat.completions.create({ messages, tools, tool_choice: "auto" }); +const final = await interfaze.chat.completions.create({ + messages, + tools, + tool_choice: "auto", +}); ``` ## Reasoning @@ -172,19 +196,29 @@ Ask for reasoning with `reasoning_effort`; the text comes back on `res.reasoning ```ts const res = await interfaze.chat.completions.create({ reasoning_effort: "high", // also accepts Interfaze's "on" / "off" / "auto" - messages: [{ role: "user", content: "Which region should we launch in first, and why?" }], + messages: [ + { + role: "user", + content: "Which region should we launch in first, and why?", + }, + ], }); res.reasoning; // reasoning text - present with reasoning_effort and no schema ``` -## Inputs +## Multimodal Inputs Interfaze handles images, PDFs, audio, video, and CSV. The simplest way is to drop a public URL into the prompt - Interfaze fetches and reads it: ```ts await interfaze.chat.completions.create({ - messages: [{ role: "user", content: "Summarize this document: https://arxiv.org/pdf/1706.03762" }], + messages: [ + { + role: "user", + content: "Summarize this document: https://arxiv.org/pdf/1706.03762", + }, + ], }); ``` @@ -197,7 +231,13 @@ await interfaze.chat.completions.create({ role: "user", content: [ { type: "text", text: "Summarize this document." }, - { type: "file", file: { filename: "paper.pdf", file_data: "https://arxiv.org/pdf/1706.03762" } }, + { + type: "file", + file: { + filename: "paper.pdf", + file_data: "https://arxiv.org/pdf/1706.03762", + }, + }, ], }, ], @@ -215,11 +255,13 @@ await interfaze.chat.completions.create({ ```ts import { inputs } from "interfaze"; -inputs.image("https://…/photo.png"); // image_url part -inputs.file("https://…/report.pdf"); // file part -inputs.audio("https://…/call.wav"); // input_audio part -inputs.file(await inputs.dataUrl(pdfBytes, "application/pdf"), { filename: "report.pdf" }); // bytes / Blob -inputs.image(await inputs.fromPath("./photo.png")); // Node: read a local file +inputs.image("https://…/photo.png"); // image_url part +inputs.file("https://…/report.pdf"); // file part +inputs.audio("https://…/call.wav"); // input_audio part +inputs.file(await inputs.dataUrl(pdfBytes, "application/pdf"), { + filename: "report.pdf", +}); // bytes / Blob +inputs.image(await inputs.fromPath("./photo.png")); // Node: read a local file ``` Interfaze rejects `image/gif` and `image/avif` client-side, and `inputs.fromPath` is Node-only. @@ -246,7 +288,12 @@ For a multi-part message (text plus an input), set `task` on a normal `create` c ```ts const res = await interfaze.chat.completions.create({ task: "ocr", - messages: [{ role: "user", content: [{ type: "text", text: "Extract the total." }, inputs.image(url)] }], + messages: [ + { + role: "user", + content: [{ type: "text", text: "Extract the total." }, inputs.image(url)], + }, + ], }); const { result } = JSON.parse(res.choices[0]?.message.content ?? "{}"); // same output as tasks.ocr() ``` @@ -259,7 +306,10 @@ import { emptyTaskSchema } from "interfaze"; const res = await interfaze.chat.completions.create({ messages: [ { role: "system", content: "ocr" }, - { role: "user", content: [{ type: "text", text: "Extract the total." }, inputs.image(url)] }, + { + role: "user", + content: [{ type: "text", text: "Extract the total." }, inputs.image(url)], + }, ], response_format: emptyTaskSchema(), // an empty JSON schema }); @@ -279,18 +329,6 @@ const res = await interfaze.chat.completions.create({ A match returns the plain string `unsafe S1` as `message.content` - so check for it. See the exported `GUARD_CODES` / `GUARD_LABELS`. -## Semantic cache - -Interfaze serves semantically-similar requests from a cache. `res.vcache` reports whether a response was a cache hit: - -```ts -const res = await interfaze.chat.completions.create({ - messages: [{ role: "user", content: "..." }], -}); - -res.vcache; // boolean -``` - ## Client options Set router, cache, and streaming behavior once on the client: @@ -298,8 +336,8 @@ Set router, cache, and streaming behavior once on the client: ```ts const interfaze = new Interfaze({ showAdditionalInfo: true, // stream deltas as they're produced - bypassMoe: true, // skip the mixture-of-experts router - bypassCache: true, // skip the semantic cache + bypassMoe: true, // skip the mixture-of-experts router + bypassCache: true, // skip the semantic cache }); ``` @@ -330,8 +368,7 @@ import { BadRequestError, InterfazeError, RateLimitError } from "interfaze"; | [Translation](#tasks) | `tasks.translate` | | [Forecasting](#tasks) | `tasks.forecast` | | [Guardrails](#guardrails) | `guard` | -| [precontext](#precontext) | `res.precontext` | -| [Semantic cache](#semantic-cache) | `res.vcache` | +| [Precontext](#precontext) | `res.precontext` | ## Examples diff --git a/biome.json b/biome.json deleted file mode 100644 index 070ea65..0000000 --- a/biome.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "https://biomejs.dev/schemas/2.5.5/schema.json", - "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true }, - "files": { "includes": ["src/**", "test/**", "scripts/**", "examples/**"] }, - "assist": { "enabled": false }, - "formatter": { - "enabled": true, - "indentStyle": "space", - "indentWidth": 2, - "lineWidth": 120 - }, - "linter": { - "enabled": true, - "rules": { - "recommended": true, - "style": { "noNonNullAssertion": "off" }, - "complexity": { "useLiteralKeys": "off" } - } - }, - "javascript": { - "formatter": { "quoteStyle": "double", "semicolons": "always" } - } -} diff --git a/examples/file-inputs.ts b/examples/file-inputs.ts index bc97965..8898961 100644 --- a/examples/file-inputs.ts +++ b/examples/file-inputs.ts @@ -7,10 +7,7 @@ const a = await interfaze.chat.completions.create({ messages: [ { role: "user", - content: [ - { type: "text", text: "Summarize this PDF." }, - inputs.file("https://arxiv.org/pdf/1706.03762", { filename: "paper.pdf" }), - ], + content: [{ type: "text", text: "Summarize this PDF." }, inputs.file("https://arxiv.org/pdf/1706.03762", { filename: "paper.pdf" })], }, ], }); diff --git a/package-lock.json b/package-lock.json index af507ff..e6fe891 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "interfaze-js", + "name": "interfaze", "version": "1.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "interfaze-js", + "name": "interfaze", "version": "1.0.1", "license": "MIT", "dependencies": { @@ -16,6 +16,8 @@ "@biomejs/biome": "2.5.5", "@types/node": "~22.20.1", "@vitest/coverage-v8": "^2.1.9", + "openai": "~6.47.0", + "prettier": "^3.9.6", "publint": "0.3.22", "tsup": "^8.5.1", "tsx": "^4.23.1", @@ -2561,6 +2563,7 @@ "version": "6.47.0", "resolved": "https://registry.npmjs.org/openai/-/openai-6.47.0.tgz", "integrity": "sha512-xYr+R9woSzWxVxeiqkkNbHhv89tZDEI6eBMbrdPnv3poh+mijHvbhS35a+3o6xHa411/ns8j5ENY3So9DCXWYw==", + "dev": true, "license": "Apache-2.0", "peerDependencies": { "@aws-sdk/credential-provider-node": ">=3.972.0 <4", @@ -2783,6 +2786,22 @@ } } }, + "node_modules/prettier": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/publint": { "version": "0.3.22", "resolved": "https://registry.npmjs.org/publint/-/publint-0.3.22.tgz", @@ -4689,7 +4708,7 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", - "devOptional": true, + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index dd7f42d..39632cb 100644 --- a/package.json +++ b/package.json @@ -48,8 +48,7 @@ "build": "tsup", "typecheck": "tsc --noEmit", "lint": "biome check .", - "format": "biome format --write .", - "format:check": "biome format --check .", + "format": "prettier --write .", "check:pkg": "publint --strict && attw --pack", "test": "vitest run", "test:watch": "vitest", @@ -74,8 +73,9 @@ "@biomejs/biome": "2.5.5", "@types/node": "~22.20.1", "@vitest/coverage-v8": "^2.1.9", - "publint": "0.3.22", "openai": "~6.47.0", + "prettier": "^3.9.6", + "publint": "0.3.22", "tsup": "^8.5.1", "tsx": "^4.23.1", "typescript": "~5.9.3", diff --git a/scripts/capture-fixtures.ts b/scripts/capture-fixtures.ts index e8cc211..4148af7 100644 --- a/scripts/capture-fixtures.ts +++ b/scripts/capture-fixtures.ts @@ -30,7 +30,7 @@ save( model: "interfaze-beta", messages: [{ role: "user", content: "Say hi in one short sentence." }], max_tokens: 60, - }), + }) ); save( @@ -39,7 +39,7 @@ save( model: "interfaze-beta", messages: [{ role: "user", content: "Return a JSON object with keys city and temp_c for Tokyo." }], response_format: { type: "json_object" }, - }), + }) ); save( @@ -51,17 +51,15 @@ save( { role: "user", content: [{ type: "text", text: "Extract total price" }, receipt as never] }, ], response_format: { type: "json_schema", json_schema: { name: "empty_schema", schema: {} } } as never, - }), + }) ); save( "precontext.json", await client.chat.completions.create({ model: "interfaze-beta", - messages: [ - { role: "user", content: [{ type: "text", text: "Extract total price from this receipt" }, receipt as never] }, - ], - }), + messages: [{ role: "user", content: [{ type: "text", text: "Extract total price from this receipt" }, receipt as never] }], + }) ); { diff --git a/scripts/qa-live.ts b/scripts/qa-live.ts index ec60141..39593a5 100644 --- a/scripts/qa-live.ts +++ b/scripts/qa-live.ts @@ -54,7 +54,7 @@ await check("structured output (responseFormat)", async () => { properties: { greeting: { type: "string" }, count: { type: "number" } }, required: ["greeting", "count"], }, - "greeting", + "greeting" ), }); const p = JSON.parse(r.choices[0]!.message.content!); @@ -219,10 +219,7 @@ await check("input: base64 image (data URI)", async () => { messages: [ { role: "user", - content: [ - { type: "text", text: "What is in this image? One sentence." }, - inputs.image(await inputs.dataUrl(bytes, "image/jpeg")), - ], + content: [{ type: "text", text: "What is in this image? One sentence." }, inputs.image(await inputs.dataUrl(bytes, "image/jpeg"))], }, ], }); diff --git a/src/chat.ts b/src/chat.ts index 1894387..596c1c1 100644 --- a/src/chat.ts +++ b/src/chat.ts @@ -1,11 +1,7 @@ import type OpenAI from "openai"; import type { APIPromise } from "openai"; import type { Stream } from "openai/streaming"; -import type { - ChatCompletion, - ChatCompletionChunk, - ChatCompletionMessageParam, -} from "openai/resources/chat/completions/completions"; +import type { ChatCompletion, ChatCompletionChunk, ChatCompletionMessageParam } from "openai/resources/chat/completions/completions"; import { INTERFAZE_MODEL } from "./constants.js"; import { InterfazeError } from "./errors.js"; @@ -31,11 +27,7 @@ export function toInterfaze(raw: ChatCompletion, opts: { stripFence: boolean }): return r; } -function injectTags( - messages: readonly ChatCompletionMessageParam[], - task?: string, - guard?: string, -): ChatCompletionMessageParam[] { +function injectTags(messages: readonly ChatCompletionMessageParam[], task?: string, guard?: string): ChatCompletionMessageParam[] { const tags = [task, guard].filter(Boolean).join(" "); if (!tags) return messages.slice(); const out = messages.slice(); @@ -68,9 +60,7 @@ function prepare(params: InterfazeChatCompletionCreateParams): { let rf = response_format; if (task) { if (rf && isNonEmptySchema(rf)) { - throw new InterfazeError( - "A non-empty `response_format` cannot be combined with `task` (Interfaze runs tasks with raw output).", - ); + throw new InterfazeError("A non-empty `response_format` cannot be combined with `task` (Interfaze runs tasks with raw output)."); } rf = emptyTaskSchema(); } @@ -81,7 +71,7 @@ function prepare(params: InterfazeChatCompletionCreateParams): { messages: injectTags( messages as ChatCompletionMessageParam[], task ? `${task}` : undefined, - guard?.length ? guardTag(guard) : undefined, + guard?.length ? guardTag(guard) : undefined ), }; if (rf !== undefined) body["response_format"] = rf; @@ -95,17 +85,11 @@ export class InterfazeCompletions { this.#openai = openai; } - create( - params: InterfazeChatCompletionCreateParamsNonStreaming, - options?: RequestOptions, - ): APIPromise; - create( - params: InterfazeChatCompletionCreateParamsStreaming, - options?: RequestOptions, - ): APIPromise>; + create(params: InterfazeChatCompletionCreateParamsNonStreaming, options?: RequestOptions): APIPromise; + create(params: InterfazeChatCompletionCreateParamsStreaming, options?: RequestOptions): APIPromise>; create( params: InterfazeChatCompletionCreateParams, - options?: RequestOptions, + options?: RequestOptions ): APIPromise | APIPromise> { const { body, stripFence } = prepare(params); const raw = this.#openai.chat.completions.create(body as never, options); @@ -116,10 +100,7 @@ export class InterfazeCompletions { } /** Streaming with an Interfaze-tolerant accumulator; also surfaces ``/``. */ - stream( - params: Omit, - options?: RequestOptions, - ): InterfazeChatCompletionStream { + stream(params: Omit, options?: RequestOptions): InterfazeChatCompletionStream { const { body, stripFence } = prepare({ ...params, stream: true } as InterfazeChatCompletionCreateParamsStreaming); return new InterfazeChatCompletionStream(this.#openai, body, options, stripFence); } diff --git a/src/client.ts b/src/client.ts index 5be1161..b7ce732 100644 --- a/src/client.ts +++ b/src/client.ts @@ -32,9 +32,7 @@ export class Interfaze { const resolvedKey = apiKey ?? envKey(); if (!resolvedKey) { - throw new InterfazeError( - "Missing API key. Pass `new Interfaze({ apiKey })` or set the INTERFAZE_API_KEY environment variable.", - ); + throw new InterfazeError("Missing API key. Pass `new Interfaze({ apiKey })` or set the INTERFAZE_API_KEY environment variable."); } const headers: Record = { ...(defaultHeaders as Record | undefined) }; diff --git a/src/constants.ts b/src/constants.ts index a12b04c..41ec737 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -3,16 +3,7 @@ export const INTERFAZE_MODEL = "interfaze-beta"; export const DEFAULT_TIMEOUT_MS = 900_000; /** Task names accepted in a `` tag. */ -export const TASK_NAMES = [ - "ocr", - "object_detection", - "gui_detection", - "web_search", - "scraper", - "translate", - "speech_to_text", - "forecast", -] as const; +export const TASK_NAMES = ["ocr", "object_detection", "gui_detection", "web_search", "scraper", "translate", "speech_to_text", "forecast"] as const; /** Guardrail categories (`ALL` enables everything). */ export const GUARD_CODES = [ diff --git a/src/index.ts b/src/index.ts index 9810847..70e59f1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,14 +19,7 @@ export type { InterfazeChatCompletionCreateParamsStreaming, } from "./types.js"; -export { - TASK_NAMES, - GUARD_CODES, - GUARD_LABELS, - INTERFAZE_MODEL, - INTERFAZE_BASE_URL, - LIMITS, -} from "./constants.js"; +export { TASK_NAMES, GUARD_CODES, GUARD_LABELS, INTERFAZE_MODEL, INTERFAZE_BASE_URL, LIMITS } from "./constants.js"; export { toFile } from "openai"; export { diff --git a/src/stream.ts b/src/stream.ts index f4ab718..4dad507 100644 --- a/src/stream.ts +++ b/src/stream.ts @@ -43,10 +43,9 @@ export class InterfazeChatCompletionStream implements AsyncIterable> { if (!this.#raw) { - this.#raw = this.#openai.chat.completions.create( - { ...this.#body, stream: true } as never, - this.#options, - ) as unknown as Promise>; + this.#raw = this.#openai.chat.completions.create({ ...this.#body, stream: true } as never, this.#options) as unknown as Promise< + AsyncIterable + >; } return this.#raw; } diff --git a/src/tasks.ts b/src/tasks.ts index 15716d2..d46935c 100644 --- a/src/tasks.ts +++ b/src/tasks.ts @@ -21,11 +21,7 @@ export class Tasks { this.#c = completions; } - async #run( - task: TaskName, - content: string | ChatCompletionContentPart[], - options?: RequestOptions, - ): Promise { + async #run(task: TaskName, content: string | ChatCompletionContentPart[], options?: RequestOptions): Promise { const res = await this.#c.create({ task, messages: [{ role: "user", content }] }, options); const raw = res.choices[0]?.message.content; if (!raw) return undefined; @@ -73,17 +69,10 @@ export class Tasks { } /** Forecast a time series from a CSV (MoE-selected, so prompt-driven; reads the `forecast` precontext). */ - async forecast( - csvSource: string, - opts: { periods?: number; unit?: string } = {}, - options?: RequestOptions, - ): Promise { + async forecast(csvSource: string, opts: { periods?: number; unit?: string } = {}, options?: RequestOptions): Promise { const n = opts.periods ?? 10; const unit = opts.unit ?? "days"; - const res = await this.#c.create( - { messages: [{ role: "user", content: `Forecast the next ${n} ${unit} of this: ${csvSource}` }] }, - options, - ); + const res = await this.#c.create({ messages: [{ role: "user", content: `Forecast the next ${n} ${unit} of this: ${csvSource}` }] }, options); const pc = res.precontext?.find((p) => p.name === "forecast"); return pc?.result ?? res.choices[0]?.message.content ?? undefined; } diff --git a/src/types.ts b/src/types.ts index 795adb3..76c4604 100644 --- a/src/types.ts +++ b/src/types.ts @@ -39,18 +39,10 @@ interface InterfazeExtraParams { guard?: GuardCode[]; } -export type InterfazeChatCompletionCreateParamsNonStreaming = Omit< - ChatCompletionCreateParamsNonStreaming, - "reasoning_effort" | "model" -> & +export type InterfazeChatCompletionCreateParamsNonStreaming = Omit & InterfazeExtraParams & { stream?: false | null }; -export type InterfazeChatCompletionCreateParamsStreaming = Omit< - ChatCompletionCreateParamsStreaming, - "reasoning_effort" | "model" -> & +export type InterfazeChatCompletionCreateParamsStreaming = Omit & InterfazeExtraParams & { stream: true }; -export type InterfazeChatCompletionCreateParams = - | InterfazeChatCompletionCreateParamsNonStreaming - | InterfazeChatCompletionCreateParamsStreaming; +export type InterfazeChatCompletionCreateParams = InterfazeChatCompletionCreateParamsNonStreaming | InterfazeChatCompletionCreateParamsStreaming; diff --git a/test/chat.test.ts b/test/chat.test.ts index 012a53e..6a40d81 100644 --- a/test/chat.test.ts +++ b/test/chat.test.ts @@ -1,11 +1,7 @@ import { describe, expect, it } from "vitest"; import { toInterfaze } from "../src/chat.js"; import { HEADERS } from "../src/constants.js"; -import type { - ChatCompletion, - ChatCompletionChunk, - ChatCompletionMessageToolCall, -} from "openai/resources/chat/completions/completions"; +import type { ChatCompletion, ChatCompletionChunk, ChatCompletionMessageToolCall } from "openai/resources/chat/completions/completions"; import { completion, fixture, jsonResponse, mockInterfaze, sseResponse, systemContent } from "./helpers.js"; function functionName(call: ChatCompletionMessageToolCall): string { @@ -91,7 +87,7 @@ describe("request serialization", () => { type: "json_schema", json_schema: { name: "s", schema: { type: "object", properties: { a: { type: "string" } } } }, }, - }), + }) ).toThrow(/non-empty `response_format` cannot be combined with `task`/); }); @@ -171,7 +167,7 @@ describe("request serialization", () => { const { interfaze, calls } = mockInterfaze(() => jsonResponse(basic), { adminKey: "client-default" }); await interfaze.chat.completions.create( { messages: [{ role: "user", content: "x" }] }, - { headers: { [HEADERS.adminKey]: "per-request-override" } }, + { headers: { [HEADERS.adminKey]: "per-request-override" } } ); expect(calls[0]!.headers.get(HEADERS.adminKey)).toBe("per-request-override"); }); @@ -224,9 +220,7 @@ describe("response mapping", () => { it("preserves the raw HTTP response via .withResponse() (guards the _thenUnwrap mapping)", async () => { const { interfaze } = mockInterfaze(() => jsonResponse(basic)); - const { data, response } = await interfaze.chat.completions - .create({ messages: [{ role: "user", content: "hi" }] }) - .withResponse(); + const { data, response } = await interfaze.chat.completions.create({ messages: [{ role: "user", content: "hi" }] }).withResponse(); expect(response.status).toBe(200); expect(typeof data.vcache).toBe("boolean"); expect(data.choices[0]!.message.content).toBeDefined(); @@ -260,9 +254,7 @@ describe("response mapping", () => { it("leaves tool-call responses with content: null untouched", async () => { const toolCall = completion(null, { finishReason: "tool_calls", - toolCalls: [ - { id: "call_1", type: "function", function: { name: "get_weather", arguments: '{"city": "Paris"}' } }, - ], + toolCalls: [{ id: "call_1", type: "function", function: { name: "get_weather", arguments: '{"city": "Paris"}' } }], }); const { interfaze } = mockInterfaze(() => jsonResponse(toolCall)); const r = await interfaze.chat.completions.create({ diff --git a/test/errors.test.ts b/test/errors.test.ts index fb11f1a..c5359f5 100644 --- a/test/errors.test.ts +++ b/test/errors.test.ts @@ -18,11 +18,7 @@ const STATUS_MAP = [ { status: 400, ExcType: BadRequestError, - body: errorBody( - "Field 'temperature': Too big: expected number to be <=1", - "invalid_request_error", - "invalid_request", - ), + body: errorBody("Field 'temperature': Too big: expected number to be <=1", "invalid_request_error", "invalid_request"), }, { status: 401, @@ -88,7 +84,7 @@ describe("task + response_format conflict", () => { type: "json_schema", json_schema: { name: "s", schema: { type: "object", properties: { a: { type: "string" } } } }, }, - }), + }) ).toThrow(InterfazeError); expect(calls).toHaveLength(0); }); @@ -101,7 +97,7 @@ describe("invalid guard code", () => { interfaze.chat.completions.create({ guard: ["NOT_A_CODE" as never], messages: [{ role: "user", content: "x" }], - }), + }) ).toThrow(InterfazeError); expect(calls).toHaveLength(0); }); diff --git a/test/guard.test.ts b/test/guard.test.ts index 25fddfa..aee5e87 100644 --- a/test/guard.test.ts +++ b/test/guard.test.ts @@ -44,7 +44,7 @@ describe("create() + guard integration", () => { interfaze.chat.completions.create({ guard: ["NOT_A_CODE" as never], messages: [{ role: "user", content: "x" }], - }), + }) ).toThrow(/Invalid guard code/); expect(calls).toHaveLength(0); }); diff --git a/test/helpers.ts b/test/helpers.ts index db11ba0..2b0f23d 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -18,7 +18,7 @@ export function fixture(name: string): T { /** Build an Interfaze client whose transport is a mock `fetch`, capturing every request. */ export function mockInterfaze( responder: (req: CapturedRequest) => Response | Promise, - options: Partial = {}, + options: Partial = {} ): { interfaze: Interfaze; calls: CapturedRequest[] } { const calls: CapturedRequest[] = []; const fetchImpl = async (input: unknown, init: RequestInit = {}): Promise => { @@ -78,7 +78,7 @@ export function systemContent(body: Record | undefined): string /** Build a synthetic chat.completion body, for shapes not worth a fixture file. */ export function completion( content: unknown = "Hi!", - options: { finishReason?: string; toolCalls?: unknown[] } & Record = {}, + options: { finishReason?: string; toolCalls?: unknown[] } & Record = {} ): Record { const { finishReason = "stop", toolCalls, ...extra } = options; const message: Record = { role: "assistant", content, refusal: null }; diff --git a/test/models.test.ts b/test/models.test.ts index b8dafde..b1b3589 100644 --- a/test/models.test.ts +++ b/test/models.test.ts @@ -26,7 +26,7 @@ describe("models endpoints (interfaze#218 shapes)", () => { message: "The model 'nope' does not exist", type: "invalid_request_error", code: "model_not_found", - }), + }) ); await expect(interfaze.models.retrieve("nope")).rejects.toThrow(); }); diff --git a/test/stream.test.ts b/test/stream.test.ts index d01d8ac..56347d3 100644 --- a/test/stream.test.ts +++ b/test/stream.test.ts @@ -22,12 +22,7 @@ const mkChunk = (delta: object, finish: string | null = null) => ({ // A plain stream with NO side-channels (neither nor ) — the accumulator must not hang. const plainChunks = [mkChunk({ content: "Hello " }), mkChunk({ content: "world" }), mkChunk({}, "stop")]; -const fencedJson = [ - mkChunk({ content: "```json\n" }), - mkChunk({ content: '{"city": "Tokyo"}' }), - mkChunk({ content: "\n```" }), - mkChunk({}, "stop"), -]; +const fencedJson = [mkChunk({ content: "```json\n" }), mkChunk({ content: '{"city": "Tokyo"}' }), mkChunk({ content: "\n```" }), mkChunk({}, "stop")]; const usageChunks = [ mkChunk({ content: "Hi" }), @@ -93,9 +88,7 @@ describe("streaming accumulator", () => { it("finalChatCompletion works without iterating first", async () => { const { interfaze } = mockInterfaze(() => sseResponse(plainChunks)); - const final = await interfaze.chat.completions - .stream({ messages: [{ role: "user", content: "hi" }] }) - .finalChatCompletion(); + const final = await interfaze.chat.completions.stream({ messages: [{ role: "user", content: "hi" }] }).finalChatCompletion(); expect(final.choices[0]!.message.content).toBe("Hello world"); }); @@ -190,14 +183,11 @@ describe("streaming accumulator", () => { it("surfaces an aborted signal as APIUserAbortError instead of a silent end", async () => { const controller = new AbortController(); const { interfaze } = mockInterfaze(() => sseResponse(plainChunks)); - const s = interfaze.chat.completions.stream( - { messages: [{ role: "user", content: "x" }] }, - { signal: controller.signal }, - ); + const s = interfaze.chat.completions.stream({ messages: [{ role: "user", content: "x" }] }, { signal: controller.signal }); await expect( (async () => { for await (const _chunk of s) controller.abort(); - })(), + })() ).rejects.toBeInstanceOf(APIUserAbortError); }); }); diff --git a/test/tasks.test.ts b/test/tasks.test.ts index e961b12..32b4d2c 100644 --- a/test/tasks.test.ts +++ b/test/tasks.test.ts @@ -13,9 +13,7 @@ interface Part { describe("tasks.ocr", () => { it("builds the ocr request and returns the raw result", async () => { - const { interfaze, calls } = mockInterfaze(() => - taskResult("ocr", { extracted_text: "See back of receipt", width: 800 }), - ); + const { interfaze, calls } = mockInterfaze(() => taskResult("ocr", { extracted_text: "See back of receipt", width: 800 })); const result = await interfaze.tasks.ocr(ASSETS.image); const body = calls[0]!.body!; expect(systemContent(body)).toContain("ocr"); @@ -30,9 +28,7 @@ describe("tasks.ocr", () => { describe("tasks.objectDetection", () => { it("builds the object_detection request and returns the raw result", async () => { - const { interfaze, calls } = mockInterfaze(() => - taskResult("object_detection", { objects: [{ label: "bus", box: [0, 0, 10, 10] }] }), - ); + const { interfaze, calls } = mockInterfaze(() => taskResult("object_detection", { objects: [{ label: "bus", box: [0, 0, 10, 10] }] })); const result = await interfaze.tasks.objectDetection(ASSETS.scene); const body = calls[0]!.body!; expect(systemContent(body)).toContain("object_detection"); @@ -47,9 +43,7 @@ describe("tasks.objectDetection", () => { describe("tasks.guiDetection", () => { it("falls through to a generic file part (no extension to sniff) and returns the raw result", async () => { - const { interfaze, calls } = mockInterfaze(() => - taskResult("gui_detection", { elements: [{ label: "button", box: [1, 2, 3, 4] }] }), - ); + const { interfaze, calls } = mockInterfaze(() => taskResult("gui_detection", { elements: [{ label: "button", box: [1, 2, 3, 4] }] })); const result = await interfaze.tasks.guiDetection(ASSETS.gui); const body = calls[0]!.body!; expect(systemContent(body)).toContain("gui_detection"); @@ -81,9 +75,7 @@ describe("tasks.transcribe", () => { describe("tasks.webSearch", () => { it("sends the query as plain string content under web_search", async () => { - const { interfaze, calls } = mockInterfaze(() => - taskResult("web_search", { results: [{ title: "AI agents", url: ASSETS.scrape }] }), - ); + const { interfaze, calls } = mockInterfaze(() => taskResult("web_search", { results: [{ title: "AI agents", url: ASSETS.scrape }] })); const result = await interfaze.tasks.webSearch("latest AI agent news"); const body = calls[0]!.body!; expect(systemContent(body)).toContain("web_search"); diff --git a/tsup.config.ts b/tsup.config.ts index 22b17f4..1ee01a0 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -12,4 +12,3 @@ export default defineConfig({ // `openai` (and optional `zod`) stay external — they're deps, not bundled. external: ["openai", "zod"], }); - From 976a2aabb73859054c152ef07b29c84590b1033f Mon Sep 17 00:00:00 2001 From: Abhinavexist Date: Thu, 30 Jul 2026 00:25:23 +0530 Subject: [PATCH 7/7] refactor(format): switch to prettier <- biome --- .github/workflows/ci.yml | 6 +- .prettierignore | 3 + package-lock.json | 164 --------------------------------------- package.json | 3 +- 4 files changed, 7 insertions(+), 169 deletions(-) create mode 100644 .prettierignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c297801..c554fb0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ concurrency: jobs: static: - name: static (lint · types · build · package) + name: static (format · types · build · package) runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 @@ -20,8 +20,8 @@ jobs: node-version: 22 cache: npm - run: npm ci - - name: Lint + format check (biome) - run: npm run lint + - name: Format check (prettier) + run: npm run format:check - name: Type check run: npm run typecheck - name: Build diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..9eb4595 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +dist +coverage +package-lock.json diff --git a/package-lock.json b/package-lock.json index e6fe891..47b3b53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,6 @@ }, "devDependencies": { "@arethetypeswrong/cli": "0.18.5", - "@biomejs/biome": "2.5.5", "@types/node": "~22.20.1", "@vitest/coverage-v8": "^2.1.9", "openai": "~6.47.0", @@ -190,169 +189,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@biomejs/biome": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.5.tgz", - "integrity": "sha512-r1S8nFsAG1MY+vJFZALzIvwXAJv6ejDQ0mxP21Tgr9YK3ZFtjrvbBwDdNhx1rUqvccEIeNg20cYCNzl6Cr69pQ==", - "dev": true, - "license": "MIT OR Apache-2.0", - "bin": { - "biome": "bin/biome" - }, - "engines": { - "node": ">=14.21.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/biome" - }, - "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.5.5", - "@biomejs/cli-darwin-x64": "2.5.5", - "@biomejs/cli-linux-arm64": "2.5.5", - "@biomejs/cli-linux-arm64-musl": "2.5.5", - "@biomejs/cli-linux-x64": "2.5.5", - "@biomejs/cli-linux-x64-musl": "2.5.5", - "@biomejs/cli-win32-arm64": "2.5.5", - "@biomejs/cli-win32-x64": "2.5.5" - } - }, - "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.5.tgz", - "integrity": "sha512-kUrAhXVWUrwmAUnV2iXSK7umxKFysTwvqK+Ty6ptUcLY/7T3SnCAjUowE4uvwaEej6nXZ7hu/dTtbokKdsPeag==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.5.tgz", - "integrity": "sha512-DamiYc5bUYZ2uxlfc+RLEPtz1Abb6PO5eTbOkufLpSGwd/7AMQAdxhFYiXmwwkJL8IsT8S7GvdgwDHqaMFAvKw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.5.tgz", - "integrity": "sha512-lRKF/pH/1RiYiBKExi3TCZVAtvzEm77aifrvcNiDFrR9WxeAnDUjDnseb6y2XV85mjitLs6SILGm2XG77cHtSQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.5.tgz", - "integrity": "sha512-U4WMl/sy/E/Q73vf15VspakLRRs2LDFcCeBxJnQfXzssb88zpV6PJPaQ3ezhQ7H6Ht2/8bvuZeHgJWzmoxllZg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.5.tgz", - "integrity": "sha512-H/O39nJEw/2Zm/fm7hrmxxoF8kK/aU1uCoPp70ruXVbomaAdLpJJnCmL11Q2JotT8QVHH06So04Oq53lCSwSwQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.5.tgz", - "integrity": "sha512-m7wC7tjX5Lrmo69dc4md8FeKpPU1NTCY1v7xUoQQ2vadWwNnBS0KZOG8471otFPHrTHihQJAjQPgMObpLvDe6A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.5.tgz", - "integrity": "sha512-7BryINPuYypLUAH3o/o5ZdgomJ4zn3EDR0ChZJst7n32S6ZhKbgHXuYydLu+YAnx59ehGFR0z/MG6qnzQi3Yyw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-x64": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.5.tgz", - "integrity": "sha512-bIBFo+n6MIxdNcVFy5CrurbKiZQiUciK3bt8+O9I4wjFZNTfXLpi+giq47522eXqW5NBc9ulx7dR1SlZKi2J5g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, "node_modules/@braidai/lang": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@braidai/lang/-/lang-1.1.2.tgz", diff --git a/package.json b/package.json index 39632cb..1fad271 100644 --- a/package.json +++ b/package.json @@ -47,8 +47,8 @@ "scripts": { "build": "tsup", "typecheck": "tsc --noEmit", - "lint": "biome check .", "format": "prettier --write .", + "format:check": "prettier --check .", "check:pkg": "publint --strict && attw --pack", "test": "vitest run", "test:watch": "vitest", @@ -70,7 +70,6 @@ }, "devDependencies": { "@arethetypeswrong/cli": "0.18.5", - "@biomejs/biome": "2.5.5", "@types/node": "~22.20.1", "@vitest/coverage-v8": "^2.1.9", "openai": "~6.47.0",