Skip to content

Commit

Permalink
HParams: Create hparams effects to trigger data to be fetched from th…
Browse files Browse the repository at this point in the history
…e hparams plugin (#6540)

## Motivation for features / changes
This is a natural continuation of the work done in #6535

I added a new effects file which fires a newly created action whenever
the runs table is shown, a navigation occurs, or the user reloads the
data.

The data loading is then filtered and throttled by the existence of
experiment ids.

Finally the raw response from the `hparams_data_source` is written to
the redux state. In a future PR I will add a series of selectors to
remap and inject that data into the runs metadata.

## Alternate designs / implementations considered (or N/A)
I've opted to include the effects and data source in the hparams module
but hide the functionality behind the feature flag. This should make it
easier to develop changes downstream from this pr. In the future I will
gate the existing behavior behind the inverse of this flag and that
should allow us to test the full flow without needing to remove the old
code (I have tested removing the old code).

The reducer change could have been split out from this but it's quite
small and I thought including it made it easier to see the entire write
path.

Because I added two new attributes to the hparams redux state there are
a few changes to test utilities that are not strictly necessary but are
useful to have.
  • Loading branch information
rileyajones authored Aug 14, 2023
1 parent 4669e6d commit d4df603
Show file tree
Hide file tree
Showing 13 changed files with 597 additions and 97 deletions.
40 changes: 40 additions & 0 deletions tensorboard/webapp/hparams/_redux/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ tf_ng_module(
"hparams_module.ts",
],
deps = [
":hparams_data_source",
":hparams_effects",
":hparams_reducers",
":types",
"//tensorboard/webapp/hparams:types",
"@npm//@angular/core",
"@npm//@ngrx/effects",
"@npm//@ngrx/store",
],
)
Expand Down Expand Up @@ -95,6 +98,31 @@ tf_ng_module(
],
)

tf_ng_module(
name = "hparams_effects",
srcs = [
"hparams_effects.ts",
],
deps = [
":hparams_actions",
":hparams_data_source",
":types",
"//tensorboard/webapp:app_state",
"//tensorboard/webapp/app_routing:types",
"//tensorboard/webapp/app_routing/actions",
"//tensorboard/webapp/app_routing/store",
"//tensorboard/webapp/core/actions",
"//tensorboard/webapp/feature_flag/store",
"//tensorboard/webapp/hparams:types",
"//tensorboard/webapp/runs/actions",
"//tensorboard/webapp/webapp_data_source:http_client",
"@npm//@angular/core",
"@npm//@ngrx/effects",
"@npm//@ngrx/store",
"@npm//rxjs",
],
)

tf_ts_library(
name = "testing",
testonly = True,
Expand All @@ -105,6 +133,7 @@ tf_ts_library(
":types",
":utils",
"//tensorboard/webapp/hparams:_types",
"//tensorboard/webapp/util:types",
],
)

Expand All @@ -113,6 +142,7 @@ tf_ts_library(
testonly = True,
srcs = [
"hparams_data_source_test.ts",
"hparams_effects_test.ts",
"hparams_reducers_test.ts",
"hparams_selectors_test.ts",
"hparams_selectors_utils_test.ts",
Expand All @@ -121,16 +151,26 @@ tf_ts_library(
deps = [
":hparams_actions",
":hparams_data_source",
":hparams_effects",
":hparams_reducers",
":hparams_selectors",
":testing",
":utils",
"//tensorboard/webapp:app_state",
"//tensorboard/webapp:selectors",
"//tensorboard/webapp/angular:expect_angular_core_testing",
"//tensorboard/webapp/angular:expect_ngrx_store_testing",
"//tensorboard/webapp/app_routing:types",
"//tensorboard/webapp/app_routing/actions",
"//tensorboard/webapp/core/actions",
"//tensorboard/webapp/hparams:types",
"//tensorboard/webapp/runs/actions",
"//tensorboard/webapp/runs/data_source:testing",
"//tensorboard/webapp/runs/store:testing",
"//tensorboard/webapp/testing:utils",
"//tensorboard/webapp/webapp_data_source:http_client_testing",
"@npm//@ngrx/effects",
"@npm//@types/jasmine",
"@npm//rxjs",
],
)
14 changes: 13 additions & 1 deletion tensorboard/webapp/hparams/_redux/hparams_actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ limitations under the License.
*/

import {createAction, props} from '@ngrx/store';
import {DiscreteHparamValues} from '../types';
import {
DiscreteHparamValues,
HparamAndMetricSpec,
SessionGroup,
} from '../types';

export const hparamsDiscreteHparamFilterChanged = createAction(
'[Hparams] Hparams Discrete Hparam Filter Changed',
Expand Down Expand Up @@ -50,3 +54,11 @@ export const hparamsMetricFilterChanged = createAction(
includeUndefined: boolean;
}>()
);

export const hparamsFetchSessionGroupsSucceeded = createAction(
'[Hparams] Hparams Fetch Session Groups Succeeded',
props<{
hparamsAndMetricsSpecs: HparamAndMetricSpec;
sessionGroups: SessionGroup[];
}>()
);
117 changes: 117 additions & 0 deletions tensorboard/webapp/hparams/_redux/hparams_effects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
import {Injectable} from '@angular/core';
import {Actions, createEffect, ofType} from '@ngrx/effects';
import {Store} from '@ngrx/store';
import {Observable, of, throwError, merge} from 'rxjs';
import {
catchError,
filter,
map,
switchMap,
withLatestFrom,
throttleTime,
combineLatestWith,
} from 'rxjs/operators';

import {navigated} from '../../app_routing/actions';
import {
getActiveRoute,
getExperimentIdsFromRoute,
} from '../../app_routing/store/app_routing_selectors';
import {State} from '../../app_state';
import * as coreActions from '../../core/actions';
import * as runsActions from '../../runs/actions/runs_actions';
import {HttpErrorResponse} from '../../webapp_data_source/tb_http_client';

import * as hparamsActions from './hparams_actions';
import {HparamsDataSource} from './hparams_data_source';
import {HparamAndMetricSpec, SessionGroup} from '../types';
import {getEnableHparamsInTimeSeries} from '../../feature_flag/store/feature_flag_selectors';
import {RouteKind} from '../../app_routing/types';

/**
* Effects for fetching the hparams data from the backend.
*/
@Injectable()
export class HparamsEffects {
constructor(
private readonly actions$: Actions,
private readonly store: Store<State>,
private readonly dataSource: HparamsDataSource
) {}

private readonly runTableShown$: Observable<string[]> = this.actions$.pipe(
ofType(runsActions.runTableShown),
map(({experimentIds}) => experimentIds)
);

private readonly loadHparamsOnNavigationOrReload$: Observable<string[]> =
this.actions$.pipe(
ofType(navigated, coreActions.reload, coreActions.manualReload),
withLatestFrom(this.store.select(getExperimentIdsFromRoute)),
filter(([, experimentIds]) => Boolean(experimentIds)),
map(([, experimentIds]) => experimentIds as string[])
);

/** @export */
loadHparamsData$ = createEffect(() => {
return merge(
this.runTableShown$,
this.loadHparamsOnNavigationOrReload$
).pipe(
combineLatestWith(
this.store.select(getEnableHparamsInTimeSeries),
this.store.select(getActiveRoute)
),
filter(
([, getEnableHparamsInTimeSeries]) => getEnableHparamsInTimeSeries
),
filter(
([, , activeRoute]) =>
activeRoute?.routeKind === RouteKind.EXPERIMENT ||
activeRoute?.routeKind === RouteKind.COMPARE_EXPERIMENT
),
throttleTime(10),
switchMap(([experimentIds]) =>
this.loadHparamsForExperiments(experimentIds)
),
map((resp) => hparamsActions.hparamsFetchSessionGroupsSucceeded(resp))
);
});

private loadHparamsForExperiments(experimentIds: string[]): Observable<{
hparamsAndMetricsSpecs: HparamAndMetricSpec;
sessionGroups: SessionGroup[];
}> {
return this.dataSource.fetchExperimentInfo(experimentIds).pipe(
switchMap((hparamsAndMetricsSpecs) => {
return this.dataSource
.fetchSessionGroups(experimentIds, hparamsAndMetricsSpecs)
.pipe(
catchError((error) => {
// HParam plugin return 400 when there are no hparams
// for an experiment.
if (error instanceof HttpErrorResponse && error.status === 400) {
return of([] as SessionGroup[]);
}
return throwError(() => error);
}),
map((sessionGroups) => ({hparamsAndMetricsSpecs, sessionGroups}))
);
})
);
}
}
Loading

0 comments on commit d4df603

Please sign in to comment.