diff --git a/.changeset/fix-default-model-tier.md b/.changeset/fix-default-model-tier.md new file mode 100644 index 00000000..5d583a42 --- /dev/null +++ b/.changeset/fix-default-model-tier.md @@ -0,0 +1,11 @@ +--- +"@perstack/runtime": patch +"@perstack/installer": patch +"perstack": patch +--- + +Fix: Preserve `defaultModelTier` when resolving experts from API + +The `toRuntimeExpert` function in both runtime and installer packages was dropping the `defaultModelTier` field when converting API response experts to runtime Expert objects. This caused all API-fetched experts to ignore their configured model tier and fall back to the provider's highest-tier model (e.g., claude-opus-4-6 instead of claude-haiku-4-5 for `defaultModelTier: "low"`). + +Also fixed `configExpertToExpert` in the installer package which had the same omission when converting TOML config experts. diff --git a/bun.lock b/bun.lock index bb7c4449..be1afd4a 100644 --- a/bun.lock +++ b/bun.lock @@ -18,10 +18,10 @@ }, "apps/base": { "name": "@perstack/base", - "version": "0.0.77", + "version": "0.0.78", "dependencies": { "@modelcontextprotocol/sdk": "^1.26.0", - "@perstack/core": "0.0.60", + "@perstack/core": "0.0.64", "commander": "^14.0.3", "zod": "^4.3.6", }, @@ -49,7 +49,7 @@ }, "apps/perstack": { "name": "perstack", - "version": "0.0.114", + "version": "0.0.115", "dependencies": { "commander": "^14.0.3", }, @@ -67,7 +67,7 @@ }, "packages/core": { "name": "@perstack/core", - "version": "0.0.63", + "version": "0.0.64", "dependencies": { "@paralleldrive/cuid2": "^3.3.0", "zod": "^4.3.6", @@ -80,7 +80,7 @@ }, "packages/filesystem": { "name": "@perstack/filesystem-storage", - "version": "0.0.34", + "version": "0.0.35", "dependencies": { "@perstack/core": "workspace:*", }, @@ -93,9 +93,9 @@ }, "packages/installer": { "name": "@perstack/installer", - "version": "0.0.33", + "version": "0.0.34", "dependencies": { - "@perstack/api-client": "^0.0.57", + "@perstack/api-client": "^0.0.58", "@perstack/core": "workspace:*", "@perstack/perstack-toml": "workspace:*", "@perstack/skill-manager": "workspace:*", @@ -109,7 +109,7 @@ }, "packages/log": { "name": "@perstack/log", - "version": "0.0.20", + "version": "0.0.21", "dependencies": { "@perstack/core": "workspace:*", "@perstack/filesystem-storage": "workspace:*", @@ -122,7 +122,7 @@ }, "packages/perstack-toml": { "name": "@perstack/perstack-toml", - "version": "0.0.20", + "version": "0.0.21", "dependencies": { "@perstack/core": "workspace:*", "smol-toml": "^1.6.0", @@ -136,7 +136,7 @@ }, "packages/providers/anthropic": { "name": "@perstack/anthropic-provider", - "version": "0.0.37", + "version": "0.0.38", "dependencies": { "@ai-sdk/anthropic": "^3.0.47", "@perstack/core": "workspace:*", @@ -151,7 +151,7 @@ }, "packages/providers/azure-openai": { "name": "@perstack/azure-openai-provider", - "version": "0.0.36", + "version": "0.0.37", "dependencies": { "@ai-sdk/azure": "^3.0.31", "@perstack/core": "workspace:*", @@ -166,7 +166,7 @@ }, "packages/providers/bedrock": { "name": "@perstack/bedrock-provider", - "version": "0.0.36", + "version": "0.0.37", "dependencies": { "@ai-sdk/amazon-bedrock": "^4.0.60", "@perstack/core": "workspace:*", @@ -181,7 +181,7 @@ }, "packages/providers/core": { "name": "@perstack/provider-core", - "version": "0.0.36", + "version": "0.0.37", "dependencies": { "@perstack/core": "workspace:*", "undici": "^7.22.0", @@ -195,7 +195,7 @@ }, "packages/providers/deepseek": { "name": "@perstack/deepseek-provider", - "version": "0.0.36", + "version": "0.0.37", "dependencies": { "@ai-sdk/deepseek": "^2.0.20", "@perstack/core": "workspace:*", @@ -210,7 +210,7 @@ }, "packages/providers/fireworks": { "name": "@perstack/fireworks-provider", - "version": "0.0.4", + "version": "0.0.5", "dependencies": { "@ai-sdk/fireworks": "^2.0.36", "@perstack/core": "workspace:*", @@ -225,7 +225,7 @@ }, "packages/providers/google": { "name": "@perstack/google-provider", - "version": "0.0.36", + "version": "0.0.37", "dependencies": { "@ai-sdk/google": "^3.0.29", "@perstack/core": "workspace:*", @@ -240,7 +240,7 @@ }, "packages/providers/ollama": { "name": "@perstack/ollama-provider", - "version": "0.0.36", + "version": "0.0.37", "dependencies": { "@perstack/core": "workspace:*", "@perstack/provider-core": "workspace:*", @@ -255,7 +255,7 @@ }, "packages/providers/openai": { "name": "@perstack/openai-provider", - "version": "0.0.36", + "version": "0.0.37", "dependencies": { "@ai-sdk/openai": "^3.0.29", "@perstack/core": "workspace:*", @@ -270,7 +270,7 @@ }, "packages/providers/vertex": { "name": "@perstack/vertex-provider", - "version": "0.0.36", + "version": "0.0.37", "dependencies": { "@ai-sdk/google-vertex": "^4.0.58", "@perstack/core": "workspace:*", @@ -285,9 +285,9 @@ }, "packages/react": { "name": "@perstack/react", - "version": "0.0.67", + "version": "0.0.68", "dependencies": { - "@perstack/core": "0.0.58", + "@perstack/core": "0.0.64", }, "devDependencies": { "@testing-library/react": "^16.3.2", @@ -304,7 +304,7 @@ }, "packages/runtime": { "name": "@perstack/runtime", - "version": "0.0.130", + "version": "0.0.131", "dependencies": { "@ai-sdk/amazon-bedrock": "^4.0.60", "@ai-sdk/anthropic": "^3.0.44", @@ -316,9 +316,9 @@ "@ai-sdk/openai": "^3.0.29", "@modelcontextprotocol/sdk": "^1.26.0", "@paralleldrive/cuid2": "^3.3.0", - "@perstack/api-client": "^0.0.57", - "@perstack/base": "0.0.73", - "@perstack/core": "0.0.60", + "@perstack/api-client": "^0.0.58", + "@perstack/base": "0.0.78", + "@perstack/core": "0.0.64", "ai": "^6.0.86", "ollama-ai-provider-v2": "^3.3.0", "smol-toml": "^1.6.0", @@ -345,7 +345,7 @@ }, "packages/skill-manager": { "name": "@perstack/skill-manager", - "version": "0.0.24", + "version": "0.0.25", "dependencies": { "@modelcontextprotocol/sdk": "^1.26.0", "@paralleldrive/cuid2": "^3.3.0", @@ -361,9 +361,9 @@ }, "packages/studio": { "name": "@perstack/studio", - "version": "0.0.14", + "version": "0.0.15", "dependencies": { - "@perstack/api-client": "^0.0.57", + "@perstack/api-client": "^0.0.58", "@perstack/core": "workspace:*", "@perstack/installer": "workspace:*", "@perstack/perstack-toml": "workspace:*", @@ -376,7 +376,7 @@ }, "packages/tui": { "name": "@perstack/tui", - "version": "0.0.26", + "version": "0.0.27", "dependencies": { "@paralleldrive/cuid2": "^3.3.0", "@perstack/core": "workspace:*", @@ -393,7 +393,7 @@ }, "packages/tui-components": { "name": "@perstack/tui-components", - "version": "0.0.28", + "version": "0.0.29", "dependencies": { "@perstack/core": "workspace:*", "@perstack/react": "workspace:*", @@ -623,7 +623,7 @@ "@perstack/anthropic-provider": ["@perstack/anthropic-provider@workspace:packages/providers/anthropic"], - "@perstack/api-client": ["@perstack/api-client@0.0.57", "", { "peerDependencies": { "@perstack/core": ">0.0.42", "zod": ">=4.3.6" } }, "sha512-SAVC3FiqdsntA2ITRcuGmoYBNYHtgGaKPMKzJXi9M6yUXw3g0OU5FqH69OgOFoKccC4bev+x5vWI1xSQQEpLSA=="], + "@perstack/api-client": ["@perstack/api-client@0.0.58", "", { "peerDependencies": { "@perstack/core": ">0.0.42", "zod": ">=4.3.6" } }, "sha512-cWJIJ77plToEy1FSSm9IZbXhdi0MKoETvmBO7n6HGLTjExqWJA89eL2KIZDsJIxxXV3gUs5q54xNUZqw/VWl1w=="], "@perstack/azure-openai-provider": ["@perstack/azure-openai-provider@workspace:packages/providers/azure-openai"], diff --git a/packages/installer/package.json b/packages/installer/package.json index 5474fe47..d441a49a 100644 --- a/packages/installer/package.json +++ b/packages/installer/package.json @@ -27,7 +27,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@perstack/api-client": "^0.0.57", + "@perstack/api-client": "^0.0.58", "@perstack/core": "workspace:*", "@perstack/perstack-toml": "workspace:*", "@perstack/skill-manager": "workspace:*" diff --git a/packages/installer/src/expert-resolver.ts b/packages/installer/src/expert-resolver.ts index fd36ff56..145bb734 100644 --- a/packages/installer/src/expert-resolver.ts +++ b/packages/installer/src/expert-resolver.ts @@ -3,6 +3,7 @@ import { defaultPerstackApiBaseUrl, type Expert, expertSchema, + type ModelTier, type PerstackConfig, PerstackError, type RuntimeVersion, @@ -47,6 +48,7 @@ export type PublishedExpertData = { > delegates?: string[] tags?: string[] + defaultModelTier?: ModelTier } export function toRuntimeExpert(key: string, expert: PublishedExpertData): Expert { @@ -117,6 +119,7 @@ export function toRuntimeExpert(key: string, expert: PublishedExpertData): Exper skills, delegates: expert.delegates ?? [], tags: expert.tags ?? [], + defaultModelTier: expert.defaultModelTier, } } @@ -136,6 +139,7 @@ export function configExpertToExpert( providerTools: configExpert.providerTools, providerSkills: configExpert.providerSkills, providerToolOptions: configExpert.providerToolOptions, + defaultModelTier: configExpert.defaultModelTier, }) } diff --git a/packages/runtime/package.json b/packages/runtime/package.json index f8fe2d54..422e4c7c 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -37,7 +37,7 @@ "@ai-sdk/openai": "^3.0.29", "@modelcontextprotocol/sdk": "^1.26.0", "@paralleldrive/cuid2": "^3.3.0", - "@perstack/api-client": "^0.0.57", + "@perstack/api-client": "^0.0.58", "@perstack/base": "0.0.78", "@perstack/core": "0.0.64", "ai": "^6.0.86", diff --git a/packages/runtime/src/helpers/resolve-expert.test.ts b/packages/runtime/src/helpers/resolve-expert.test.ts index a30f3405..9a6efba6 100644 --- a/packages/runtime/src/helpers/resolve-expert.test.ts +++ b/packages/runtime/src/helpers/resolve-expert.test.ts @@ -46,6 +46,7 @@ mock.module("@perstack/api-client", () => ({ skills: {}, delegates: [], tags: [], + defaultModelTier: "low", }, }, }, @@ -136,6 +137,14 @@ describe("@perstack/runtime: resolveExpertToRun", () => { expect(result.key).toBe("published-expert") }) + it("preserves defaultModelTier from API response", async () => { + const experts: Record = {} + await resolveExpertToRun("remote-expert", experts, { + perstackApiBaseUrl: "https://api.test.com", + }) + expect(experts["@remote-expert/delegate"].defaultModelTier).toBe("low") + }) + it("resolves delegate from already-populated experts without API call", async () => { const experts: Record = {} await resolveExpertToRun("remote-expert", experts, { diff --git a/packages/runtime/src/helpers/resolve-expert.ts b/packages/runtime/src/helpers/resolve-expert.ts index e36d11d3..16396bff 100644 --- a/packages/runtime/src/helpers/resolve-expert.ts +++ b/packages/runtime/src/helpers/resolve-expert.ts @@ -1,5 +1,11 @@ import { createApiClient } from "@perstack/api-client" -import { type Expert, PerstackError, type RuntimeVersion, type Skill } from "@perstack/core" +import { + type Expert, + type ModelTier, + PerstackError, + type RuntimeVersion, + type Skill, +} from "@perstack/core" export async function resolveExpertToRun( expertKey: string, @@ -73,6 +79,7 @@ function toRuntimeExpert( > delegates?: string[] tags?: string[] + defaultModelTier?: ModelTier }, ): Expert { const skills: Record = Object.fromEntries( @@ -142,5 +149,6 @@ function toRuntimeExpert( skills, delegates: expert.delegates ?? [], tags: expert.tags ?? [], + defaultModelTier: expert.defaultModelTier, } } diff --git a/packages/studio/package.json b/packages/studio/package.json index c6472cbd..12c3bca0 100644 --- a/packages/studio/package.json +++ b/packages/studio/package.json @@ -27,7 +27,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@perstack/api-client": "^0.0.57", + "@perstack/api-client": "^0.0.58", "@perstack/core": "workspace:*", "@perstack/installer": "workspace:*", "@perstack/perstack-toml": "workspace:*"