Skip to content
Open
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
22 changes: 18 additions & 4 deletions packages/core/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,26 @@ const JsonValue: z.ZodType<JsonValue> = z.lazy(() =>
]),
);

/**
* The catalog's effort levels, exported so a sync provider can filter an upstream
* list against this one rather than restating it. A provider that restates it
* silently drops any level added here until someone remembers to copy the
* addition across.
*/
export const REASONING_EFFORT_VALUES = [
"none",
"minimal",
"low",
"medium",
"high",
"xhigh",
"max",
"default",
] as const;

const ReasoningEffortValue = z.preprocess(
(value) => (value === "null" ? null : value),
z.union([
z.null(),
z.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max", "default"]),
]),
z.union([z.null(), z.enum(REASONING_EFFORT_VALUES)]),
);

export const ReasoningOption = z
Expand Down
28 changes: 26 additions & 2 deletions packages/core/src/sync/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AuthoredModel, AuthoredModelShape, ModelMetadata } from "../schema.js";
import { openMissingModelIssues } from "./missing-issues.js";
import { MissingReasoningOptionsError } from "./missing-reasoning-options.js";
import { aiand } from "./providers/aiand.js";
import { aihubmix } from "./providers/aihubmix.js";
import { ambient } from "./providers/ambient.js";
import { anthropic } from "./providers/anthropic.js";
import { baseten } from "./providers/baseten.js";
Expand Down Expand Up @@ -84,7 +85,12 @@ export interface SyncProvider<SourceModel> {
* deduped GitHub issue per missing model ID.
*/
skipCreates?: boolean;
/** Report remote-only models skipped by skipCreates as GitHub issues. */
/**
* Open one deduped GitHub issue per model the provider skipped. Implied by
* skipCreates, and settable on its own by a provider that creates models but
* still skips the ones it cannot write — without it those skips produce a
* notice nobody acts on.
*/
trackMissingModels?: boolean;
deleteMissing?: boolean;
preserveSymlinks?: boolean;
Expand Down Expand Up @@ -113,6 +119,12 @@ export interface SyncProvider<SourceModel> {
context: {
existing(id: string): ExistingModel | undefined;
authored(id: string): ExistingModel | undefined;
/**
* The leading comment block already on the file, so a provider that owns
* its header (authoritativeHeaders) can refresh the part it generates
* without discarding notes a human wrote around it.
*/
header?(id: string): string | undefined;
},
): {
id: string;
Expand Down Expand Up @@ -141,6 +153,7 @@ export interface SyncResult {

export const providers: {
aiand: SyncProvider<any>;
aihubmix: SyncProvider<any>;
ambient: SyncProvider<any>;
anthropic: SyncProvider<any>;
baseten: SyncProvider<any>;
Expand Down Expand Up @@ -180,6 +193,7 @@ export const providers: {
xai: SyncProvider<any>;
} = {
aiand,
aihubmix,
ambient,
anthropic,
baseten,
Expand Down Expand Up @@ -221,6 +235,7 @@ export const providers: {

export const groups = {
aggregators: [
"aihubmix",
"crossmodel",
"edenai",
"empiriolabs",
Expand Down Expand Up @@ -283,6 +298,9 @@ export async function syncProvider<SourceModel>(
authored(id) {
return existing.get(`${id}.toml`)?.authored;
},
header(id) {
return existing.get(`${id}.toml`)?.header || undefined;
},
});
} catch (error) {
if (!(error instanceof MissingReasoningOptionsError)) throw error;
Expand Down Expand Up @@ -511,7 +529,7 @@ export async function syncProvider<SourceModel>(

const issueModels = [...new Set([
...missingRemote.values(),
...(provider.skipCreates === true ? skippedRemote : []),
...(provider.skipCreates === true || provider.trackMissingModels === true ? skippedRemote : []),
...missingReasoning.keys(),
])];
if (
Expand Down Expand Up @@ -1081,6 +1099,12 @@ export function formatToml(model: z.infer<typeof SyncedAuthoredModel>) {
if (tier.reasoning !== undefined) lines.push(`reasoning = ${formatNumber(tier.reasoning)}`);
if (tier.cache_read !== undefined) lines.push(`cache_read = ${formatNumber(tier.cache_read)}`);
if (tier.cache_write !== undefined) lines.push(`cache_write = ${formatNumber(tier.cache_write)}`);
if (tier.input_audio !== undefined) {
lines.push(`input_audio = ${formatNumber(tier.input_audio)}`);
}
if (tier.output_audio !== undefined) {
lines.push(`output_audio = ${formatNumber(tier.output_audio)}`);
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/sync/missing-issues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function issueBody(provider: MissingModelIssueTarget, modelId: string, reason?:
`| Expected path | \`${provider.modelsDir}/${modelId}.toml\` |`,
"",
reason === undefined
? "Automatic creation was skipped because the remote source is not enough to auto-author a complete catalog entry."
? "Automatic creation was skipped because the remote source is not enough to auto-author a complete catalog entry, or the model belongs on `base_model` and its `models/` metadata is missing."
: `Sync diagnostic: ${reason}`,
"Add the model manually (prefer `base_model` when matching `models/` metadata exists).",
...(reason === undefined ? [] : [
Expand Down
Loading
Loading