feat: rename sdk package
This commit is contained in:
@@ -30,7 +30,7 @@
|
||||
},
|
||||
},
|
||||
"packages/sdk": {
|
||||
"name": "models.dev",
|
||||
"name": "@opencode-ai/models",
|
||||
"version": "0.0.0",
|
||||
"devDependencies": {
|
||||
"@models.dev/core": "workspace:*",
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 models.dev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,9 +1,9 @@
|
||||
# models.dev
|
||||
# @opencode-ai/models
|
||||
|
||||
Official typed client for the [models.dev](https://models.dev) API — an open-source database of AI model capabilities, pricing, and limits.
|
||||
|
||||
```sh
|
||||
npm install models.dev
|
||||
npm install @opencode-ai/models
|
||||
```
|
||||
|
||||
- **Zero dependencies.** The root client is a small `fetch` wrapper; works on Node ≥ 18, Bun, Deno, browsers, and edge runtimes.
|
||||
@@ -13,7 +13,7 @@ npm install models.dev
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { Models } from "models.dev"
|
||||
import { Models } from "@opencode-ai/models"
|
||||
|
||||
const client = Models.make()
|
||||
|
||||
@@ -58,7 +58,7 @@ Errors are a single `ModelsDevError` with `reason: "Transport" | "UnexpectedStat
|
||||
A full copy of the database ships inside the package as a separate, tree-shakable entrypoint — nothing from it is loaded or bundled unless you import it:
|
||||
|
||||
```ts
|
||||
import snapshot, { providers, models, generatedAt } from "models.dev/snapshot"
|
||||
import snapshot, { providers, models, generatedAt } from "@opencode-ai/models/snapshot"
|
||||
|
||||
providers["anthropic"]?.models["claude-opus-4-6"]?.limit.context
|
||||
```
|
||||
@@ -66,17 +66,17 @@ providers["anthropic"]?.models["claude-opus-4-6"]?.limit.context
|
||||
Use it for no-network runtimes, tests, cold-start-sensitive paths, or as an explicit fallback:
|
||||
|
||||
```ts
|
||||
const providers = await client.providers().catch(async () => (await import("models.dev/snapshot")).providers)
|
||||
const providers = await client.providers().catch(async () => (await import("@opencode-ai/models/snapshot")).providers)
|
||||
```
|
||||
|
||||
Freshness: the published snapshot is at most ~24h behind the live API (data releases are automated). The client is the freshness path; the snapshot is the availability path.
|
||||
|
||||
## Effect
|
||||
|
||||
An Effect-native client lives at `models.dev/effect` (requires the optional peer dependency `effect`):
|
||||
An Effect-native client lives at `@opencode-ai/models/effect` (requires the optional peer dependency `effect`):
|
||||
|
||||
```ts
|
||||
import { Models } from "models.dev/effect"
|
||||
import { Models } from "@opencode-ai/models/effect"
|
||||
import { FetchHttpClient } from "effect/unstable/http"
|
||||
import { Effect } from "effect"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "models.dev",
|
||||
"name": "@opencode-ai/models",
|
||||
"version": "0.0.0",
|
||||
"description": "Official typed client for the models.dev API \u2014 an open database of AI model capabilities, pricing, and limits",
|
||||
"type": "module",
|
||||
@@ -26,9 +26,18 @@
|
||||
"node": ">=18"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./effect": "./src/effect.ts",
|
||||
"./snapshot": "./src/snapshot.js"
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./effect": {
|
||||
"types": "./dist/effect.d.ts",
|
||||
"default": "./dist/effect.js"
|
||||
},
|
||||
"./snapshot": {
|
||||
"types": "./dist/snapshot.d.ts",
|
||||
"default": "./dist/snapshot.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
@@ -36,6 +45,7 @@
|
||||
"scripts": {
|
||||
"generate": "bun script/generate.ts",
|
||||
"build": "bun script/build.ts",
|
||||
"prepack": "bun run build",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "bun run generate && bun run typecheck && bun test"
|
||||
},
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
#!/usr/bin/env bun
|
||||
// Publishes models.dev to npm, opencode-style:
|
||||
// - the version is never stored in git: it is `npm view models.dev version`
|
||||
// Publishes @opencode-ai/models to npm, opencode-style:
|
||||
// - the version is never stored in git: it is read from npm
|
||||
// plus a semver bump computed here (patch by default);
|
||||
// - `--if-changed` (scheduled data releases) skips publishing when the
|
||||
// freshly generated snapshot payload is byte-identical to the one inside
|
||||
// the currently published tarball;
|
||||
// - package.json exports are rewritten src -> dist for the tarball and
|
||||
// restored afterwards.
|
||||
// - package.json and src/version.ts are restored after publishing.
|
||||
//
|
||||
// Auth: npm Trusted Publishing (OIDC) in CI — no token needed once the
|
||||
// package is linked to this repo+workflow on npmjs.com. `--provenance` is
|
||||
@@ -17,9 +16,9 @@ import { appendFile, mkdtemp, rm } from "node:fs/promises"
|
||||
import { tmpdir } from "node:os"
|
||||
import { $ } from "bun"
|
||||
import { loadCatalog, snapshotPayload } from "./generate.ts"
|
||||
import { build } from "./build.ts"
|
||||
|
||||
const pkg = path.join(import.meta.dirname, "..")
|
||||
const packageName = "@opencode-ai/models"
|
||||
const packageJsonPath = path.join(pkg, "package.json")
|
||||
const versionTsPath = path.join(pkg, "src", "version.ts")
|
||||
|
||||
@@ -32,11 +31,7 @@ if (!["patch", "minor", "major"].includes(bumpArg)) {
|
||||
}
|
||||
|
||||
async function currentVersion(): Promise<string> {
|
||||
try {
|
||||
return (await $`npm view models.dev version`.text()).trim()
|
||||
} catch {
|
||||
return "0.0.0"
|
||||
}
|
||||
return (await $`npm view ${packageName} version`.text()).trim()
|
||||
}
|
||||
|
||||
function bump(version: string, kind: string): string {
|
||||
@@ -50,7 +45,7 @@ function bump(version: string, kind: string): string {
|
||||
async function publishedSnapshotLine(): Promise<string | undefined> {
|
||||
const directory = await mkdtemp(path.join(tmpdir(), "models-dev-publish-"))
|
||||
try {
|
||||
const tarball = (await $`npm pack models.dev@latest --pack-destination ${directory}`.cwd(directory).text())
|
||||
const tarball = (await $`npm pack ${packageName}@latest --pack-destination ${directory}`.cwd(directory).text())
|
||||
.trim()
|
||||
.split("\n")
|
||||
.at(-1)!
|
||||
@@ -59,8 +54,6 @@ async function publishedSnapshotLine(): Promise<string | undefined> {
|
||||
if (!(await file.exists())) return undefined
|
||||
const text = await file.text()
|
||||
return text.split("\n").find((line) => line.startsWith("const data = "))
|
||||
} catch {
|
||||
return undefined
|
||||
} finally {
|
||||
await rm(directory, { recursive: true, force: true })
|
||||
}
|
||||
@@ -80,27 +73,16 @@ if (ifChanged) {
|
||||
const current = await currentVersion()
|
||||
const next = bump(current, bumpArg)
|
||||
|
||||
const alreadyPublished = await $`npm view models.dev@${next} version`.quiet().nothrow().text()
|
||||
if (alreadyPublished.trim() === next) {
|
||||
console.log(`models.dev@${next} already published; skipping`)
|
||||
process.exit(0)
|
||||
}
|
||||
console.log(`Publishing ${packageName}@${next} (${bumpArg} bump from ${current})`)
|
||||
|
||||
console.log(`Publishing models.dev@${next} (${bumpArg} bump from ${current})`)
|
||||
|
||||
const packageJson = await Bun.file(packageJsonPath).json()
|
||||
const packageJsonText = await Bun.file(packageJsonPath).text()
|
||||
const packageJson = JSON.parse(packageJsonText)
|
||||
const versionTs = await Bun.file(versionTsPath).text()
|
||||
|
||||
try {
|
||||
await Bun.write(versionTsPath, versionTs.replace('"0.0.0"', JSON.stringify(next)))
|
||||
await build()
|
||||
|
||||
packageJson.version = next
|
||||
packageJson.exports = {
|
||||
".": { types: "./dist/index.d.ts", default: "./dist/index.js" },
|
||||
"./effect": { types: "./dist/effect.d.ts", default: "./dist/effect.js" },
|
||||
"./snapshot": { types: "./dist/snapshot.d.ts", default: "./dist/snapshot.js" },
|
||||
}
|
||||
await Bun.write(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n")
|
||||
|
||||
const provenance = process.env["GITHUB_ACTIONS"] === "true" ? ["--provenance"] : []
|
||||
@@ -108,7 +90,7 @@ try {
|
||||
|
||||
const output = process.env["GITHUB_OUTPUT"]
|
||||
if (output !== undefined) await appendFile(output, `version=${next}\n`)
|
||||
console.log(`Published models.dev@${next}`)
|
||||
console.log(`Published ${packageName}@${next}`)
|
||||
} finally {
|
||||
await $`git checkout -- ${packageJsonPath} ${versionTsPath}`.nothrow()
|
||||
await Promise.all([Bun.write(packageJsonPath, packageJsonText), Bun.write(versionTsPath, versionTs)])
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ModelsDevError } from "./error.js"
|
||||
import type { Catalog, ModelMetadataMap, ProviderMap } from "./types.js"
|
||||
import { VERSION } from "./version.js"
|
||||
|
||||
/** Accepted anywhere headers can be passed. Same shapes as the standard `HeadersInit`. */
|
||||
export type HeadersInput = Headers | Record<string, string> | Array<[string, string]>
|
||||
@@ -28,7 +27,7 @@ export interface RequestOptions {
|
||||
* Creates a stateless models.dev client. Every method performs exactly one
|
||||
* `GET` and nothing is ever cached — callers who want caching should wrap
|
||||
* calls with their own policy. For a no-network alternative, see the
|
||||
* `models.dev/snapshot` entrypoint.
|
||||
* `@opencode-ai/models/snapshot` entrypoint.
|
||||
*/
|
||||
export function make(options: ClientOptions = {}) {
|
||||
const baseUrl = options.baseUrl ?? "https://models.dev"
|
||||
@@ -36,7 +35,7 @@ export function make(options: ClientOptions = {}) {
|
||||
|
||||
const request = async <A>(path: string, requestOptions?: RequestOptions): Promise<A> => {
|
||||
const fetch = options.fetch ?? globalThis.fetch
|
||||
const headers = new Headers({ "user-agent": `models.dev/${VERSION}` })
|
||||
const headers = new Headers()
|
||||
for (const [key, value] of new Headers(options.headers)) headers.set(key, value)
|
||||
for (const [key, value] of new Headers(requestOptions?.headers)) headers.set(key, value)
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Context, Effect, Layer, Schema } from "effect"
|
||||
import { HttpClient, HttpClientResponse } from "effect/unstable/http"
|
||||
import type { Catalog, ModelMetadataMap, ProviderMap } from "../types.js"
|
||||
import { VERSION } from "../version.js"
|
||||
|
||||
/** The only error in the failure channel of client methods. Wraps the underlying `HttpClientError` as `cause`. */
|
||||
export class ModelsDevError extends Schema.TaggedErrorClass<ModelsDevError>()("ModelsDevError", {
|
||||
@@ -30,7 +29,7 @@ export const make = (options?: ClientOptions) =>
|
||||
const get = <A>(path: string): Effect.Effect<A, ModelsDevError> =>
|
||||
http
|
||||
.get(new URL(path, base), {
|
||||
headers: { "user-agent": `models.dev/${VERSION}`, ...options?.headers },
|
||||
headers: options?.headers,
|
||||
})
|
||||
.pipe(
|
||||
Effect.flatMap(HttpClientResponse.filterStatusOk),
|
||||
@@ -52,7 +51,7 @@ export const make = (options?: ClientOptions) =>
|
||||
export type ModelsClient = Effect.Success<ReturnType<typeof make>>
|
||||
|
||||
/** Service key for dependency-injecting a shared client: `yield* Models.Service`. */
|
||||
export class Service extends Context.Service<Service, ModelsClient>()("models.dev/Models") {}
|
||||
export class Service extends Context.Service<Service, ModelsClient>()("@opencode-ai/models/Models") {}
|
||||
|
||||
/** Layer providing `Models.Service`; requires an `HttpClient` in the environment. */
|
||||
export const layer = (options?: ClientOptions) => Layer.effect(Service)(make(options))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { expect, test } from "bun:test"
|
||||
import { Models, ModelsDevError, VERSION } from "../src/index.js"
|
||||
import { Models, ModelsDevError } from "../src/index.js"
|
||||
|
||||
interface Call {
|
||||
url: URL
|
||||
@@ -50,13 +50,13 @@ test("baseUrl with subpath is preserved, with or without trailing slash", async
|
||||
])
|
||||
})
|
||||
|
||||
test("identifies itself with a versioned user-agent", async () => {
|
||||
test("does not add headers by default", async () => {
|
||||
const { calls, fetch } = stub({})
|
||||
await Models.make({ fetch }).providers()
|
||||
expect(headers(calls[0]!).get("user-agent")).toBe(`models.dev/${VERSION}`)
|
||||
expect([...headers(calls[0]!).entries()]).toEqual([])
|
||||
})
|
||||
|
||||
test("client headers override defaults, request headers override client headers", async () => {
|
||||
test("request headers override client headers", async () => {
|
||||
const { calls, fetch } = stub({})
|
||||
const client = Models.make({ fetch, headers: { "user-agent": "custom", "x-one": "client", "x-two": "client" } })
|
||||
await client.providers({ headers: { "x-two": "request" } })
|
||||
|
||||
@@ -25,7 +25,7 @@ test("providers() succeeds through an injected transport", async () => {
|
||||
const result = await program.pipe(Effect.provide(layer), Effect.runPromise)
|
||||
expect(result["anthropic"]?.id).toBe("anthropic")
|
||||
expect(requests[0]?.url).toBe("https://models.dev/api.json")
|
||||
expect(requests[0]?.headers.get("user-agent")).toMatch(/^models\.dev\//)
|
||||
expect(requests[0]?.headers.get("user-agent")).toBeNull()
|
||||
})
|
||||
|
||||
test("models() and catalog() hit their endpoints, baseUrl subpath preserved", async () => {
|
||||
|
||||
Reference in New Issue
Block a user