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
6 changes: 6 additions & 0 deletions .changeset/add-zai-glm-provider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@open-codesign/shared": minor
"@open-codesign/providers": patch
---

Add Z.ai (GLM Coding Plan) as a built-in provider with support for GLM-5.1, GLM-5-Turbo, GLM-4.7, and GLM-4.5-Air models via the Anthropic-compatible API endpoint.
13 changes: 13 additions & 0 deletions packages/providers/src/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,19 @@ function endpoint(provider: SupportedOnboardingProvider, baseUrl?: string): Prov
headers: () => ({}),
};
}
case 'zai': {
const root = baseUrl ? normalizeValidateBaseUrl(baseUrl) : 'https://api.z.ai/api/anthropic';
return {
url: `${root}/v1/models`,
headers: (apiKey) => {
const auth: Record<string, string> = {
'x-api-key': apiKey,
'anthropic-version': '2023-06-01',
};
return withClaudeCodeIdentity('anthropic', baseUrl, auth);
},
};
}
}
}

Expand Down
43 changes: 43 additions & 0 deletions packages/shared/src/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -499,3 +499,46 @@ describe('provider capability helpers', () => {
expect(caps.modelDiscoveryMode).toBe('manual');
});
});

describe('zai builtin provider', () => {
it('has a well-formed zai entry in BUILTIN_PROVIDERS', () => {
const zai = BUILTIN_PROVIDERS.zai;
expect(zai).toBeDefined();
expect(zai.id).toBe('zai');
expect(zai.name).toBe('Z.ai (GLM Coding Plan)');
expect(zai.builtin).toBe(true);
expect(zai.wire).toBe('anthropic');
expect(zai.baseUrl).toBe('https://api.z.ai/api/anthropic');
expect(zai.defaultModel).toBe('glm-5.1');
expect(zai.modelsHint).toEqual(['glm-5.1', 'glm-5-turbo', 'glm-4.7', 'glm-4.5-air']);
expect(zai.requiresApiKey).toBe(true);
});

it('zai capabilities are set for static-hint discovery with reasoning', () => {
const caps = resolveProviderCapabilities('zai', BUILTIN_PROVIDERS.zai);
expect(caps.supportsKeyless).toBe(false);
expect(caps.supportsModelsEndpoint).toBe(false);
expect(caps.supportsReasoning).toBe(true);
expect(caps.modelDiscoveryMode).toBe('static-hint');
});

it('zai is included in SUPPORTED_ONBOARDING_PROVIDERS', () => {
expect(SUPPORTED_ONBOARDING_PROVIDERS).toContain('zai');
});

it('detects z.ai anthropic URL as anthropic wire', () => {
expect(detectWireFromBaseUrl('https://api.z.ai/api/anthropic')).toBe('anthropic');
});

it('accepts zai as active provider in v3 config', () => {
const parsed = ConfigV3Schema.parse({
version: 3,
activeProvider: 'zai',
activeModel: 'glm-5.1',
secrets: { zai: { ciphertext: 'plain:sk-test', mask: 'sk-***test' } },
providers: { zai: BUILTIN_PROVIDERS.zai },
});
expect(parsed.activeProvider).toBe('zai');
expect(parsed.activeModel).toBe('glm-5.1');
});
});
25 changes: 25 additions & 0 deletions packages/shared/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const SUPPORTED_ONBOARDING_PROVIDERS = [
'openai',
'openrouter',
'ollama',
'zai',
] as const;
export type SupportedOnboardingProvider = (typeof SUPPORTED_ONBOARDING_PROVIDERS)[number];

Expand Down Expand Up @@ -294,6 +295,23 @@ export const BUILTIN_PROVIDERS: Readonly<Record<SupportedOnboardingProvider, Pro
modelDiscoveryMode: 'models',
},
},
zai: {
id: 'zai',
name: 'Z.ai (GLM Coding Plan)',
builtin: true,
wire: 'anthropic',
baseUrl: 'https://api.z.ai/api/anthropic',
defaultModel: 'glm-5.1',
modelsHint: ['glm-5.1', 'glm-5-turbo', 'glm-4.7', 'glm-4.5-air'],
requiresApiKey: true,
capabilities: {
supportsKeyless: false,
supportsModelsEndpoint: false,
supportsReasoning: true,
requiresClaudeCodeIdentity: false,
modelDiscoveryMode: 'static-hint',
},
},
} as const;

// ── ConfigSchema v3 — canonical on-disk shape ────────────────────────────────
Expand Down Expand Up @@ -514,6 +532,13 @@ export const PROVIDER_SHORTLIST: Record<SupportedOnboardingProvider, ProviderSho
primary: [OLLAMA_DEFAULT_MODEL, 'llama3.1', 'qwen2.5'],
defaultPrimary: OLLAMA_DEFAULT_MODEL,
},
zai: {
provider: 'zai',
label: 'Z.ai (GLM Coding Plan)',
keyHelpUrl: 'https://z.ai/manage-apikey/apikey-list',
primary: ['glm-5.1', 'glm-5-turbo', 'glm-4.7', 'glm-4.5-air'],
defaultPrimary: 'glm-5.1',
},
};

export function isSupportedOnboardingProvider(p: string): p is SupportedOnboardingProvider {
Expand Down
7 changes: 7 additions & 0 deletions packages/shared/src/proxy-presets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ export const PROXY_PRESETS = [
baseUrl: 'http://127.0.0.1:8317',
notes: '',
},
{
id: 'zai-glm',
label: 'Z.ai GLM Coding Plan',
provider: 'anthropic',
baseUrl: 'https://api.z.ai/api/anthropic',
notes: 'GLM-5.1, GLM-5-Turbo, GLM-4.7, GLM-4.5-Air',
},
{
id: 'custom',
label: 'Custom...',
Expand Down
Loading