Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion .github/workflows/sync-models.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,19 @@ jobs:

pnpm exec tsx packages/proxy/scripts/sync_models.ts sync-cohere --write

- name: Sync Typesafe models and documented pricing
env:
TYPESAFE_API_KEY: ${{ secrets.TYPESAFE_API_KEY }}
run: |
set -euo pipefail

if [ -z "${TYPESAFE_API_KEY:-}" ]; then
echo "TYPESAFE_API_KEY not set; skipping Typesafe model sync."
exit 0
fi

pnpm exec tsx packages/proxy/scripts/sync_models.ts sync-typesafe

- name: Sync OpenRouter alternates from /api/v1/models
run: |
set -euo pipefail
Expand Down Expand Up @@ -243,7 +256,7 @@ jobs:

- name: Validate model schema
if: ${{ steps.changes.outputs.changed == 'true' }}
run: pnpm exec vitest run packages/proxy/schema/models.test.ts packages/proxy/schema/index.test.ts packages/proxy/scripts/sync_models.test.ts packages/proxy/scripts/collect_changed_model_ids.test.ts
run: pnpm exec vitest run packages/proxy/schema/models.test.ts packages/proxy/schema/index.test.ts packages/proxy/scripts/sync_models.test.ts packages/proxy/scripts/sync_typesafe.test.ts packages/proxy/scripts/collect_changed_model_ids.test.ts

- name: Create PR
id: pr
Expand Down
7 changes: 7 additions & 0 deletions packages/proxy/schema/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,13 @@ describe("getDirectModelEndpointTypes", () => {
});

describe("APISecretSchema compatibility", () => {
it("accepts Typesafe provider secrets", () => {
expect(APISecretSchema.parse({ type: "typesafe", secret: null })).toEqual({
type: "typesafe",
secret: null,
});
});

it("accepts null secrets from the control plane", () => {
const parsed = APISecretSchema.parse({
secret: null,
Expand Down
4 changes: 4 additions & 0 deletions packages/proxy/schema/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export const defaultModelParamSettings: {
verbosity: undefined,
},
js: {},
typesafe: {},
window: {
temperature: undefined,
topK: 5,
Expand All @@ -171,6 +172,7 @@ export const modelProviderHasTools: {
anthropic: true,
google: true,
js: false,
typesafe: false,
window: false,
converse: true,
};
Expand All @@ -195,6 +197,7 @@ export const DefaultEndpointTypes: {
js: ["js"],
window: ["js"],
converse: ["bedrock"],
typesafe: ["typesafe"],
};

export const AvailableEndpointTypes: { [name: string]: ModelEndpointType[] } = {
Expand Down Expand Up @@ -1527,6 +1530,7 @@ export const EndpointProviderToBaseURL: {
} = {
openai: "https://api.openai.com/v1",
braintrust: null,
typesafe: null,
anthropic: "https://api.anthropic.com/v1",
perplexity: "https://api.perplexity.ai",
replicate: "https://openai-proxy.replicate.com/v1",
Expand Down
1 change: 1 addition & 0 deletions packages/proxy/schema/media-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export const ModelFormatMediaTypes: {
...toMediaTypeSupport(DOCUMENT_MEDIA_TYPES),
},
js: {},
typesafe: {},
window: {},
};

Expand Down
32 changes: 32 additions & 0 deletions packages/proxy/schema/model_list.json
Original file line number Diff line number Diff line change
Expand Up @@ -14153,6 +14153,38 @@
"openrouter"
]
},
"jev-preview": {
"format": "typesafe",
"flavor": "evaluation",
"input_cost_per_mil_tokens": 0.042,
"output_cost_per_mil_tokens": 0,
"displayName": "jev-preview",
"description": "A preview version of `jev-latest`: should be better in most ways",
"available_providers": [
"typesafe"
]
},
"jev-latest": {
"format": "typesafe",
"flavor": "evaluation",
"input_cost_per_mil_tokens": 0.042,
"output_cost_per_mil_tokens": 0,
"displayName": "jev-latest",
"description": "The latest iteration of TypeSafe's System One Model: Jev",
"available_providers": [
"typesafe"
]
},
"jev-1.13.0": {
"format": "typesafe",
"flavor": "evaluation",
"input_cost_per_mil_tokens": 0.042,
"output_cost_per_mil_tokens": 0,
"displayName": "Jev 1.13",
"available_providers": [
"typesafe"
]
},
"inference-net/schematron-v2-small": {
"format": "openai",
"flavor": "chat",
Expand Down
14 changes: 14 additions & 0 deletions packages/proxy/schema/models.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ it("parse model list", () => {
}
});

it.each(["jev-1.13.0", "jev-latest", "jev-preview"] as const)(
"exposes Typesafe evaluation pricing for %s",
(model) => {
expect(raw_models[model]).toMatchObject({
format: "typesafe",
flavor: "evaluation",
available_providers: ["typesafe"],
input_cost_per_mil_tokens: 0.042,
output_cost_per_mil_tokens: 0,
});
expect(getModelEndpointTypes(model)).toEqual(["typesafe"]);
},
);

it("keeps equivalent model references within the catalog", () => {
const models = z.record(ModelSchema).parse(raw_models);
for (const [key, value] of Object.entries(models)) {
Expand Down
9 changes: 8 additions & 1 deletion packages/proxy/schema/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { _urljoin } from "../src/util";
export const PromptInputs = ["chat", "completion"] as const;
export type PromptInputType = (typeof PromptInputs)[number];

export const ModelFlavors = ["chat", "completion", "embedding"] as const;
export const ModelFlavors = [
"chat",
"completion",
"embedding",
"evaluation",
] as const;
export type ModelFlavor = (typeof ModelFlavors)[number];

export const ModelFormats = [
Expand All @@ -14,6 +19,7 @@ export const ModelFormats = [
"window",
"js",
"converse",
"typesafe",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move the Typesafe provider integration to gateway

Adding typesafe as a format here implements provider behavior in the deprecated proxy package, so the active gateway will not receive the corresponding routing, translation, and secret handling. Keep the model_list.json catalog entries here, but move the schema, provider, and automation integration to the parent repository's gateway/ directory as required.

AGENTS.md reference: AGENTS.md:L10-L15

Useful? React with 👍 / 👎.

] as const;
export type ModelFormat = (typeof ModelFormats)[number];

Expand All @@ -39,6 +45,7 @@ export const ModelEndpointType = [
"ollama",
"replicate",
"cohere",
"typesafe",
"openrouter",
"js",
] as const;
Expand Down
1 change: 1 addition & 0 deletions packages/proxy/schema/secrets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ export const APISecretSchema = z.union([
"cerebras",
"xAI",
"openrouter",
"typesafe",
"js",
]),
metadata: BaseMetadataSchema.nullish(),
Expand Down
38 changes: 38 additions & 0 deletions packages/proxy/scripts/sync_models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
GOOGLE_VERTEX_LOCATIONS_URL,
syncVertexSupportedRegions,
} from "./sync_vertex_regions";
import { fetchTypesafeModels, mergeTypesafeModels } from "./sync_typesafe";

const execAsync = promisify(exec);

Expand Down Expand Up @@ -388,6 +389,7 @@ const SYNC_DEFAULT_ENDPOINT_TYPES = {
js: ["js"],
window: ["js"],
converse: ["bedrock"],
typesafe: ["typesafe"],
} satisfies Record<
ModelSpec["format"],
NonNullable<ModelSpec["available_providers"]>
Expand Down Expand Up @@ -3904,6 +3906,34 @@ async function syncCohereModelsCommand(argv: any) {
}
}

async function syncTypesafeModelsCommand() {
try {
const remoteModels = await fetchTypesafeModels();
const localModels = await readLocalModels(LOCAL_MODEL_LIST_PATH);
const { models, changed } = mergeTypesafeModels(localModels, remoteModels);

console.log(
`Typesafe: ${Object.keys(remoteModels).length} priced models, ${changed.length} changes.`,
);
for (const name of changed) {
console.log(`[UPDATE] ${name}`);
}

if (changed.length > 0) {
await writeLocalModels(models);
await syncProviderMappingsForLocalModels(
models,
Object.keys(remoteModels),
);
}
} catch (error) {
console.error(
error instanceof Error ? error.message : "Typesafe sync failed.",
);
process.exitCode = 1;
}
}

// Format schema/index.ts with Prettier so the catalog scripts never emit
// unlinted TypeScript. fix_bot_issue.ts, the LLM enrichment/Codex-response
// steps, and older writers can all leave entries like ["openai","azure"]
Expand Down Expand Up @@ -3994,6 +4024,14 @@ async function normalizeLocalModelsCommand(argv: any) {

async function main() {
await yargs(hideBin(process.argv))
.command(
"sync-typesafe",
"Sync Typesafe model IDs and documented pricing. Requires TYPESAFE_API_KEY.",
(y) => y,
async () => {
await syncTypesafeModelsCommand();
},
)
.command(
"normalize-local-models",
"Normalize legacy local model ids and canonicalize model_list.json",
Expand Down
Loading
Loading