cognitive-modules-cli 2.2.10 → 2.2.12
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.
- package/CHANGELOG.md +9 -0
- package/README.md +47 -40
- package/dist/cli-args.d.ts +34 -0
- package/dist/cli-args.js +149 -0
- package/dist/cli.js +86 -75
- package/dist/commands/add.js +2 -2
- package/dist/commands/compose.js +1 -24
- package/dist/commands/conformance.d.ts +39 -0
- package/dist/commands/conformance.js +517 -0
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.js +1 -0
- package/dist/commands/pipe.js +1 -16
- package/dist/commands/run.js +1 -22
- package/dist/modules/composition.d.ts +3 -3
- package/dist/modules/composition.js +4 -3
- package/dist/modules/json-extract.d.ts +7 -0
- package/dist/modules/json-extract.js +132 -0
- package/dist/modules/runner.d.ts +11 -1
- package/dist/modules/runner.js +614 -47
- package/dist/policy-summary.d.ts +16 -0
- package/dist/policy-summary.js +33 -0
- package/dist/profile.d.ts +1 -0
- package/dist/profile.js +38 -17
- package/dist/providers/base.d.ts +2 -1
- package/dist/providers/base.js +6 -0
- package/dist/providers/gemini.d.ts +5 -0
- package/dist/providers/gemini.js +57 -7
- package/dist/providers/index.d.ts +10 -1
- package/dist/providers/index.js +34 -8
- package/dist/providers/moonshot.d.ts +3 -0
- package/dist/providers/moonshot.js +58 -13
- package/dist/registry/assets.d.ts +8 -0
- package/dist/registry/assets.js +48 -13
- package/dist/types.d.ts +43 -3
- package/package.json +3 -2
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ExecutionPolicy, JsonSchemaMode, StructuredOutputPreference } from './types.js';
|
|
2
|
+
export type EffectiveValidation = {
|
|
3
|
+
validateInput: boolean;
|
|
4
|
+
validateOutput: boolean;
|
|
5
|
+
reason?: string | null;
|
|
6
|
+
};
|
|
7
|
+
export type EffectiveStructured = {
|
|
8
|
+
requested: StructuredOutputPreference;
|
|
9
|
+
applied: JsonSchemaMode | 'off';
|
|
10
|
+
reason?: string | null;
|
|
11
|
+
};
|
|
12
|
+
export declare function compactReason(reason: string | null | undefined, maxLen?: number): string | null;
|
|
13
|
+
export declare function formatPolicySummaryLine(policy: ExecutionPolicy, effectiveValidation: EffectiveValidation, effectiveStructured: EffectiveStructured, extras?: {
|
|
14
|
+
enableRepair?: boolean;
|
|
15
|
+
requireV22?: boolean;
|
|
16
|
+
}): string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
function oneLine(s) {
|
|
2
|
+
return s.replace(/\s+/g, ' ').trim();
|
|
3
|
+
}
|
|
4
|
+
export function compactReason(reason, maxLen = 90) {
|
|
5
|
+
if (!reason)
|
|
6
|
+
return null;
|
|
7
|
+
const s = oneLine(reason);
|
|
8
|
+
if (s.length <= maxLen)
|
|
9
|
+
return s;
|
|
10
|
+
return `${s.slice(0, Math.max(0, maxLen - 1))}…`;
|
|
11
|
+
}
|
|
12
|
+
function fmtOnOff(v) {
|
|
13
|
+
return v ? 'on' : 'off';
|
|
14
|
+
}
|
|
15
|
+
export function formatPolicySummaryLine(policy, effectiveValidation, effectiveStructured, extras) {
|
|
16
|
+
const validateMode = policy.validate;
|
|
17
|
+
const vReason = compactReason(effectiveValidation.reason ?? null);
|
|
18
|
+
const sReason = compactReason(effectiveStructured.reason ?? null);
|
|
19
|
+
const parts = [];
|
|
20
|
+
parts.push(`profile=${policy.profile}`);
|
|
21
|
+
parts.push(`validate=${validateMode}(in:${fmtOnOff(effectiveValidation.validateInput)} out:${fmtOnOff(effectiveValidation.validateOutput)})`);
|
|
22
|
+
if (vReason)
|
|
23
|
+
parts.push(`validate_reason="${vReason}"`);
|
|
24
|
+
const structuredArrow = effectiveStructured.requested === 'auto' ? '->' : ':';
|
|
25
|
+
parts.push(`structured=${effectiveStructured.requested}${structuredArrow}${effectiveStructured.applied}`);
|
|
26
|
+
if (sReason)
|
|
27
|
+
parts.push(`structured_reason="${sReason}"`);
|
|
28
|
+
parts.push(`audit=${fmtOnOff(Boolean(policy.audit))}`);
|
|
29
|
+
if (typeof extras?.enableRepair === 'boolean')
|
|
30
|
+
parts.push(`repair=${fmtOnOff(extras.enableRepair)}`);
|
|
31
|
+
parts.push(`requireV22=${fmtOnOff(Boolean(extras?.requireV22 ?? policy.requireV22))}`);
|
|
32
|
+
return `Policy: ${parts.join(' | ')}`;
|
|
33
|
+
}
|
package/dist/profile.d.ts
CHANGED
package/dist/profile.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
function
|
|
1
|
+
function parseProfile(raw) {
|
|
2
2
|
const v = (raw ?? '').trim().toLowerCase();
|
|
3
3
|
if (v === 'core')
|
|
4
|
-
return 'core';
|
|
5
|
-
if (v === '
|
|
6
|
-
return '
|
|
7
|
-
if (v === 'strict')
|
|
8
|
-
return 'strict';
|
|
4
|
+
return { profile: 'core' };
|
|
5
|
+
if (v === 'standard' || v === '' || v === 'default')
|
|
6
|
+
return { profile: 'standard' };
|
|
9
7
|
if (v === 'certified' || v === 'cert')
|
|
10
|
-
return 'certified';
|
|
11
|
-
|
|
8
|
+
return { profile: 'certified' };
|
|
9
|
+
if (v === 'strict')
|
|
10
|
+
return { profile: 'standard', legacyPreset: 'strict' }; // deprecated alias
|
|
11
|
+
throw new Error(`Invalid --profile: ${raw}. Expected one of: core|standard|certified`);
|
|
12
12
|
}
|
|
13
13
|
function normalizeValidate(raw) {
|
|
14
14
|
const v = (raw ?? '').trim().toLowerCase();
|
|
@@ -20,24 +20,42 @@ function normalizeValidate(raw) {
|
|
|
20
20
|
return 'off';
|
|
21
21
|
throw new Error(`Invalid --validate: ${raw}. Expected one of: auto|on|off`);
|
|
22
22
|
}
|
|
23
|
+
function normalizeStructured(raw) {
|
|
24
|
+
const v = (raw ?? '').trim().toLowerCase();
|
|
25
|
+
if (v === '' || v === 'auto')
|
|
26
|
+
return 'auto';
|
|
27
|
+
if (v === 'off' || v === 'none' || v === 'false' || v === '0')
|
|
28
|
+
return 'off';
|
|
29
|
+
if (v === 'prompt')
|
|
30
|
+
return 'prompt';
|
|
31
|
+
if (v === 'native')
|
|
32
|
+
return 'native';
|
|
33
|
+
throw new Error(`Invalid --structured: ${raw}. Expected one of: auto|off|prompt|native`);
|
|
34
|
+
}
|
|
23
35
|
export function resolveExecutionPolicy(input) {
|
|
24
|
-
const profile =
|
|
36
|
+
const { profile, legacyPreset } = parseProfile(input.profile);
|
|
25
37
|
// Base defaults per profile.
|
|
26
|
-
|
|
38
|
+
// standard: auto validation chooses based on module tier/strictness in the runner
|
|
39
|
+
let validate = profile === 'core' ? 'off' : 'auto';
|
|
27
40
|
let audit = false;
|
|
28
41
|
let enableRepair = true;
|
|
29
42
|
let requireV22 = false;
|
|
30
|
-
|
|
31
|
-
validate = 'on';
|
|
32
|
-
audit = false;
|
|
33
|
-
enableRepair = true;
|
|
34
|
-
requireV22 = false;
|
|
35
|
-
}
|
|
43
|
+
let structured = 'auto';
|
|
36
44
|
if (profile === 'certified') {
|
|
37
45
|
validate = 'on';
|
|
38
46
|
audit = true;
|
|
39
47
|
enableRepair = false; // certification prefers fail-fast over runtime repair
|
|
40
48
|
requireV22 = true;
|
|
49
|
+
structured = 'auto';
|
|
50
|
+
}
|
|
51
|
+
// Legacy preset: strict = validate on, no audit, keep repair on, do not require v2.2.
|
|
52
|
+
// This keeps backward compatibility while presenting only core/standard/certified externally.
|
|
53
|
+
if (legacyPreset === 'strict') {
|
|
54
|
+
validate = 'on';
|
|
55
|
+
audit = false;
|
|
56
|
+
enableRepair = true;
|
|
57
|
+
requireV22 = false;
|
|
58
|
+
structured = 'auto';
|
|
41
59
|
}
|
|
42
60
|
// CLI overrides.
|
|
43
61
|
const validateExplicit = input.validate != null || Boolean(input.noValidate);
|
|
@@ -50,10 +68,13 @@ export function resolveExecutionPolicy(input) {
|
|
|
50
68
|
if (typeof input.audit === 'boolean') {
|
|
51
69
|
audit = input.audit;
|
|
52
70
|
}
|
|
71
|
+
if (input.structured != null) {
|
|
72
|
+
structured = normalizeStructured(input.structured);
|
|
73
|
+
}
|
|
53
74
|
// Trigger rule: if audit is enabled and validate wasn't explicitly turned off,
|
|
54
75
|
// force validation on (auditing without validation is usually not meaningful).
|
|
55
76
|
if (audit && !(validateExplicit && validate === 'off')) {
|
|
56
77
|
validate = 'on';
|
|
57
78
|
}
|
|
58
|
-
return { profile, validate, audit, enableRepair, requireV22 };
|
|
79
|
+
return { profile, validate, audit, enableRepair, structured, requireV22 };
|
|
59
80
|
}
|
package/dist/providers/base.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Base Provider - Abstract class for all LLM providers
|
|
3
3
|
*/
|
|
4
|
-
import type { Provider, InvokeParams, InvokeResult } from '../types.js';
|
|
4
|
+
import type { Provider, InvokeParams, InvokeResult, ProviderCapabilities } from '../types.js';
|
|
5
5
|
export declare abstract class BaseProvider implements Provider {
|
|
6
6
|
abstract name: string;
|
|
7
7
|
abstract invoke(params: InvokeParams): Promise<InvokeResult>;
|
|
8
8
|
abstract isConfigured(): boolean;
|
|
9
|
+
getCapabilities(): ProviderCapabilities;
|
|
9
10
|
/**
|
|
10
11
|
* Check if this provider supports streaming.
|
|
11
12
|
* Override in subclasses that implement streaming.
|
package/dist/providers/base.js
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
* Base Provider - Abstract class for all LLM providers
|
|
3
3
|
*/
|
|
4
4
|
export class BaseProvider {
|
|
5
|
+
getCapabilities() {
|
|
6
|
+
return {
|
|
7
|
+
structuredOutput: 'prompt',
|
|
8
|
+
streaming: this.supportsStreaming(),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
5
11
|
/**
|
|
6
12
|
* Check if this provider supports streaming.
|
|
7
13
|
* Override in subclasses that implement streaming.
|
|
@@ -16,6 +16,11 @@ export declare class GeminiProvider extends BaseProvider {
|
|
|
16
16
|
* Gemini supports streaming.
|
|
17
17
|
*/
|
|
18
18
|
supportsStreaming(): boolean;
|
|
19
|
+
getCapabilities(): {
|
|
20
|
+
structuredOutput: "native";
|
|
21
|
+
streaming: boolean;
|
|
22
|
+
nativeSchemaDialect: "gemini-responseSchema";
|
|
23
|
+
};
|
|
19
24
|
/**
|
|
20
25
|
* Clean JSON Schema for Gemini API compatibility
|
|
21
26
|
* Removes unsupported fields like additionalProperties
|
package/dist/providers/gemini.js
CHANGED
|
@@ -23,6 +23,16 @@ export class GeminiProvider extends BaseProvider {
|
|
|
23
23
|
supportsStreaming() {
|
|
24
24
|
return true;
|
|
25
25
|
}
|
|
26
|
+
getCapabilities() {
|
|
27
|
+
// Gemini has a native response schema surface (generationConfig.responseSchema),
|
|
28
|
+
// but it is NOT JSON Schema. The runner will treat this as "native but non-JSON-schema"
|
|
29
|
+
// and will safely downgrade to prompt-guided JSON unless/ until a real dialect mapping exists.
|
|
30
|
+
return {
|
|
31
|
+
structuredOutput: 'native',
|
|
32
|
+
streaming: true,
|
|
33
|
+
nativeSchemaDialect: 'gemini-responseSchema',
|
|
34
|
+
};
|
|
35
|
+
}
|
|
26
36
|
/**
|
|
27
37
|
* Clean JSON Schema for Gemini API compatibility
|
|
28
38
|
* Removes unsupported fields like additionalProperties
|
|
@@ -36,10 +46,36 @@ export class GeminiProvider extends BaseProvider {
|
|
|
36
46
|
if (obj && typeof obj === 'object') {
|
|
37
47
|
const result = {};
|
|
38
48
|
for (const [key, value] of Object.entries(obj)) {
|
|
39
|
-
// Gemini's responseSchema
|
|
40
|
-
//
|
|
49
|
+
// Gemini's responseSchema has a restricted schema subset.
|
|
50
|
+
// In particular:
|
|
51
|
+
// - `const` is not supported
|
|
52
|
+
// - `enum` values appear to be string-only in the API surface
|
|
41
53
|
if (key === 'const') {
|
|
42
|
-
|
|
54
|
+
// Preserve type info as best-effort but drop the exact-value constraint.
|
|
55
|
+
// (Core runtime will still validate/repair the envelope.)
|
|
56
|
+
if (value === null) {
|
|
57
|
+
// Leave unconstrained; null typing is not consistently supported.
|
|
58
|
+
}
|
|
59
|
+
else if (typeof value === 'boolean') {
|
|
60
|
+
result.type = 'boolean';
|
|
61
|
+
}
|
|
62
|
+
else if (typeof value === 'number') {
|
|
63
|
+
result.type = 'number';
|
|
64
|
+
}
|
|
65
|
+
else if (typeof value === 'string') {
|
|
66
|
+
result.type = 'string';
|
|
67
|
+
// String enums are supported, so we can keep a single-value constraint.
|
|
68
|
+
result.enum = [value];
|
|
69
|
+
}
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (key === 'enum') {
|
|
73
|
+
if (Array.isArray(value) && value.every((v) => typeof v === 'string')) {
|
|
74
|
+
result.enum = value.map((v) => v);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// Drop non-string enums for Gemini compatibility.
|
|
78
|
+
}
|
|
43
79
|
continue;
|
|
44
80
|
}
|
|
45
81
|
if (!unsupportedFields.includes(key)) {
|
|
@@ -56,15 +92,29 @@ export class GeminiProvider extends BaseProvider {
|
|
|
56
92
|
* Build request body for Gemini API
|
|
57
93
|
*/
|
|
58
94
|
buildRequestBody(params) {
|
|
95
|
+
// If the caller wants schema guidance but not native enforcement, inject the schema into the prompt.
|
|
96
|
+
// (Gemini's native responseSchema supports only a restricted schema subset and can reject valid JSON Schema.)
|
|
97
|
+
const mode = params.jsonSchemaMode ?? (params.jsonSchema ? 'prompt' : undefined);
|
|
98
|
+
let messages = params.messages;
|
|
99
|
+
if (params.jsonSchema && mode === 'prompt') {
|
|
100
|
+
const lastUserIdx = messages.findLastIndex(m => m.role === 'user');
|
|
101
|
+
if (lastUserIdx >= 0) {
|
|
102
|
+
messages = [...messages];
|
|
103
|
+
messages[lastUserIdx] = {
|
|
104
|
+
...messages[lastUserIdx],
|
|
105
|
+
content: messages[lastUserIdx].content + this.buildJsonPrompt(params.jsonSchema),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
59
109
|
// Convert messages to Gemini format
|
|
60
|
-
const contents =
|
|
110
|
+
const contents = messages
|
|
61
111
|
.filter(m => m.role !== 'system')
|
|
62
112
|
.map(m => ({
|
|
63
113
|
role: m.role === 'assistant' ? 'model' : 'user',
|
|
64
114
|
parts: [{ text: m.content }]
|
|
65
115
|
}));
|
|
66
116
|
// Add system instruction if present
|
|
67
|
-
const systemMessage =
|
|
117
|
+
const systemMessage = messages.find(m => m.role === 'system');
|
|
68
118
|
const body = {
|
|
69
119
|
contents,
|
|
70
120
|
generationConfig: {
|
|
@@ -75,8 +125,8 @@ export class GeminiProvider extends BaseProvider {
|
|
|
75
125
|
if (systemMessage) {
|
|
76
126
|
body.systemInstruction = { parts: [{ text: systemMessage.content }] };
|
|
77
127
|
}
|
|
78
|
-
// Add JSON schema constraint if provided
|
|
79
|
-
if (params.jsonSchema) {
|
|
128
|
+
// Add JSON schema constraint if provided and caller requested native mode.
|
|
129
|
+
if (params.jsonSchema && mode === 'native') {
|
|
80
130
|
const cleanedSchema = this.cleanSchemaForGemini(params.jsonSchema);
|
|
81
131
|
body.generationConfig = {
|
|
82
132
|
...body.generationConfig,
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Provider Registry
|
|
3
|
+
*
|
|
4
|
+
* Publish-grade note:
|
|
5
|
+
* Providers expose a small capability surface (structured output, streaming).
|
|
6
|
+
* The runner uses this to decide whether to pass native schemas or prompt-only guidance.
|
|
3
7
|
*/
|
|
4
|
-
import type { Provider } from '../types.js';
|
|
8
|
+
import type { Provider, StructuredOutputMode } from '../types.js';
|
|
5
9
|
export { BaseProvider } from './base.js';
|
|
6
10
|
export { GeminiProvider } from './gemini.js';
|
|
7
11
|
export { OpenAIProvider } from './openai.js';
|
|
@@ -16,4 +20,9 @@ export declare function listProviders(): Array<{
|
|
|
16
20
|
name: string;
|
|
17
21
|
configured: boolean;
|
|
18
22
|
model: string;
|
|
23
|
+
structuredOutput: StructuredOutputMode;
|
|
24
|
+
structuredJsonSchema: Exclude<StructuredOutputMode, 'native'> | 'native';
|
|
25
|
+
nativeSchemaDialect?: string;
|
|
26
|
+
maxNativeSchemaBytes?: number;
|
|
27
|
+
streaming: boolean;
|
|
19
28
|
}>;
|
package/dist/providers/index.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Provider Registry
|
|
3
|
+
*
|
|
4
|
+
* Publish-grade note:
|
|
5
|
+
* Providers expose a small capability surface (structured output, streaming).
|
|
6
|
+
* The runner uses this to decide whether to pass native schemas or prompt-only guidance.
|
|
3
7
|
*/
|
|
4
8
|
import { GeminiProvider } from './gemini.js';
|
|
5
9
|
import { OpenAIProvider } from './openai.js';
|
|
@@ -60,15 +64,37 @@ export function getProvider(name, model) {
|
|
|
60
64
|
}
|
|
61
65
|
return factory(modelOverride);
|
|
62
66
|
}
|
|
67
|
+
function safeCapabilities(p) {
|
|
68
|
+
const caps = p.getCapabilities?.();
|
|
69
|
+
if (caps)
|
|
70
|
+
return caps;
|
|
71
|
+
return { structuredOutput: 'prompt', streaming: p.supportsStreaming?.() ?? false };
|
|
72
|
+
}
|
|
73
|
+
function providerRow(name, configured, model, make) {
|
|
74
|
+
const caps = safeCapabilities(make());
|
|
75
|
+
const structuredJsonSchema = caps.structuredOutput === 'native' && (caps.nativeSchemaDialect ?? 'json-schema') !== 'json-schema'
|
|
76
|
+
? 'prompt'
|
|
77
|
+
: caps.structuredOutput;
|
|
78
|
+
return {
|
|
79
|
+
name,
|
|
80
|
+
configured,
|
|
81
|
+
model,
|
|
82
|
+
structuredOutput: caps.structuredOutput,
|
|
83
|
+
structuredJsonSchema,
|
|
84
|
+
nativeSchemaDialect: caps.nativeSchemaDialect,
|
|
85
|
+
maxNativeSchemaBytes: caps.maxNativeSchemaBytes,
|
|
86
|
+
streaming: caps.streaming,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
63
89
|
export function listProviders() {
|
|
64
90
|
return [
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
91
|
+
providerRow('gemini', !!process.env.GEMINI_API_KEY, 'gemini-3-flash', () => new GeminiProvider('')),
|
|
92
|
+
providerRow('openai', !!process.env.OPENAI_API_KEY, 'gpt-5.2', () => new OpenAIProvider('')),
|
|
93
|
+
providerRow('anthropic', !!process.env.ANTHROPIC_API_KEY, 'claude-sonnet-4.5', () => new AnthropicProvider('')),
|
|
94
|
+
providerRow('deepseek', !!process.env.DEEPSEEK_API_KEY, 'deepseek-v3.2', () => new DeepSeekProvider('')),
|
|
95
|
+
providerRow('minimax', !!process.env.MINIMAX_API_KEY, 'MiniMax-M2.1', () => new MiniMaxProvider('')),
|
|
96
|
+
providerRow('moonshot', !!process.env.MOONSHOT_API_KEY, 'kimi-k2.5', () => new MoonshotProvider('')),
|
|
97
|
+
providerRow('qwen', !!(process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY), 'qwen3-max', () => new QwenProvider('')),
|
|
98
|
+
providerRow('ollama', true, 'llama4 (local)', () => new OllamaProvider()),
|
|
73
99
|
];
|
|
74
100
|
}
|
|
@@ -10,5 +10,8 @@ export declare class MoonshotProvider extends BaseProvider {
|
|
|
10
10
|
private baseUrl;
|
|
11
11
|
constructor(apiKey?: string, model?: string);
|
|
12
12
|
isConfigured(): boolean;
|
|
13
|
+
private buildRequestBody;
|
|
14
|
+
private parseRequiredTemperature;
|
|
15
|
+
private fetchOnce;
|
|
13
16
|
invoke(params: InvokeParams): Promise<InvokeResult>;
|
|
14
17
|
}
|
|
@@ -15,31 +15,56 @@ export class MoonshotProvider extends BaseProvider {
|
|
|
15
15
|
isConfigured() {
|
|
16
16
|
return !!this.apiKey;
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
buildRequestBody(params, overrides) {
|
|
19
|
+
// Moonshot (Kimi) model-specific constraints:
|
|
20
|
+
// - Some Kimi models reject arbitrary temperatures (e.g. `kimi-k2.5` only allows 1).
|
|
21
|
+
const model = this.model;
|
|
22
|
+
let temperature = overrides?.temperature ?? params.temperature ?? 0.7;
|
|
23
|
+
if (model === 'kimi-k2.5')
|
|
24
|
+
temperature = 1;
|
|
23
25
|
const body = {
|
|
24
|
-
model
|
|
25
|
-
messages: params.messages.map(m => ({ role: m.role, content: m.content })),
|
|
26
|
-
temperature
|
|
26
|
+
model,
|
|
27
|
+
messages: params.messages.map((m) => ({ role: m.role, content: m.content })),
|
|
28
|
+
temperature,
|
|
27
29
|
max_tokens: params.maxTokens ?? 4096,
|
|
28
30
|
};
|
|
29
31
|
// Add JSON mode if schema provided
|
|
30
32
|
if (params.jsonSchema) {
|
|
31
33
|
body.response_format = { type: 'json_object' };
|
|
32
|
-
const lastUserIdx = params.messages.findLastIndex(m => m.role === 'user');
|
|
34
|
+
const lastUserIdx = params.messages.findLastIndex((m) => m.role === 'user');
|
|
33
35
|
if (lastUserIdx >= 0) {
|
|
34
36
|
const messages = [...params.messages];
|
|
35
37
|
messages[lastUserIdx] = {
|
|
36
38
|
...messages[lastUserIdx],
|
|
37
39
|
content: messages[lastUserIdx].content + this.buildJsonPrompt(params.jsonSchema),
|
|
38
40
|
};
|
|
39
|
-
body.messages = messages.map(m => ({ role: m.role, content: m.content }));
|
|
41
|
+
body.messages = messages.map((m) => ({ role: m.role, content: m.content }));
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
|
-
|
|
44
|
+
return body;
|
|
45
|
+
}
|
|
46
|
+
parseRequiredTemperature(errorText) {
|
|
47
|
+
const s = String(errorText ?? '');
|
|
48
|
+
// Example: {"error":{"message":"invalid temperature: only 1 is allowed for this model","type":"invalid_request_error"}}
|
|
49
|
+
// Also tolerate plain text.
|
|
50
|
+
const m = s.match(/invalid temperature[^0-9]*only\s+([0-9]+(?:\.[0-9]+)?)\s+is allowed/i);
|
|
51
|
+
if (m?.[1]) {
|
|
52
|
+
const n = Number(m[1]);
|
|
53
|
+
if (Number.isFinite(n))
|
|
54
|
+
return n;
|
|
55
|
+
}
|
|
56
|
+
// Some variants omit "only" but still contain a single allowed value.
|
|
57
|
+
const m2 = s.match(/invalid temperature[^0-9]*([0-9]+(?:\.[0-9]+)?)\s+is allowed/i);
|
|
58
|
+
if (m2?.[1]) {
|
|
59
|
+
const n = Number(m2[1]);
|
|
60
|
+
if (Number.isFinite(n))
|
|
61
|
+
return n;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
async fetchOnce(body) {
|
|
66
|
+
const url = `${this.baseUrl}/chat/completions`;
|
|
67
|
+
return await fetch(url, {
|
|
43
68
|
method: 'POST',
|
|
44
69
|
headers: {
|
|
45
70
|
'Content-Type': 'application/json',
|
|
@@ -47,9 +72,29 @@ export class MoonshotProvider extends BaseProvider {
|
|
|
47
72
|
},
|
|
48
73
|
body: JSON.stringify(body),
|
|
49
74
|
});
|
|
75
|
+
}
|
|
76
|
+
async invoke(params) {
|
|
77
|
+
if (!this.isConfigured()) {
|
|
78
|
+
throw new Error('Moonshot API key not configured. Set MOONSHOT_API_KEY environment variable.');
|
|
79
|
+
}
|
|
80
|
+
// Moonshot is strict about some generation params for certain models.
|
|
81
|
+
// Keep UX stable: if we hit a known parameter-compat error, retry once with a safer value.
|
|
82
|
+
const initialBody = this.buildRequestBody(params);
|
|
83
|
+
let response = await this.fetchOnce(initialBody);
|
|
50
84
|
if (!response.ok) {
|
|
51
|
-
const
|
|
52
|
-
|
|
85
|
+
const errorText = await response.text();
|
|
86
|
+
const requiredTemp = this.parseRequiredTemperature(errorText);
|
|
87
|
+
if (response.status === 400 && requiredTemp !== null) {
|
|
88
|
+
const retryBody = this.buildRequestBody(params, { temperature: requiredTemp });
|
|
89
|
+
response = await this.fetchOnce(retryBody);
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
const retryError = await response.text();
|
|
92
|
+
throw new Error(`Moonshot API error: ${response.status} - ${retryError}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
throw new Error(`Moonshot API error: ${response.status} - ${errorText}`);
|
|
97
|
+
}
|
|
53
98
|
}
|
|
54
99
|
const data = await response.json();
|
|
55
100
|
const content = data.choices?.[0]?.message?.content || '';
|
|
@@ -39,9 +39,17 @@ export interface RegistryVerifyResult {
|
|
|
39
39
|
checked: number;
|
|
40
40
|
passed: number;
|
|
41
41
|
failed: number;
|
|
42
|
+
/**
|
|
43
|
+
* Failure diagnostics (best-effort).
|
|
44
|
+
*
|
|
45
|
+
* This structure is intentionally extensible. Consumers should treat unknown keys as optional.
|
|
46
|
+
*/
|
|
42
47
|
failures: Array<{
|
|
43
48
|
module: string;
|
|
44
49
|
reason: string;
|
|
50
|
+
phase?: 'entry' | 'download' | 'size' | 'checksum' | 'extract' | 'files' | 'identity';
|
|
51
|
+
tarball_ref?: string;
|
|
52
|
+
tarball_resolved?: string;
|
|
45
53
|
}>;
|
|
46
54
|
}
|
|
47
55
|
export declare function buildRegistryAssets(opts: BuildRegistryOptions): Promise<RegistryBuildResult>;
|
package/dist/registry/assets.js
CHANGED
|
@@ -24,6 +24,21 @@ function tarballFileName(tarballRef) {
|
|
|
24
24
|
}
|
|
25
25
|
return path.basename(tarballRef);
|
|
26
26
|
}
|
|
27
|
+
function resolveRemoteUrl(indexUrl, ref) {
|
|
28
|
+
if (isHttpUrl(ref))
|
|
29
|
+
return ref;
|
|
30
|
+
// Allow relative tarball refs in remote indexes for portability.
|
|
31
|
+
// Example:
|
|
32
|
+
// - index: https://host/registry.json
|
|
33
|
+
// - tarball: demo-1.0.0.tar.gz
|
|
34
|
+
// resolves to https://host/demo-1.0.0.tar.gz
|
|
35
|
+
try {
|
|
36
|
+
return new URL(ref, indexUrl).toString();
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return ref;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
27
42
|
async function fetchTextWithLimit(url, maxBytes, timeoutMs) {
|
|
28
43
|
const controller = new AbortController();
|
|
29
44
|
const t = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -578,8 +593,9 @@ export async function verifyRegistryAssets(opts) {
|
|
|
578
593
|
const concurrency = Math.max(1, Math.min(8, Math.floor(desiredConcurrency)));
|
|
579
594
|
const localTarPath = async (moduleName, tarballRef) => {
|
|
580
595
|
const fileName = tarballFileName(tarballRef);
|
|
581
|
-
// Avoid collisions
|
|
582
|
-
|
|
596
|
+
// Avoid collisions on remote verify (query strings can produce identical basenames).
|
|
597
|
+
// Even when the caller provides --assets-dir, we isolate per module to keep verification correct.
|
|
598
|
+
if (wantRemote) {
|
|
583
599
|
const p = path.join(assetsDir, moduleName, fileName);
|
|
584
600
|
await fs.mkdir(path.dirname(p), { recursive: true });
|
|
585
601
|
return p;
|
|
@@ -589,24 +605,30 @@ export async function verifyRegistryAssets(opts) {
|
|
|
589
605
|
const verifyOne = async (moduleName, entry) => {
|
|
590
606
|
checked += 1;
|
|
591
607
|
let tarPathForCleanup = null;
|
|
608
|
+
let phase = 'entry';
|
|
592
609
|
try {
|
|
593
610
|
const dist = entry.distribution ?? {};
|
|
594
|
-
const
|
|
611
|
+
const tarballRef = String(dist.tarball ?? '');
|
|
595
612
|
const checksum = String(dist.checksum ?? '');
|
|
596
|
-
const
|
|
613
|
+
const sizeBytesRaw = dist.size_bytes;
|
|
614
|
+
const expectedSizeBytes = Number.isFinite(Number(sizeBytesRaw)) ? Number(sizeBytesRaw) : null;
|
|
597
615
|
const expectedFiles = Array.isArray(dist.files) ? dist.files.map(String) : [];
|
|
598
|
-
if (!
|
|
616
|
+
if (!tarballRef)
|
|
599
617
|
throw new Error('Missing distribution.tarball');
|
|
618
|
+
const tarballUrl = wantRemote ? resolveRemoteUrl(opts.registryIndexPath, tarballRef) : tarballRef;
|
|
600
619
|
const tarPath = await localTarPath(moduleName, tarballUrl);
|
|
601
620
|
tarPathForCleanup = tarPath;
|
|
602
621
|
if (wantRemote) {
|
|
603
622
|
if (!isHttpUrl(tarballUrl)) {
|
|
604
|
-
throw new Error(`Remote verify requires http(s) tarball URL, got: ${
|
|
623
|
+
throw new Error(`Remote verify requires http(s) tarball URL (or a relative URL), got: ${tarballRef}`);
|
|
605
624
|
}
|
|
625
|
+
phase = 'download';
|
|
606
626
|
const downloaded = await downloadToFileWithSha256(tarballUrl, tarPath, maxTarballBytes, fetchTimeoutMs);
|
|
607
|
-
|
|
608
|
-
|
|
627
|
+
phase = 'size';
|
|
628
|
+
if (expectedSizeBytes !== null && downloaded.sizeBytes !== expectedSizeBytes) {
|
|
629
|
+
throw new Error(`Size mismatch: expected ${expectedSizeBytes}, got ${downloaded.sizeBytes}`);
|
|
609
630
|
}
|
|
631
|
+
phase = 'checksum';
|
|
610
632
|
const m = checksum.match(/^sha256:([a-f0-9]{64})$/);
|
|
611
633
|
if (!m)
|
|
612
634
|
throw new Error(`Unsupported checksum format: ${checksum}`);
|
|
@@ -615,11 +637,12 @@ export async function verifyRegistryAssets(opts) {
|
|
|
615
637
|
throw new Error(`Checksum mismatch: expected ${expectedSha}, got ${downloaded.sha256}`);
|
|
616
638
|
}
|
|
617
639
|
}
|
|
640
|
+
phase = 'size';
|
|
618
641
|
const st = await fs.stat(tarPath);
|
|
619
|
-
if (
|
|
620
|
-
throw new Error(
|
|
621
|
-
|
|
622
|
-
|
|
642
|
+
if (expectedSizeBytes !== null && st.size !== expectedSizeBytes) {
|
|
643
|
+
throw new Error(`Size mismatch: expected ${expectedSizeBytes}, got ${st.size}`);
|
|
644
|
+
}
|
|
645
|
+
phase = 'checksum';
|
|
623
646
|
const m = checksum.match(/^sha256:([a-f0-9]{64})$/);
|
|
624
647
|
if (!m)
|
|
625
648
|
throw new Error(`Unsupported checksum format: ${checksum}`);
|
|
@@ -630,6 +653,7 @@ export async function verifyRegistryAssets(opts) {
|
|
|
630
653
|
// Extract and validate contents (layout + file list).
|
|
631
654
|
const tmp = await fs.mkdtemp(path.join(tmpdir(), 'cog-reg-verify-'));
|
|
632
655
|
try {
|
|
656
|
+
phase = 'extract';
|
|
633
657
|
const extractedRoot = path.join(tmp, 'pkg');
|
|
634
658
|
await fs.mkdir(extractedRoot, { recursive: true });
|
|
635
659
|
await extractTarGzFile(tarPath, extractedRoot, {
|
|
@@ -651,6 +675,7 @@ export async function verifyRegistryAssets(opts) {
|
|
|
651
675
|
throw new Error('Root directory is not a valid module');
|
|
652
676
|
}
|
|
653
677
|
if (expectedFiles.length > 0) {
|
|
678
|
+
phase = 'files';
|
|
654
679
|
const actualFiles = await listFiles(moduleDir);
|
|
655
680
|
const exp = expectedFiles.slice().sort();
|
|
656
681
|
const act = actualFiles.slice().sort();
|
|
@@ -664,6 +689,7 @@ export async function verifyRegistryAssets(opts) {
|
|
|
664
689
|
}
|
|
665
690
|
// Basic identity check: module.yaml version matches registry identity.version
|
|
666
691
|
try {
|
|
692
|
+
phase = 'identity';
|
|
667
693
|
const y = await readModuleMeta(path.join(moduleDir, 'module.yaml'));
|
|
668
694
|
const identityVersion = String(entry.identity?.version ?? '').trim();
|
|
669
695
|
if (identityVersion && y.version !== identityVersion) {
|
|
@@ -684,7 +710,16 @@ export async function verifyRegistryAssets(opts) {
|
|
|
684
710
|
passed += 1;
|
|
685
711
|
}
|
|
686
712
|
catch (e) {
|
|
687
|
-
|
|
713
|
+
const dist = entry?.distribution ?? {};
|
|
714
|
+
const tarball_ref = typeof dist.tarball === 'string' ? dist.tarball : undefined;
|
|
715
|
+
const tarball_resolved = tarball_ref && wantRemote ? resolveRemoteUrl(opts.registryIndexPath, tarball_ref) : tarball_ref;
|
|
716
|
+
failures.push({
|
|
717
|
+
module: moduleName,
|
|
718
|
+
reason: e instanceof Error ? e.message : String(e),
|
|
719
|
+
phase: (typeof phase === 'string' ? phase : undefined),
|
|
720
|
+
tarball_ref,
|
|
721
|
+
tarball_resolved,
|
|
722
|
+
});
|
|
688
723
|
}
|
|
689
724
|
finally {
|
|
690
725
|
// If we downloaded tarballs into a temp dir, keep disk usage bounded.
|