Skip to content

Commit ada62ff

Browse files
committed
Convert yaml.ts to handle multiple models per method
This changes YAML parsing/creating functions for the model editor to handle multiple models per method. The changes in the actual YAML handling are fairly small because the format itself already supports multiple models per method. I've introduced a few helper functions to convert between the old and new types. This should only be necessary while we're in the middle of the transition to the new types and can be removed later. For now, we'll just take the first model in the array when converting from the new to the old type. This is a change in the behavior since currently we always take the last model in the array but this behavior is undocumented and unsupported, so it should be fine to change it.
1 parent 16af4c4 commit ada62ff

File tree

5 files changed

+299
-212
lines changed

5 files changed

+299
-212
lines changed

extensions/ql-vscode/src/model-editor/auto-modeler.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { QueryRunner } from "../query-server";
1616
import { DatabaseItem } from "../databases/local-databases";
1717
import { Mode } from "./shared/mode";
1818
import { CancellationTokenSource } from "vscode";
19+
import { convertToLegacyModeledMethods } from "./modeled-methods-legacy";
1920

2021
// Limit the number of candidates we send to the model in each request
2122
// to avoid long requests.
@@ -192,11 +193,13 @@ export class AutoModeler {
192193
filename: "auto-model.yml",
193194
});
194195

195-
const loadedMethods = loadDataExtensionYaml(models);
196-
if (!loadedMethods) {
196+
const rawLoadedMethods = loadDataExtensionYaml(models);
197+
if (!rawLoadedMethods) {
197198
return;
198199
}
199200

201+
const loadedMethods = convertToLegacyModeledMethods(rawLoadedMethods);
202+
200203
// Any candidate that was part of the response is a negative result
201204
// meaning that the canidate is not a sink for the kinds that the LLM is checking for.
202205
// For now we model this as a sink neutral method, however this is subject

extensions/ql-vscode/src/model-editor/modeled-method-fs.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
1010
import { load as loadYaml } from "js-yaml";
1111
import { CodeQLCliServer } from "../codeql-cli/cli";
1212
import { pathsEqual } from "../common/files";
13+
import {
14+
convertFromLegacyModeledMethods,
15+
convertFromLegacyModeledMethodsFiles,
16+
convertToLegacyModeledMethods,
17+
} from "./modeled-methods-legacy";
1318

1419
export async function saveModeledMethods(
1520
extensionPack: ExtensionPack,
@@ -29,8 +34,8 @@ export async function saveModeledMethods(
2934
const yamls = createDataExtensionYamls(
3035
language,
3136
methods,
32-
modeledMethods,
33-
existingModeledMethods,
37+
convertFromLegacyModeledMethods(modeledMethods),
38+
convertFromLegacyModeledMethodsFiles(existingModeledMethods),
3439
mode,
3540
);
3641

@@ -68,7 +73,8 @@ async function loadModeledMethodFiles(
6873
);
6974
continue;
7075
}
71-
modeledMethodsByFile[modelFile] = modeledMethods;
76+
modeledMethodsByFile[modelFile] =
77+
convertToLegacyModeledMethods(modeledMethods);
7278
}
7379

7480
return modeledMethodsByFile;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { ModeledMethod } from "./modeled-method";
2+
3+
export function convertFromLegacyModeledMethods(
4+
modeledMethods: Record<string, ModeledMethod>,
5+
): Record<string, ModeledMethod[]> {
6+
// Convert a single ModeledMethod to an array of ModeledMethods
7+
return Object.fromEntries(
8+
Object.entries(modeledMethods).map(([signature, modeledMethod]) => {
9+
return [signature, [modeledMethod]];
10+
}),
11+
);
12+
}
13+
14+
export function convertToLegacyModeledMethods(
15+
modeledMethods: Record<string, ModeledMethod[]>,
16+
): Record<string, ModeledMethod> {
17+
// Always take the first modeled method in the array
18+
return Object.fromEntries(
19+
Object.entries(modeledMethods).map(([signature, modeledMethods]) => {
20+
return [signature, modeledMethods[0]];
21+
}),
22+
);
23+
}
24+
25+
export function convertFromLegacyModeledMethodsFiles(
26+
modeledMethods: Record<string, Record<string, ModeledMethod>>,
27+
): Record<string, Record<string, ModeledMethod[]>> {
28+
return Object.fromEntries(
29+
Object.entries(modeledMethods).map(([filename, modeledMethods]) => {
30+
return [filename, convertFromLegacyModeledMethods(modeledMethods)];
31+
}),
32+
);
33+
}

extensions/ql-vscode/src/model-editor/yaml.ts

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ ${extensions.join("\n")}`;
7171
export function createDataExtensionYamls(
7272
language: string,
7373
methods: Method[],
74-
newModeledMethods: Record<string, ModeledMethod>,
75-
existingModeledMethods: Record<string, Record<string, ModeledMethod>>,
74+
newModeledMethods: Record<string, ModeledMethod[]>,
75+
existingModeledMethods: Record<string, Record<string, ModeledMethod[]>>,
7676
mode: Mode,
7777
) {
7878
switch (mode) {
@@ -98,11 +98,11 @@ export function createDataExtensionYamls(
9898
function createDataExtensionYamlsByGrouping(
9999
language: string,
100100
methods: Method[],
101-
newModeledMethods: Record<string, ModeledMethod>,
102-
existingModeledMethods: Record<string, Record<string, ModeledMethod>>,
101+
newModeledMethods: Record<string, ModeledMethod[]>,
102+
existingModeledMethods: Record<string, Record<string, ModeledMethod[]>>,
103103
createFilename: (method: Method) => string,
104104
): Record<string, string> {
105-
const methodsByFilename: Record<string, Record<string, ModeledMethod>> = {};
105+
const methodsByFilename: Record<string, Record<string, ModeledMethod[]>> = {};
106106

107107
// We only want to generate a yaml file when it's a known external API usage
108108
// and there are new modeled methods for it. This avoids us overwriting other
@@ -114,21 +114,25 @@ function createDataExtensionYamlsByGrouping(
114114
}
115115

116116
// First populate methodsByFilename with any existing modeled methods.
117-
for (const [filename, methods] of Object.entries(existingModeledMethods)) {
117+
for (const [filename, methodsBySignature] of Object.entries(
118+
existingModeledMethods,
119+
)) {
118120
if (filename in methodsByFilename) {
119-
for (const [signature, method] of Object.entries(methods)) {
120-
methodsByFilename[filename][signature] = method;
121+
for (const [signature, methods] of Object.entries(methodsBySignature)) {
122+
methodsByFilename[filename][signature] = methods;
121123
}
122124
}
123125
}
124126

125127
// Add the new modeled methods, potentially overwriting existing modeled methods
126128
// but not removing existing modeled methods that are not in the new set.
127129
for (const method of methods) {
128-
const newMethod = newModeledMethods[method.signature];
129-
if (newMethod) {
130+
const newMethods = newModeledMethods[method.signature];
131+
if (newMethods) {
130132
const filename = createFilename(method);
131-
methodsByFilename[filename][newMethod.signature] = newMethod;
133+
134+
// Override any existing modeled methods with the new ones.
135+
methodsByFilename[filename][method.signature] = newMethods;
132136
}
133137
}
134138

@@ -137,7 +141,7 @@ function createDataExtensionYamlsByGrouping(
137141
for (const [filename, methods] of Object.entries(methodsByFilename)) {
138142
result[filename] = createDataExtensionYaml(
139143
language,
140-
Object.values(methods),
144+
Object.values(methods).flatMap((methods) => methods),
141145
);
142146
}
143147

@@ -147,8 +151,8 @@ function createDataExtensionYamlsByGrouping(
147151
export function createDataExtensionYamlsForApplicationMode(
148152
language: string,
149153
methods: Method[],
150-
newModeledMethods: Record<string, ModeledMethod>,
151-
existingModeledMethods: Record<string, Record<string, ModeledMethod>>,
154+
newModeledMethods: Record<string, ModeledMethod[]>,
155+
existingModeledMethods: Record<string, Record<string, ModeledMethod[]>>,
152156
): Record<string, string> {
153157
return createDataExtensionYamlsByGrouping(
154158
language,
@@ -162,8 +166,8 @@ export function createDataExtensionYamlsForApplicationMode(
162166
export function createDataExtensionYamlsForFrameworkMode(
163167
language: string,
164168
methods: Method[],
165-
newModeledMethods: Record<string, ModeledMethod>,
166-
existingModeledMethods: Record<string, Record<string, ModeledMethod>>,
169+
newModeledMethods: Record<string, ModeledMethod[]>,
170+
existingModeledMethods: Record<string, Record<string, ModeledMethod[]>>,
167171
): Record<string, string> {
168172
return createDataExtensionYamlsByGrouping(
169173
language,
@@ -228,14 +232,14 @@ function validateModelExtensionFile(data: unknown): data is ModelExtensionFile {
228232

229233
export function loadDataExtensionYaml(
230234
data: unknown,
231-
): Record<string, ModeledMethod> | undefined {
235+
): Record<string, ModeledMethod[]> | undefined {
232236
if (!validateModelExtensionFile(data)) {
233237
return undefined;
234238
}
235239

236240
const extensions = data.extensions;
237241

238-
const modeledMethods: Record<string, ModeledMethod> = {};
242+
const modeledMethods: Record<string, ModeledMethod[]> = {};
239243

240244
for (const extension of extensions) {
241245
const addsTo = extension.addsTo;
@@ -250,11 +254,16 @@ export function loadDataExtensionYaml(
250254
}
251255

252256
for (const row of data) {
253-
const modeledMethod = definition.readModeledMethod(row);
257+
const modeledMethod: ModeledMethod = definition.readModeledMethod(row);
254258
if (!modeledMethod) {
255259
continue;
256260
}
257-
modeledMethods[modeledMethod.signature] = modeledMethod;
261+
262+
if (!(modeledMethod.signature in modeledMethods)) {
263+
modeledMethods[modeledMethod.signature] = [];
264+
}
265+
266+
modeledMethods[modeledMethod.signature].push(modeledMethod);
258267
}
259268
}
260269

0 commit comments

Comments
 (0)