Skip to content

Commit

Permalink
chore: no implicit any linter rule (#954)
Browse files Browse the repository at this point in the history
* chore: no implicit any linter rule

Signed-off-by: axel7083 <[email protected]>

* fix: tests

Signed-off-by: axel7083 <[email protected]>

* Update packages/backend/src/models/AIConfig.ts

Co-authored-by: Florent BENOIT <[email protected]>
Signed-off-by: axel7083 <[email protected]>

---------

Signed-off-by: axel7083 <[email protected]>
Co-authored-by: Florent BENOIT <[email protected]>
  • Loading branch information
axel7083 and benoitf authored Apr 23, 2024
1 parent 2c8b818 commit 8136608
Show file tree
Hide file tree
Showing 8 changed files with 265 additions and 55 deletions.
3 changes: 2 additions & 1 deletion packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"@types/js-yaml": "^4.0.9",
"@types/node": "^20",
"@types/postman-collection": "^3.5.10",
"vitest": "^1.5.0"
"vitest": "^1.5.0",
"@types/mustache": "^4.2.5"
}
}
2 changes: 1 addition & 1 deletion packages/backend/src/managers/applicationManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ export class ApplicationManager extends Publisher<ApplicationState[]> implements
}

// create new pod
const labels = {
const labels: Record<string, string> = {
[LABEL_RECIPE_ID]: recipe.id,
[LABEL_MODEL_ID]: model.id,
};
Expand Down
4 changes: 2 additions & 2 deletions packages/backend/src/managers/playgroundV2Manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class PlaygroundV2Manager implements Disposable {
trackingId: trackingId,
});

const telemetry = {
const telemetry: Record<string, unknown> = {
hasName: !!name,
hasSystemPrompt: !!systemPrompt,
modelId: model.id,
Expand Down Expand Up @@ -221,7 +221,7 @@ export class PlaygroundV2Manager implements Disposable {

if (!modelInfo.file?.file) throw new Error('model info has undefined file.');

const telemetry = {
const telemetry: Record<string, unknown> = {
conversationId: conversationId,
...options,
promptLength: userInput.length,
Expand Down
209 changes: 200 additions & 9 deletions packages/backend/src/models/AIConfig.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,205 @@ import { type AIConfig, parseYamlFile } from './AIConfig';

// Define mock file paths and contents
const mockYamlPath = '/path/to/mock.yml';
const mockYamlContent = `
const defaultArch = 'x64';

const readFileSync = vi.spyOn(fs, 'readFileSync');

describe('parseYaml', () => {
test('malformed configuration', () => {
readFileSync.mockReturnValue(``);
expect(() => {
parseYamlFile(mockYamlPath, defaultArch);
}).toThrowError('malformed configuration file.');
});

test('missing application property', () => {
readFileSync.mockReturnValue(`
wrong:
`);
expect(() => {
parseYamlFile(mockYamlPath, defaultArch);
}).toThrowError('AIConfig has bad formatting: missing application property');
});

test('application primitive', () => {
readFileSync.mockReturnValue(`
application: true
`);
expect(() => {
parseYamlFile(mockYamlPath, defaultArch);
}).toThrowError('AIConfig has bad formatting: application does not have valid container property');
});

test('containers not an array', () => {
readFileSync.mockReturnValue(`
application:
containers:
name: container1
contextdir: /path/to/dir1
arch: ["x86"]
model-service: true
gpu-env: ["env1", "env2"]
ports: [ 8080 ]
`);
expect(() => {
parseYamlFile(mockYamlPath, defaultArch);
}).toThrowError('AIConfig has bad formatting: containers property must be an array.');
});

test('containers object', () => {
readFileSync.mockReturnValue(`
application:
containers: true
`);
expect(() => {
parseYamlFile(mockYamlPath, defaultArch);
}).toThrowError('AIConfig has bad formatting: containers property must be an array.');
});

test('should use architecture as string', () => {
readFileSync.mockReturnValue(`
application:
containers:
- name: container1
contextdir: /path/to/dir1
arch: x86
ports: [ 8080 ]
`);

const expectedConfig: AIConfig = {
application: {
containers: [
{
name: 'container1',
contextdir: '/path/to/dir1',
arch: ['x86'],
gpu_env: [],
modelService: false,
ports: [8080],
},
],
},
};

expect(parseYamlFile(mockYamlPath, defaultArch)).toEqual(expectedConfig);
});

test('should use all architectures', () => {
readFileSync.mockReturnValue(`
application:
containers:
- name: container1
contextdir: /path/to/dir1
arch: ['arch1', 'arch2']
ports: [ 8080 ]
`);

const expectedConfig: AIConfig = {
application: {
containers: [
{
name: 'container1',
contextdir: '/path/to/dir1',
arch: ['arch1', 'arch2'],
gpu_env: [],
modelService: false,
ports: [8080],
},
],
},
};

expect(parseYamlFile(mockYamlPath, defaultArch)).toEqual(expectedConfig);
});

test('should put the default architecture', () => {
readFileSync.mockReturnValue(`
application:
containers:
- name: container1
contextdir: /path/to/dir1
ports: [ 8080 ]
`);

const expectedConfig: AIConfig = {
application: {
containers: [
{
name: 'container1',
contextdir: '/path/to/dir1',
arch: [defaultArch],
gpu_env: [],
modelService: false,
ports: [8080],
},
],
},
};

expect(parseYamlFile(mockYamlPath, defaultArch)).toEqual(expectedConfig);
});

test('should use the image provided in the config', () => {
readFileSync.mockReturnValue(`
application:
containers:
- name: container1
contextdir: /path/to/dir1
ports: [ 8080 ]
image: dummy-image
`);

const expectedConfig: AIConfig = {
application: {
containers: [
{
name: 'container1',
contextdir: '/path/to/dir1',
arch: [defaultArch],
gpu_env: [],
modelService: false,
ports: [8080],
image: 'dummy-image',
},
],
},
};

expect(parseYamlFile(mockYamlPath, defaultArch)).toEqual(expectedConfig);
});

test('ports should always be a final number', () => {
readFileSync.mockReturnValue(`
application:
containers:
- name: container1
contextdir: /path/to/dir1
ports: [ '8080', 8888 ]
image: dummy-image
`);

const expectedConfig: AIConfig = {
application: {
containers: [
{
name: 'container1',
contextdir: '/path/to/dir1',
arch: [defaultArch],
gpu_env: [],
modelService: false,
ports: [8080, 8888],
image: 'dummy-image',
},
],
},
};

expect(parseYamlFile(mockYamlPath, defaultArch)).toEqual(expectedConfig);
});

test('should use gpu env', () => {
readFileSync.mockReturnValue(`
application:
containers:
- name: container1
Expand All @@ -34,15 +232,8 @@ application:
- name: container2
arch: ["arm"]
ports: [ 8001 ]
`;

const readFileSync = vi.spyOn(fs, 'readFileSync');

describe('parseYaml', () => {
test('should parse valid YAML file', () => {
readFileSync.mockReturnValue(mockYamlContent);
`);

const defaultArch = 'x64';
const expectedConfig: AIConfig = {
application: {
containers: [
Expand Down
59 changes: 48 additions & 11 deletions packages/backend/src/models/AIConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,25 @@ export function assertString(value: unknown): string {
export function parseYamlFile(filepath: string, defaultArch: string): AIConfig {
const raw: string = fs.readFileSync(filepath, 'utf-8');

const aiLabConfig = jsYaml.load(raw);
const application = aiLabConfig?.['application'];
if (!application) throw new Error('AIConfig has bad formatting.');
const aiLabConfig: unknown = jsYaml.load(raw);
if (!aiLabConfig || typeof aiLabConfig !== 'object') {
throw new Error('malformed configuration file.');
}

const containers: unknown[] = application['containers'] ?? [];
if (!('application' in aiLabConfig)) {
throw new Error('AIConfig has bad formatting: missing application property');
}

const application: unknown = aiLabConfig['application'];
if (!application || typeof application !== 'object' || !('containers' in application)) {
throw new Error('AIConfig has bad formatting: application does not have valid container property');
}

if (!Array.isArray(application['containers'])) {
throw new Error('AIConfig has bad formatting: containers property must be an array.');
}

const containers: unknown[] = application['containers'];

return {
application: {
Expand All @@ -70,15 +84,38 @@ export function parseYamlFile(filepath: string, defaultArch: string): AIConfig {
contextdir = '.';
}

const architectures: string[] = [];
if (!('arch' in container)) {
architectures.push(defaultArch);
} else if (Array.isArray(container['arch']) && container['arch'].every(arch => typeof arch === 'string')) {
architectures.push(...container['arch']);
} else if (typeof container['arch'] === 'string') {
architectures.push(container['arch']);
} else {
throw new Error('malformed arch property');
}

let containerfile: string | undefined = undefined;
if ('containerfile' in container && isString(container['containerfile'])) {
containerfile = container['containerfile'];
}

if (!('name' in container) || typeof container['name'] !== 'string') {
throw new Error('invalid name property: must be string');
}

return {
arch: Array.isArray(container['arch']) ? container['arch'] : [defaultArch],
modelService: container['model-service'] === true,
containerfile: isString(container['containerfile']) ? container['containerfile'] : undefined,
arch: architectures,
modelService: 'model-service' in container && container['model-service'] === true,
containerfile,
contextdir: contextdir,
name: assertString(container['name']),
gpu_env: Array.isArray(container['gpu-env']) ? container['gpu-env'] : [],
ports: Array.isArray(container['ports']) ? container['ports'] : [],
image: isString(container['image']) ? container['image'] : undefined,
name: container['name'],
gpu_env: 'gpu-env' in container && Array.isArray(container['gpu-env']) ? container['gpu-env'] : [],
ports:
'ports' in container && Array.isArray(container['ports'])
? container['ports'].map(port => parseInt(port))
: [],
image: 'image' in container && isString(container['image']) ? container['image'] : undefined,
};
}),
},
Expand Down
4 changes: 2 additions & 2 deletions packages/backend/src/studio-api-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export class StudioApiImpl implements StudioAPI {
}

async openFile(file: string, recipeId?: string): Promise<boolean> {
const telemetry = {
const telemetry: Record<string, unknown> = {
'recipe.id': recipeId,
};
try {
Expand Down Expand Up @@ -393,7 +393,7 @@ export class StudioApiImpl implements StudioAPI {
}

async openVSCode(directory: string, recipeId?: string): Promise<void> {
const telemetry = {
const telemetry: Record<string, unknown> = {
'recipe.id': recipeId,
};

Expand Down
3 changes: 2 additions & 1 deletion packages/backend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"alwaysStrict": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"noImplicitThis": true
"noImplicitThis": true,
"noImplicitAny": true
},
"include": [
"src",
Expand Down
Loading

0 comments on commit 8136608

Please sign in to comment.