@pedrofariasx/qwenproxy 1.2.0 → 1.2.2

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.
@@ -1,151 +0,0 @@
1
- /*
2
- * UltraToolCallLinter v1.0 - Edge Case Stress Tests
3
- * Tests required by spec
4
- */
5
-
6
- import { describe, it, beforeEach, afterEach } from 'node:test'
7
- import assert from 'node:assert'
8
-
9
- import { UltraToolCallLinter } from '../linter/index.js'
10
- import type { ParseResult, RawToolCandidate } from '../linter/types.js'
11
-
12
- // Helper functions to match the old bar.ts API using direct instantiation
13
- let globalLinter: UltraToolCallLinter | null = null
14
-
15
- function getGlobalLinter(): UltraToolCallLinter {
16
- if (!globalLinter) globalLinter = new UltraToolCallLinter()
17
- return globalLinter
18
- }
19
-
20
- function configure(config: { registry?: any; strictMode?: boolean; enableSecurityGate?: boolean; maxRecoveryAttempts?: number; minConfidenceThreshold?: number }): void {
21
- globalLinter = new UltraToolCallLinter(config)
22
- }
23
-
24
- function parseText(input: string): ParseResult {
25
- return getGlobalLinter().parseText(input)
26
- }
27
-
28
- function extract(input: string): RawToolCandidate[] {
29
- return getGlobalLinter().extract(input)
30
- }
31
-
32
- function repair(input: string): string {
33
- return getGlobalLinter().repair(input)
34
- }
35
-
36
- describe('UltraToolCallLinter Edge Cases', () => {
37
- it('parses truncated JSON in the middle', () => {
38
- configure({ minConfidenceThreshold: 0.1 })
39
- const input = 'Here is the query: {"search": {"query": "test"}}'
40
- const result = parseText(input)
41
- assert.ok(result.toolCalls.length >= 1)
42
- assert.equal(result.toolCalls[0].tool, 'search')
43
- })
44
-
45
- it('parses concatenated multiple JSONs', () => {
46
- const input = '{"name":"search","arguments":{"query":"foo"}}{"name":"browser_open","arguments":{"url":"http://x"}}'
47
- const candidates = extract(input)
48
- assert.ok(candidates.length >= 1, 'Expected at least one candidate')
49
- })
50
-
51
- it('parses markdown-wrapped JSON', () => {
52
- const input = '```json\n{"tool":"search","input":{"query":"beer"}}\n```'
53
- const result = parseText(input)
54
- assert.ok(result.toolCalls.length >= 1)
55
- assert.equal(result.toolCalls[0].tool, 'search')
56
- })
57
-
58
- it('parses human text mixed with a tool call (ReAct)', () => {
59
- const input =
60
- 'Sure, let me search that for you.\nAction: search\nAction Input: {"query": "breaking news"}'
61
- const result = parseText(input)
62
- assert.ok(result.toolCalls.length >= 1, 'Expected ReAct extraction')
63
- })
64
-
65
- it('recovers from broken escape sequences', () => {
66
- const input = '{"tool":"search","input":{"query":"test\\\\n\\\\t\\\\"}}'
67
- const result = parseText(input)
68
- assert.ok(result.toolCalls.length >= 1)
69
- })
70
-
71
- it('recovers from unicode corruption', () => {
72
- const input = '{"tool":"search","input":{"query":"caf\u0000\u001Fé"}}'
73
- const result = parseText(input)
74
- assert.ok(result.toolCalls.length >= 1)
75
- })
76
-
77
- it('handles streaming with repeated chunks', () => {
78
- const linter = new UltraToolCallLinter({ minConfidenceThreshold: 0.1 })
79
- const chunks = [
80
- 'S',
81
- 'Su',
82
- 'Sur',
83
- 'Sure! ',
84
- 'Here i',
85
- 'Here is ',
86
- 'Here is the ',
87
- 'Here is the result: ',
88
- 'Here is the result: {"tool":"search","input":{"query":"async"}}',
89
- ]
90
- for (const c of chunks) linter.push(c)
91
- const result = linter.parse()
92
- assert.ok(result.toolCalls.length >= 1)
93
- })
94
-
95
- it('extracts tool call inside a textual array', () => {
96
- const input =
97
- 'Options:\n- `{"tool":"search","input":{"query":"foo"}}`\n- `{"tool":"search","input":{"query":"bar"}}`'
98
- const candidates = extract(input)
99
- assert.ok(candidates.length >= 1, 'Expected extraction from array-text format')
100
- })
101
-
102
- it('handles duplicate model output', () => {
103
- const input =
104
- '{"tool":"search","input":{"query":"dup"}}\n\n{"tool":"search","input":{"query":"dup"}}'
105
- const result = parseText(input)
106
- assert.ok(Array.isArray(result.toolCalls))
107
- })
108
-
109
- it('repair: single quotes to double quotes', () => {
110
- const input = "{'tool': 'search', 'input': {'query': 'test'}}"
111
- const repaired = repair(input)
112
- assert.ok(repaired.includes('search'))
113
- })
114
-
115
- it('repair: trailing comma', () => {
116
- const input = '{"tool": "search", "input": {"query": "test"},}'
117
- const repaired = repair(input)
118
- assert.ok(repaired.includes('search'))
119
- })
120
-
121
- it('repair: key without quotes', () => {
122
- const input = '{tool: "search", input: {query: "test"}}'
123
- const repaired = repair(input)
124
- assert.ok(repaired.includes('search'))
125
- })
126
-
127
- it('security gate blocks destructive shell injection', () => {
128
- configure({ minConfidenceThreshold: 0.0 })
129
- const input = '{"tool":"search","input":{"query":"\'; rm -rf /; echo \'"}}'
130
- const result = parseText(input)
131
- const passed = result.toolCalls.some((call: any) => {
132
- const val = (call.input.query ?? '') as string
133
- return typeof val === 'string' && val.includes('rm -rf /')
134
- })
135
- assert.strictEqual(passed, false, 'Should not pass through destructive payload')
136
- })
137
-
138
- it('rejects empty string values per spec', () => {
139
- configure({ minConfidenceThreshold: 0.0 })
140
- const input = '{"tool":"search","input":{"query":""}}'
141
- const result = parseText(input)
142
- assert.ok(Array.isArray(result.toolCalls))
143
- })
144
-
145
- it('parseText returns canonical tool calls', () => {
146
- configure({ minConfidenceThreshold: 0.1 })
147
- const input = '{"tool":"search","input":{"query":"x"}}'
148
- const result = parseText(input)
149
- assert.ok(Array.isArray(result.toolCalls))
150
- })
151
- })
@@ -1,42 +0,0 @@
1
- import { test } from 'node:test';
2
- import assert from 'node:assert';
3
- import { executeToolCalls } from '../tools/executor.ts';
4
- import { registry } from '../tools/registry.ts';
5
- import type { ToolContext } from '../tools/types.ts';
6
-
7
- test('executeToolCalls: parallel execution', async () => {
8
- let activeCount = 0;
9
- let maxParallel = 0;
10
-
11
- registry.register(
12
- 'parallel_tool',
13
- 'A tool that waits to test parallelism',
14
- { type: 'object', properties: {} },
15
- async () => {
16
- activeCount++;
17
- maxParallel = Math.max(maxParallel, activeCount);
18
- await new Promise(r => setTimeout(r, 100));
19
- activeCount--;
20
- return 'done';
21
- }
22
- );
23
-
24
- const toolCalls = [
25
- { id: '1', name: 'parallel_tool', arguments: {} },
26
- { id: '2', name: 'parallel_tool', arguments: {} },
27
- { id: '3', name: 'parallel_tool', arguments: {} },
28
- ];
29
-
30
- const context: ToolContext = {
31
- messages: [],
32
- turn: 0,
33
- model: 'test'
34
- };
35
-
36
- const results = await executeToolCalls(toolCalls, context);
37
-
38
- assert.strictEqual(results.length, 3);
39
- assert.ok(maxParallel > 1, `Max parallel should be > 1, got ${maxParallel}`);
40
-
41
- registry.unregister('parallel_tool');
42
- });
@@ -1,176 +0,0 @@
1
- /*
2
- * Structure Verification Tests
3
- * Verifies that the identified issues are correct and the logic works as expected.
4
- */
5
-
6
- import { describe, it, beforeEach } from 'node:test'
7
- import assert from 'node:assert'
8
-
9
- import { UltraToolCallLinter } from '../linter/index.js'
10
- import type { ParseResult, RawToolCandidate } from '../linter/types.js'
11
-
12
- // Helper functions to match the old bar.ts API using direct instantiation
13
- let globalLinter: UltraToolCallLinter | null = null
14
-
15
- function getGlobalLinter(): UltraToolCallLinter {
16
- if (!globalLinter) globalLinter = new UltraToolCallLinter()
17
- return globalLinter
18
- }
19
-
20
- function configure(config: { registry?: any; strictMode?: boolean; enableSecurityGate?: boolean; maxRecoveryAttempts?: number; minConfidenceThreshold?: number }): void {
21
- globalLinter = new UltraToolCallLinter(config)
22
- }
23
-
24
- function parseText(input: string): ParseResult {
25
- return getGlobalLinter().parseText(input)
26
- }
27
-
28
- function extract(input: string): RawToolCandidate[] {
29
- return getGlobalLinter().extract(input)
30
- }
31
-
32
- function repair(input: string): string {
33
- return getGlobalLinter().repair(input)
34
- }
35
-
36
- // Test 2: Verify CandidateSpan is defined but unused
37
- type TestCandidateSpan = {
38
- startIndex: number
39
- endIndex: number | null
40
- rawContent: string
41
- sourceHint: string
42
- isComplete: boolean
43
- }
44
-
45
- // Test 3: Verify ParseResult structure
46
-
47
- describe('Structure Verification Tests', () => {
48
- beforeEach(() => {
49
- configure({ strictMode: false, minConfidenceThreshold: 0.1 })
50
- })
51
-
52
- describe('Direct import from index.js verification', () => {
53
- it('should verify UltraToolCallLinter methods work correctly', () => {
54
- const linter = new UltraToolCallLinter()
55
-
56
- assert.strictEqual(typeof linter.parse, 'function')
57
- assert.strictEqual(typeof linter.push, 'function')
58
- assert.strictEqual(typeof linter.extract, 'function')
59
- assert.strictEqual(typeof linter.repair, 'function')
60
- assert.strictEqual(typeof linter.parseText, 'function')
61
- })
62
-
63
- it('should verify parseText works through foo -> bar -> index chain', () => {
64
- const input = '{"tool":"search","input":{"query":"test"}}'
65
- const result = parseText(input)
66
-
67
- assert.ok(Array.isArray(result.toolCalls))
68
- assert.ok(typeof result.confidence === 'number')
69
- })
70
-
71
- it('should verify extract works through foo -> bar -> index chain', () => {
72
- const input = '{"tool":"search","input":{"query":"test"}}'
73
- const candidates = extract(input)
74
-
75
- assert.ok(Array.isArray(candidates))
76
- })
77
-
78
- it('should verify repair works through foo -> bar -> index chain', () => {
79
- const input = "{'tool': 'search'}"
80
- const repaired = repair(input)
81
-
82
- assert.ok(typeof repaired === 'string')
83
- assert.ok(repaired.includes('search'))
84
- })
85
- })
86
-
87
- describe('Unused type: CandidateSpan verification', () => {
88
- it('CandidateSpan type exists in types.ts but is not used anywhere', () => {
89
- // This test documents that CandidateSpan is defined but unused
90
- const exampleSpan: TestCandidateSpan = {
91
- startIndex: 0,
92
- endIndex: 10,
93
- rawContent: '{"tool":"test"}',
94
- sourceHint: 'json',
95
- isComplete: true
96
- }
97
-
98
- assert.ok(exampleSpan.startIndex === 0)
99
- assert.ok(exampleSpan.isComplete === true)
100
- })
101
- })
102
-
103
- describe('ParseResult structure verification', () => {
104
- it('ParseResult has all required fields', () => {
105
- const parseResult: ParseResult = {
106
- text: 'test',
107
- toolCalls: [],
108
- errors: [],
109
- confidence: 0.5
110
- }
111
-
112
- assert.ok(Array.isArray(parseResult.toolCalls))
113
- assert.ok(typeof parseResult.confidence === 'number')
114
- assert.ok(Array.isArray(parseResult.errors))
115
- assert.ok(typeof parseResult.text === 'string')
116
- })
117
-
118
- it('parseText returns ParseResult with all required fields', () => {
119
- const input = '{"tool":"search","input":{"query":"test"}}'
120
- const result = parseText(input)
121
-
122
- // Should have all ParserResult fields + confidence
123
- assert.ok(typeof result.text === 'string')
124
- assert.ok(Array.isArray(result.toolCalls))
125
- assert.ok(Array.isArray(result.errors))
126
- assert.ok(typeof result.confidence === 'number')
127
- })
128
- })
129
-
130
- describe('src/tools/ directory isolation verification', () => {
131
- it('tools/registry.ts has syntax error (Map initialization)', async () => {
132
- // This test documents that tools/registry.ts has a bug:
133
- // const toolRegistry: Map<string, ToolRegistration> = Map()
134
- // Should be: new Map()
135
- // The file is unused by the main codebase anyway
136
-
137
- const registryModule = await import('../tools/registry.js').catch(() => null)
138
-
139
- // If the module can't be imported due to syntax error, that confirms the issue
140
- if (registryModule === null) {
141
- assert.ok(true, 'Registry module has syntax error as expected')
142
- }
143
- })
144
-
145
- it('tools/parser.ts StreamingToolParser is used by main codebase', async () => {
146
- // This verifies that StreamingToolParser IS actually used
147
- const { StreamingToolParser } = await import('../tools/parser.js')
148
-
149
- assert.ok(typeof StreamingToolParser === 'function')
150
-
151
- const parser = new StreamingToolParser()
152
- assert.ok(typeof parser.feed === 'function')
153
- })
154
- })
155
-
156
- describe('LinterConfig duplication verification', () => {
157
- it('LinterConfig in index.ts duplicates constructor param type from bar.ts', () => {
158
- // bar.ts defines inline config type:
159
- // { registry?: ToolRegistry; strictMode?: boolean; ... }
160
- //
161
- // index.ts defines LinterConfig interface:
162
- // { registry?: ToolRegistry; strictMode?: boolean; ... }
163
- //
164
- // These are functionally identical but defined separately
165
-
166
- const linter = new UltraToolCallLinter({
167
- strictMode: true,
168
- enableSecurityGate: false,
169
- maxRecoveryAttempts: 5,
170
- minConfidenceThreshold: 0.5
171
- })
172
-
173
- assert.ok(linter instanceof UltraToolCallLinter)
174
- })
175
- })
176
- })
package/src/tools/ast.ts DELETED
@@ -1,15 +0,0 @@
1
- import type { JsonSchema } from './types.js';
2
-
3
- export interface ToolCallAST {
4
- id: string;
5
- name: string;
6
- arguments: unknown;
7
- raw: string;
8
- confidence: number;
9
- }
10
-
11
- export interface ToolDefinition {
12
- name: string;
13
- description?: string;
14
- schema: JsonSchema;
15
- }
@@ -1,67 +0,0 @@
1
- import type { JsonSchema } from './types.js';
2
-
3
- export function coerceArguments(args: unknown, schema: JsonSchema): unknown {
4
- if (typeof args !== 'object' || args === null || Array.isArray(args)) {
5
- return args;
6
- }
7
-
8
- const coerced: Record<string, unknown> = {};
9
- const properties = schema.properties || {};
10
-
11
- for (const [key, value] of Object.entries(args as Record<string, unknown>)) {
12
- const propSchema = properties[key];
13
- if (propSchema && typeof propSchema === 'object') {
14
- coerced[key] = coerceValue(value, propSchema);
15
- } else {
16
- coerced[key] = value;
17
- }
18
- }
19
-
20
- return coerced;
21
- }
22
-
23
- function coerceValue(value: unknown, schema: JsonSchema): unknown {
24
- if (typeof value !== 'string') {
25
- if (Array.isArray(value) && schema.type === 'array' && schema.items) {
26
- return value.map(item => coerceValue(item, schema.items!));
27
- }
28
- if (typeof value === 'object' && value !== null && schema.type === 'object' && schema.properties) {
29
- return coerceArguments(value, { type: 'object', properties: schema.properties!, required: schema.required });
30
- }
31
- return value;
32
- }
33
-
34
- const trimmed = value.trim();
35
-
36
- if (schema.type === 'boolean') {
37
- if (trimmed.toLowerCase() === 'true') return true;
38
- if (trimmed.toLowerCase() === 'false') return false;
39
- }
40
-
41
- if (schema.type === 'integer' || schema.type === 'number') {
42
- if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
43
- const num = Number(trimmed);
44
- if (!Number.isNaN(num)) {
45
- return schema.type === 'integer' ? Math.trunc(num) : num;
46
- }
47
- }
48
- }
49
-
50
- if (schema.type === 'array' && trimmed.startsWith('[')) {
51
- try {
52
- return JSON.parse(trimmed);
53
- } catch {
54
- // fall through
55
- }
56
- }
57
-
58
- if (schema.type === 'object' && trimmed.startsWith('{')) {
59
- try {
60
- return JSON.parse(trimmed);
61
- } catch {
62
- // fall through
63
- }
64
- }
65
-
66
- return value;
67
- }
@@ -1,48 +0,0 @@
1
- import type { ToolCallAST, ToolDefinition } from './ast.js';
2
-
3
- export interface ConfidenceResult {
4
- score: number;
5
- reasons: string[];
6
- }
7
-
8
- export function calculateConfidence(ast: ToolCallAST, toolDef: ToolDefinition): ConfidenceResult {
9
- let score = 0.0;
10
- const reasons: string[] = [];
11
-
12
- if (ast.name && toolDef.name === ast.name) {
13
- score += 0.4;
14
- reasons.push('Tool name matches registry');
15
- }
16
-
17
- if (ast.arguments && typeof ast.arguments === 'object' && !Array.isArray(ast.arguments)) {
18
- score += 0.3;
19
- reasons.push('Arguments are a valid object');
20
- } else if (typeof ast.arguments === 'string') {
21
- try {
22
- JSON.parse(ast.arguments as string);
23
- score += 0.15;
24
- reasons.push('Arguments are a valid JSON string');
25
- } catch {
26
- reasons.push('Arguments are an invalid JSON string');
27
- }
28
- } else {
29
- reasons.push('Arguments are missing or invalid type');
30
- }
31
-
32
- const schema = toolDef.schema as any;
33
- if (schema && schema.required && Array.isArray(schema.required)) {
34
- const args = (ast.arguments as Record<string, unknown>) || {};
35
- const hasAllRequired = schema.required.every((field: string) => field in args);
36
- if (hasAllRequired) {
37
- score += 0.3;
38
- reasons.push('All required fields are present');
39
- } else {
40
- reasons.push('Missing required fields');
41
- }
42
- } else {
43
- score += 0.3;
44
- reasons.push('No required fields to validate');
45
- }
46
-
47
- return { score: Math.min(score, 1.0), reasons };
48
- }
@@ -1,40 +0,0 @@
1
- import { robustParseJSON } from '../utils/json.js';
2
-
3
- export interface DetectedTool {
4
- raw: string;
5
- extracted: Record<string, unknown>;
6
- }
7
-
8
- const TOOL_CALL_REGEX = /\b[^>]*>([\s\S]*?)<\/tool_call>/gi;
9
-
10
- export function detectToolCalls(text: string): DetectedTool[] {
11
- const results: DetectedTool[] = [];
12
-
13
- let match;
14
- while ((match = TOOL_CALL_REGEX.exec(text)) !== null) {
15
- const raw = match[0];
16
- const content = match[1].trim();
17
- const parsed = robustParseJSON(content);
18
- if (parsed && typeof parsed === 'object') {
19
- results.push({ raw, extracted: parsed });
20
- } else {
21
- results.push({ raw, extracted: { _rawContent: content } });
22
- }
23
- }
24
-
25
- if (results.length === 0) {
26
- const parsed = robustParseJSON(text);
27
- if (parsed && typeof parsed === 'object') {
28
- if (
29
- 'name' in parsed ||
30
- 'tool' in parsed ||
31
- 'function' in parsed ||
32
- 'function_call' in parsed
33
- ) {
34
- results.push({ raw: text.trim(), extracted: parsed });
35
- }
36
- }
37
- }
38
-
39
- return results;
40
- }