@pedrofariasx/qwenproxy 1.2.1 → 1.2.3
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/README.md +3 -13
- package/package.json +1 -1
- package/src/api/server.ts +4 -6
- package/src/cache/memory-cache.ts +5 -3
- package/src/core/account-manager.ts +1 -1
- package/src/core/accounts.ts +1 -1
- package/src/login.ts +2 -2
- package/src/routes/chat.ts +122 -91
- package/src/routes/upload.ts +5 -5
- package/src/services/playwright.ts +40 -120
- package/src/services/qwen.ts +29 -27
- package/src/tests/concurrency.test.ts +1 -1
- package/src/tests/concurrentChat.test.ts +1 -1
- package/src/tests/contextTruncation.test.ts +142 -0
- package/src/tests/delta.test.ts +80 -10
- package/src/tests/jsonFix.test.ts +110 -98
- package/src/tests/multimodal.test.ts +1 -1
- package/src/tests/parser.test.ts +40 -2
- package/src/tools/parser.ts +98 -33
- package/src/utils/context-truncation.ts +1 -6
- package/src/utils/json.ts +9 -8
- package/src/utils/types.ts +1 -1
- package/src/linter/extraction-engine.ts +0 -165
- package/src/linter/index.ts +0 -258
- package/src/linter/repair-normalize.ts +0 -245
- package/src/linter/safety-gate.ts +0 -219
- package/src/linter/streaming-state-machine.ts +0 -252
- package/src/linter/structural-parser.ts +0 -352
- package/src/linter/types.ts +0 -74
- package/src/tests/linter.test.ts +0 -151
- package/src/tests/parallel.test.ts +0 -42
- package/src/tests/structureVerification.test.ts +0 -176
- package/src/tools/ast.ts +0 -15
- package/src/tools/coercion.ts +0 -67
- package/src/tools/confidence.ts +0 -48
- package/src/tools/detector.ts +0 -40
- package/src/tools/executor.ts +0 -236
- package/src/tools/pipeline.ts +0 -122
- package/src/tools/registry-runtime.ts +0 -34
- package/src/tools/repair.ts +0 -42
- package/src/tools/validator.ts +0 -33
package/src/tools/executor.ts
DELETED
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* File: executor.ts
|
|
3
|
-
* Project: qwenproxy
|
|
4
|
-
* Execution loop for tool calling - agentic loop that handles
|
|
5
|
-
* send -> tool calls -> execute -> re-send until completion
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
9
|
-
import type { ParsedToolCall, ToolCallResult, ToolContext } from './types';
|
|
10
|
-
import { SchemaValidationError } from './schema.js';
|
|
11
|
-
import { registry } from './registry';
|
|
12
|
-
import { robustParseJSON } from '../utils/json.js';
|
|
13
|
-
|
|
14
|
-
export interface ExecutionLoopConfig {
|
|
15
|
-
maxTurns?: number;
|
|
16
|
-
debug?: boolean;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface LoopTurnResult {
|
|
20
|
-
toolCalls: ParsedToolCall[];
|
|
21
|
-
toolResults: ToolCallResult[];
|
|
22
|
-
content: string | null;
|
|
23
|
-
finishReason: string | null;
|
|
24
|
-
turn: number;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export type LLMSendFunction = (
|
|
28
|
-
messages: unknown[],
|
|
29
|
-
tools: unknown[] | undefined,
|
|
30
|
-
model: string
|
|
31
|
-
) => Promise<LLMResponse>;
|
|
32
|
-
|
|
33
|
-
export interface LLMResponse {
|
|
34
|
-
content: string | null;
|
|
35
|
-
toolCalls: ParsedToolCall[];
|
|
36
|
-
finishReason: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const TOOL_START_TAG = '<' + 'tool_call>';
|
|
40
|
-
const TOOL_END_TAG = '</' + 'tool_call>';
|
|
41
|
-
|
|
42
|
-
export function parseToolCallsFromContent(content: string): {
|
|
43
|
-
textContent: string;
|
|
44
|
-
toolCalls: ParsedToolCall[];
|
|
45
|
-
} {
|
|
46
|
-
const toolCalls: ParsedToolCall[] = [];
|
|
47
|
-
let remaining = content;
|
|
48
|
-
let textContent = '';
|
|
49
|
-
|
|
50
|
-
while (true) {
|
|
51
|
-
const startIdx = remaining.indexOf(TOOL_START_TAG);
|
|
52
|
-
if (startIdx === -1) {
|
|
53
|
-
textContent += remaining;
|
|
54
|
-
break;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
textContent += remaining.substring(0, startIdx);
|
|
58
|
-
|
|
59
|
-
const endIdx = remaining.indexOf(TOOL_END_TAG, startIdx + TOOL_START_TAG.length);
|
|
60
|
-
if (endIdx === -1) {
|
|
61
|
-
textContent += remaining.substring(startIdx);
|
|
62
|
-
break;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const jsonStr = remaining
|
|
66
|
-
.substring(startIdx + TOOL_START_TAG.length, endIdx)
|
|
67
|
-
.trim();
|
|
68
|
-
|
|
69
|
-
try {
|
|
70
|
-
const parsed = robustParseJSON(jsonStr);
|
|
71
|
-
if (!parsed) throw new Error('Failed to parse JSON');
|
|
72
|
-
|
|
73
|
-
toolCalls.push({
|
|
74
|
-
id: 'call_' + uuidv4(),
|
|
75
|
-
name: parsed.name || '',
|
|
76
|
-
arguments: parsed.arguments
|
|
77
|
-
? (typeof parsed.arguments === 'string' ? JSON.parse(parsed.arguments) : parsed.arguments)
|
|
78
|
-
: (() => {
|
|
79
|
-
const { name, ...rest } = parsed;
|
|
80
|
-
return rest;
|
|
81
|
-
})(),
|
|
82
|
-
});
|
|
83
|
-
} catch (e) {
|
|
84
|
-
textContent += TOOL_START_TAG + jsonStr + TOOL_END_TAG;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
remaining = remaining.substring(endIdx + TOOL_END_TAG.length);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return { textContent: textContent.trim(), toolCalls };
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export async function executeToolCalls(
|
|
94
|
-
toolCalls: ParsedToolCall[],
|
|
95
|
-
context: ToolContext
|
|
96
|
-
): Promise<ToolCallResult[]> {
|
|
97
|
-
return await Promise.all(
|
|
98
|
-
toolCalls.map(async (tc) => {
|
|
99
|
-
try {
|
|
100
|
-
if (!registry.has(tc.name)) {
|
|
101
|
-
return {
|
|
102
|
-
toolCallId: tc.id,
|
|
103
|
-
name: tc.name,
|
|
104
|
-
result: JSON.stringify({ error: `Unknown tool: '${tc.name}'` }),
|
|
105
|
-
isError: true,
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const result = await registry.execute(tc.name, tc.arguments, context);
|
|
110
|
-
return {
|
|
111
|
-
toolCallId: tc.id,
|
|
112
|
-
name: tc.name,
|
|
113
|
-
result,
|
|
114
|
-
isError: false,
|
|
115
|
-
};
|
|
116
|
-
} catch (err: unknown) {
|
|
117
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
118
|
-
const isValidation = err instanceof SchemaValidationError;
|
|
119
|
-
return {
|
|
120
|
-
toolCallId: tc.id,
|
|
121
|
-
name: tc.name,
|
|
122
|
-
result: JSON.stringify({
|
|
123
|
-
error: isValidation ? 'Schema validation failed' : 'Tool execution error',
|
|
124
|
-
details: message,
|
|
125
|
-
...(isValidation ? { path: (err as SchemaValidationError).path } : {}),
|
|
126
|
-
}),
|
|
127
|
-
isError: true,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
})
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function buildToolMessage(result: ToolCallResult): Record<string, unknown> {
|
|
135
|
-
return {
|
|
136
|
-
role: 'tool',
|
|
137
|
-
tool_call_id: result.toolCallId,
|
|
138
|
-
content: result.result,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function buildAssistantToolCallMessage(
|
|
143
|
-
content: string | null,
|
|
144
|
-
toolCalls: ParsedToolCall[]
|
|
145
|
-
): Record<string, unknown> {
|
|
146
|
-
return {
|
|
147
|
-
role: 'assistant',
|
|
148
|
-
content: content || null,
|
|
149
|
-
tool_calls: toolCalls.map((tc) => ({
|
|
150
|
-
id: tc.id,
|
|
151
|
-
type: 'function',
|
|
152
|
-
function: {
|
|
153
|
-
name: tc.name,
|
|
154
|
-
arguments: typeof tc.arguments === 'string'
|
|
155
|
-
? tc.arguments
|
|
156
|
-
: JSON.stringify(tc.arguments),
|
|
157
|
-
},
|
|
158
|
-
})),
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export async function runExecutionLoop(
|
|
163
|
-
sendToLLM: LLMSendFunction,
|
|
164
|
-
messages: unknown[],
|
|
165
|
-
model: string,
|
|
166
|
-
config: ExecutionLoopConfig = {}
|
|
167
|
-
): Promise<string> {
|
|
168
|
-
const maxTurns = config.maxTurns ?? 10;
|
|
169
|
-
const debug = config.debug ?? false;
|
|
170
|
-
|
|
171
|
-
const tools = registry.listNames().length > 0
|
|
172
|
-
? registry.toOpenAITools()
|
|
173
|
-
: undefined;
|
|
174
|
-
|
|
175
|
-
for (let turn = 0; turn < maxTurns; turn++) {
|
|
176
|
-
if (debug) {
|
|
177
|
-
console.log(`[executor] Turn ${turn + 1}/${maxTurns}, messages: ${messages.length}`);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const response = await sendToLLM(messages, tools, model);
|
|
181
|
-
|
|
182
|
-
const hasStructuredToolCalls = response.toolCalls && response.toolCalls.length > 0;
|
|
183
|
-
let parsedFromContent: { textContent: string; toolCalls: ParsedToolCall[] } | null = null;
|
|
184
|
-
|
|
185
|
-
if (!hasStructuredToolCalls && response.content) {
|
|
186
|
-
parsedFromContent = parseToolCallsFromContent(response.content);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const effectiveToolCalls = hasStructuredToolCalls
|
|
190
|
-
? response.toolCalls
|
|
191
|
-
: parsedFromContent?.toolCalls || [];
|
|
192
|
-
|
|
193
|
-
const effectiveContent = parsedFromContent
|
|
194
|
-
? parsedFromContent.textContent
|
|
195
|
-
: response.content;
|
|
196
|
-
|
|
197
|
-
if (effectiveToolCalls.length === 0) {
|
|
198
|
-
if (debug) {
|
|
199
|
-
console.log('[executor] No tool calls, loop complete');
|
|
200
|
-
}
|
|
201
|
-
return effectiveContent || '';
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const context: ToolContext = {
|
|
205
|
-
messages,
|
|
206
|
-
turn,
|
|
207
|
-
model,
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
if (debug) {
|
|
211
|
-
console.log(
|
|
212
|
-
`[executor] Executing ${effectiveToolCalls.length} tool calls:`,
|
|
213
|
-
effectiveToolCalls.map((tc) => tc.name)
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const toolResults = await executeToolCalls(effectiveToolCalls, context);
|
|
218
|
-
|
|
219
|
-
messages.push(buildAssistantToolCallMessage(effectiveContent, effectiveToolCalls));
|
|
220
|
-
|
|
221
|
-
for (const result of toolResults) {
|
|
222
|
-
messages.push(buildToolMessage(result));
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (debug) {
|
|
226
|
-
console.log(
|
|
227
|
-
`[executor] Tool results:`,
|
|
228
|
-
toolResults.map((r) => ({ name: r.name, isError: r.isError }))
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
throw new Error(
|
|
234
|
-
`Execution loop exceeded maximum turns (${maxTurns}). The agent may be stuck in a cycle.`
|
|
235
|
-
);
|
|
236
|
-
}
|
package/src/tools/pipeline.ts
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import type { ToolCallAST, ToolDefinition } from './ast.js';
|
|
2
|
-
import { detectToolCalls } from './detector.js';
|
|
3
|
-
import { repairToolCall } from './repair.js';
|
|
4
|
-
import { RequestToolRegistry } from './registry-runtime.js';
|
|
5
|
-
import { coerceArguments } from './coercion.js';
|
|
6
|
-
import { validateToolCall, type ValidationResult } from './validator.js';
|
|
7
|
-
import { calculateConfidence } from './confidence.js';
|
|
8
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
9
|
-
|
|
10
|
-
export interface PipelineResult {
|
|
11
|
-
textContent: string;
|
|
12
|
-
toolCalls: ToolCallAST[];
|
|
13
|
-
errors: Array<{
|
|
14
|
-
toolName?: string;
|
|
15
|
-
code: string;
|
|
16
|
-
message: string;
|
|
17
|
-
details?: any;
|
|
18
|
-
}>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const CONFIDENCE_THRESHOLD = 0.7;
|
|
22
|
-
|
|
23
|
-
export function processToolCalls(
|
|
24
|
-
text: string,
|
|
25
|
-
requestTools: unknown[]
|
|
26
|
-
): PipelineResult {
|
|
27
|
-
const registry = new RequestToolRegistry(requestTools);
|
|
28
|
-
const detected = detectToolCalls(text);
|
|
29
|
-
|
|
30
|
-
const toolCalls: ToolCallAST[] = [];
|
|
31
|
-
const errors: PipelineResult['errors'] = [];
|
|
32
|
-
let textContent = text;
|
|
33
|
-
|
|
34
|
-
for (const det of detected) {
|
|
35
|
-
while (textContent.includes(det.raw)) {
|
|
36
|
-
textContent = textContent.replace(det.raw, '').trim();
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
for (const det of detected) {
|
|
41
|
-
const repaired = repairToolCall(det.extracted);
|
|
42
|
-
if (!repaired) {
|
|
43
|
-
errors.push({
|
|
44
|
-
code: 'MALFORMED_TOOL_CALL',
|
|
45
|
-
message: 'Could not repair or identify tool call structure',
|
|
46
|
-
details: det.extracted,
|
|
47
|
-
});
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (!registry.has(repaired.name)) {
|
|
52
|
-
errors.push({
|
|
53
|
-
toolName: repaired.name,
|
|
54
|
-
code: 'UNKNOWN_TOOL',
|
|
55
|
-
message: `Tool '${repaired.name}' is not registered or provided in the request`,
|
|
56
|
-
});
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const toolDef = registry.get(repaired.name)!;
|
|
61
|
-
const coercedArgs = coerceArguments(repaired.arguments, toolDef.schema);
|
|
62
|
-
|
|
63
|
-
const ast: ToolCallAST = {
|
|
64
|
-
id: `call_${uuidv4()}`,
|
|
65
|
-
name: repaired.name,
|
|
66
|
-
arguments: coercedArgs,
|
|
67
|
-
raw: det.raw,
|
|
68
|
-
confidence: 0.0,
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const validation: ValidationResult = validateToolCall(ast, toolDef);
|
|
72
|
-
const confidenceResult = calculateConfidence(ast, toolDef);
|
|
73
|
-
ast.confidence = confidenceResult.score;
|
|
74
|
-
|
|
75
|
-
if (!validation.valid) {
|
|
76
|
-
if (validation.missingFields.length > 0) {
|
|
77
|
-
errors.push({
|
|
78
|
-
toolName: ast.name,
|
|
79
|
-
code: 'MISSING_REQUIRED_FIELD',
|
|
80
|
-
message: `Missing required fields: ${validation.missingFields.join(', ')}`,
|
|
81
|
-
details: validation.errors,
|
|
82
|
-
});
|
|
83
|
-
} else {
|
|
84
|
-
errors.push({
|
|
85
|
-
toolName: ast.name,
|
|
86
|
-
code: 'SCHEMA_VALIDATION_FAILED',
|
|
87
|
-
message: 'Arguments do not match the tool schema',
|
|
88
|
-
details: validation.errors,
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (ast.confidence >= CONFIDENCE_THRESHOLD) {
|
|
95
|
-
toolCalls.push(ast);
|
|
96
|
-
} else {
|
|
97
|
-
errors.push({
|
|
98
|
-
toolName: ast.name,
|
|
99
|
-
code: 'LOW_CONFIDENCE',
|
|
100
|
-
message: `Tool call confidence ${ast.confidence} is below threshold ${CONFIDENCE_THRESHOLD}`,
|
|
101
|
-
details: confidenceResult.reasons,
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
textContent,
|
|
108
|
-
toolCalls,
|
|
109
|
-
errors,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export function formatOpenAIToolCalls(toolCalls: ToolCallAST[]): any[] {
|
|
114
|
-
return toolCalls.map(tc => ({
|
|
115
|
-
id: tc.id,
|
|
116
|
-
type: 'function',
|
|
117
|
-
function: {
|
|
118
|
-
name: tc.name,
|
|
119
|
-
arguments: JSON.stringify(tc.arguments),
|
|
120
|
-
},
|
|
121
|
-
}));
|
|
122
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { ToolDefinition } from './ast.js';
|
|
2
|
-
|
|
3
|
-
export class RequestToolRegistry {
|
|
4
|
-
private registry: Map<string, ToolDefinition>;
|
|
5
|
-
|
|
6
|
-
constructor(requestTools: unknown[] = []) {
|
|
7
|
-
this.registry = new Map();
|
|
8
|
-
for (const tool of requestTools) {
|
|
9
|
-
if (tool && typeof tool === 'object') {
|
|
10
|
-
const t = tool as any;
|
|
11
|
-
if (t.type === 'function' && t.function && typeof t.function === 'object') {
|
|
12
|
-
const fn = t.function;
|
|
13
|
-
this.registry.set(fn.name, {
|
|
14
|
-
name: fn.name,
|
|
15
|
-
description: fn.description,
|
|
16
|
-
schema: fn.parameters || {},
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
has(name: string): boolean {
|
|
24
|
-
return this.registry.has(name);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
get(name: string): ToolDefinition | undefined {
|
|
28
|
-
return this.registry.get(name);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
list(): ToolDefinition[] {
|
|
32
|
-
return Array.from(this.registry.values());
|
|
33
|
-
}
|
|
34
|
-
}
|
package/src/tools/repair.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
export interface RepairedTool {
|
|
2
|
-
name: string;
|
|
3
|
-
arguments: unknown;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export function repairToolCall(extracted: Record<string, unknown>): RepairedTool | null {
|
|
7
|
-
if ('tool' in extracted && !('name' in extracted)) {
|
|
8
|
-
return {
|
|
9
|
-
name: String(extracted.tool),
|
|
10
|
-
arguments: extracted.arguments || {},
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
if ('function' in extracted && typeof extracted.function === 'object' && extracted.function !== null) {
|
|
15
|
-
const fn = extracted.function as Record<string, unknown>;
|
|
16
|
-
if ('name' in fn) {
|
|
17
|
-
return {
|
|
18
|
-
name: String(fn.name),
|
|
19
|
-
arguments: fn.arguments || {},
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if ('function_call' in extracted && typeof extracted.function_call === 'object' && extracted.function_call !== null) {
|
|
25
|
-
const fn = extracted.function_call as Record<string, unknown>;
|
|
26
|
-
if ('name' in fn) {
|
|
27
|
-
return {
|
|
28
|
-
name: String(fn.name),
|
|
29
|
-
arguments: fn.arguments || {},
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if ('name' in extracted) {
|
|
35
|
-
return {
|
|
36
|
-
name: String(extracted.name),
|
|
37
|
-
arguments: extracted.arguments || {},
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return null;
|
|
42
|
-
}
|
package/src/tools/validator.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import Ajv from 'ajv';
|
|
2
|
-
import type { ToolCallAST, ToolDefinition } from './ast.js';
|
|
3
|
-
|
|
4
|
-
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
5
|
-
|
|
6
|
-
export interface ValidationResult {
|
|
7
|
-
valid: boolean;
|
|
8
|
-
errors: any[] | null;
|
|
9
|
-
missingFields: string[];
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function validateToolCall(ast: ToolCallAST, toolDef: ToolDefinition): ValidationResult {
|
|
13
|
-
const validate = ajv.compile(toolDef.schema);
|
|
14
|
-
const valid = validate(ast.arguments);
|
|
15
|
-
|
|
16
|
-
const missingFields: string[] = [];
|
|
17
|
-
if (toolDef.schema && typeof toolDef.schema === 'object' && 'required' in toolDef.schema) {
|
|
18
|
-
const required = (toolDef.schema as any).required;
|
|
19
|
-
if (Array.isArray(required)) {
|
|
20
|
-
for (const field of required) {
|
|
21
|
-
if (!(ast.arguments && typeof ast.arguments === 'object' && field in (ast.arguments as Record<string, unknown>))) {
|
|
22
|
-
missingFields.push(field);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
valid,
|
|
30
|
-
errors: validate.errors ?? null,
|
|
31
|
-
missingFields,
|
|
32
|
-
};
|
|
33
|
-
}
|