Skip to content

Commit

Permalink
feat(ava/advisor): add async and condition type to advisor plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
chenluli committed Jun 17, 2024
1 parent 3d32447 commit 716ab18
Show file tree
Hide file tree
Showing 13 changed files with 135 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,25 @@ import type {
BasicDataPropertyForAdvice,
RuleModule,
AdvisorOptions,
AdvisorPipelineContext,
} from '../../../../types';

export const getChartTypeRecommendations = ({
chartWIKI,
dataProps,
ruleBase,
options,
advisorContext,
}: {
dataProps: BasicDataPropertyForAdvice[];
chartWIKI: Record<string, ChartKnowledge>;
ruleBase: Record<string, RuleModule>;
options?: AdvisorOptions;
advisorContext?: Pick<AdvisorPipelineContext, 'extra'>;
}) => {
const chatTypes = Object.keys(chartWIKI);
const list: ScoringResultForChartType[] = chatTypes.map((chartType) => {
return scoreRules(chartType, chartWIKI, dataProps, ruleBase, options);
return scoreRules(chartType, chartWIKI, dataProps, ruleBase, options, advisorContext);
});

// filter and sorter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ export const chartTypeRecommendPlugin: AdvisorPluginType<ChartTypeRecommendInput
stage: ['chartTypeRecommend'],
execute(input: ChartTypeRecommendInput, context?: AdvisorPipelineContext): ChartTypeRecommendOutput {
const { dataProps } = input;
const { advisor, options } = context || {};
const { advisor, options, extra } = context || {};
const chartTypeRecommendations = getChartTypeRecommendations({
dataProps,
chartWIKI: advisor.ckb,
ruleBase: advisor.ruleBase,
options,
advisorContext: { extra },
});
return { chartTypeRecommendations };
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const specGeneratorPlugin: AdvisorPluginType<SpecGeneratorInput, SpecGene
const { chartType } = chartTypeAdvice;
const chartKnowledge = advisor.ckb[chartType];
const chartTypeSpec =
chartKnowledge?.toSpec(data, dataProps) ??
chartKnowledge?.toSpec?.(data, dataProps) ??
getChartTypeSpec({
chartType,
data,
Expand All @@ -35,7 +35,7 @@ export const specGeneratorPlugin: AdvisorPluginType<SpecGeneratorInput, SpecGene

// step 3: apply spec processors such as design rules, theme, color, to improve spec
if (chartTypeSpec && refine) {
const partEncSpec = applyDesignRules(chartType, dataProps, advisor.ruleBase, chartTypeSpec);
const partEncSpec = applyDesignRules(chartType, dataProps, advisor.ruleBase, chartTypeSpec, context);
deepMix(chartTypeSpec, partEncSpec);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import { BasicDataPropertyForAdvice, RuleModule, Specification, DesignRuleModule } from '../../../../../types';
import {
BasicDataPropertyForAdvice,
RuleModule,
Specification,
DesignRuleModule,
AdvisorPipelineContext,
} from '../../../../../types';
import { deepMix } from '../../../../../utils';

export function applyDesignRules(
chartType: string,
dataProps: BasicDataPropertyForAdvice[],
ruleBase: Record<string, RuleModule>,
chartSpec: Specification
chartSpec: Specification,
advisorContext?: Pick<AdvisorPipelineContext, 'extra'>
) {
const toCheckRules = Object.values(ruleBase).filter(
(rule: RuleModule) =>
rule.type === 'DESIGN' && rule.trigger({ dataProps, chartType }) && !ruleBase[rule.id].option?.off
rule.type === 'DESIGN' && rule.trigger({ dataProps, chartType, advisorContext }) && !ruleBase[rule.id].option?.off
);
const encodingSpec = toCheckRules.reduce((lastSpec, rule: RuleModule) => {
const relatedSpec = (rule as DesignRuleModule).optimizer(dataProps, chartSpec);
const relatedSpec = (rule as DesignRuleModule).optimizer(dataProps, chartSpec, advisorContext);
return deepMix(lastSpec, relatedSpec);
}, {});
return encodingSpec;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { AdvisorPluginType, VisualEncoderInput } from '../../../../types';
import type { AdvisorPluginType, VisualEncoderInput, VisualEncoderOutput } from '../../../../types';

export const visualEncoderPlugin: AdvisorPluginType<VisualEncoderInput, VisualEncoderInput> = {
export const visualEncoderPlugin: AdvisorPluginType<VisualEncoderInput, VisualEncoderOutput> = {
name: 'defaultVisualEncoder',
stage: ['encode'],
execute: (input) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Info, RuleModule } from '../../ruler';
import { DEFAULT_RULE_WEIGHTS } from '../../constants';

import type { ScoringResultForRule } from '../../types';
import type { AdvisorPipelineContext, ScoringResultForRule } from '../../types';
import type { ChartRuleModule } from '../../ruler/types';
import type { ChartId, ChartKnowledgeBase } from '../../../ckb';

Expand All @@ -13,20 +13,32 @@ export const computeScore = (
ruleBase: Record<string, RuleModule>,
ruleType: 'HARD' | 'SOFT',
info: Info,
log: ScoringResultForRule[]
log: ScoringResultForRule[],
advisorContext?: Pick<AdvisorPipelineContext, 'extra'>
) => {
// initial score is 1 for HARD rules and 0 for SOFT rules
let computedScore = 1;
Object.values(ruleBase)
.filter((r: RuleModule) => {
const weight = r.option?.weight || defaultWeights[r.id] || 1;
const extra = r.option?.extra;
return r.type === ruleType && r.trigger({ ...info, weight, ...extra, chartType, chartWIKI }) && !r.option?.off;
return (
r.type === ruleType &&
r.trigger({ ...info, weight, ...extra, chartType, chartWIKI, advisorContext }) &&
!r.option?.off
);
})
.forEach((r: RuleModule) => {
const weight = r.option?.weight || defaultWeights[r.id] || 1;
const extra = r.option?.extra;
const base = (r as ChartRuleModule).validator({ ...info, weight, ...extra, chartType, chartWIKI }) as number;
const base = (r as ChartRuleModule).validator({
...info,
weight,
...extra,
chartType,
chartWIKI,
advisorContext,
}) as number;
const score = weight * base;
computedScore *= score;
log.push({ phase: 'ADVISE', ruleId: r.id, score, base, weight, ruleType });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
AdvisorOptions,
ScoringResultForChartType,
ScoringResultForRule,
AdvisorPipelineContext,
} from '../../types';

import { computeScore } from './compute-score';
Expand All @@ -24,7 +25,8 @@ export function scoreRules(
chartWIKI: ChartKnowledgeBase,
dataProps: BasicDataPropertyForAdvice[],
ruleBase: Record<string, RuleModule>,
options?: AdvisorOptions
options?: AdvisorOptions,
advisorContext?: Pick<AdvisorPipelineContext, 'extra'>
): ScoringResultForChartType {
const purpose = options ? options.purpose : '';
const preferences = options ? options.preferences : undefined;
Expand All @@ -34,15 +36,15 @@ export function scoreRules(

const info = { dataProps, chartType, purpose, preferences };

const hardScore = computeScore(chartType, chartWIKI, ruleBase, 'HARD', info, log);
const hardScore = computeScore(chartType, chartWIKI, ruleBase, 'HARD', info, log, advisorContext);

// Hard-Rule pruning
if (hardScore === 0) {
const result: ScoringResultForChartType = { chartType, score: 0, log };
return result;
}

const softScore = computeScore(chartType, chartWIKI, ruleBase, 'SOFT', info, log);
const softScore = computeScore(chartType, chartWIKI, ruleBase, 'SOFT', info, log, advisorContext);

const score = hardScore * softScore;

Expand Down
35 changes: 25 additions & 10 deletions packages/ava/src/advisor/advisor.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { forEach, size } from 'lodash';

import { ckb } from '../ckb';

import { processRuleCfg } from './ruler';
import { dataToAdvices } from './advise-pipeline';
import { checkRules } from './lint-pipeline/check-rules';
import { BaseComponent } from './pipeline/component';
import { Pipeline } from './pipeline/pipeline';
import { dataProcessorPlugin, specGeneratorPlugin, chartTypeRecommendPlugin } from './advise-pipeline/plugin';
import {
dataProcessorPlugin,
specGeneratorPlugin,
chartTypeRecommendPlugin,
visualEncoderPlugin,
} from './advise-pipeline/plugin';

import type { ChartKnowledgeBase } from '../ckb';
import type { RuleModule } from './ruler';
Expand Down Expand Up @@ -51,7 +58,7 @@ export class Advisor {

context: AdvisorPipelineContext;

plugins: AdvisorPluginType[];
plugins: AdvisorPluginType[] = [];

pipeline: Pipeline;

Expand All @@ -73,7 +80,7 @@ export class Advisor {
this.context = { advisor: this, extra };
this.initDefaultComponents();
const defaultComponents = [this.dataAnalyzer, this.chartTypeRecommender, this.chartEncoder, this.specGenerator];
this.plugins = plugins;
this.registerPlugins(plugins);
this.pipeline = new Pipeline({ components: components ?? defaultComponents });
}

Expand All @@ -83,7 +90,7 @@ export class Advisor {
plugins: [chartTypeRecommendPlugin],
context: this.context,
});
// this.chartEncoder = new BaseComponent('chartEncode', { plugins: [visualEncoderPlugin], context: this.context });
this.chartEncoder = new BaseComponent('chartEncode', { plugins: [visualEncoderPlugin], context: this.context });
this.specGenerator = new BaseComponent('specGenerate', { plugins: [specGeneratorPlugin], context: this.context });
}

Expand All @@ -93,6 +100,11 @@ export class Advisor {
return adviseResult.advices;
}

adviseWithLog(params: AdviseParams): AdviseResult {
const adviseResult = dataToAdvices({ adviseParams: params, ckb: this.ckb, ruleBase: this.ruleBase });
return adviseResult;
}

async adviseAsync(params: AdviseParams): Promise<Advice[]> {
this.context = {
...this.context,
Expand All @@ -103,10 +115,7 @@ export class Advisor {
return adviseResult.advices;
}

adviseWithLog(params: AdviseParams): AdviseResult {
const adviseResult = dataToAdvices({ adviseParams: params, ckb: this.ckb, ruleBase: this.ruleBase });
return adviseResult;
}
// todo 补充 adviseAsyncWithLog

lint(params: LintParams): Lint[] {
const lintResult = checkRules(params, this.ruleBase, this.ckb);
Expand All @@ -118,18 +127,24 @@ export class Advisor {
return lintResult;
}

registerPlugins(plugins: AdvisorPluginType[]) {
registerPlugins(plugins: AdvisorPluginType[] = []) {
const stage2Components: Record<PipelineStageType, BaseComponent> = {
dataAnalyze: this.dataAnalyzer,
chartTypeRecommend: this.chartTypeRecommender,
encode: this.chartEncoder,
specGenerate: this.specGenerator,
};

plugins.forEach((plugin) => {
forEach(plugins, (plugin) => {
this.plugins.push(plugin);
if (typeof plugin.stage === 'string') {
const pipelineComponent = stage2Components[plugin.stage];
pipelineComponent.registerPlugin(plugin);
return;
}
if (size(plugin.stage) === 1) {
const pipelineComponent = stage2Components[plugin.stage[0]];
pipelineComponent.registerPlugin(plugin);
}
});
}
Expand Down
19 changes: 14 additions & 5 deletions packages/ava/src/advisor/lint-pipeline/lintRules.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Info, RuleModule } from '../ruler';

import type { Specification } from '../../common/types';
import type { ScoringResultForRule, Lint } from '../types';
import type { ScoringResultForRule, Lint, AdvisorPipelineContext } from '../types';
import type { ChartRuleModule, DesignRuleModule } from '../ruler';
import type { ChartKnowledgeBase } from '../../ckb';

Expand All @@ -12,7 +12,8 @@ export function lintRules(
log: ScoringResultForRule[],
lints: Lint[],
ckb: ChartKnowledgeBase,
spec?: Specification
spec?: Specification,
advisorContext?: Pick<AdvisorPipelineContext, 'extra'>
) {
const judge = (type: 'HARD' | 'SOFT' | 'DESIGN') => {
if (ruleTypeToLint === 'DESIGN') {
Expand All @@ -24,20 +25,28 @@ export function lintRules(
Object.values(ruleBase)
.filter((r: RuleModule) => {
const { weight, extra } = r.option || {};
return judge(r.type) && !r.option?.off && r.trigger({ ...info, weight, ...extra, chartWIKI: ckb });
return (
judge(r.type) && !r.option?.off && r.trigger({ ...info, weight, ...extra, chartWIKI: ckb, advisorContext })
);
})
.forEach((r: RuleModule) => {
const { type, id, docs } = r;
let score: number;
if (ruleTypeToLint === 'DESIGN') {
const fix = (r as DesignRuleModule).optimizer(info.dataProps, spec);
const fix = (r as DesignRuleModule).optimizer(info.dataProps, spec, advisorContext);
// no fix -> means no violation
score = Object.keys(fix).length === 0 ? 1 : 0;
lints.push({ type, id, score, fix, docs });
} else {
const { weight, extra } = r.option || {};
// no weight for linter's result
score = (r as ChartRuleModule).validator({ ...info, weight, ...extra, chartWIKI: ckb }) as number;
score = (r as ChartRuleModule).validator({
...info,
weight,
...extra,
chartWIKI: ckb,
advisorContext,
}) as number;
lints.push({ type, id, score, docs });
}
log.push({ phase: 'LINT', ruleId: id, score, base: score, weight: 1, ruleType: type });
Expand Down
Loading

0 comments on commit 716ab18

Please sign in to comment.