Skip to content

Commit 465ebcb

Browse files
ThisCakeIsALieZbigorDen1552
authored
Small reqs2x improvements (#324)
Improves parsing of JSON output for llm2check (solves headaches relating to non-JSON output on stdout). Also adds and slightly reorganizes some CLI settings for reqs2tests and their associated VSCode settings. Additionally adds an a new advanced LLM provider --------- Co-authored-by: Zbigor <igor.martinovic@vector.com> Co-authored-by: Den1552 <denis.moslavac@vector.com>
1 parent d10644e commit 465ebcb

File tree

7 files changed

+246
-33
lines changed

7 files changed

+246
-33
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33
All notable changes to the "vectorcastTestExplorer" extension will be documented in this file.
44

55

6+
## [1.0.30] - 2026-03-24
7+
8+
### Added
9+
- Added setting to allow equirements-driven test generation without passing of function definitions (for blackbox-style testing)
10+
- Added new advanced LLM provider for use with Azure APIM
11+
- Reorganized LLM providers (into common and advanced/uncommon)
12+
613
## [1.0.29] - 2026-02-25
714

815
### Added

ci/get_most_recent_reqs2tests_distribution.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,15 @@ def download_file(url, filename=None):
5151
with open(tmp) as f:
5252
data = json.load(f)
5353

54+
def parse_date(uri):
55+
try:
56+
return datetime.fromisoformat(uri.rsplit("-", 1)[0][1:])
57+
except ValueError:
58+
return None
59+
5460
children_urls = sorted(
55-
[c["uri"] for c in data["children"] if c["uri"].rsplit("-", 1)[0][1:]],
56-
key=lambda x: datetime.fromisoformat(x.rsplit("-", 1)[0][1:]),
61+
[c["uri"] for c in data["children"] if parse_date(c["uri"]) is not None],
62+
key=lambda x: parse_date(x),
5763
reverse=True,
5864
)
5965

docs/reqs2x/reqs2x_documentation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ The **VectorCAST Reqs2x tools** provide LLM-powered capabilities for requirement
1010
- **Generate VectorCAST test cases from requirements**
1111

1212

13-
All tools require a configured LLM provider to function. The tools support multiple LLM providers including Azure OpenAI, OpenAI, Anthropic, and LiteLLM.
13+
All tools require a configured LLM provider to function. The tools support multiple LLM providers including Azure OpenAI, OpenAI, Anthropic, LiteLLM, Azure APIM, and OpenAI Access Token.
1414

1515
This manual demonstrates Reqs2x usage workflows from inside this VS-Code extension using the `TUTORIAL_C` demo environment. Before starting, ensure you have the necessary components ready.
1616

package.json

Lines changed: 96 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "vectorcasttestexplorer",
33
"displayName": "VectorCAST Test Explorer",
44
"description": "VectorCAST Test Explorer for VS Code",
5-
"version": "1.0.29",
5+
"version": "1.0.30",
66
"license": "MIT",
77
"repository": {
88
"type": "git",
@@ -494,15 +494,21 @@
494494
"default": false,
495495
"description": "Do not provide additional test examples to the LLM during test generation"
496496
},
497+
"vectorcastTestExplorer.reqs2x.functionDefinitions": {
498+
"type": "boolean",
499+
"order": 9,
500+
"default": true,
501+
"description": "Supply function definitions to the language model during test generation. Disable for blackbox-style testing."
502+
},
497503
"vectorcastTestExplorer.reqs2x.enableUutStubbing": {
498504
"type": "boolean",
499-
"order": 8,
505+
"order": 10,
500506
"default": true,
501507
"description": "Enable UUT stubbing during requirements-based test generation"
502508
},
503509
"vectorcastTestExplorer.reqs2x.generationLanguage": {
504510
"type": "string",
505-
"order": 9,
511+
"order": 11,
506512
"default": "en",
507513
"enum": [
508514
"af", "sq", "am", "ar", "hy", "as", "az", "be", "bn", "bs", "bg", "my", "ch", "hr", "cs", "da", "nl", "en", "et", "tl", "fi", "fr", "ka", "de", "gu", "ha", "he", "hi", "hu", "is", "ig", "id", "ga", "it", "jp", "kn", "kk", "km", "ko", "ky", "lo", "lv", "lt", "mk", "ms", "ml", "mt", "mn", "me", "ne", "no", "or", "fa", "pl", "pt", "pa", "ro", "ru", "sr", "si", "sk", "sl", "es", "sw", "sv", "tg", "ta", "te", "th", "tr", "uk", "ur", "uz", "vi", "cy", "xh", "yo", "zu"
@@ -514,119 +520,185 @@
514520
},
515521
"vectorcastTestExplorer.reqs2x.modelCompatibilityMode": {
516522
"type": "boolean",
517-
"order": 10,
523+
"order": 12,
518524
"default": false,
519525
"description": "Enable model compatibility mode (runs Reqs2X tools without structured outputs which enables compatibility with more providers)."
520526
},
521527
"vectorcastTestExplorer.reqs2x.provider": {
522528
"type": "string",
523-
"order": 11,
529+
"order": 13,
524530
"default": "openai",
525-
"enum": ["openai", "azure_openai", "anthropic", "litellm"],
526-
"enumDescriptions": ["OpenAI", "Azure OpenAI", "Anthropic", "LiteLLM"],
531+
"enum": ["openai", "azure_openai", "anthropic", "litellm", "azure_apim", "openai_at"],
532+
"enumDescriptions": ["OpenAI", "Azure OpenAI", "Anthropic", "LiteLLM", "Azure APIM (Advanced)", "OpenAI Access Token (Advanced)"],
527533
"description": "Language model provider for automatic requirements and test generation using Reqs2X"
528534
},
529535
"vectorcastTestExplorer.reqs2x.openai.apiKey": {
530536
"type": "string",
531-
"order": 12,
537+
"order": 14,
532538
"default": "",
533539
"description": "OpenAI API key"
534540
},
535541
"vectorcastTestExplorer.reqs2x.openai.modelName": {
536542
"type": "string",
537-
"order": 13,
543+
"order": 15,
538544
"default": "",
539545
"description": "OpenAI model name"
540546
},
541547
"vectorcastTestExplorer.reqs2x.openai.reasoningModelName": {
542548
"type": "string",
543-
"order": 14,
549+
"order": 16,
544550
"default": "",
545551
"description": "Optional OpenAI reasoning model name used for specialized reasoning subtasks."
546552
},
547553
"vectorcastTestExplorer.reqs2x.openai.baseUrl": {
548554
"type": "string",
549-
"order": 15,
555+
"order": 17,
550556
"default": "",
551557
"description": "OpenAI base URL (optional, set this to use OpenAI-compatible providers such as Ollama, vLLM or Bedrock)"
552558
},
553559
"vectorcastTestExplorer.reqs2x.azure.baseUrl": {
554560
"type": "string",
555-
"order": 16,
561+
"order": 18,
556562
"default": "",
557563
"description": "Azure OpenAI endpoint/base URL, e.g., https://my-example-instance.openai.azure.com"
558564
},
559565
"vectorcastTestExplorer.reqs2x.azure.apiKey": {
560566
"type": "string",
561-
"order": 17,
567+
"order": 19,
562568
"default": "",
563569
"description": "Azure OpenAI API key"
564570
},
565571
"vectorcastTestExplorer.reqs2x.azure.deployment": {
566572
"type": "string",
567-
"order": 18,
573+
"order": 20,
568574
"default": "",
569575
"description": "Azure OpenAI deployment name, e.g., my-custom-gpt-4o-deployment"
570576
},
571577
"vectorcastTestExplorer.reqs2x.azure.modelName": {
572578
"type": "string",
573-
"order": 19,
579+
"order": 21,
574580
"default": "",
575581
"description": "Azure OpenAI model name, e.g., gpt-4o"
576582
},
577583
"vectorcastTestExplorer.reqs2x.azure.reasoningModelName": {
578584
"type": "string",
579-
"order": 20,
585+
"order": 22,
580586
"default": "",
581587
"description": "Optional Azure OpenAI reasoning model name used for specialized reasoning subtasks."
582588
},
583589
"vectorcastTestExplorer.reqs2x.azure.reasoningDeployment": {
584590
"type": "string",
585-
"order": 21,
591+
"order": 23,
586592
"default": "",
587593
"description": "Azure OpenAI reasoning deployment name (required if Azure reasoning model name is provided)."
588594
},
589595
"vectorcastTestExplorer.reqs2x.azure.apiVersion": {
590596
"type": "string",
591-
"order": 22,
597+
"order": 24,
592598
"default": "2024-12-01-preview",
593599
"description": "Azure OpenAI API version"
594600
},
595601
"vectorcastTestExplorer.reqs2x.anthropic.apiKey": {
596602
"type": "string",
597-
"order": 23,
603+
"order": 25,
598604
"default": "",
599605
"description": "Anthropic API key"
600606
},
601607
"vectorcastTestExplorer.reqs2x.anthropic.modelName": {
602608
"type": "string",
603-
"order": 24,
609+
"order": 26,
604610
"default": "",
605611
"description": "Anthropic model name"
606612
},
607613
"vectorcastTestExplorer.reqs2x.anthropic.reasoningModelName": {
608614
"type": "string",
609-
"order": 25,
615+
"order": 27,
610616
"default": "",
611617
"description": "Optional Anthropic reasoning model name used for specialized reasoning subtasks."
612618
},
613619
"vectorcastTestExplorer.reqs2x.litellm.modelName": {
614620
"type": "string",
615-
"order": 26,
621+
"order": 28,
616622
"default": "",
617623
"description": "LiteLLM model name (prefix with provider used, e.g., openai/gpt-4o or azure/o3-mini)"
618624
},
619625
"vectorcastTestExplorer.reqs2x.litellm.reasoningModelName": {
620626
"type": "string",
621-
"order": 27,
627+
"order": 29,
622628
"default": "",
623629
"description": "Optional LiteLLM reasoning model name used for specialized reasoning subtasks."
624630
},
625631
"vectorcastTestExplorer.reqs2x.litellm.providerEnvVars": {
626632
"type": "string",
627-
"order": 28,
633+
"order": 30,
628634
"default": "",
629635
"description": "Environment variables to set when running LiteLLM, e.g., OPENAI_API_KEY=xxxx. Multiple variables can be separated by commas, e.g., OPENAI_API_KEY=xxxx,ANOTHER_ENV_VAR=yyyy. For a list of variables to set for a specific LiteLLM-compatible provider, see https://docs.litellm.ai/docs/providers."
636+
},
637+
"vectorcastTestExplorer.reqs2x.azureApim.subscriptionKey": {
638+
"type": "string",
639+
"order": 31,
640+
"default": "",
641+
"description": "[Advanced] Azure APIM subscription key (sent as the Ocp-Apim-Subscription-Key header)"
642+
},
643+
"vectorcastTestExplorer.reqs2x.azureApim.baseUrl": {
644+
"type": "string",
645+
"order": 32,
646+
"default": "",
647+
"description": "[Advanced] Azure APIM gateway base URL, e.g., https://some-domain.azure-api.net/v1"
648+
},
649+
"vectorcastTestExplorer.reqs2x.azureApim.modelName": {
650+
"type": "string",
651+
"order": 33,
652+
"default": "",
653+
"description": "[Advanced] Model name for the APIM-proxied LLM, e.g., gpt-4.1"
654+
},
655+
"vectorcastTestExplorer.reqs2x.azureApim.apiKey": {
656+
"type": "string",
657+
"order": 34,
658+
"default": "",
659+
"description": "[Advanced] Optional downstream Azure OpenAI API key (forwarded when the APIM policy requires it)"
660+
},
661+
"vectorcastTestExplorer.reqs2x.azureApim.reasoningModelName": {
662+
"type": "string",
663+
"order": 35,
664+
"default": "",
665+
"description": "[Advanced] Optional reasoning model name used for specialized reasoning subtasks (Azure APIM)."
666+
},
667+
"vectorcastTestExplorer.reqs2x.openaiAt.modelName": {
668+
"type": "string",
669+
"order": 36,
670+
"default": "",
671+
"description": "[Advanced] OpenAI Access Token model name"
672+
},
673+
"vectorcastTestExplorer.reqs2x.openaiAt.modelUrl": {
674+
"type": "string",
675+
"order": 37,
676+
"default": "",
677+
"description": "[Advanced] OpenAI Access Token model URL"
678+
},
679+
"vectorcastTestExplorer.reqs2x.openaiAt.authUrl": {
680+
"type": "string",
681+
"order": 38,
682+
"default": "",
683+
"description": "[Advanced] OpenAI Access Token authentication URL"
684+
},
685+
"vectorcastTestExplorer.reqs2x.openaiAt.appKey": {
686+
"type": "string",
687+
"order": 39,
688+
"default": "",
689+
"description": "[Advanced] OpenAI Access Token application key"
690+
},
691+
"vectorcastTestExplorer.reqs2x.openaiAt.appSecret": {
692+
"type": "string",
693+
"order": 40,
694+
"default": "",
695+
"description": "[Advanced] OpenAI Access Token application secret"
696+
},
697+
"vectorcastTestExplorer.reqs2x.openaiAt.reasoningModelName": {
698+
"type": "string",
699+
"order": 41,
700+
"default": "",
701+
"description": "[Advanced] Optional reasoning model name used for specialized reasoning subtasks (OpenAI Access Token)."
630702
}
631703
}
632704
}

src-common/commonUtilities.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,77 @@ export function getVcastOptionValues(enviroPath: string): cfgOptionType {
8989
export const vpythonSplitString = "ACTUAL-DATA";
9090
export const atgAndClicastSplitString =
9191
"If you want to use VECTORCAST_DIR, use this syntax:";
92+
/**
93+
* Attempt to extract and parse a JSON object or array from a string that may
94+
* contain non-JSON text before or after the actual JSON (e.g. log lines,
95+
* warnings). Returns the parsed value on success, or undefined if no valid
96+
* JSON is found.
97+
*
98+
* The `escape` flag below only handles backslash-escaped quotes (`\"`) so that
99+
* they don't falsely toggle the in-string state. Other escape sequences such
100+
* as multi-character unicode escapes (`\uXXXX`) are not fully parsed, which is
101+
* fine — we only need to track quote boundaries correctly, and `JSON.parse`
102+
* handles the real validation at the end.
103+
*/
104+
export function extractJson(raw: string): any | undefined {
105+
const trimmed = raw.trim();
106+
107+
// Fast path: the whole string is valid JSON
108+
try {
109+
return JSON.parse(trimmed);
110+
} catch {
111+
// Fall through to extraction
112+
}
113+
114+
// Find the first '{' or '[' — whichever comes first
115+
const objStart = trimmed.indexOf("{");
116+
const arrStart = trimmed.indexOf("[");
117+
const start =
118+
objStart === -1
119+
? arrStart
120+
: arrStart === -1
121+
? objStart
122+
: Math.min(objStart, arrStart);
123+
if (start === -1) return undefined;
124+
125+
let depth = 0;
126+
let inString = false;
127+
let escape = false;
128+
129+
for (let i = start; i < trimmed.length; i++) {
130+
const ch = trimmed[i];
131+
if (escape) {
132+
escape = false;
133+
continue;
134+
}
135+
136+
// Deal with escaped quotes so that we don't accidentally toggle inString when we see a \" sequence
137+
if (ch === "\\" && inString) {
138+
escape = true;
139+
continue;
140+
}
141+
142+
if (ch === '"') {
143+
inString = !inString;
144+
continue;
145+
}
146+
if (inString) continue;
147+
if (ch === "{" || ch === "[") depth++;
148+
else if (ch === "}" || ch === "]") {
149+
depth--;
150+
if (depth === 0) {
151+
try {
152+
return JSON.parse(trimmed.substring(start, i + 1));
153+
} catch {
154+
return undefined;
155+
}
156+
}
157+
}
158+
}
159+
160+
return undefined;
161+
}
162+
92163
export function cleanVectorcastOutput(outputString: string): string {
93164
if (outputString.includes(vpythonSplitString)) {
94165
const pieces = outputString.split(vpythonSplitString, 2);

src/requirements/requirementsOperations.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ export async function generateTestsFromRequirements(
378378

379379
const noTestExamples = config.get<boolean>("noTestExamples", false);
380380
const reorder = config.get<boolean>("reorder", true);
381+
const funcDefs = config.get<boolean>("functionDefinitions", true);
381382
const allowUUTStubs = config.get<boolean>("enableUutStubbing", true);
382383

383384
const retries = config.get<number>("retries", 2);
@@ -404,8 +405,9 @@ export async function generateTestsFromRequirements(
404405
"--batched",
405406
...(decomposeRequirements ? [] : ["--no-requirement-decomposition"]),
406407
...(noTestExamples ? ["--no-test-examples"] : []),
407-
...(!reorder ? ["--no-reorder"] : []),
408-
...(allowUUTStubs ? ["--allow-uut-stubs"] : ["--no-allow-uut-stubs"]),
408+
...(reorder ? [] : ["--no-reorder"]),
409+
...(funcDefs ? [] : ["--no-func-defs"]),
410+
...(allowUUTStubs ? [] : ["--no-allow-uut-stubs"]),
409411
"--allow-partial",
410412
"--json-events",
411413
...(enableRequirementKeys ? ["--requirement-keys"] : []),

0 commit comments

Comments
 (0)