@otto-code/brain 0.8.3 → 0.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -818,6 +818,7 @@ export declare const CatalogModelSchema: z.ZodObject<{
818
818
  moe: z.ZodOptional<z.ZodBoolean>;
819
819
  vision: z.ZodOptional<z.ZodBoolean>;
820
820
  thinking: z.ZodOptional<z.ZodBoolean>;
821
+ reasoningEfforts: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
821
822
  contextMax: z.ZodOptional<z.ZodNumber>;
822
823
  useCases: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
823
824
  tier: z.ZodOptional<z.ZodString>;
@@ -835,6 +836,7 @@ export declare const CatalogModelSchema: z.ZodObject<{
835
836
  moe: z.ZodOptional<z.ZodBoolean>;
836
837
  vision: z.ZodOptional<z.ZodBoolean>;
837
838
  thinking: z.ZodOptional<z.ZodBoolean>;
839
+ reasoningEfforts: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
838
840
  contextMax: z.ZodOptional<z.ZodNumber>;
839
841
  useCases: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
840
842
  tier: z.ZodOptional<z.ZodString>;
@@ -852,6 +854,7 @@ export declare const CatalogModelSchema: z.ZodObject<{
852
854
  moe: z.ZodOptional<z.ZodBoolean>;
853
855
  vision: z.ZodOptional<z.ZodBoolean>;
854
856
  thinking: z.ZodOptional<z.ZodBoolean>;
857
+ reasoningEfforts: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
855
858
  contextMax: z.ZodOptional<z.ZodNumber>;
856
859
  useCases: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
857
860
  tier: z.ZodOptional<z.ZodString>;
@@ -876,6 +879,7 @@ export declare const CatalogSchema: z.ZodObject<{
876
879
  moe: z.ZodOptional<z.ZodBoolean>;
877
880
  vision: z.ZodOptional<z.ZodBoolean>;
878
881
  thinking: z.ZodOptional<z.ZodBoolean>;
882
+ reasoningEfforts: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
879
883
  contextMax: z.ZodOptional<z.ZodNumber>;
880
884
  useCases: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
881
885
  tier: z.ZodOptional<z.ZodString>;
@@ -893,6 +897,7 @@ export declare const CatalogSchema: z.ZodObject<{
893
897
  moe: z.ZodOptional<z.ZodBoolean>;
894
898
  vision: z.ZodOptional<z.ZodBoolean>;
895
899
  thinking: z.ZodOptional<z.ZodBoolean>;
900
+ reasoningEfforts: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
896
901
  contextMax: z.ZodOptional<z.ZodNumber>;
897
902
  useCases: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
898
903
  tier: z.ZodOptional<z.ZodString>;
@@ -910,6 +915,7 @@ export declare const CatalogSchema: z.ZodObject<{
910
915
  moe: z.ZodOptional<z.ZodBoolean>;
911
916
  vision: z.ZodOptional<z.ZodBoolean>;
912
917
  thinking: z.ZodOptional<z.ZodBoolean>;
918
+ reasoningEfforts: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
913
919
  contextMax: z.ZodOptional<z.ZodNumber>;
914
920
  useCases: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
915
921
  tier: z.ZodOptional<z.ZodString>;
@@ -933,6 +939,7 @@ export declare const CatalogSchema: z.ZodObject<{
933
939
  moe: z.ZodOptional<z.ZodBoolean>;
934
940
  vision: z.ZodOptional<z.ZodBoolean>;
935
941
  thinking: z.ZodOptional<z.ZodBoolean>;
942
+ reasoningEfforts: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
936
943
  contextMax: z.ZodOptional<z.ZodNumber>;
937
944
  useCases: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
938
945
  tier: z.ZodOptional<z.ZodString>;
@@ -950,6 +957,7 @@ export declare const CatalogSchema: z.ZodObject<{
950
957
  moe: z.ZodOptional<z.ZodBoolean>;
951
958
  vision: z.ZodOptional<z.ZodBoolean>;
952
959
  thinking: z.ZodOptional<z.ZodBoolean>;
960
+ reasoningEfforts: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
953
961
  contextMax: z.ZodOptional<z.ZodNumber>;
954
962
  useCases: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
955
963
  tier: z.ZodOptional<z.ZodString>;
@@ -967,6 +975,7 @@ export declare const CatalogSchema: z.ZodObject<{
967
975
  moe: z.ZodOptional<z.ZodBoolean>;
968
976
  vision: z.ZodOptional<z.ZodBoolean>;
969
977
  thinking: z.ZodOptional<z.ZodBoolean>;
978
+ reasoningEfforts: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
970
979
  contextMax: z.ZodOptional<z.ZodNumber>;
971
980
  useCases: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
972
981
  tier: z.ZodOptional<z.ZodString>;
@@ -990,6 +999,7 @@ export declare const CatalogSchema: z.ZodObject<{
990
999
  moe: z.ZodOptional<z.ZodBoolean>;
991
1000
  vision: z.ZodOptional<z.ZodBoolean>;
992
1001
  thinking: z.ZodOptional<z.ZodBoolean>;
1002
+ reasoningEfforts: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
993
1003
  contextMax: z.ZodOptional<z.ZodNumber>;
994
1004
  useCases: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
995
1005
  tier: z.ZodOptional<z.ZodString>;
@@ -1007,6 +1017,7 @@ export declare const CatalogSchema: z.ZodObject<{
1007
1017
  moe: z.ZodOptional<z.ZodBoolean>;
1008
1018
  vision: z.ZodOptional<z.ZodBoolean>;
1009
1019
  thinking: z.ZodOptional<z.ZodBoolean>;
1020
+ reasoningEfforts: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
1010
1021
  contextMax: z.ZodOptional<z.ZodNumber>;
1011
1022
  useCases: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
1012
1023
  tier: z.ZodOptional<z.ZodString>;
@@ -1024,6 +1035,7 @@ export declare const CatalogSchema: z.ZodObject<{
1024
1035
  moe: z.ZodOptional<z.ZodBoolean>;
1025
1036
  vision: z.ZodOptional<z.ZodBoolean>;
1026
1037
  thinking: z.ZodOptional<z.ZodBoolean>;
1038
+ reasoningEfforts: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
1027
1039
  contextMax: z.ZodOptional<z.ZodNumber>;
1028
1040
  useCases: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
1029
1041
  tier: z.ZodOptional<z.ZodString>;
@@ -172,6 +172,7 @@ export const CatalogModelSchema = z
172
172
  moe: z.boolean().optional(),
173
173
  vision: z.boolean().optional(),
174
174
  thinking: z.boolean().optional(),
175
+ reasoningEfforts: z.array(z.string()).optional(),
175
176
  contextMax: z.number().optional(),
176
177
  useCases: z.array(z.string()).optional(),
177
178
  tier: z.string().optional(),
@@ -42,8 +42,30 @@ export function saveProfilesStore(store, paths = resolveBrainPaths()) {
42
42
  // -------------------------------------------------------------------- catalog
43
43
  export function loadCatalog(paths = resolveBrainPaths()) {
44
44
  const current = readJson(paths.catalogFile, CatalogSchema);
45
- if (current)
46
- return current;
45
+ if (current) {
46
+ // The catalog is intentionally persisted so local Brain homes survive
47
+ // package updates, but additive metadata still needs to reach existing
48
+ // homes. Backfill only fields introduced by the seed catalog; user-owned
49
+ // catalog values remain authoritative.
50
+ const legacy = readJson(path.join(packageRoot(), "config", "downloads.json"), CatalogSchema);
51
+ if (!legacy)
52
+ return current;
53
+ const seedById = new Map(legacy.models.map((model) => [model.id, model]));
54
+ let changed = false;
55
+ const models = current.models.map((model) => {
56
+ const seed = seedById.get(model.id);
57
+ if (model.reasoningEfforts === undefined && seed?.reasoningEfforts !== undefined) {
58
+ changed = true;
59
+ return { ...model, reasoningEfforts: seed.reasoningEfforts };
60
+ }
61
+ return model;
62
+ });
63
+ if (!changed)
64
+ return current;
65
+ const merged = { ...current, models };
66
+ writeJson(paths.catalogFile, merged);
67
+ return merged;
68
+ }
47
69
  const legacy = path.join(packageRoot(), "config", "downloads.json");
48
70
  const migrated = readJson(legacy, CatalogSchema);
49
71
  if (migrated) {
@@ -61,6 +61,7 @@ export function enrichWithCatalog(models, catalog) {
61
61
  useCases: entry.useCases,
62
62
  tier: entry.tier,
63
63
  thinking: entry.thinking,
64
+ reasoningEfforts: entry.reasoningEfforts,
64
65
  contextMax: entry.contextMax,
65
66
  };
66
67
  });
@@ -160,9 +160,15 @@ export function describeModel(model, options = {}) {
160
160
  quantization: model.quant || null,
161
161
  state,
162
162
  max_context_length: md.contextLength ?? null,
163
- reasoning: Boolean(md.reasoning ?? model.thinking),
163
+ // GGUF template detection is deliberately conservative. A false result
164
+ // means "not detected", not proof that a catalog-marked reasoner is not
165
+ // one, so preserve the catalog's positive capability metadata.
166
+ reasoning: Boolean(md.reasoning || model.thinking),
164
167
  };
165
- const reasoningEfforts = md["reasoning_efforts"];
168
+ // Prefer explicit GGUF metadata when a runtime supplies it, but preserve
169
+ // catalog knowledge for models whose chat template does not encode the
170
+ // accepted request levels (GPT-OSS is one such model).
171
+ const reasoningEfforts = md["reasoning_efforts"] ?? model.reasoningEfforts;
166
172
  if (Array.isArray(reasoningEfforts) &&
167
173
  reasoningEfforts.every((value) => typeof value === "string")) {
168
174
  entry.reasoning_efforts = reasoningEfforts;
package/dist/types.d.ts CHANGED
@@ -49,6 +49,8 @@ export interface Model {
49
49
  tier?: string;
50
50
  /** Whether the catalog marks this as a thinking/reasoning model. */
51
51
  thinking?: boolean;
52
+ /** Per-request reasoning levels accepted by the model's OpenAI-compatible API. */
53
+ reasoningEfforts?: string[];
52
54
  /** The catalog's advertised max context, if known. */
53
55
  contextMax?: number;
54
56
  /** Back-reference: the id of the reconciled catalog entry, if matched. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@otto-code/brain",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "description": "Otto Brain - self-contained host for local GGUF models, with measured VRAM budgeting and reasoning-budget control",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "bin": {
@@ -50,7 +50,7 @@
50
50
  "devDependencies": {
51
51
  "@types/node": "^20.9.0",
52
52
  "typescript": "^5.9.3",
53
- "vitest": "^4.1.6"
53
+ "vitest": "^4.1.10"
54
54
  },
55
55
  "engines": {
56
56
  "node": ">=20.6.0"