Skip to content

Commit a1f1895

Browse files
authored
fix(config): allow thinkingLevelMap in persisted model schema
Allow persisted provider model entries to carry strict thinkingLevelMap values so Microsoft Foundry Entra onboarding can save generated reasoning model config. Closes #91011.
1 parent 78135c3 commit a1f1895

4 files changed

Lines changed: 104 additions & 3 deletions

File tree

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
60c0700719fd2fe3f7cec4c35da10227b681d87ed1a3876ef830eb6bd80d43f2 config-baseline.json
2-
2ed21fa4a416ac2cec55eb2b6d1b11859aa04b40bd78c6ed9f3eb45b7240261c config-baseline.core.json
1+
a5a97a8b484acd13e68604037c8d8f448699700103c6ea2186f5914ad35a0623 config-baseline.json
2+
b0d668dbd794d2f54738152a4bcfd2a306c7954901e78d4dfbde7545a8301ce5 config-baseline.core.json
33
0637c9bdcb9517f56049dd786563366877458d35df575328a6b80a890c8bc915 config-baseline.channel.json
4-
e6a1d6f51f0d9c04bd92d51deebfaca8c7917dd28d7998d225c0074e0a095348 config-baseline.plugin.json
4+
f9d1f50bfa8403891e76cd99dc1357cdece4a71e8ae18a39b190c2a14e6f97b0 config-baseline.plugin.json

extensions/microsoft-foundry/index.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,38 @@ describe("microsoft-foundry plugin", () => {
823823
expect(provider?.models[0]?.compat?.maxTokensField).toBe("max_completion_tokens");
824824
});
825825

826+
it("emits only persisted-schema thinkingLevelMap level keys for Entra ID reasoning onboarding (openclaw#91011)", () => {
827+
// The persisted ModelDefinitionSchema only accepts these ModelThinkingLevel keys; if the writer
828+
// emits one outside the set, updateConfig rolls the Entra ID onboarding write back.
829+
const allowedThinkingLevels = new Set([
830+
"off",
831+
"minimal",
832+
"low",
833+
"medium",
834+
"high",
835+
"xhigh",
836+
"max",
837+
]);
838+
839+
const result = buildFoundryAuthResult({
840+
profileId: "microsoft-foundry:entra",
841+
apiKey: "__entra_id_dynamic__",
842+
endpoint: "https://example.services.ai.azure.com",
843+
modelId: "gpt-5.1-chat",
844+
modelNameHint: "gpt-5.1-chat",
845+
api: "openai-responses",
846+
authMethod: "entra-id",
847+
});
848+
849+
const thinkingLevelMap =
850+
result.configPatch?.models?.providers?.["microsoft-foundry"]?.models[0]?.thinkingLevelMap;
851+
expect(thinkingLevelMap).toBeDefined();
852+
for (const [level, value] of Object.entries(thinkingLevelMap ?? {})) {
853+
expect(allowedThinkingLevels.has(level)).toBe(true);
854+
expect(value === null || typeof value === "string").toBe(true);
855+
}
856+
});
857+
826858
it("records model-name reasoning effort limits for Foundry deployment aliases", () => {
827859
const result = buildFoundryAuthResult({
828860
profileId: "microsoft-foundry:default",

src/config/config.schema-regressions.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,4 +488,57 @@ describe("config schema regressions", () => {
488488

489489
expect(res.ok).toBe(true);
490490
});
491+
492+
it("accepts a microsoft-foundry model entry carrying thinkingLevelMap (openclaw#91011)", () => {
493+
// Foundry's writer (buildFoundryThinkingLevelMap) persists this during Entra ID onboarding; the
494+
// strict schema used to reject thinkingLevelMap, so updateConfig rolled the whole write back.
495+
const res = validateConfigObject({
496+
models: {
497+
providers: {
498+
"microsoft-foundry": {
499+
models: [
500+
{
501+
id: "gpt-5.1-chat",
502+
name: "gpt-5.1-chat",
503+
api: "openai-responses",
504+
reasoning: true,
505+
thinkingLevelMap: {
506+
off: "none",
507+
minimal: null,
508+
low: "low",
509+
medium: "medium",
510+
high: "high",
511+
xhigh: null,
512+
max: null,
513+
},
514+
},
515+
],
516+
},
517+
},
518+
},
519+
});
520+
521+
expect(res.ok).toBe(true);
522+
});
523+
524+
it("rejects thinkingLevelMap keys outside the model thinking levels", () => {
525+
// "adaptive" is a valid agent thinkingDefault but not a ModelThinkingLevel; the map stays strict.
526+
const res = validateConfigObject({
527+
models: {
528+
providers: {
529+
"microsoft-foundry": {
530+
models: [
531+
{
532+
id: "gpt-5.1-chat",
533+
name: "gpt-5.1-chat",
534+
thinkingLevelMap: { adaptive: "high" },
535+
},
536+
],
537+
},
538+
},
539+
},
540+
});
541+
542+
expect(res.ok).toBe(false);
543+
});
491544
});

src/config/zod-schema.core.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,21 @@ const ModelMediaInputSchema = z
347347
})
348348
.strict();
349349

350+
// Mirrors the runtime ThinkingLevelMap contract (model-registry TypeBox schema). Persisted model
351+
// entries carry thinkingLevelMap, so the strict config schema must accept it or updateConfig rolls back.
352+
const ThinkingLevelMapValueSchema = z.string().nullable();
353+
const ThinkingLevelMapSchema = z
354+
.object({
355+
off: ThinkingLevelMapValueSchema.optional(),
356+
minimal: ThinkingLevelMapValueSchema.optional(),
357+
low: ThinkingLevelMapValueSchema.optional(),
358+
medium: ThinkingLevelMapValueSchema.optional(),
359+
high: ThinkingLevelMapValueSchema.optional(),
360+
xhigh: ThinkingLevelMapValueSchema.optional(),
361+
max: ThinkingLevelMapValueSchema.optional(),
362+
})
363+
.strict();
364+
350365
const ModelDefinitionSchema = z
351366
.object({
352367
id: z.string().min(1),
@@ -384,6 +399,7 @@ const ModelDefinitionSchema = z
384399
contextWindow: z.number().positive().optional(),
385400
contextTokens: z.number().int().positive().optional(),
386401
maxTokens: z.number().positive().optional(),
402+
thinkingLevelMap: ThinkingLevelMapSchema.optional(),
387403
params: z.record(z.string(), z.unknown()).optional(),
388404
agentRuntime: ModelAgentRuntimePolicySchema,
389405
headers: z.record(z.string(), z.string()).optional(),

0 commit comments

Comments
 (0)