From 485ad7663554316b9003673783c3790b617a49b7 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2026 22:49:47 +0000 Subject: [PATCH 01/61] chore(ui): untrack tsconfig.tsbuildinfo and gitignore *.tsbuildinfo (#44261) Co-authored-by: Yujong Lee Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .gitignore | 1 + ui/litellm-dashboard/tsconfig.tsbuildinfo | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 ui/litellm-dashboard/tsconfig.tsbuildinfo diff --git a/.gitignore b/.gitignore index ac8e2919372..399458eced5 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,7 @@ litellm/proxy/tests/package-lock.json ui/litellm-dashboard/.next ui/litellm-dashboard/node_modules ui/litellm-dashboard/next-env.d.ts +*.tsbuildinfo ui/litellm-dashboard/package.json ui/litellm-dashboard/package-lock.json helm/litellm-helm/*.tgz diff --git a/ui/litellm-dashboard/tsconfig.tsbuildinfo b/ui/litellm-dashboard/tsconfig.tsbuildinfo deleted file mode 100644 index 1036720c31e..00000000000 --- a/ui/litellm-dashboard/tsconfig.tsbuildinfo +++ /dev/null @@ -1 +0,0 @@ -{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.es2024.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2024.collection.d.ts","./node_modules/typescript/lib/lib.es2024.object.d.ts","./node_modules/typescript/lib/lib.es2024.promise.d.ts","./node_modules/typescript/lib/lib.es2024.regexp.d.ts","./node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2024.string.d.ts","./node_modules/typescript/lib/lib.esnext.array.d.ts","./node_modules/typescript/lib/lib.esnext.collection.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.promise.d.ts","./node_modules/typescript/lib/lib.esnext.decorators.d.ts","./node_modules/typescript/lib/lib.esnext.iterator.d.ts","./node_modules/typescript/lib/lib.esnext.float16.d.ts","./node_modules/typescript/lib/lib.esnext.error.d.ts","./node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/@types/react/global.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@types/react/index.d.ts","./node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/next/dist/server/get-page-files.d.ts","./node_modules/@types/node/compatibility/disposable.d.ts","./node_modules/@types/node/compatibility/indexable.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/compatibility/index.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/file.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/filereader.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/@types/react/canary.d.ts","./node_modules/@types/react/experimental.d.ts","./node_modules/@types/react-dom/index.d.ts","./node_modules/@types/react-dom/canary.d.ts","./node_modules/@types/react-dom/experimental.d.ts","./node_modules/next/dist/lib/fallback.d.ts","./node_modules/next/dist/compiled/webpack/webpack.d.ts","./node_modules/next/dist/shared/lib/modern-browserslist-target.d.ts","./node_modules/next/dist/shared/lib/entry-constants.d.ts","./node_modules/next/dist/shared/lib/constants.d.ts","./node_modules/next/dist/lib/bundler.d.ts","./node_modules/next/dist/server/config.d.ts","./node_modules/next/dist/lib/load-custom-routes.d.ts","./node_modules/next/dist/shared/lib/image-config.d.ts","./node_modules/next/dist/build/webpack/plugins/subresource-integrity-plugin.d.ts","./node_modules/next/dist/server/body-streams.d.ts","./node_modules/next/dist/server/request/search-params.d.ts","./node_modules/next/dist/shared/lib/segment-cache/vary-params-decoding.d.ts","./node_modules/next/dist/server/app-render/vary-params.d.ts","./node_modules/next/dist/server/request/params.d.ts","./node_modules/next/dist/server/route-kind.d.ts","./node_modules/next/dist/server/route-definitions/route-definition.d.ts","./node_modules/next/dist/server/route-matches/route-match.d.ts","./node_modules/next/dist/client/components/app-router-headers.d.ts","./node_modules/next/dist/server/lib/cache-control.d.ts","./node_modules/next/dist/shared/lib/app-router-types.d.ts","./node_modules/next/dist/server/lib/cache-handlers/types.d.ts","./node_modules/next/dist/server/use-cache/use-cache-wrapper.d.ts","./node_modules/next/dist/server/resume-data-cache/cache-store.d.ts","./node_modules/next/dist/server/resume-data-cache/resume-data-cache.d.ts","./node_modules/next/dist/lib/constants.d.ts","./node_modules/next/dist/server/render-result.d.ts","./node_modules/next/dist/server/response-cache/types.d.ts","./node_modules/next/dist/server/response-cache/index.d.ts","./node_modules/@types/react/jsx-runtime.d.ts","./node_modules/next/dist/next-devtools/userspace/pages/pages-dev-overlay-setup.d.ts","./node_modules/next/dist/build/static-paths/types.d.ts","./node_modules/next/dist/server/route-definitions/app-page-route-definition.d.ts","./node_modules/next/dist/build/adapter/setup-node-env.external.d.ts","./node_modules/next/dist/server/instrumentation/types.d.ts","./node_modules/next/dist/lib/setup-exception-listeners.d.ts","./node_modules/next/dist/lib/worker.d.ts","./node_modules/next/dist/server/lib/experimental/ppr.d.ts","./node_modules/next/dist/lib/page-types.d.ts","./node_modules/next/dist/build/segment-config/app/app-segment-config.d.ts","./node_modules/next/dist/build/segment-config/pages/pages-segment-config.d.ts","./node_modules/next/dist/build/analysis/get-page-static-info.d.ts","./node_modules/next/dist/build/webpack/loaders/get-module-build-info.d.ts","./node_modules/next/dist/build/webpack/plugins/middleware-plugin.d.ts","./node_modules/next/dist/server/require-hook.d.ts","./node_modules/next/dist/server/node-polyfill-crypto.d.ts","./node_modules/next/dist/server/node-environment-baseline.d.ts","./node_modules/next/dist/server/node-environment-extensions/error-inspect.d.ts","./node_modules/next/dist/server/node-environment-extensions/console-file.d.ts","./node_modules/next/dist/server/node-environment-extensions/console-exit.d.ts","./node_modules/next/dist/server/node-environment-extensions/console-dim.external.d.ts","./node_modules/next/dist/server/node-environment-extensions/unhandled-rejection.external.d.ts","./node_modules/next/dist/server/node-environment-extensions/random.d.ts","./node_modules/next/dist/server/node-environment-extensions/date.d.ts","./node_modules/next/dist/server/node-environment-extensions/web-crypto.d.ts","./node_modules/next/dist/server/node-environment-extensions/node-crypto.d.ts","./node_modules/next/dist/server/node-environment-extensions/fast-set-immediate.external.d.ts","./node_modules/next/dist/server/node-environment.d.ts","./node_modules/next/dist/build/page-extensions-type.d.ts","./node_modules/next/dist/server/route-modules/app-page/module.compiled.d.ts","./node_modules/next/dist/server/route-definitions/app-route-route-definition.d.ts","./node_modules/next/dist/server/lib/i18n-provider.d.ts","./node_modules/next/dist/server/web/next-url.d.ts","./node_modules/next/dist/compiled/@edge-runtime/cookies/index.d.ts","./node_modules/next/dist/server/web/spec-extension/cookies.d.ts","./node_modules/next/dist/server/web/spec-extension/request.d.ts","./node_modules/next/dist/shared/lib/deep-readonly.d.ts","./node_modules/next/dist/server/lib/incremental-cache/index.d.ts","./node_modules/next/dist/shared/lib/router/utils/middleware-route-matcher.d.ts","./node_modules/next/dist/build/webpack/plugins/flight-manifest-plugin.d.ts","./node_modules/next/dist/build/webpack/plugins/next-font-manifest-plugin.d.ts","./node_modules/next/dist/server/route-definitions/locale-route-definition.d.ts","./node_modules/next/dist/server/route-definitions/pages-route-definition.d.ts","./node_modules/next/dist/shared/lib/mitt.d.ts","./node_modules/next/dist/client/with-router.d.ts","./node_modules/next/dist/client/router.d.ts","./node_modules/next/dist/client/route-loader.d.ts","./node_modules/next/dist/client/page-loader.d.ts","./node_modules/next/dist/shared/lib/bloom-filter.d.ts","./node_modules/next/dist/shared/lib/router/router.d.ts","./node_modules/next/dist/shared/lib/router-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/loadable-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/loadable.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/image-config-context.shared-runtime.d.ts","./node_modules/next/dist/client/components/readonly-url-search-params.d.ts","./node_modules/next/dist/shared/lib/hooks-client-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/head-manager-context.shared-runtime.d.ts","./node_modules/next/dist/client/flight-data-helpers.d.ts","./node_modules/next/dist/client/components/segment-cache/cache-key.d.ts","./node_modules/next/dist/client/components/router-reducer/fetch-server-response.d.ts","./node_modules/next/dist/client/components/segment-cache/types.d.ts","./node_modules/next/dist/shared/lib/segment-cache/segment-value-encoding.d.ts","./node_modules/next/dist/client/components/segment-cache/scheduler.d.ts","./node_modules/next/dist/client/components/segment-cache/cache-map.d.ts","./node_modules/next/dist/client/components/segment-cache/vary-path.d.ts","./node_modules/next/dist/client/components/segment-cache/cache.d.ts","./node_modules/next/dist/client/components/router-reducer/ppr-navigations.d.ts","./node_modules/next/dist/client/components/segment-cache/navigation.d.ts","./node_modules/next/dist/client/components/router-reducer/router-reducer-types.d.ts","./node_modules/next/dist/shared/lib/app-router-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/server-inserted-html.shared-runtime.d.ts","./node_modules/next/dist/server/route-modules/pages/vendored/contexts/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/pages/module.compiled.d.ts","./node_modules/next/dist/build/templates/pages.d.ts","./node_modules/next/dist/server/route-modules/pages/module.d.ts","./node_modules/next/dist/server/render.d.ts","./node_modules/next/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","./node_modules/next/dist/server/route-definitions/pages-api-route-definition.d.ts","./node_modules/next/dist/server/route-matches/pages-api-route-match.d.ts","./node_modules/next/dist/server/route-matchers/route-matcher.d.ts","./node_modules/next/dist/server/route-matcher-providers/route-matcher-provider.d.ts","./node_modules/next/dist/server/route-matcher-managers/route-matcher-manager.d.ts","./node_modules/next/dist/server/normalizers/normalizer.d.ts","./node_modules/next/dist/server/normalizers/locale-route-normalizer.d.ts","./node_modules/next/dist/server/normalizers/request/pathname-normalizer.d.ts","./node_modules/next/dist/server/normalizers/request/suffix.d.ts","./node_modules/next/dist/server/normalizers/request/rsc.d.ts","./node_modules/next/dist/server/normalizers/request/next-data.d.ts","./node_modules/next/dist/server/after/builtin-request-context.d.ts","./node_modules/next/dist/server/normalizers/request/segment-prefix-rsc.d.ts","./node_modules/next/dist/server/route-modules/pages/builtin/_error.d.ts","./node_modules/next/dist/server/load-default-error-components.d.ts","./node_modules/next/dist/server/base-server.d.ts","./node_modules/next/dist/server/after/after.d.ts","./node_modules/next/dist/server/after/after-context.d.ts","./node_modules/next/dist/server/use-cache/cache-life.d.ts","./node_modules/next/dist/server/app-render/work-async-storage-instance.d.ts","./node_modules/next/dist/server/lib/lazy-result.d.ts","./node_modules/next/dist/server/app-render/create-error-handler.d.ts","./node_modules/next/dist/shared/lib/action-revalidation-kind.d.ts","./node_modules/next/dist/server/app-render/work-async-storage.external.d.ts","./node_modules/next/dist/server/async-storage/work-store.d.ts","./node_modules/next/dist/server/web/http.d.ts","./node_modules/next/dist/client/components/hooks-server-context.d.ts","./node_modules/next/dist/server/route-modules/app-route/shared-modules.d.ts","./node_modules/next/dist/client/components/redirect-status-code.d.ts","./node_modules/next/dist/client/components/redirect-error.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/request-cookies.d.ts","./node_modules/next/dist/server/async-storage/draft-mode-provider.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/headers.d.ts","./node_modules/next/dist/server/app-render/cache-signal.d.ts","./node_modules/next/dist/server/app-render/instant-validation/boundary-tracking.d.ts","./node_modules/next/dist/server/app-render/instant-validation/instant-validation-error.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-relative-url.d.ts","./node_modules/next/dist/server/app-render/instant-validation/instant-samples.d.ts","./node_modules/next/dist/server/app-render/dynamic-rendering.d.ts","./node_modules/next/dist/server/app-render/work-unit-async-storage-instance.d.ts","./node_modules/next/dist/server/lib/implicit-tags.d.ts","./node_modules/next/dist/server/app-render/staged-rendering.d.ts","./node_modules/next/dist/server/app-render/work-unit-async-storage.external.d.ts","./node_modules/next/dist/build/templates/app-route.d.ts","./node_modules/next/dist/server/app-render/action-async-storage-instance.d.ts","./node_modules/next/dist/server/app-render/action-async-storage.external.d.ts","./node_modules/next/dist/server/route-modules/app-route/module.d.ts","./node_modules/next/dist/server/route-modules/app-route/module.compiled.d.ts","./node_modules/next/dist/build/segment-config/app/app-segments.d.ts","./node_modules/next/dist/build/get-supported-browsers.d.ts","./node_modules/next/dist/build/utils.d.ts","./node_modules/next/dist/build/rendering-mode.d.ts","./node_modules/next/dist/server/lib/router-utils/build-prefetch-segment-data-route.d.ts","./node_modules/next/dist/server/lib/cpu-profile.d.ts","./node_modules/next/dist/build/turborepo-access-trace/types.d.ts","./node_modules/next/dist/build/turborepo-access-trace/result.d.ts","./node_modules/next/dist/build/turborepo-access-trace/helpers.d.ts","./node_modules/next/dist/build/turborepo-access-trace/index.d.ts","./node_modules/next/dist/export/routes/types.d.ts","./node_modules/next/dist/export/types.d.ts","./node_modules/next/dist/export/worker.d.ts","./node_modules/next/dist/build/worker.d.ts","./node_modules/next/dist/build/index.d.ts","./node_modules/next/dist/lib/coalesced-function.d.ts","./node_modules/next/dist/server/lib/router-utils/types.d.ts","./node_modules/next/dist/trace/types.d.ts","./node_modules/next/dist/trace/trace.d.ts","./node_modules/next/dist/trace/shared.d.ts","./node_modules/next/dist/trace/index.d.ts","./node_modules/next/dist/build/load-jsconfig.d.ts","./node_modules/@next/env/dist/index.d.ts","./node_modules/next/dist/build/webpack/plugins/telemetry-plugin/use-cache-tracker-utils.d.ts","./node_modules/next/dist/build/webpack/plugins/telemetry-plugin/telemetry-plugin.d.ts","./node_modules/next/dist/telemetry/storage.d.ts","./node_modules/next/dist/build/build-context.d.ts","./node_modules/next/dist/build/webpack-config.d.ts","./node_modules/next/dist/build/swc/generated-native.d.ts","./node_modules/next/dist/build/define-env.d.ts","./node_modules/next/dist/build/swc/index.d.ts","./node_modules/next/dist/build/swc/types.d.ts","./node_modules/next/dist/server/dev/parse-version-info.d.ts","./node_modules/next/dist/next-devtools/shared/types.d.ts","./node_modules/next/dist/server/dev/dev-indicator-server-state.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/cache-indicator.d.ts","./node_modules/next/dist/server/lib/parse-stack.d.ts","./node_modules/next/dist/next-devtools/server/shared.d.ts","./node_modules/next/dist/next-devtools/shared/stack-frame.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/utils/get-error-by-type.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/container/runtime-error/render-error.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/shared.d.ts","./node_modules/next/dist/server/dev/debug-channel.d.ts","./node_modules/next/dist/server/dev/hot-reloader-types.d.ts","./node_modules/next/dist/server/web/spec-extension/fetch-event.d.ts","./node_modules/next/dist/server/web/spec-extension/response.d.ts","./node_modules/next/dist/build/segment-config/middleware/middleware-config.d.ts","./node_modules/next/dist/server/web/types.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-url.d.ts","./node_modules/next/dist/server/base-http/node.d.ts","./node_modules/next/dist/server/lib/async-callback-set.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-regex.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-matcher.d.ts","./node_modules/@img/colour/index.d.ts","./node_modules/sharp/dist/index.d.mts","./node_modules/next/dist/server/image-optimizer.d.ts","./node_modules/next/dist/server/next-server.d.ts","./node_modules/next/dist/server/lib/types.d.ts","./node_modules/next/dist/server/lib/lru-cache.d.ts","./node_modules/next/dist/server/lib/dev-bundler-service.d.ts","./node_modules/next/dist/server/dev/static-paths-worker.d.ts","./node_modules/next/dist/server/dev/next-dev-server.d.ts","./node_modules/next/dist/server/next.d.ts","./node_modules/next/dist/server/lib/render-server.d.ts","./node_modules/next/dist/server/lib/router-server.d.ts","./node_modules/next/dist/shared/lib/router/utils/path-match.d.ts","./node_modules/next/dist/server/lib/router-utils/filesystem.d.ts","./node_modules/next/dist/server/lib/router-utils/setup-dev-bundler.d.ts","./node_modules/next/dist/server/lib/router-utils/router-server-context.d.ts","./node_modules/next/dist/server/route-modules/route-module.d.ts","./node_modules/next/dist/server/load-components.d.ts","./node_modules/next/dist/server/web/adapter.d.ts","./node_modules/next/dist/server/app-render/types.d.ts","./node_modules/next/dist/build/webpack/loaders/metadata/types.d.ts","./node_modules/next/dist/build/webpack/loaders/next-app-loader/index.d.ts","./node_modules/next/dist/server/lib/app-dir-module.d.ts","./node_modules/next/dist/server/app-render/app-render.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/contexts/entrypoints.d.ts","./node_modules/next/dist/client/components/error-boundary.d.ts","./node_modules/next/dist/client/components/layout-router.d.ts","./node_modules/next/dist/client/components/render-from-template-context.d.ts","./node_modules/next/dist/client/components/client-page.d.ts","./node_modules/next/dist/client/components/client-segment.d.ts","./node_modules/next/dist/client/components/http-access-fallback/error-boundary.d.ts","./node_modules/next/dist/lib/metadata/types/alternative-urls-types.d.ts","./node_modules/next/dist/lib/metadata/types/extra-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-types.d.ts","./node_modules/next/dist/lib/metadata/types/manifest-types.d.ts","./node_modules/next/dist/lib/metadata/types/opengraph-types.d.ts","./node_modules/next/dist/lib/metadata/types/twitter-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-interface.d.ts","./node_modules/next/dist/lib/metadata/types/resolvers.d.ts","./node_modules/next/dist/lib/metadata/types/icons.d.ts","./node_modules/next/dist/lib/metadata/resolve-metadata.d.ts","./node_modules/next/dist/lib/metadata/metadata.d.ts","./node_modules/next/dist/lib/framework/boundary-components.d.ts","./node_modules/next/dist/server/app-render/rsc/preloads.d.ts","./node_modules/next/dist/server/app-render/rsc/postpone.d.ts","./node_modules/next/dist/server/app-render/rsc/taint.d.ts","./node_modules/next/dist/server/app-render/collect-segment-data.d.ts","./node_modules/next/dist/server/app-render/instant-validation/instant-validation.d.ts","./node_modules/next/dist/next-devtools/userspace/app/segment-explorer-node.d.ts","./node_modules/next/dist/server/app-render/entry-base.d.ts","./node_modules/next/dist/build/templates/app-page.d.ts","./node_modules/next/dist/server/route-modules/app-page/helpers/prerender-manifest-matcher.d.ts","./node_modules/@types/react/jsx-dev-runtime.d.ts","./node_modules/@types/react/compiler-runtime.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/rsc/entrypoints.d.ts","./node_modules/@types/react-dom/client.d.ts","./node_modules/@types/react-dom/static.d.ts","./node_modules/@types/react-dom/server.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/ssr/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/app-page/module.d.ts","./node_modules/next/dist/server/request/fallback-params.d.ts","./node_modules/next/dist/server/web/spec-extension/image-response.d.ts","./node_modules/next/dist/server/web/spec-extension/user-agent.d.ts","./node_modules/next/dist/server/web/spec-extension/url-pattern.d.ts","./node_modules/next/dist/server/after/index.d.ts","./node_modules/next/dist/server/request/connection.d.ts","./node_modules/next/dist/server/web/exports/index.d.ts","./node_modules/next/dist/server/request-meta.d.ts","./node_modules/next/dist/cli/next-test.d.ts","./node_modules/next/dist/shared/lib/size-limit.d.ts","./node_modules/next/dist/server/config-shared.d.ts","./node_modules/next/dist/server/base-http/index.d.ts","./node_modules/next/dist/server/api-utils/index.d.ts","./node_modules/next/dist/build/adapter/build-complete.d.ts","./node_modules/next/dist/types.d.ts","./node_modules/next/dist/shared/lib/html-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/utils.d.ts","./node_modules/next/dist/pages/_app.d.ts","./node_modules/next/app.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-cache.d.ts","./node_modules/next/dist/server/web/spec-extension/revalidate.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-no-store.d.ts","./node_modules/next/dist/server/use-cache/cache-tag.d.ts","./node_modules/next/cache.d.ts","./node_modules/next/dist/pages/_document.d.ts","./node_modules/next/document.d.ts","./node_modules/next/dist/shared/lib/dynamic.d.ts","./node_modules/next/dynamic.d.ts","./node_modules/next/dist/pages/_error.d.ts","./node_modules/next/dist/client/components/catch-error.d.ts","./node_modules/next/dist/api/error.d.ts","./node_modules/next/error.d.ts","./node_modules/next/dist/shared/lib/head.d.ts","./node_modules/next/head.d.ts","./node_modules/next/dist/server/request/cookies.d.ts","./node_modules/next/dist/server/request/headers.d.ts","./node_modules/next/dist/server/request/draft-mode.d.ts","./node_modules/next/headers.d.ts","./node_modules/next/dist/shared/lib/get-img-props.d.ts","./node_modules/next/dist/client/image-component.d.ts","./node_modules/next/dist/shared/lib/image-external.d.ts","./node_modules/next/image.d.ts","./node_modules/next/dist/client/link.d.ts","./node_modules/next/link.d.ts","./node_modules/next/dist/client/components/unrecognized-action-error.d.ts","./node_modules/next/dist/client/components/redirect.d.ts","./node_modules/next/dist/client/components/not-found.d.ts","./node_modules/next/dist/client/components/forbidden.d.ts","./node_modules/next/dist/client/components/unauthorized.d.ts","./node_modules/next/dist/client/components/unstable-rethrow.server.d.ts","./node_modules/next/dist/client/components/unstable-rethrow.d.ts","./node_modules/next/dist/client/components/navigation.react-server.d.ts","./node_modules/next/dist/client/components/navigation.d.ts","./node_modules/next/navigation.d.ts","./node_modules/next/router.d.ts","./node_modules/next/dist/client/script.d.ts","./node_modules/next/script.d.ts","./node_modules/next/dist/compiled/@edge-runtime/primitives/url.d.ts","./node_modules/next/dist/compiled/@vercel/og/satori/index.d.ts","./node_modules/next/dist/compiled/@vercel/og/types.d.ts","./node_modules/next/server.d.ts","./node_modules/next/types/global.d.ts","./node_modules/next/types/compiled.d.ts","./node_modules/next/types.d.ts","./node_modules/next/index.d.ts","./node_modules/next/image-types/global.d.ts","./.next/dev/types/routes.d.ts","./next-env.d.ts","./node_modules/@vitest/spy/dist/index.d.ts","./node_modules/@vitest/pretty-format/dist/index.d.ts","./node_modules/@vitest/utils/dist/types.d.ts","./node_modules/@vitest/utils/dist/helpers.d.ts","./node_modules/tinyrainbow/dist/index-8b61d5bc.d.ts","./node_modules/tinyrainbow/dist/node.d.ts","./node_modules/@vitest/utils/dist/index.d.ts","./node_modules/@vitest/utils/dist/types.d-bcelap-c.d.ts","./node_modules/@vitest/utils/dist/diff.d.ts","./node_modules/@vitest/expect/dist/index.d.ts","./node_modules/vite/types/hmrpayload.d.ts","./node_modules/vite/dist/node/chunks/modulerunnertransport.d.ts","./node_modules/vite/types/customevent.d.ts","./node_modules/@types/estree/index.d.ts","./node_modules/rollup/dist/rollup.d.ts","./node_modules/rollup/dist/parseast.d.ts","./node_modules/vite/types/hot.d.ts","./node_modules/vite/dist/node/module-runner.d.ts","./node_modules/esbuild/lib/main.d.ts","./node_modules/vite/types/internal/terseroptions.d.ts","./node_modules/source-map-js/source-map.d.ts","./node_modules/postcss/lib/previous-map.d.ts","./node_modules/postcss/lib/input.d.ts","./node_modules/postcss/lib/css-syntax-error.d.ts","./node_modules/postcss/lib/declaration.d.ts","./node_modules/postcss/lib/root.d.ts","./node_modules/postcss/lib/warning.d.ts","./node_modules/postcss/lib/lazy-result.d.ts","./node_modules/postcss/lib/no-work-result.d.ts","./node_modules/postcss/lib/processor.d.ts","./node_modules/postcss/lib/result.d.ts","./node_modules/postcss/lib/document.d.ts","./node_modules/postcss/lib/rule.d.ts","./node_modules/postcss/lib/node.d.ts","./node_modules/postcss/lib/comment.d.ts","./node_modules/postcss/lib/container.d.ts","./node_modules/postcss/lib/at-rule.d.ts","./node_modules/postcss/lib/list.d.ts","./node_modules/postcss/lib/postcss.d.ts","./node_modules/postcss/lib/postcss.d.mts","./node_modules/vite/types/internal/csspreprocessoroptions.d.ts","./node_modules/lightningcss/node/ast.d.ts","./node_modules/lightningcss/node/targets.d.ts","./node_modules/lightningcss/node/index.d.ts","./node_modules/vite/types/internal/lightningcssoptions.d.ts","./node_modules/vite/types/importglob.d.ts","./node_modules/vite/types/metadata.d.ts","./node_modules/vite/dist/node/index.d.ts","./node_modules/@vitest/runner/dist/tasks.d-cksck4of.d.ts","./node_modules/@vitest/runner/dist/types.d.ts","./node_modules/@vitest/utils/dist/error.d.ts","./node_modules/@vitest/runner/dist/index.d.ts","./node_modules/vitest/optional-types.d.ts","./node_modules/vitest/dist/chunks/environment.d.cl3nlxbe.d.ts","./node_modules/@vitest/mocker/dist/registry.d-d765pazg.d.ts","./node_modules/@vitest/mocker/dist/types.d-d_arzrdy.d.ts","./node_modules/@vitest/mocker/dist/index.d.ts","./node_modules/@vitest/utils/dist/source-map.d.ts","./node_modules/vite-node/dist/trace-mapping.d-dlvdeqop.d.ts","./node_modules/vite-node/dist/index.d-dgmxd2u7.d.ts","./node_modules/vite-node/dist/index.d.ts","./node_modules/@vitest/snapshot/dist/environment.d-dhdq1csl.d.ts","./node_modules/@vitest/snapshot/dist/rawsnapshot.d-lfsmjfud.d.ts","./node_modules/@vitest/snapshot/dist/index.d.ts","./node_modules/@vitest/snapshot/dist/environment.d.ts","./node_modules/vitest/dist/chunks/config.d.bkdhh7zx.d.ts","./node_modules/vitest/dist/chunks/worker.d.cugipz9v.d.ts","./node_modules/@types/deep-eql/index.d.ts","./node_modules/assertion-error/index.d.ts","./node_modules/@types/chai/index.d.ts","./node_modules/@vitest/runner/dist/utils.d.ts","./node_modules/tinybench/dist/index.d.ts","./node_modules/vitest/dist/chunks/benchmark.d.bwvbvtda.d.ts","./node_modules/vite-node/dist/client.d.ts","./node_modules/vitest/dist/chunks/coverage.d.s9rmnxie.d.ts","./node_modules/@vitest/snapshot/dist/manager.d.ts","./node_modules/vitest/dist/chunks/reporters.d.buron0i0.d.ts","./node_modules/vitest/dist/chunks/vite.d.bnoppc46.d.ts","./node_modules/vitest/dist/config.d.ts","./node_modules/vitest/config.d.ts","./vitest.config.ts","./src/types.ts","./node_modules/vitest/dist/chunks/worker.d.uzwscv9x.d.ts","./node_modules/vitest/dist/chunks/global.d.mamajcmj.d.ts","./node_modules/vitest/dist/chunks/mocker.d.be_2ls6u.d.ts","./node_modules/vitest/dist/chunks/suite.d.fvehnv49.d.ts","./node_modules/expect-type/dist/utils.d.ts","./node_modules/expect-type/dist/overloads.d.ts","./node_modules/expect-type/dist/branding.d.ts","./node_modules/expect-type/dist/messages.d.ts","./node_modules/expect-type/dist/index.d.ts","./node_modules/vitest/dist/index.d.ts","./node_modules/@tanstack/query-core/build/modern/_tsup-dts-rollup.d.ts","./node_modules/@tanstack/query-core/build/modern/index.d.ts","./node_modules/@tanstack/react-query/build/modern/_tsup-dts-rollup.d.ts","./node_modules/@tanstack/react-query/build/modern/index.d.ts","./node_modules/sonner/dist/index.d.mts","./src/lib/http/client.ts","./src/lib/toast.ts","./src/utils/securestorage.ts","./src/utils/mcptokenstore.ts","./src/utils/cookieutils.ts","./node_modules/jwt-decode/build/esm/index.d.ts","./src/utils/jwtutils.ts","./src/components/tag_management/types.tsx","./src/components/email_events/types.ts","./src/lib/http/schema.d.ts","./src/components/claude_code_plugins/types.ts","./node_modules/@base-ui/react/internals/reason-parts.d.mts","./node_modules/@base-ui/react/internals/reasons.d.mts","./node_modules/@base-ui/react/internals/createbaseuieventdetails.d.mts","./node_modules/@base-ui/react/types/index.d.mts","./node_modules/@base-ui/react/internals/types.d.mts","./node_modules/@base-ui/react/accordion/root/accordionroot.d.mts","./node_modules/@base-ui/react/internals/usetransitionstatus.d.mts","./node_modules/@base-ui/react/collapsible/root/collapsibleroot.d.mts","./node_modules/@base-ui/react/collapsible/root/usecollapsibleroot.d.mts","./node_modules/@base-ui/react/accordion/item/accordionitem.d.mts","./node_modules/@base-ui/react/accordion/header/accordionheader.d.mts","./node_modules/@base-ui/react/accordion/trigger/accordiontrigger.d.mts","./node_modules/@base-ui/react/accordion/panel/accordionpanel.d.mts","./node_modules/@base-ui/react/accordion/index.parts.d.mts","./node_modules/@base-ui/react/accordion/index.d.mts","./node_modules/reselect/dist/reselect.d.ts","./node_modules/@base-ui/utils/store/createselector.d.mts","./node_modules/@base-ui/utils/store/createselectormemoized.d.mts","./node_modules/@base-ui/utils/fasthooks.d.mts","./node_modules/@base-ui/utils/store/store.d.mts","./node_modules/@base-ui/utils/store/usestore.d.mts","./node_modules/@base-ui/utils/store/reactstore.d.mts","./node_modules/@base-ui/utils/store/storeinspector.d.mts","./node_modules/@base-ui/utils/store/index.d.mts","./node_modules/@base-ui/utils/useenhancedclickhandler.d.mts","./node_modules/@floating-ui/utils/dist/floating-ui.utils.d.mts","./node_modules/@floating-ui/core/dist/floating-ui.core.d.mts","./node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.d.mts","./node_modules/@floating-ui/dom/dist/floating-ui.dom.d.mts","./node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.d.mts","./node_modules/@base-ui/react/utils/popups/inlinerect.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingtreestore.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingrootstore.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingfocusmanager.d.mts","./node_modules/@base-ui/react/internals/getstateattributesprops.d.mts","./node_modules/@base-ui/react/internals/userenderelement.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingportal.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/useclientpoint.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usedismiss.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usefocus.d.mts","./node_modules/@base-ui/react/internals/shadowdom.d.mts","./node_modules/@base-ui/react/floating-ui-react/utils/element.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usehovershared.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usehover.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usehoverfloatinginteraction.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usehoverreferenceinteraction.d.mts","./node_modules/@base-ui/react/floating-ui-react/utils/composite.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/gridnavigation.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/uselistnavigation.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usetypeahead.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usefloatingrootcontext.d.mts","./node_modules/@base-ui/react/floating-ui-react/safepolygon.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingtree.d.mts","./node_modules/@base-ui/react/floating-ui-react/types.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingdelaygroup.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/useclick.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usefloating.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usesyncedfloatingrootcontext.d.mts","./node_modules/@base-ui/react/floating-ui-react/index.d.mts","./node_modules/@base-ui/react/utils/popups/popuptriggermap.d.mts","./node_modules/@base-ui/react/utils/popups/store.d.mts","./node_modules/@base-ui/react/utils/popups/popupstoreutils.d.mts","./node_modules/@base-ui/react/utils/popups/index.d.mts","./node_modules/@base-ui/react/dialog/store/dialogstore.d.mts","./node_modules/@base-ui/react/dialog/store/dialoghandle.d.mts","./node_modules/@base-ui/react/dialog/root/dialogroot.d.mts","./node_modules/@base-ui/react/alert-dialog/handle.d.mts","./node_modules/@base-ui/react/alert-dialog/root/alertdialogroot.d.mts","./node_modules/@base-ui/react/dialog/backdrop/dialogbackdrop.d.mts","./node_modules/@base-ui/react/dialog/close/dialogclose.d.mts","./node_modules/@base-ui/react/dialog/description/dialogdescription.d.mts","./node_modules/@base-ui/react/dialog/popup/dialogpopup.d.mts","./node_modules/@base-ui/react/dialog/portal/dialogportal.d.mts","./node_modules/@base-ui/react/dialog/title/dialogtitle.d.mts","./node_modules/@base-ui/react/dialog/trigger/dialogtrigger.d.mts","./node_modules/@base-ui/react/alert-dialog/trigger/alertdialogtrigger.d.mts","./node_modules/@base-ui/react/dialog/viewport/dialogviewport.d.mts","./node_modules/@base-ui/react/alert-dialog/index.parts.d.mts","./node_modules/@base-ui/react/alert-dialog/index.d.mts","./node_modules/@base-ui/react/internals/resolvevaluelabel.d.mts","./node_modules/@base-ui/react/combobox/root/ariacombobox.d.mts","./node_modules/@base-ui/react/autocomplete/root/autocompleteroot.d.mts","./node_modules/@base-ui/react/autocomplete/value/autocompletevalue.d.mts","./node_modules/@base-ui/react/internals/form-context/formcontext.d.mts","./node_modules/@base-ui/react/form/form.d.mts","./node_modules/@base-ui/react/form/index.d.mts","./node_modules/@base-ui/react/field/root/fieldroot.d.mts","./node_modules/@base-ui/react/utils/useanchorpositioning.d.mts","./node_modules/@base-ui/react/autocomplete/trigger/autocompletetrigger.d.mts","./node_modules/@base-ui/react/combobox/input/comboboxinput.d.mts","./node_modules/@base-ui/react/autocomplete/input-group/autocompleteinputgroup.d.mts","./node_modules/@base-ui/react/combobox/icon/comboboxicon.d.mts","./node_modules/@base-ui/react/combobox/clear/comboboxclear.d.mts","./node_modules/@base-ui/react/combobox/list/comboboxlist.d.mts","./node_modules/@base-ui/react/combobox/status/comboboxstatus.d.mts","./node_modules/@base-ui/react/combobox/portal/comboboxportal.d.mts","./node_modules/@base-ui/react/combobox/backdrop/comboboxbackdrop.d.mts","./node_modules/@base-ui/react/combobox/positioner/comboboxpositioner.d.mts","./node_modules/@base-ui/react/combobox/popup/comboboxpopup.d.mts","./node_modules/@base-ui/react/combobox/arrow/comboboxarrow.d.mts","./node_modules/@base-ui/react/combobox/group/comboboxgroup.d.mts","./node_modules/@base-ui/react/combobox/group-label/comboboxgrouplabel.d.mts","./node_modules/@base-ui/react/autocomplete/item/autocompleteitem.d.mts","./node_modules/@base-ui/react/combobox/row/comboboxrow.d.mts","./node_modules/@base-ui/react/combobox/collection/comboboxcollection.d.mts","./node_modules/@base-ui/react/combobox/empty/comboboxempty.d.mts","./node_modules/@base-ui/react/separator/separator.d.mts","./node_modules/@base-ui/react/internals/filter.d.mts","./node_modules/@base-ui/react/combobox/root/utils/usefilter.d.mts","./node_modules/@base-ui/react/combobox/root/utils/usefiltereditems.d.mts","./node_modules/@base-ui/react/autocomplete/index.parts.d.mts","./node_modules/@base-ui/react/autocomplete/index.d.mts","./node_modules/@base-ui/react/avatar/root/avatarroot.d.mts","./node_modules/@base-ui/react/avatar/image/avatarimage.d.mts","./node_modules/@base-ui/react/avatar/fallback/avatarfallback.d.mts","./node_modules/@base-ui/react/avatar/index.parts.d.mts","./node_modules/@base-ui/react/avatar/index.d.mts","./node_modules/@base-ui/react/button/button.d.mts","./node_modules/@base-ui/react/button/index.d.mts","./node_modules/@base-ui/react/checkbox/root/checkboxroot.d.mts","./node_modules/@base-ui/react/checkbox/indicator/checkboxindicator.d.mts","./node_modules/@base-ui/react/checkbox/index.parts.d.mts","./node_modules/@base-ui/react/checkbox/index.d.mts","./node_modules/@base-ui/react/checkbox-group/checkboxgroup.d.mts","./node_modules/@base-ui/react/checkbox-group/index.d.mts","./node_modules/@base-ui/react/collapsible/trigger/collapsibletrigger.d.mts","./node_modules/@base-ui/react/collapsible/panel/collapsiblepanel.d.mts","./node_modules/@base-ui/react/collapsible/index.parts.d.mts","./node_modules/@base-ui/react/collapsible/index.d.mts","./node_modules/@base-ui/react/combobox/root/comboboxroot.d.mts","./node_modules/@base-ui/react/combobox/label/comboboxlabel.d.mts","./node_modules/@base-ui/react/combobox/value/comboboxvalue.d.mts","./node_modules/@base-ui/react/combobox/input-group/comboboxinputgroup.d.mts","./node_modules/@base-ui/react/combobox/trigger/comboboxtrigger.d.mts","./node_modules/@base-ui/react/combobox/item/comboboxitem.d.mts","./node_modules/@base-ui/react/combobox/item-indicator/comboboxitemindicator.d.mts","./node_modules/@base-ui/react/combobox/chips/comboboxchips.d.mts","./node_modules/@base-ui/react/combobox/chip/comboboxchip.d.mts","./node_modules/@base-ui/react/combobox/chip-remove/comboboxchipremove.d.mts","./node_modules/@base-ui/react/separator/index.d.mts","./node_modules/@base-ui/react/combobox/index.parts.d.mts","./node_modules/@base-ui/react/combobox/index.d.mts","./node_modules/@base-ui/react/menu/arrow/menuarrow.d.mts","./node_modules/@base-ui/react/menu/backdrop/menubackdrop.d.mts","./node_modules/@base-ui/react/menu/store/menustore.d.mts","./node_modules/@base-ui/react/menu/root/menurootcontext.d.mts","./node_modules/@base-ui/react/menubar/menubarcontext.d.mts","./node_modules/@base-ui/react/context-menu/root/contextmenurootcontext.d.mts","./node_modules/@base-ui/react/menu/store/menuhandle.d.mts","./node_modules/@base-ui/react/menu/root/menuroot.d.mts","./node_modules/@base-ui/react/menu/checkbox-item/menucheckboxitem.d.mts","./node_modules/@base-ui/react/menu/checkbox-item-indicator/menucheckboxitemindicator.d.mts","./node_modules/@base-ui/react/menu/group/menugroup.d.mts","./node_modules/@base-ui/react/menu/group-label/menugrouplabel.d.mts","./node_modules/@base-ui/react/menu/item/menuitem.d.mts","./node_modules/@base-ui/react/menu/link-item/menulinkitem.d.mts","./node_modules/@base-ui/react/menu/popup/menupopup.d.mts","./node_modules/@base-ui/react/menu/portal/menuportal.d.mts","./node_modules/@base-ui/react/menu/positioner/menupositioner.d.mts","./node_modules/@base-ui/react/menu/radio-group/menuradiogroup.d.mts","./node_modules/@base-ui/react/menu/radio-item/menuradioitem.d.mts","./node_modules/@base-ui/react/menu/radio-item-indicator/menuradioitemindicator.d.mts","./node_modules/@base-ui/react/menu/submenu-root/menusubmenurootcontext.d.mts","./node_modules/@base-ui/react/menu/submenu-root/menusubmenuroot.d.mts","./node_modules/@base-ui/react/menu/trigger/menutrigger.d.mts","./node_modules/@base-ui/react/menu/viewport/menuviewport.d.mts","./node_modules/@base-ui/react/menu/submenu-trigger/menusubmenutrigger.d.mts","./node_modules/@base-ui/react/menu/index.parts.d.mts","./node_modules/@base-ui/react/menu/index.d.mts","./node_modules/@base-ui/react/context-menu/root/contextmenuroot.d.mts","./node_modules/@base-ui/react/context-menu/trigger/contextmenutrigger.d.mts","./node_modules/@base-ui/react/context-menu/index.parts.d.mts","./node_modules/@base-ui/react/context-menu/index.d.mts","./node_modules/@base-ui/react/csp-provider/cspprovider.d.mts","./node_modules/@base-ui/react/csp-provider/index.parts.d.mts","./node_modules/@base-ui/react/csp-provider/index.d.mts","./node_modules/@base-ui/react/dialog/index.parts.d.mts","./node_modules/@base-ui/react/dialog/index.d.mts","./node_modules/@base-ui/react/internals/direction-context/directioncontext.d.mts","./node_modules/@base-ui/react/direction-provider/directionprovider.d.mts","./node_modules/@base-ui/react/direction-provider/index.parts.d.mts","./node_modules/@base-ui/react/direction-provider/index.d.mts","./node_modules/@base-ui/react/drawer/backdrop/drawerbackdrop.d.mts","./node_modules/@base-ui/react/drawer/close/drawerclose.d.mts","./node_modules/@base-ui/react/drawer/content/drawercontent.d.mts","./node_modules/@base-ui/react/drawer/description/drawerdescription.d.mts","./node_modules/@base-ui/react/drawer/indent/drawerindent.d.mts","./node_modules/@base-ui/react/drawer/indent-background/drawerindentbackground.d.mts","./node_modules/@base-ui/react/utils/useswipedismiss.d.mts","./node_modules/@base-ui/react/drawer/root/drawerroot.d.mts","./node_modules/@base-ui/react/drawer/root/drawerrootcontext.d.mts","./node_modules/@base-ui/react/drawer/popup/drawerpopup.d.mts","./node_modules/@base-ui/react/drawer/portal/drawerportal.d.mts","./node_modules/@base-ui/react/drawer/provider/drawerprovider.d.mts","./node_modules/@base-ui/react/drawer/swipe-area/drawerswipearea.d.mts","./node_modules/@base-ui/react/drawer/title/drawertitle.d.mts","./node_modules/@base-ui/react/drawer/trigger/drawertrigger.d.mts","./node_modules/@base-ui/react/drawer/viewport/drawerviewport.d.mts","./node_modules/@base-ui/react/drawer/virtual-keyboard-provider/drawervirtualkeyboardprovider.d.mts","./node_modules/@base-ui/react/drawer/index.parts.d.mts","./node_modules/@base-ui/react/drawer/index.d.mts","./node_modules/@base-ui/react/field/label/fieldlabel.d.mts","./node_modules/@base-ui/react/field/error/fielderror.d.mts","./node_modules/@base-ui/react/field/description/fielddescription.d.mts","./node_modules/@base-ui/react/field/control/fieldcontrol.d.mts","./node_modules/@base-ui/react/field/validity/fieldvalidity.d.mts","./node_modules/@base-ui/react/field/item/fielditem.d.mts","./node_modules/@base-ui/react/field/index.parts.d.mts","./node_modules/@base-ui/react/field/index.d.mts","./node_modules/@base-ui/react/fieldset/root/fieldsetroot.d.mts","./node_modules/@base-ui/react/fieldset/legend/fieldsetlegend.d.mts","./node_modules/@base-ui/react/fieldset/index.parts.d.mts","./node_modules/@base-ui/react/fieldset/index.d.mts","./node_modules/@base-ui/react/input/input.d.mts","./node_modules/@base-ui/react/input/index.d.mts","./node_modules/@base-ui/react/menubar/menubar.d.mts","./node_modules/@base-ui/react/menubar/index.d.mts","./node_modules/@base-ui/react/merge-props/mergeprops.d.mts","./node_modules/@base-ui/react/merge-props/index.d.mts","./node_modules/@base-ui/react/meter/root/meterroot.d.mts","./node_modules/@base-ui/react/meter/track/metertrack.d.mts","./node_modules/@base-ui/react/meter/indicator/meterindicator.d.mts","./node_modules/@base-ui/react/meter/value/metervalue.d.mts","./node_modules/@base-ui/react/meter/label/meterlabel.d.mts","./node_modules/@base-ui/react/meter/index.parts.d.mts","./node_modules/@base-ui/react/meter/index.d.mts","./node_modules/@base-ui/react/navigation-menu/root/navigationmenuroot.d.mts","./node_modules/@base-ui/react/navigation-menu/list/navigationmenulist.d.mts","./node_modules/@base-ui/react/navigation-menu/item/navigationmenuitem.d.mts","./node_modules/@base-ui/react/navigation-menu/content/navigationmenucontent.d.mts","./node_modules/@base-ui/react/navigation-menu/trigger/navigationmenutrigger.d.mts","./node_modules/@base-ui/react/navigation-menu/portal/navigationmenuportal.d.mts","./node_modules/@base-ui/react/navigation-menu/positioner/navigationmenupositioner.d.mts","./node_modules/@base-ui/react/navigation-menu/viewport/navigationmenuviewport.d.mts","./node_modules/@base-ui/react/navigation-menu/backdrop/navigationmenubackdrop.d.mts","./node_modules/@base-ui/react/navigation-menu/popup/navigationmenupopup.d.mts","./node_modules/@base-ui/react/navigation-menu/arrow/navigationmenuarrow.d.mts","./node_modules/@base-ui/react/navigation-menu/link/navigationmenulink.d.mts","./node_modules/@base-ui/react/navigation-menu/icon/navigationmenuicon.d.mts","./node_modules/@base-ui/react/navigation-menu/index.parts.d.mts","./node_modules/@base-ui/react/navigation-menu/index.d.mts","./node_modules/@base-ui/react/number-field/utils/types.d.mts","./node_modules/@base-ui/react/number-field/root/numberfieldroot.d.mts","./node_modules/@base-ui/react/number-field/group/numberfieldgroup.d.mts","./node_modules/@base-ui/react/number-field/increment/numberfieldincrement.d.mts","./node_modules/@base-ui/react/number-field/decrement/numberfielddecrement.d.mts","./node_modules/@base-ui/react/number-field/input/numberfieldinput.d.mts","./node_modules/@base-ui/react/number-field/scrub-area/numberfieldscrubarea.d.mts","./node_modules/@base-ui/react/number-field/scrub-area-cursor/numberfieldscrubareacursor.d.mts","./node_modules/@base-ui/react/number-field/index.parts.d.mts","./node_modules/@base-ui/react/number-field/index.d.mts","./node_modules/@base-ui/react/otp-field/utils/otp.d.mts","./node_modules/@base-ui/react/otp-field/root/otpfieldroot.d.mts","./node_modules/@base-ui/react/otp-field/input/otpfieldinput.d.mts","./node_modules/@base-ui/react/otp-field/index.parts.d.mts","./node_modules/@base-ui/react/otp-field/index.d.mts","./node_modules/@base-ui/utils/usetimeout.d.mts","./node_modules/@base-ui/react/popover/store/popoverstore.d.mts","./node_modules/@base-ui/react/popover/store/popoverhandle.d.mts","./node_modules/@base-ui/react/popover/root/popoverroot.d.mts","./node_modules/@base-ui/react/popover/trigger/popovertrigger.d.mts","./node_modules/@base-ui/react/popover/portal/popoverportal.d.mts","./node_modules/@base-ui/react/popover/positioner/popoverpositioner.d.mts","./node_modules/@base-ui/react/popover/popup/popoverpopup.d.mts","./node_modules/@base-ui/react/popover/arrow/popoverarrow.d.mts","./node_modules/@base-ui/react/popover/backdrop/popoverbackdrop.d.mts","./node_modules/@base-ui/react/popover/title/popovertitle.d.mts","./node_modules/@base-ui/react/popover/description/popoverdescription.d.mts","./node_modules/@base-ui/react/popover/close/popoverclose.d.mts","./node_modules/@base-ui/react/popover/viewport/popoverviewport.d.mts","./node_modules/@base-ui/react/popover/index.parts.d.mts","./node_modules/@base-ui/react/popover/index.d.mts","./node_modules/@base-ui/react/preview-card/store/previewcardstore.d.mts","./node_modules/@base-ui/react/preview-card/store/previewcardhandle.d.mts","./node_modules/@base-ui/react/preview-card/root/previewcardroot.d.mts","./node_modules/@base-ui/react/utils/floatingportallite.d.mts","./node_modules/@base-ui/react/preview-card/portal/previewcardportal.d.mts","./node_modules/@base-ui/react/preview-card/trigger/previewcardtrigger.d.mts","./node_modules/@base-ui/react/preview-card/positioner/previewcardpositioner.d.mts","./node_modules/@base-ui/react/preview-card/popup/previewcardpopup.d.mts","./node_modules/@base-ui/react/preview-card/arrow/previewcardarrow.d.mts","./node_modules/@base-ui/react/preview-card/backdrop/previewcardbackdrop.d.mts","./node_modules/@base-ui/react/preview-card/viewport/previewcardviewport.d.mts","./node_modules/@base-ui/react/preview-card/index.parts.d.mts","./node_modules/@base-ui/react/preview-card/index.d.mts","./node_modules/@base-ui/react/progress/root/progressroot.d.mts","./node_modules/@base-ui/react/progress/track/progresstrack.d.mts","./node_modules/@base-ui/react/progress/indicator/progressindicator.d.mts","./node_modules/@base-ui/react/progress/value/progressvalue.d.mts","./node_modules/@base-ui/react/progress/label/progresslabel.d.mts","./node_modules/@base-ui/react/progress/index.parts.d.mts","./node_modules/@base-ui/react/progress/index.d.mts","./node_modules/@base-ui/react/radio/root/radioroot.d.mts","./node_modules/@base-ui/react/radio/indicator/radioindicator.d.mts","./node_modules/@base-ui/react/radio/index.parts.d.mts","./node_modules/@base-ui/react/radio/index.d.mts","./node_modules/@base-ui/react/radio-group/radiogroup.d.mts","./node_modules/@base-ui/react/radio-group/index.d.mts","./node_modules/@base-ui/react/scroll-area/root/scrollarearoot.d.mts","./node_modules/@base-ui/react/scroll-area/viewport/scrollareaviewport.d.mts","./node_modules/@base-ui/react/scroll-area/scrollbar/scrollareascrollbar.d.mts","./node_modules/@base-ui/react/scroll-area/content/scrollareacontent.d.mts","./node_modules/@base-ui/react/scroll-area/thumb/scrollareathumb.d.mts","./node_modules/@base-ui/react/scroll-area/corner/scrollareacorner.d.mts","./node_modules/@base-ui/react/scroll-area/index.parts.d.mts","./node_modules/@base-ui/react/scroll-area/index.d.mts","./node_modules/@base-ui/react/select/root/selectroot.d.mts","./node_modules/@base-ui/react/select/label/selectlabel.d.mts","./node_modules/@base-ui/react/select/trigger/selecttrigger.d.mts","./node_modules/@base-ui/react/select/value/selectvalue.d.mts","./node_modules/@base-ui/react/select/icon/selecticon.d.mts","./node_modules/@base-ui/react/select/portal/selectportal.d.mts","./node_modules/@base-ui/react/select/backdrop/selectbackdrop.d.mts","./node_modules/@base-ui/react/select/positioner/selectpositioner.d.mts","./node_modules/@base-ui/react/select/popup/selectpopup.d.mts","./node_modules/@base-ui/react/select/list/selectlist.d.mts","./node_modules/@base-ui/react/select/item/selectitem.d.mts","./node_modules/@base-ui/react/select/item-indicator/selectitemindicator.d.mts","./node_modules/@base-ui/react/select/item-text/selectitemtext.d.mts","./node_modules/@base-ui/react/select/arrow/selectarrow.d.mts","./node_modules/@base-ui/react/select/scroll-down-arrow/selectscrolldownarrow.d.mts","./node_modules/@base-ui/react/select/scroll-up-arrow/selectscrolluparrow.d.mts","./node_modules/@base-ui/react/select/group/selectgroup.d.mts","./node_modules/@base-ui/react/select/group-label/selectgrouplabel.d.mts","./node_modules/@base-ui/react/select/index.parts.d.mts","./node_modules/@base-ui/react/select/index.d.mts","./node_modules/@base-ui/react/slider/root/sliderroot.d.mts","./node_modules/@base-ui/react/slider/label/sliderlabel.d.mts","./node_modules/@base-ui/react/slider/value/slidervalue.d.mts","./node_modules/@base-ui/react/slider/control/slidercontrol.d.mts","./node_modules/@base-ui/react/slider/track/slidertrack.d.mts","./node_modules/@base-ui/react/internals/labelable-provider/labelablecontext.d.mts","./node_modules/@base-ui/react/slider/thumb/sliderthumb.d.mts","./node_modules/@base-ui/react/slider/indicator/sliderindicator.d.mts","./node_modules/@base-ui/react/slider/index.parts.d.mts","./node_modules/@base-ui/react/slider/index.d.mts","./node_modules/@base-ui/react/switch/root/switchroot.d.mts","./node_modules/@base-ui/react/switch/thumb/switchthumb.d.mts","./node_modules/@base-ui/react/switch/index.parts.d.mts","./node_modules/@base-ui/react/switch/index.d.mts","./node_modules/@base-ui/react/tabs/tab/tabstab.d.mts","./node_modules/@base-ui/react/tabs/root/tabsroot.d.mts","./node_modules/@base-ui/react/tabs/indicator/tabsindicator.d.mts","./node_modules/@base-ui/react/tabs/panel/tabspanel.d.mts","./node_modules/@base-ui/react/tabs/list/tabslist.d.mts","./node_modules/@base-ui/react/tabs/index.parts.d.mts","./node_modules/@base-ui/react/tabs/index.d.mts","./node_modules/@base-ui/react/toast/positioner/toastpositioner.d.mts","./node_modules/@base-ui/react/toast/usetoastmanager.d.mts","./node_modules/@base-ui/react/toast/createtoastmanager.d.mts","./node_modules/@base-ui/react/toast/provider/toastprovider.d.mts","./node_modules/@base-ui/react/toast/viewport/toastviewport.d.mts","./node_modules/@base-ui/react/toast/root/toastroot.d.mts","./node_modules/@base-ui/react/toast/content/toastcontent.d.mts","./node_modules/@base-ui/react/toast/description/toastdescription.d.mts","./node_modules/@base-ui/react/toast/title/toasttitle.d.mts","./node_modules/@base-ui/react/toast/close/toastclose.d.mts","./node_modules/@base-ui/react/toast/action/toastaction.d.mts","./node_modules/@base-ui/react/toast/portal/toastportal.d.mts","./node_modules/@base-ui/react/toast/arrow/toastarrow.d.mts","./node_modules/@base-ui/react/toast/index.parts.d.mts","./node_modules/@base-ui/react/toast/index.d.mts","./node_modules/@base-ui/react/toggle/toggle.d.mts","./node_modules/@base-ui/react/toggle/index.d.mts","./node_modules/@base-ui/react/toggle-group/togglegroup.d.mts","./node_modules/@base-ui/react/toggle-group/index.d.mts","./node_modules/@base-ui/react/toolbar/separator/toolbarseparator.d.mts","./node_modules/@base-ui/react/toolbar/root/toolbarroot.d.mts","./node_modules/@base-ui/react/toolbar/group/toolbargroup.d.mts","./node_modules/@base-ui/react/toolbar/button/toolbarbutton.d.mts","./node_modules/@base-ui/react/toolbar/link/toolbarlink.d.mts","./node_modules/@base-ui/react/toolbar/input/toolbarinput.d.mts","./node_modules/@base-ui/react/toolbar/index.parts.d.mts","./node_modules/@base-ui/react/toolbar/index.d.mts","./node_modules/@base-ui/react/tooltip/store/tooltipstore.d.mts","./node_modules/@base-ui/react/tooltip/store/tooltiphandle.d.mts","./node_modules/@base-ui/react/tooltip/root/tooltiproot.d.mts","./node_modules/@base-ui/react/tooltip/trigger/tooltiptrigger.d.mts","./node_modules/@base-ui/react/tooltip/portal/tooltipportal.d.mts","./node_modules/@base-ui/react/tooltip/positioner/tooltippositioner.d.mts","./node_modules/@base-ui/react/tooltip/popup/tooltippopup.d.mts","./node_modules/@base-ui/react/tooltip/arrow/tooltiparrow.d.mts","./node_modules/@base-ui/react/tooltip/provider/tooltipprovider.d.mts","./node_modules/@base-ui/react/tooltip/viewport/tooltipviewport.d.mts","./node_modules/@base-ui/react/tooltip/index.parts.d.mts","./node_modules/@base-ui/react/tooltip/index.d.mts","./node_modules/@base-ui/react/use-render/userender.d.mts","./node_modules/@base-ui/react/use-render/index.d.mts","./node_modules/@base-ui/react/index.d.mts","./node_modules/clsx/clsx.d.mts","./node_modules/tailwind-merge/dist/types.d.ts","./node_modules/class-variance-authority/dist/types.d.ts","./node_modules/class-variance-authority/dist/index.d.ts","./src/lib/cva.config.ts","./src/components/ui/button.tsx","./src/components/ui/input.tsx","./src/components/ui/textarea.tsx","./src/components/ui/input-group.tsx","./node_modules/lucide-react/dist/lucide-react.d.ts","./src/components/ui/combobox.tsx","./src/components/shared/searchselect.tsx","./src/components/ui/label.tsx","./src/components/ui/separator.tsx","./src/components/ui/field.tsx","./src/components/ui/select.tsx","./src/components/key_team_helpers/modelmaxbudgeteditor.tsx","./src/components/object_permission_types.ts","./src/components/ui/tooltip.tsx","./node_modules/react-hook-form/dist/constants.d.ts","./node_modules/react-hook-form/dist/utils/createsubject.d.ts","./node_modules/react-hook-form/dist/types/events.d.ts","./node_modules/react-hook-form/dist/types/path/common.d.ts","./node_modules/react-hook-form/dist/types/path/eager.d.ts","./node_modules/react-hook-form/dist/types/path/index.d.ts","./node_modules/react-hook-form/dist/types/fieldarray.d.ts","./node_modules/react-hook-form/dist/types/resolvers.d.ts","./node_modules/react-hook-form/dist/types/form.d.ts","./node_modules/react-hook-form/dist/types/utils.d.ts","./node_modules/react-hook-form/dist/types/fields.d.ts","./node_modules/react-hook-form/dist/types/errors.d.ts","./node_modules/react-hook-form/dist/types/validator.d.ts","./node_modules/react-hook-form/dist/types/controller.d.ts","./node_modules/react-hook-form/dist/types/watch.d.ts","./node_modules/react-hook-form/dist/types/index.d.ts","./node_modules/react-hook-form/dist/controller.d.ts","./node_modules/react-hook-form/dist/fieldarray.d.ts","./node_modules/react-hook-form/dist/form.d.ts","./node_modules/react-hook-form/dist/formstatesubscribe.d.ts","./node_modules/react-hook-form/dist/logic/appenderrors.d.ts","./node_modules/react-hook-form/dist/logic/createformcontrol.d.ts","./node_modules/react-hook-form/dist/logic/index.d.ts","./node_modules/react-hook-form/dist/usecontroller.d.ts","./node_modules/react-hook-form/dist/usefieldarray.d.ts","./node_modules/react-hook-form/dist/useform.d.ts","./node_modules/react-hook-form/dist/useformcontext.d.ts","./node_modules/react-hook-form/dist/useformstate.d.ts","./node_modules/react-hook-form/dist/usewatch.d.ts","./node_modules/react-hook-form/dist/utils/get.d.ts","./node_modules/react-hook-form/dist/utils/set.d.ts","./node_modules/react-hook-form/dist/utils/index.d.ts","./node_modules/react-hook-form/dist/watch.d.ts","./node_modules/react-hook-form/dist/index.d.ts","./src/utils/textutils.ts","./src/components/common_components/mountedformfield.tsx","./src/components/common_components/check_openapi_schema.tsx","./src/components/mcp_tools/types.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/types.ts","./src/components/mcp_tools/constants.ts","./src/components/key_team_helpers/fetch_available_models_team_key.tsx","./src/components/llm_calls/fetch_models.tsx","./src/components/shared/multiselect.tsx","./src/components/ui/card.tsx","./src/components/add_model/complexity_router_keywords.ts","./src/components/ui/switch.tsx","./src/components/add_model/affinitycontrols.tsx","./src/components/add_model/nonreasoningtiertoggle.tsx","./src/components/add_model/tierrestrictions.tsx","./src/components/add_model/tierconfigintro.tsx","./src/components/add_model/tierrowselect.tsx","./src/components/add_model/modalityroutingcontrols.tsx","./src/components/ui/collapsible.tsx","./src/components/ui/radio-group.tsx","./src/components/ui/slider.tsx","./src/components/add_model/adaptiveroutingconfig.tsx","./src/utils/returnurlutils.ts","./src/utils/roles.ts","./src/app/(dashboard)/hooks/common/querykeysfactory.ts","./src/app/(dashboard)/hooks/uiconfig/useuiconfig.ts","./src/app/(dashboard)/hooks/useauthorized.ts","./src/components/ui/dialog.tsx","./src/components/add_model/classifierprompteditorstate.ts","./src/components/add_model/classifierprompteditor.tsx","./src/components/add_model/openingprompteditor.tsx","./src/app/(dashboard)/hooks/autorouter/usecomplexityscorerdefaults.ts","./src/components/ui/badge.tsx","./src/components/add_model/heuristic_scoring_knobs.ts","./src/components/add_model/heuristicscoringconfig.tsx","./src/components/add_model/classifierreasoningeffortselect.tsx","./src/components/add_model/classifiercircuitbreakerconfig.tsx","./src/components/add_model/classifiervisionconfig.tsx","./src/components/add_model/classificationmethodconfig.tsx","./src/components/add_model/contextwindowescalationconfig.tsx","./src/components/add_model/responseformatcontrols.tsx","./src/components/add_model/stallescalationconfig.tsx","./src/components/add_model/tier_set_actions.ts","./src/components/add_model/tiermodeleffortrows.tsx","./src/components/add_model/escalationkeywords.tsx","./src/components/add_model/semantickeywordmatching.tsx","./src/app/(dashboard)/hooks/guardrails/useguardrails.ts","./src/components/add_model/buildautoroutercompression.ts","./src/components/add_model/compressioncontrols.tsx","./src/components/add_model/complexityrouterconfig.tsx","./src/components/add_model/tier_rows.ts","./src/components/add_model/complexity_router_tiers.ts","./src/components/add_model/keywordtierrules.tsx","./src/components/add_model/build_complexity_router_config.ts","./src/lib/autorouter_presets.ts","./src/components/vector_store_management/types.tsx","./node_modules/@tanstack/table-core/build/lib/utils.d.ts","./node_modules/@tanstack/table-core/build/lib/core/table.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columnvisibility.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columnordering.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columnpinning.d.ts","./node_modules/@tanstack/table-core/build/lib/features/rowpinning.d.ts","./node_modules/@tanstack/table-core/build/lib/core/headers.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columnfaceting.d.ts","./node_modules/@tanstack/table-core/build/lib/features/globalfaceting.d.ts","./node_modules/@tanstack/table-core/build/lib/filterfns.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columnfiltering.d.ts","./node_modules/@tanstack/table-core/build/lib/features/globalfiltering.d.ts","./node_modules/@tanstack/table-core/build/lib/sortingfns.d.ts","./node_modules/@tanstack/table-core/build/lib/features/rowsorting.d.ts","./node_modules/@tanstack/table-core/build/lib/aggregationfns.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columngrouping.d.ts","./node_modules/@tanstack/table-core/build/lib/features/rowexpanding.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columnsizing.d.ts","./node_modules/@tanstack/table-core/build/lib/features/rowpagination.d.ts","./node_modules/@tanstack/table-core/build/lib/features/rowselection.d.ts","./node_modules/@tanstack/table-core/build/lib/core/row.d.ts","./node_modules/@tanstack/table-core/build/lib/core/cell.d.ts","./node_modules/@tanstack/table-core/build/lib/core/column.d.ts","./node_modules/@tanstack/table-core/build/lib/types.d.ts","./node_modules/@tanstack/table-core/build/lib/columnhelper.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getcorerowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getexpandedrowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getfacetedminmaxvalues.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getfacetedrowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getfaceteduniquevalues.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getfilteredrowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getgroupedrowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getpaginationrowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getsortedrowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/index.d.ts","./node_modules/@tanstack/react-table/build/lib/index.d.ts","./src/components/shared/datatable/types.ts","./src/components/shared/datatable/columnmeta.ts","./src/components/ui/skeleton.tsx","./src/components/ui/table.tsx","./src/components/shared/datatable/datatablepagination.tsx","./src/components/shared/datatable/datatable.tsx","./src/components/ui/sheet.tsx","./src/components/shared/datatable/datatablefilterdrawer.tsx","./src/components/ui/checkbox.tsx","./src/components/shared/datatable/datatableselectioncolumn.tsx","./src/components/shared/datatable/datatableviewoptions.tsx","./src/components/shared/datatable/datatabletoolbar.tsx","./src/components/shared/datatable/datatablesortheader.tsx","./src/components/shared/datatable/index.ts","./src/app/(dashboard)/hooks/models/usemodels.ts","./src/components/shared/table_cells/autoroutertag.tsx","./src/components/shared/table_cells/cell_tooltip.tsx","./src/components/shared/table_cells/date_cell.tsx","./src/utils/datautils.ts","./src/components/shared/table_cells/id_cell.tsx","./src/components/shared/entitylink.tsx","./src/components/shared/table_cells/identity_cell.tsx","./src/components/key_scope.ts","./src/components/shared/table_cells/models_cell.tsx","./src/components/shared/table_cells/money_cell.tsx","./src/components/shared/inheritedbudgethint.tsx","./src/components/shared/meter.tsx","./src/components/shared/table_cells/spend_budget_cell.tsx","./src/components/shared/table_cells/status_badge.tsx","./src/components/shared/table_cells/index.ts","./src/lib/serverrootpath.ts","./src/utils/uihref.ts","./src/utils/entitylinks.ts","./src/app/(dashboard)/vector-stores/_components/indexestablecolumns.tsx","./src/app/(dashboard)/vector-stores/_components/indexestable.tsx","./src/app/(dashboard)/vector-stores/_components/indexestab.tsx","./src/components/view_logs/logdetailsdrawer/routingdecisioncard.tsx","./src/lib/http/resolveapibase.ts","./src/lib/http/runtime.ts","./src/components/networking.tsx","./src/components/key_team_helpers/key_list.tsx","./src/app/(dashboard)/networking.ts","./src/app/(dashboard)/hooks/teams/useteams.ts","./src/app/(dashboard)/hooks/organizations/useorganizations.ts","./src/app/(dashboard)/hooks/useisorgadmin.ts","./src/app/(dashboard)/hooks/healthreadiness/usehealthreadinessdetails.ts","./src/utils/proxyutils.ts","./src/app/(dashboard)/hooks/proxysettings/useproxysettings.ts","./src/app/(dashboard)/hooks/uselogout.ts","./src/contexts/themecontext.tsx","./src/components/ui/scroll-area.tsx","./src/components/shared/sidebar.tsx","./src/utils/capabilities.ts","./src/utils/localstorageutils.ts","./src/app/(dashboard)/hooks/usedisableshownewbadge.ts","./src/components/betabadge.tsx","./src/app/(dashboard)/hooks/usedisableblogposts.ts","./src/app/(dashboard)/hooks/usedisablebouncingicon.ts","./src/app/(dashboard)/hooks/usedisableshowprompts.ts","./src/components/navbar/navdisplayname.ts","./src/components/shared/copybutton.tsx","./src/components/ui/avatar.tsx","./src/components/ui/popover.tsx","./src/components/sidebaraccountmenu/sidebaraccountmenu.tsx","./src/app/(dashboard)/hooks/license/uselicenseinfo.ts","./src/utils/licenseutils.ts","./src/components/sidebarusagecard.tsx","./src/components/leftnav.tsx","./src/app/(dashboard)/legacypageroutes.ts","./src/app/(dashboard)/legacypageroutes.test.ts","./src/app/(dashboard)/access-groups/_components/types.ts","./node_modules/zod/v4/core/standard-schema.d.cts","./node_modules/zod/v4/core/util.d.cts","./node_modules/zod/v4/core/versions.d.cts","./node_modules/zod/v4/core/schemas.d.cts","./node_modules/zod/v4/core/checks.d.cts","./node_modules/zod/v4/core/errors.d.cts","./node_modules/zod/v4/core/core.d.cts","./node_modules/zod/v4/core/parse.d.cts","./node_modules/zod/v4/core/regexes.d.cts","./node_modules/zod/v4/locales/ar.d.cts","./node_modules/zod/v4/locales/az.d.cts","./node_modules/zod/v4/locales/be.d.cts","./node_modules/zod/v4/locales/ca.d.cts","./node_modules/zod/v4/locales/cs.d.cts","./node_modules/zod/v4/locales/de.d.cts","./node_modules/zod/v4/locales/en.d.cts","./node_modules/zod/v4/locales/eo.d.cts","./node_modules/zod/v4/locales/es.d.cts","./node_modules/zod/v4/locales/fa.d.cts","./node_modules/zod/v4/locales/fi.d.cts","./node_modules/zod/v4/locales/fr.d.cts","./node_modules/zod/v4/locales/fr-ca.d.cts","./node_modules/zod/v4/locales/he.d.cts","./node_modules/zod/v4/locales/hu.d.cts","./node_modules/zod/v4/locales/id.d.cts","./node_modules/zod/v4/locales/it.d.cts","./node_modules/zod/v4/locales/ja.d.cts","./node_modules/zod/v4/locales/kh.d.cts","./node_modules/zod/v4/locales/ko.d.cts","./node_modules/zod/v4/locales/mk.d.cts","./node_modules/zod/v4/locales/ms.d.cts","./node_modules/zod/v4/locales/nl.d.cts","./node_modules/zod/v4/locales/no.d.cts","./node_modules/zod/v4/locales/ota.d.cts","./node_modules/zod/v4/locales/ps.d.cts","./node_modules/zod/v4/locales/pl.d.cts","./node_modules/zod/v4/locales/pt.d.cts","./node_modules/zod/v4/locales/ru.d.cts","./node_modules/zod/v4/locales/sl.d.cts","./node_modules/zod/v4/locales/sv.d.cts","./node_modules/zod/v4/locales/ta.d.cts","./node_modules/zod/v4/locales/th.d.cts","./node_modules/zod/v4/locales/tr.d.cts","./node_modules/zod/v4/locales/ua.d.cts","./node_modules/zod/v4/locales/ur.d.cts","./node_modules/zod/v4/locales/vi.d.cts","./node_modules/zod/v4/locales/zh-cn.d.cts","./node_modules/zod/v4/locales/zh-tw.d.cts","./node_modules/zod/v4/locales/index.d.cts","./node_modules/zod/v4/core/registries.d.cts","./node_modules/zod/v4/core/doc.d.cts","./node_modules/zod/v4/core/function.d.cts","./node_modules/zod/v4/core/api.d.cts","./node_modules/zod/v4/core/json-schema.d.cts","./node_modules/zod/v4/core/to-json-schema.d.cts","./node_modules/zod/v4/core/index.d.cts","./node_modules/zod/v4/classic/errors.d.cts","./node_modules/zod/v4/classic/parse.d.cts","./node_modules/zod/v4/classic/schemas.d.cts","./node_modules/zod/v4/classic/checks.d.cts","./node_modules/zod/v4/classic/compat.d.cts","./node_modules/zod/v4/classic/iso.d.cts","./node_modules/zod/v4/classic/coerce.d.cts","./node_modules/zod/v4/classic/external.d.cts","./node_modules/zod/v4/classic/index.d.cts","./node_modules/zod/v4/index.d.cts","./src/app/(dashboard)/access-groups/_components/access-group-create/schema.ts","./src/app/(dashboard)/access-groups/_components/access-group-create/mapper.ts","./src/app/(dashboard)/access-groups/_components/access-group-create/mapper.test.ts","./src/app/(dashboard)/agents/_components/agent_config.ts","./src/app/(dashboard)/agents/_components/agent_discovery_utils.ts","./src/app/(dashboard)/agents/_components/agent_discovery_utils.test.ts","./src/components/agents/types.ts","./src/app/(dashboard)/agents/_components/agent_type_utils.ts","./src/app/(dashboard)/agents/_components/agent_type_utils.test.ts","./src/app/(dashboard)/budgets/_components/budgetprecision.ts","./src/app/(dashboard)/budgets/_components/budgetprecision.test.ts","./src/app/(dashboard)/budgets/_components/constants.ts","./src/app/(dashboard)/caching/_components/cache_settings/cachesettingsfields.ts","./src/app/(dashboard)/caching/_components/cache_settings/cachesettingsutils.ts","./src/app/(dashboard)/caching/_components/cache_settings/cachesettingsutils.test.ts","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredisfields.ts","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredisutils.ts","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredisutils.test.ts","./src/app/(dashboard)/cost-optimization/_components/autorouterbenchmarks.ts","./src/app/(dashboard)/cost-optimization/_components/autorouterbenchmarks.test.ts","./src/components/usagepage/types.ts","./src/app/(dashboard)/cost-optimization/_components/costoptimizationutils.ts","./src/app/(dashboard)/cost-optimization/_components/costoptimizationutils.test.ts","./src/app/(dashboard)/cost-optimization/_components/helpers.ts","./src/app/(dashboard)/cost-optimization/_components/helpers.test.ts","./node_modules/openapi-typescript-helpers/dist/index.d.mts","./node_modules/openapi-fetch/dist/index.d.mts","./node_modules/openapi-react-query/dist/index.d.mts","./src/lib/http/api.ts","./src/app/(dashboard)/usage/_components/hooks/usepaginateddailyactivity.ts","./src/app/(dashboard)/cost-optimization/_components/usedailyactivityrange.ts","./src/app/(dashboard)/cost-optimization/_components/useautorouterbenchmarks.ts","./src/app/(dashboard)/cost-optimization/_components/useautorouterbenchmarks.test.ts","./src/app/(dashboard)/cost-optimization/_components/useshadoweval.ts","./src/app/(dashboard)/cost-optimization/_components/useshadoweval.test.ts","./src/components/ui/alert-dialog.tsx","./src/components/ui/tabs.tsx","./src/app/(dashboard)/cost-tracking/_components/types.ts","./src/components/common_components/simple_table.tsx","./src/lib/assetpaths.ts","./src/components/provider_info_helpers.tsx","./src/lib/logotreatments.ts","./src/components/molecules/logo/logo.tsx","./src/app/(dashboard)/cost-tracking/_components/provider_discount_table.tsx","./src/app/(dashboard)/cost-tracking/_components/add_provider_form.tsx","./src/app/(dashboard)/cost-tracking/_components/provider_margin_table.tsx","./src/app/(dashboard)/cost-tracking/_components/add_margin_form.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/types.ts","./src/hooks/use-safe-layout-effect.ts","./src/components/ui/ui-loading-spinner.tsx","./src/components/ui/dropdown-menu.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_utils.ts","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_dropdown.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_cost_results.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/use_multi_cost_estimate.ts","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/index.tsx","./src/components/helplink.tsx","./node_modules/@types/react-syntax-highlighter/index.d.ts","./node_modules/next-themes/dist/index.d.ts","./src/hooks/usesyntaxtheme.ts","./src/components/codeblock.tsx","./src/app/(dashboard)/cost-tracking/_components/how_it_works.tsx","./src/app/(dashboard)/cost-tracking/_components/provider_display_helpers.ts","./src/app/(dashboard)/cost-tracking/_components/use_discount_config.ts","./src/app/(dashboard)/cost-tracking/_components/use_margin_config.ts","./src/app/(dashboard)/cost-tracking/_components/use_block_unpriced_config.ts","./src/app/(dashboard)/cost-tracking/_components/cost_tracking_settings.tsx","./src/app/(dashboard)/cost-tracking/_components/index.ts","./src/app/(dashboard)/cost-tracking/_components/provider_display_helpers.test.ts","./node_modules/@types/aria-query/index.d.ts","./node_modules/@testing-library/dom/types/matches.d.ts","./node_modules/@testing-library/dom/types/wait-for.d.ts","./node_modules/@testing-library/dom/types/query-helpers.d.ts","./node_modules/@testing-library/dom/types/queries.d.ts","./node_modules/@testing-library/dom/types/get-queries-for-element.d.ts","./node_modules/pretty-format/build/types.d.ts","./node_modules/pretty-format/build/index.d.ts","./node_modules/@testing-library/dom/types/screen.d.ts","./node_modules/@testing-library/dom/types/wait-for-element-to-be-removed.d.ts","./node_modules/@testing-library/dom/types/get-node-text.d.ts","./node_modules/@testing-library/dom/types/events.d.ts","./node_modules/@testing-library/dom/types/pretty-dom.d.ts","./node_modules/@testing-library/dom/types/role-helpers.d.ts","./node_modules/@testing-library/dom/types/config.d.ts","./node_modules/@testing-library/dom/types/suggestions.d.ts","./node_modules/@testing-library/dom/types/index.d.ts","./node_modules/@types/react-dom/test-utils/index.d.ts","./node_modules/@testing-library/react/types/index.d.ts","./src/app/(dashboard)/cost-tracking/_components/use_block_unpriced_config.test.ts","./src/app/(dashboard)/cost-tracking/_components/use_discount_config.test.ts","./src/app/(dashboard)/cost-tracking/_components/use_margin_config.test.ts","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_utils.test.ts","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/use_multi_cost_estimate.test.ts","./src/app/(dashboard)/guardrails/_components/guardrail_garden_configs.ts","./src/app/(dashboard)/guardrails/_components/guardrail_info_helpers.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden_data.ts","./src/app/(dashboard)/guardrails/_components/guardrail_garden_data.test.ts","./src/app/(dashboard)/guardrails/_components/content_filter/action_options.ts","./src/app/(dashboard)/guardrails/_components/custom_code/customcodemodal.tsx","./src/app/(dashboard)/guardrails/_components/custom_code/index.ts","./src/app/(dashboard)/hooks/useauthorized.serverrootpath.test.ts","./src/app/(dashboard)/hooks/useauthorized.test.ts","./src/app/(dashboard)/hooks/usecan.ts","./src/app/(dashboard)/hooks/usedisableshownewbadge.test.ts","./src/app/(dashboard)/hooks/usedisableshowprompts.test.ts","./src/app/(dashboard)/hooks/usehideautorouterannouncement.ts","./src/app/(dashboard)/hooks/useisorgadmin.test.ts","./src/utils/tabroutes.ts","./src/app/(dashboard)/hooks/usetabrouting.ts","./src/app/(dashboard)/hooks/accessgroups/useaccessgroups.ts","./src/app/(dashboard)/hooks/accessgroups/useaccessgroupdetails.ts","./src/app/(dashboard)/hooks/accessgroups/useaccessgroups.test.ts","./src/app/(dashboard)/hooks/accessgroups/usedeleteaccessgroup.ts","./src/app/(dashboard)/hooks/accessgroups/useeditaccessgroup.ts","./src/app/(dashboard)/hooks/agents/useagents.ts","./src/app/(dashboard)/hooks/agents/useagents.test.ts","./src/app/(dashboard)/hooks/autorouter/useautorouterpresets.ts","./src/app/(dashboard)/hooks/blogposts/useblogposts.ts","./src/app/(dashboard)/hooks/budgets/budgetfilters.ts","./src/app/(dashboard)/hooks/budgets/budgetfilters.test.ts","./node_modules/@tanstack/react-store/dist/createstorecontext.d.ts","./node_modules/@tanstack/store/dist/alien.d.ts","./node_modules/@tanstack/store/dist/types.d.ts","./node_modules/@tanstack/store/dist/atom.d.ts","./node_modules/@tanstack/store/dist/store.d.ts","./node_modules/@tanstack/store/dist/shallow.d.ts","./node_modules/@tanstack/store/dist/index.d.ts","./node_modules/@tanstack/react-store/dist/usecreateatom.d.ts","./node_modules/@tanstack/react-store/dist/usecreatestore.d.ts","./node_modules/@tanstack/react-store/dist/useselector.d.ts","./node_modules/@tanstack/react-store/dist/useatom.d.ts","./node_modules/@tanstack/react-store/dist/_usestore.d.ts","./node_modules/@tanstack/react-store/dist/usestore.d.ts","./node_modules/@tanstack/react-store/dist/index.d.ts","./node_modules/@tanstack/pacer/dist/types.d.ts","./node_modules/@tanstack/pacer/dist/debouncer.d.ts","./node_modules/@tanstack/react-pacer/dist/debouncer/usedebouncer.d.ts","./node_modules/@tanstack/react-pacer/dist/debouncer/usedebouncedcallback.d.ts","./node_modules/@tanstack/react-pacer/dist/debouncer/usedebouncedstate.d.ts","./node_modules/@tanstack/react-pacer/dist/debouncer/usedebouncedvalue.d.ts","./node_modules/@tanstack/react-pacer/dist/debouncer/index.d.ts","./src/utils/debounceconstants.ts","./src/app/(dashboard)/hooks/common/useresourcelist.ts","./src/app/(dashboard)/hooks/budgets/usebudgets.ts","./src/app/(dashboard)/hooks/caching/usecacheactivity.ts","./src/app/(dashboard)/hooks/caching/usecacheactivity.test.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzerocreate.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzerocreate.test.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzerodryrun.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzerodryrun.test.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzeroexport.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzeroexport.test.ts","./src/components/cloudzerocosttracking/types.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzerosettings.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzerosettings.test.ts","./src/app/(dashboard)/hooks/common/querykeysfactory.test.ts","./src/app/(dashboard)/hooks/configoverrides/cyberarkapi.ts","./src/app/(dashboard)/hooks/configoverrides/hashicorpvaultapi.ts","./src/app/(dashboard)/hooks/configoverrides/usecyberarkconfig.ts","./src/app/(dashboard)/hooks/configoverrides/usedeletecyberarkconfig.ts","./src/app/(dashboard)/hooks/configoverrides/usehashicorpvaultconfig.ts","./src/app/(dashboard)/hooks/configoverrides/usedeletehashicorpvaultconfig.ts","./src/app/(dashboard)/hooks/configoverrides/useupdatecyberarkconfig.ts","./src/app/(dashboard)/hooks/configoverrides/useupdatehashicorpvaultconfig.ts","./src/app/(dashboard)/hooks/coordinationredis/usecoordinationredissettings.ts","./src/app/(dashboard)/hooks/credentials/usecredentials.ts","./src/app/(dashboard)/hooks/credentials/usecredentials.test.ts","./src/app/(dashboard)/hooks/customers/usecustomers.ts","./src/app/(dashboard)/hooks/customers/usecustomers.test.ts","./src/app/(dashboard)/hooks/guardrails/useguardrails.test.ts","./src/app/(dashboard)/hooks/guardrails/useguardrailsusage.ts","./src/app/(dashboard)/hooks/guardrails/useguardrailsusage.test.ts","./src/app/(dashboard)/hooks/guardrails/useregisterguardrail.ts","./src/app/(dashboard)/hooks/keys/usekeyaliases.ts","./src/app/(dashboard)/hooks/keys/usekeyaliases.test.ts","./src/app/(dashboard)/hooks/keys/usekeys.ts","./src/app/(dashboard)/hooks/keys/usekeyinfo.ts","./src/app/(dashboard)/hooks/keys/usekeys.test.ts","./src/app/(dashboard)/hooks/keys/useresetkeyspend.ts","./src/app/(dashboard)/hooks/keys/usesetkeyblockedstate.ts","./src/app/(dashboard)/hooks/keys/usesetkeyblockedstate.test.ts","./src/app/(dashboard)/hooks/logdetails/uselogdetails.ts","./src/app/(dashboard)/hooks/login/uselogin.ts","./src/app/(dashboard)/hooks/mcpsemanticfiltersettings/usemcpsemanticfiltersettings.ts","./src/app/(dashboard)/hooks/mcpsemanticfiltersettings/useupdatemcpsemanticfiltersettings.ts","./src/app/(dashboard)/hooks/mcpservers/usemcpaccessgroups.ts","./src/app/(dashboard)/hooks/mcpservers/usemcpaccessgroups.test.ts","./src/app/(dashboard)/hooks/mcpservers/usemcpserverhealth.ts","./src/app/(dashboard)/hooks/mcpservers/usemcpserverhealth.test.ts","./src/app/(dashboard)/hooks/mcpservers/usemcpservers.ts","./src/app/(dashboard)/hooks/mcpservers/usemcpservers.test.ts","./src/app/(dashboard)/hooks/mcpservers/usemcptoolsets.ts","./src/app/(dashboard)/hooks/mcptoolsearchsettings/usemcptoolsearchsettings.ts","./src/app/(dashboard)/hooks/modelaccessgroups/usemodelaccessgroups.ts","./src/app/(dashboard)/hooks/modelaccessgroups/usedeletemodelaccessgroupbudget.ts","./src/app/(dashboard)/hooks/modelaccessgroups/usesetmodelaccessgroupbudget.ts","./src/app/(dashboard)/hooks/models/usemodelcostmap.ts","./src/app/(dashboard)/hooks/models/usemodelcostmap.test.ts","./src/app/(dashboard)/hooks/models/usemodels.test.ts","./src/app/(dashboard)/hooks/onboarding/useonboarding.ts","./src/app/(dashboard)/hooks/onboarding/useonboarding.test.ts","./src/app/(dashboard)/hooks/organizations/useorganizations.test.ts","./src/app/(dashboard)/hooks/projects/useprojects.ts","./src/app/(dashboard)/hooks/projects/usecreateproject.ts","./src/app/(dashboard)/hooks/projects/usecreateproject.test.ts","./src/app/(dashboard)/hooks/projects/usedeleteproject.ts","./src/app/(dashboard)/hooks/projects/usedeleteproject.test.ts","./src/app/(dashboard)/hooks/projects/useprojectdetails.ts","./src/app/(dashboard)/hooks/projects/useprojectdetails.test.ts","./src/app/(dashboard)/hooks/projects/useprojects.test.ts","./src/app/(dashboard)/hooks/projects/useupdateproject.ts","./src/app/(dashboard)/hooks/projects/useupdateproject.test.ts","./src/app/(dashboard)/hooks/providers/useproviderfields.ts","./src/app/(dashboard)/hooks/providers/useproviderfields.test.ts","./src/app/(dashboard)/hooks/proxyconfig/useproxyconfig.ts","./src/app/(dashboard)/hooks/proxyconfig/useproxyconfig.test.ts","./src/app/(dashboard)/hooks/router/userouterfields.ts","./src/app/(dashboard)/hooks/router/userouterfields.test.ts","./src/app/(dashboard)/hooks/routersettings/useupdateretrypolicy.ts","./src/components/routing_groups/types.ts","./src/app/(dashboard)/hooks/routinggroups/useroutinggroups.ts","./src/app/(dashboard)/hooks/spendlogs/usespendlogendusers.ts","./src/app/(dashboard)/hooks/spendlogs/usespendlogendusers.test.ts","./src/app/(dashboard)/hooks/spendlogs/usespendlogusers.ts","./src/app/(dashboard)/hooks/spendlogs/usespendlogusers.test.ts","./src/app/(dashboard)/hooks/sso/useeditssosettings.ts","./src/app/(dashboard)/hooks/sso/useeditssosettings.test.ts","./src/app/(dashboard)/hooks/sso/usessosettings.ts","./src/app/(dashboard)/hooks/sso/usessosettings.test.ts","./src/app/(dashboard)/hooks/storemodelindb/usestoremodelindb.ts","./src/app/(dashboard)/hooks/storemodelindb/usestoremodelindb.test.ts","./src/app/(dashboard)/hooks/storerequestinspendlogs/usestorerequestinspendlogs.ts","./src/app/(dashboard)/hooks/tags/usetags.ts","./src/app/(dashboard)/hooks/tags/usetags.test.ts","./src/app/(dashboard)/hooks/teams/useteammetadataschema.ts","./src/app/(dashboard)/hooks/teams/useteammetadataschema.test.ts","./src/app/(dashboard)/hooks/teams/useteams.test.ts","./src/app/(dashboard)/hooks/uiconfig/useuiconfig.test.ts","./src/app/(dashboard)/hooks/uisettings/useuisettings.ts","./src/app/(dashboard)/hooks/uisettings/useptucostattributionenabled.ts","./src/app/(dashboard)/hooks/uisettings/useptucostattributionenabled.test.ts","./src/app/(dashboard)/hooks/uisettings/useuisettings.test.ts","./src/app/(dashboard)/hooks/uisettings/useupdateuisettings.ts","./src/app/(dashboard)/hooks/uisettings/useupdateuisettings.test.ts","./src/app/(dashboard)/hooks/userbanner/useuserbanner.ts","./src/app/(dashboard)/hooks/userbanner/useupdateuserbanner.ts","./src/app/(dashboard)/hooks/users/usecurrentuser.ts","./src/app/(dashboard)/hooks/users/usecurrentuser.test.ts","./src/app/(dashboard)/hooks/users/useusers.ts","./src/app/(dashboard)/hooks/users/useusers.test.ts","./src/app/(dashboard)/mcp-servers/_components/createoauthuistate.ts","./src/app/(dashboard)/mcp-servers/_components/createoauthuistate.test.ts","./src/app/(dashboard)/mcp-servers/_components/utils.tsx","./src/app/(dashboard)/mcp-servers/_components/createserverpayload.ts","./src/app/(dashboard)/mcp-servers/_components/createserverpayload.test.ts","./src/app/(dashboard)/mcp-servers/_components/editserverpayload.ts","./src/app/(dashboard)/mcp-servers/_components/editserverpayload.differential.cases.ts","./src/app/(dashboard)/mcp-servers/_components/editserverpayload.differential.test.ts","./src/app/(dashboard)/mcp-servers/_components/importconnectorconfig.ts","./src/app/(dashboard)/mcp-servers/_components/importconnectorconfig.test.ts","./src/app/(dashboard)/mcp-servers/_components/mcpfieldrules.ts","./src/app/(dashboard)/mcp-servers/_components/mcpfieldrules.test.ts","./src/app/(dashboard)/mcp-servers/_components/mcpformstore.ts","./src/app/(dashboard)/mcp-servers/_components/mountedserverfields.ts","./src/app/(dashboard)/mcp-servers/_components/mountedserverfields.test.ts","./node_modules/@testing-library/user-event/dist/types/event/eventmap.d.ts","./node_modules/@testing-library/user-event/dist/types/event/types.d.ts","./node_modules/@testing-library/user-event/dist/types/event/dispatchevent.d.ts","./node_modules/@testing-library/user-event/dist/types/event/focus.d.ts","./node_modules/@testing-library/user-event/dist/types/event/input.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/click/isclickableinput.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/datatransfer/blob.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/datatransfer/datatransfer.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/datatransfer/filelist.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/datatransfer/clipboard.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/edit/timevalue.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/edit/iscontenteditable.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/edit/iseditable.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/edit/maxlength.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/edit/setfiles.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/focus/cursor.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/focus/getactiveelement.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/focus/gettabdestination.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/focus/isfocusable.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/focus/selection.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/focus/selector.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/keydef/readnextdescriptor.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/cloneevent.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/findclosest.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/getdocumentfromnode.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/gettreediff.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/getwindow.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/isdescendantorself.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/iselementtype.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/isvisible.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/isdisabled.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/level.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/wait.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/pointer/csspointerevents.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/index.d.ts","./node_modules/@testing-library/user-event/dist/types/document/ui.d.ts","./node_modules/@testing-library/user-event/dist/types/document/getvalueortextcontent.d.ts","./node_modules/@testing-library/user-event/dist/types/document/copyselection.d.ts","./node_modules/@testing-library/user-event/dist/types/document/trackvalue.d.ts","./node_modules/@testing-library/user-event/dist/types/document/index.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/getinputrange.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/modifyselection.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/moveselection.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/setselectionpermouse.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/modifyselectionpermouse.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/selectall.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/setselectionrange.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/setselection.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/updateselectiononfocus.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/index.d.ts","./node_modules/@testing-library/user-event/dist/types/event/index.d.ts","./node_modules/@testing-library/user-event/dist/types/system/pointer/buttons.d.ts","./node_modules/@testing-library/user-event/dist/types/system/pointer/shared.d.ts","./node_modules/@testing-library/user-event/dist/types/system/pointer/index.d.ts","./node_modules/@testing-library/user-event/dist/types/system/index.d.ts","./node_modules/@testing-library/user-event/dist/types/system/keyboard.d.ts","./node_modules/@testing-library/user-event/dist/types/options.d.ts","./node_modules/@testing-library/user-event/dist/types/convenience/click.d.ts","./node_modules/@testing-library/user-event/dist/types/convenience/hover.d.ts","./node_modules/@testing-library/user-event/dist/types/convenience/tab.d.ts","./node_modules/@testing-library/user-event/dist/types/convenience/index.d.ts","./node_modules/@testing-library/user-event/dist/types/keyboard/index.d.ts","./node_modules/@testing-library/user-event/dist/types/clipboard/copy.d.ts","./node_modules/@testing-library/user-event/dist/types/clipboard/cut.d.ts","./node_modules/@testing-library/user-event/dist/types/clipboard/paste.d.ts","./node_modules/@testing-library/user-event/dist/types/clipboard/index.d.ts","./node_modules/@testing-library/user-event/dist/types/pointer/index.d.ts","./node_modules/@testing-library/user-event/dist/types/utility/clear.d.ts","./node_modules/@testing-library/user-event/dist/types/utility/selectoptions.d.ts","./node_modules/@testing-library/user-event/dist/types/utility/type.d.ts","./node_modules/@testing-library/user-event/dist/types/utility/upload.d.ts","./node_modules/@testing-library/user-event/dist/types/utility/index.d.ts","./node_modules/@testing-library/user-event/dist/types/setup/api.d.ts","./node_modules/@testing-library/user-event/dist/types/setup/directapi.d.ts","./node_modules/@testing-library/user-event/dist/types/setup/setup.d.ts","./node_modules/@testing-library/user-event/dist/types/setup/index.d.ts","./node_modules/@testing-library/user-event/dist/types/index.d.ts","./src/app/(dashboard)/mcp-servers/_components/testutils.ts","./src/app/(dashboard)/mcp-servers/_components/toolcallarguments.ts","./src/app/(dashboard)/mcp-servers/_components/toolcallarguments.test.ts","./node_modules/nuqs/dist/defs-butbdnwx.d.ts","./node_modules/nuqs/dist/context-3xask51n.d.ts","./node_modules/nuqs/dist/adapters/testing.d.ts","./node_modules/@standard-schema/spec/dist/index.d.ts","./node_modules/nuqs/dist/index.d.ts","./src/app/(dashboard)/models-and-endpoints/detailnavigation.ts","./src/app/(dashboard)/models-and-endpoints/detailnavigation.test.ts","./src/app/(dashboard)/models-and-endpoints/usemodeldashboarddata.ts","./src/app/(dashboard)/models-and-endpoints/components/accessgroupbudgetpayload.ts","./src/app/(dashboard)/models-and-endpoints/components/accessgroupbudgetpayload.test.ts","./src/components/add_model/auto_router_strategies.ts","./src/utils/modelpermissions.ts","./src/app/(dashboard)/models-and-endpoints/components/autorouters/autorouterrows.ts","./src/app/(dashboard)/models-and-endpoints/components/autorouters/autorouterrows.test.ts","./src/app/(dashboard)/models-and-endpoints/components/autorouters/fitpills.ts","./src/app/(dashboard)/models-and-endpoints/components/autorouters/fitpills.test.ts","./src/app/(dashboard)/models-and-endpoints/utils/modeldatatransformer.ts","./src/app/(dashboard)/models-and-endpoints/utils/modeldatatransformer.test.ts","./src/components/chat_ui/mode_endpoint_mapping.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatconstants.ts","./src/app/(dashboard)/playground/components/chat_ui/uploadvalidation.ts","./src/app/(dashboard)/playground/components/chat_ui/uploadvalidation.test.ts","./src/app/(dashboard)/playground/llm_calls/fetch_agents.tsx","./src/app/(dashboard)/playground/components/compareui/endpoint_config.ts","./src/app/(dashboard)/playground/components/compareui/endpoint_config.test.ts","./src/utils/promptcacheusage.ts","./src/components/chat_ui/responsemetrics.tsx","./src/components/chat_ui/types.ts","./src/app/(dashboard)/playground/hooks/usechathistory.ts","./src/app/(dashboard)/playground/hooks/usechathistory.test.ts","./src/components/llm_calls/code_interpreter_handler.ts","./src/app/(dashboard)/playground/hooks/usecodeinterpreter.ts","./src/components/policies/types.ts","./src/app/(dashboard)/policies/_components/build_attachment_data.ts","./src/app/(dashboard)/policies/_components/build_attachment_data.test.ts","./src/app/(dashboard)/policies/_components/scope_validation.ts","./src/app/(dashboard)/policies/_components/scope_validation.test.ts","./src/app/(dashboard)/projects/_components/projectmodals/projectformschema.ts","./src/components/shared/usepaginatedcombobox.ts","./src/components/shared/paginatedsearchselect.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/tagsinput.tsx","./src/components/agent_management/agentselector.tsx","./src/components/common_components/accessgroupselector.tsx","./src/components/common_components/budget_duration_dropdown.tsx","./src/components/common_components/keylifecyclesettings.tsx","./node_modules/@heroicons/react/outline/academiccapicon.d.ts","./node_modules/@heroicons/react/outline/adjustmentsicon.d.ts","./node_modules/@heroicons/react/outline/annotationicon.d.ts","./node_modules/@heroicons/react/outline/archiveicon.d.ts","./node_modules/@heroicons/react/outline/arrowcircledownicon.d.ts","./node_modules/@heroicons/react/outline/arrowcirclelefticon.d.ts","./node_modules/@heroicons/react/outline/arrowcirclerighticon.d.ts","./node_modules/@heroicons/react/outline/arrowcircleupicon.d.ts","./node_modules/@heroicons/react/outline/arrowdownicon.d.ts","./node_modules/@heroicons/react/outline/arrowlefticon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowdownicon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowlefticon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowrighticon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowupicon.d.ts","./node_modules/@heroicons/react/outline/arrowrighticon.d.ts","./node_modules/@heroicons/react/outline/arrowsmdownicon.d.ts","./node_modules/@heroicons/react/outline/arrowsmlefticon.d.ts","./node_modules/@heroicons/react/outline/arrowsmrighticon.d.ts","./node_modules/@heroicons/react/outline/arrowsmupicon.d.ts","./node_modules/@heroicons/react/outline/arrowupicon.d.ts","./node_modules/@heroicons/react/outline/arrowsexpandicon.d.ts","./node_modules/@heroicons/react/outline/atsymbolicon.d.ts","./node_modules/@heroicons/react/outline/backspaceicon.d.ts","./node_modules/@heroicons/react/outline/badgecheckicon.d.ts","./node_modules/@heroicons/react/outline/banicon.d.ts","./node_modules/@heroicons/react/outline/beakericon.d.ts","./node_modules/@heroicons/react/outline/bellicon.d.ts","./node_modules/@heroicons/react/outline/bookopenicon.d.ts","./node_modules/@heroicons/react/outline/bookmarkalticon.d.ts","./node_modules/@heroicons/react/outline/bookmarkicon.d.ts","./node_modules/@heroicons/react/outline/briefcaseicon.d.ts","./node_modules/@heroicons/react/outline/cakeicon.d.ts","./node_modules/@heroicons/react/outline/calculatoricon.d.ts","./node_modules/@heroicons/react/outline/calendaricon.d.ts","./node_modules/@heroicons/react/outline/cameraicon.d.ts","./node_modules/@heroicons/react/outline/cashicon.d.ts","./node_modules/@heroicons/react/outline/chartbaricon.d.ts","./node_modules/@heroicons/react/outline/chartpieicon.d.ts","./node_modules/@heroicons/react/outline/chartsquarebaricon.d.ts","./node_modules/@heroicons/react/outline/chatalt2icon.d.ts","./node_modules/@heroicons/react/outline/chatalticon.d.ts","./node_modules/@heroicons/react/outline/chaticon.d.ts","./node_modules/@heroicons/react/outline/checkcircleicon.d.ts","./node_modules/@heroicons/react/outline/checkicon.d.ts","./node_modules/@heroicons/react/outline/chevrondoubledownicon.d.ts","./node_modules/@heroicons/react/outline/chevrondoublelefticon.d.ts","./node_modules/@heroicons/react/outline/chevrondoublerighticon.d.ts","./node_modules/@heroicons/react/outline/chevrondoubleupicon.d.ts","./node_modules/@heroicons/react/outline/chevrondownicon.d.ts","./node_modules/@heroicons/react/outline/chevronlefticon.d.ts","./node_modules/@heroicons/react/outline/chevronrighticon.d.ts","./node_modules/@heroicons/react/outline/chevronupicon.d.ts","./node_modules/@heroicons/react/outline/chipicon.d.ts","./node_modules/@heroicons/react/outline/clipboardcheckicon.d.ts","./node_modules/@heroicons/react/outline/clipboardcopyicon.d.ts","./node_modules/@heroicons/react/outline/clipboardlisticon.d.ts","./node_modules/@heroicons/react/outline/clipboardicon.d.ts","./node_modules/@heroicons/react/outline/clockicon.d.ts","./node_modules/@heroicons/react/outline/clouddownloadicon.d.ts","./node_modules/@heroicons/react/outline/clouduploadicon.d.ts","./node_modules/@heroicons/react/outline/cloudicon.d.ts","./node_modules/@heroicons/react/outline/codeicon.d.ts","./node_modules/@heroicons/react/outline/cogicon.d.ts","./node_modules/@heroicons/react/outline/collectionicon.d.ts","./node_modules/@heroicons/react/outline/colorswatchicon.d.ts","./node_modules/@heroicons/react/outline/creditcardicon.d.ts","./node_modules/@heroicons/react/outline/cubetransparenticon.d.ts","./node_modules/@heroicons/react/outline/cubeicon.d.ts","./node_modules/@heroicons/react/outline/currencybangladeshiicon.d.ts","./node_modules/@heroicons/react/outline/currencydollaricon.d.ts","./node_modules/@heroicons/react/outline/currencyeuroicon.d.ts","./node_modules/@heroicons/react/outline/currencypoundicon.d.ts","./node_modules/@heroicons/react/outline/currencyrupeeicon.d.ts","./node_modules/@heroicons/react/outline/currencyyenicon.d.ts","./node_modules/@heroicons/react/outline/cursorclickicon.d.ts","./node_modules/@heroicons/react/outline/databaseicon.d.ts","./node_modules/@heroicons/react/outline/desktopcomputericon.d.ts","./node_modules/@heroicons/react/outline/devicemobileicon.d.ts","./node_modules/@heroicons/react/outline/devicetableticon.d.ts","./node_modules/@heroicons/react/outline/documentaddicon.d.ts","./node_modules/@heroicons/react/outline/documentdownloadicon.d.ts","./node_modules/@heroicons/react/outline/documentduplicateicon.d.ts","./node_modules/@heroicons/react/outline/documentremoveicon.d.ts","./node_modules/@heroicons/react/outline/documentreporticon.d.ts","./node_modules/@heroicons/react/outline/documentsearchicon.d.ts","./node_modules/@heroicons/react/outline/documenttexticon.d.ts","./node_modules/@heroicons/react/outline/documenticon.d.ts","./node_modules/@heroicons/react/outline/dotscirclehorizontalicon.d.ts","./node_modules/@heroicons/react/outline/dotshorizontalicon.d.ts","./node_modules/@heroicons/react/outline/dotsverticalicon.d.ts","./node_modules/@heroicons/react/outline/downloadicon.d.ts","./node_modules/@heroicons/react/outline/duplicateicon.d.ts","./node_modules/@heroicons/react/outline/emojihappyicon.d.ts","./node_modules/@heroicons/react/outline/emojisadicon.d.ts","./node_modules/@heroicons/react/outline/exclamationcircleicon.d.ts","./node_modules/@heroicons/react/outline/exclamationicon.d.ts","./node_modules/@heroicons/react/outline/externallinkicon.d.ts","./node_modules/@heroicons/react/outline/eyeofficon.d.ts","./node_modules/@heroicons/react/outline/eyeicon.d.ts","./node_modules/@heroicons/react/outline/fastforwardicon.d.ts","./node_modules/@heroicons/react/outline/filmicon.d.ts","./node_modules/@heroicons/react/outline/filtericon.d.ts","./node_modules/@heroicons/react/outline/fingerprinticon.d.ts","./node_modules/@heroicons/react/outline/fireicon.d.ts","./node_modules/@heroicons/react/outline/flagicon.d.ts","./node_modules/@heroicons/react/outline/folderaddicon.d.ts","./node_modules/@heroicons/react/outline/folderdownloadicon.d.ts","./node_modules/@heroicons/react/outline/folderopenicon.d.ts","./node_modules/@heroicons/react/outline/folderremoveicon.d.ts","./node_modules/@heroicons/react/outline/foldericon.d.ts","./node_modules/@heroicons/react/outline/gifticon.d.ts","./node_modules/@heroicons/react/outline/globealticon.d.ts","./node_modules/@heroicons/react/outline/globeicon.d.ts","./node_modules/@heroicons/react/outline/handicon.d.ts","./node_modules/@heroicons/react/outline/hashtagicon.d.ts","./node_modules/@heroicons/react/outline/hearticon.d.ts","./node_modules/@heroicons/react/outline/homeicon.d.ts","./node_modules/@heroicons/react/outline/identificationicon.d.ts","./node_modules/@heroicons/react/outline/inboxinicon.d.ts","./node_modules/@heroicons/react/outline/inboxicon.d.ts","./node_modules/@heroicons/react/outline/informationcircleicon.d.ts","./node_modules/@heroicons/react/outline/keyicon.d.ts","./node_modules/@heroicons/react/outline/libraryicon.d.ts","./node_modules/@heroicons/react/outline/lightbulbicon.d.ts","./node_modules/@heroicons/react/outline/lightningbolticon.d.ts","./node_modules/@heroicons/react/outline/linkicon.d.ts","./node_modules/@heroicons/react/outline/locationmarkericon.d.ts","./node_modules/@heroicons/react/outline/lockclosedicon.d.ts","./node_modules/@heroicons/react/outline/lockopenicon.d.ts","./node_modules/@heroicons/react/outline/loginicon.d.ts","./node_modules/@heroicons/react/outline/logouticon.d.ts","./node_modules/@heroicons/react/outline/mailopenicon.d.ts","./node_modules/@heroicons/react/outline/mailicon.d.ts","./node_modules/@heroicons/react/outline/mapicon.d.ts","./node_modules/@heroicons/react/outline/menualt1icon.d.ts","./node_modules/@heroicons/react/outline/menualt2icon.d.ts","./node_modules/@heroicons/react/outline/menualt3icon.d.ts","./node_modules/@heroicons/react/outline/menualt4icon.d.ts","./node_modules/@heroicons/react/outline/menuicon.d.ts","./node_modules/@heroicons/react/outline/microphoneicon.d.ts","./node_modules/@heroicons/react/outline/minuscircleicon.d.ts","./node_modules/@heroicons/react/outline/minussmicon.d.ts","./node_modules/@heroicons/react/outline/minusicon.d.ts","./node_modules/@heroicons/react/outline/moonicon.d.ts","./node_modules/@heroicons/react/outline/musicnoteicon.d.ts","./node_modules/@heroicons/react/outline/newspapericon.d.ts","./node_modules/@heroicons/react/outline/officebuildingicon.d.ts","./node_modules/@heroicons/react/outline/paperairplaneicon.d.ts","./node_modules/@heroicons/react/outline/paperclipicon.d.ts","./node_modules/@heroicons/react/outline/pauseicon.d.ts","./node_modules/@heroicons/react/outline/pencilalticon.d.ts","./node_modules/@heroicons/react/outline/pencilicon.d.ts","./node_modules/@heroicons/react/outline/phoneincomingicon.d.ts","./node_modules/@heroicons/react/outline/phonemissedcallicon.d.ts","./node_modules/@heroicons/react/outline/phoneoutgoingicon.d.ts","./node_modules/@heroicons/react/outline/phoneicon.d.ts","./node_modules/@heroicons/react/outline/photographicon.d.ts","./node_modules/@heroicons/react/outline/playicon.d.ts","./node_modules/@heroicons/react/outline/pluscircleicon.d.ts","./node_modules/@heroicons/react/outline/plussmicon.d.ts","./node_modules/@heroicons/react/outline/plusicon.d.ts","./node_modules/@heroicons/react/outline/presentationchartbaricon.d.ts","./node_modules/@heroicons/react/outline/presentationchartlineicon.d.ts","./node_modules/@heroicons/react/outline/printericon.d.ts","./node_modules/@heroicons/react/outline/puzzleicon.d.ts","./node_modules/@heroicons/react/outline/qrcodeicon.d.ts","./node_modules/@heroicons/react/outline/questionmarkcircleicon.d.ts","./node_modules/@heroicons/react/outline/receiptrefundicon.d.ts","./node_modules/@heroicons/react/outline/receipttaxicon.d.ts","./node_modules/@heroicons/react/outline/refreshicon.d.ts","./node_modules/@heroicons/react/outline/replyicon.d.ts","./node_modules/@heroicons/react/outline/rewindicon.d.ts","./node_modules/@heroicons/react/outline/rssicon.d.ts","./node_modules/@heroicons/react/outline/saveasicon.d.ts","./node_modules/@heroicons/react/outline/saveicon.d.ts","./node_modules/@heroicons/react/outline/scaleicon.d.ts","./node_modules/@heroicons/react/outline/scissorsicon.d.ts","./node_modules/@heroicons/react/outline/searchcircleicon.d.ts","./node_modules/@heroicons/react/outline/searchicon.d.ts","./node_modules/@heroicons/react/outline/selectoricon.d.ts","./node_modules/@heroicons/react/outline/servericon.d.ts","./node_modules/@heroicons/react/outline/shareicon.d.ts","./node_modules/@heroicons/react/outline/shieldcheckicon.d.ts","./node_modules/@heroicons/react/outline/shieldexclamationicon.d.ts","./node_modules/@heroicons/react/outline/shoppingbagicon.d.ts","./node_modules/@heroicons/react/outline/shoppingcarticon.d.ts","./node_modules/@heroicons/react/outline/sortascendingicon.d.ts","./node_modules/@heroicons/react/outline/sortdescendingicon.d.ts","./node_modules/@heroicons/react/outline/sparklesicon.d.ts","./node_modules/@heroicons/react/outline/speakerphoneicon.d.ts","./node_modules/@heroicons/react/outline/staricon.d.ts","./node_modules/@heroicons/react/outline/statusofflineicon.d.ts","./node_modules/@heroicons/react/outline/statusonlineicon.d.ts","./node_modules/@heroicons/react/outline/stopicon.d.ts","./node_modules/@heroicons/react/outline/sunicon.d.ts","./node_modules/@heroicons/react/outline/supporticon.d.ts","./node_modules/@heroicons/react/outline/switchhorizontalicon.d.ts","./node_modules/@heroicons/react/outline/switchverticalicon.d.ts","./node_modules/@heroicons/react/outline/tableicon.d.ts","./node_modules/@heroicons/react/outline/tagicon.d.ts","./node_modules/@heroicons/react/outline/templateicon.d.ts","./node_modules/@heroicons/react/outline/terminalicon.d.ts","./node_modules/@heroicons/react/outline/thumbdownicon.d.ts","./node_modules/@heroicons/react/outline/thumbupicon.d.ts","./node_modules/@heroicons/react/outline/ticketicon.d.ts","./node_modules/@heroicons/react/outline/translateicon.d.ts","./node_modules/@heroicons/react/outline/trashicon.d.ts","./node_modules/@heroicons/react/outline/trendingdownicon.d.ts","./node_modules/@heroicons/react/outline/trendingupicon.d.ts","./node_modules/@heroicons/react/outline/truckicon.d.ts","./node_modules/@heroicons/react/outline/uploadicon.d.ts","./node_modules/@heroicons/react/outline/useraddicon.d.ts","./node_modules/@heroicons/react/outline/usercircleicon.d.ts","./node_modules/@heroicons/react/outline/usergroupicon.d.ts","./node_modules/@heroicons/react/outline/userremoveicon.d.ts","./node_modules/@heroicons/react/outline/usericon.d.ts","./node_modules/@heroicons/react/outline/usersicon.d.ts","./node_modules/@heroicons/react/outline/variableicon.d.ts","./node_modules/@heroicons/react/outline/videocameraicon.d.ts","./node_modules/@heroicons/react/outline/viewboardsicon.d.ts","./node_modules/@heroicons/react/outline/viewgridaddicon.d.ts","./node_modules/@heroicons/react/outline/viewgridicon.d.ts","./node_modules/@heroicons/react/outline/viewlisticon.d.ts","./node_modules/@heroicons/react/outline/volumeofficon.d.ts","./node_modules/@heroicons/react/outline/volumeupicon.d.ts","./node_modules/@heroicons/react/outline/wifiicon.d.ts","./node_modules/@heroicons/react/outline/xcircleicon.d.ts","./node_modules/@heroicons/react/outline/xicon.d.ts","./node_modules/@heroicons/react/outline/zoominicon.d.ts","./node_modules/@heroicons/react/outline/zoomouticon.d.ts","./node_modules/@heroicons/react/outline/index.d.ts","./src/components/common_components/modelselector.tsx","./src/components/common_components/modelaliasmanager.tsx","./src/components/common_components/passthroughroutesselector.tsx","./src/components/callback_info_helpers.tsx","./src/components/shared/numerical_input.tsx","./src/components/team/loggingsettings.tsx","./src/components/common_components/premiumloggingsettings.tsx","./src/components/common_components/ratelimittypeformitem.tsx","./src/components/router_settings/latencybasedconfiguration.tsx","./src/components/router_settings/reliabilityretriessection.tsx","./src/components/router_settings/routingstrategyselector.tsx","./src/components/router_settings/tagfilteringtoggle.tsx","./src/components/router_settings/routersettingsform.tsx","./src/components/settings/routersettings/fallbacks/addfallbacksmodal.tsx","./src/components/settings/routersettings/fallbacks/fallbackgroupconfig.tsx","./src/components/settings/routersettings/fallbacks/fallbackselectionform.tsx","./src/components/settings/routersettings/fallbacks/addfallbacks.tsx","./src/components/common_components/routersettingsaccordion.tsx","./src/components/common_components/team_dropdown.tsx","./src/components/common_components/organizationdropdown.tsx","./src/components/common_components/projectdropdown.tsx","./src/components/shared/form/formfield.tsx","./src/components/ui/alert.tsx","./src/components/shared/alert.tsx","./node_modules/@types/react-copy-to-clipboard/index.d.ts","./src/components/onboarding_link.tsx","./src/components/createuserbutton.tsx","./src/components/key_team_helpers/budgetfallbackseditor.tsx","./src/components/key_team_helpers/budgetwindowseditor.tsx","./src/components/key_team_helpers/tagratelimiteditor.tsx","./src/components/mcp_server_management/mcpserverselector.tsx","./src/utils/mcptoolcrudclassification.ts","./src/components/mcp_tools/mcpcrudpermissionpanel.tsx","./src/components/mcp_server_management/effectivemcpservers.ts","./src/components/mcp_server_management/mcptoolpermissions.tsx","./src/components/shared/createdkeydisplay.tsx","./src/components/vector_store_management/vectorstoreselector.tsx","./src/components/organisms/createkeypayload.ts","./src/components/organisms/utils.ts","./src/components/organisms/create_key_button.tsx","./src/app/(dashboard)/projects/_components/projectmodals/projectbaseform.tsx","./src/app/(dashboard)/projects/_components/projectmodals/projectformutils.ts","./src/app/(dashboard)/projects/_components/projectmodals/projectformutils.test.ts","./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","./src/app/(dashboard)/prompts/_components/prompt_editor_view/utils.ts","./src/app/(dashboard)/prompts/_components/prompt_editor_view/utils.test.ts","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/types.ts","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/useconversation.ts","./src/app/(dashboard)/search-tools/_components/types.tsx","./src/app/(dashboard)/search-tools/_components/searchtoolpayload.ts","./src/app/(dashboard)/search-tools/_components/searchtoolpayload.test.ts","./src/app/(dashboard)/usage/_components/components/gatewayactivity.ts","./src/app/(dashboard)/usage/_components/components/gatewayactivity.test.ts","./src/components/usagepage/keyactivitylabel.ts","./src/app/(dashboard)/usage/_components/components/entityusage/entityusageaggregations.ts","./src/app/(dashboard)/usage/_components/components/entityusage/entityusagesummary.ts","./src/app/(dashboard)/usage/_components/components/entityusage/entityusagesummary.test.ts","./node_modules/@types/papaparse/index.d.ts","./src/app/(dashboard)/usage/_components/components/entityusage/teamuserspend.ts","./src/app/(dashboard)/usage/_components/components/entityusage/teamuserspend.test.ts","./src/app/(dashboard)/usage/_components/hooks/usepaginateddailyactivity.test.ts","./src/app/(dashboard)/users/_components/default-user-settings/schema.ts","./src/app/(dashboard)/users/_components/default-user-settings/mapper.ts","./src/app/(dashboard)/users/_components/default-user-settings/mapper.test.ts","./src/app/(dashboard)/users/_components/default-user-settings/schema.test.ts","./src/components/key_scope.test.ts","./src/components/networking.test.ts","./src/components/page_metadata.ts","./src/components/page_utils.ts","./src/components/page_utils.test.ts","./src/components/cloudzerocosttracking/cloudzeropayload.ts","./src/components/cloudzerocosttracking/cloudzeropayload.test.ts","./src/utils/teamutils.ts","./src/components/shared/date_picker_types.ts","./src/components/entityusageexport/types.ts","./src/components/entityusageexport/exportformatselector.tsx","./src/components/entityusageexport/exportsummary.tsx","./src/components/entityusageexport/exporttypeselector.tsx","./src/components/entityusageexport/utils.ts","./src/components/entityusageexport/entityusageexportmodal.tsx","./src/components/entityusageexport/usageexportheader.tsx","./src/components/entityusageexport/index.ts","./src/components/entityusageexport/utils.test.ts","./src/components/guardrailsmonitor/mockdata.ts","./src/components/guardrailsmonitor/usageunits.ts","./src/components/guardrailsmonitor/usageunits.test.ts","./src/components/modelselect/modelutils.ts","./src/components/modelselect/modelutils.test.ts","./src/components/navbar/navdisplayname.test.ts","./src/components/navbar/navproductlinkclass.ts","./src/components/settings/adminsettings/cyberark/constants.ts","./src/components/settings/adminsettings/hashicorpvault/constants.ts","./src/components/settings/adminsettings/mcpsemanticfiltersettings/semanticfiltertestutils.ts","./src/components/settings/adminsettings/mcpsemanticfiltersettings/semanticfiltertestutils.test.ts","./src/components/settings/adminsettings/mcptoolsearchsettings/toolsearchform.ts","./src/components/settings/adminsettings/mcptoolsearchsettings/toolsearchform.test.ts","./src/components/settings/adminsettings/pluginsettings/schema.ts","./src/components/settings/adminsettings/ssosettings/constants.ts","./src/components/settings/adminsettings/ssosettings/utils.ts","./src/components/settings/adminsettings/ssosettings/utils.test.ts","./src/components/settings/loggingandalerts/loggingcallbacks/types.ts","./src/components/teamspage/teamscsvexport.ts","./src/components/teamspage/teamscsvexport.test.ts","./src/components/toolpolicies/toolpoliciesqueries.ts","./src/components/usagepage/keyactivitylabel.test.ts","./src/components/usagepage/utils/value_formatters.tsx","./src/components/usagepage/utils/value_formatters.test.ts","./src/components/add_model/auto_setup.ts","./src/components/add_model/auto_setup.test.ts","./src/components/add_model/buildautoroutercompression.test.ts","./src/components/add_model/build_auto_router_routing_test_request.ts","./src/components/add_model/build_auto_router_routing_test_request.test.ts","./src/components/add_model/build_auto_router_test_targets.ts","./src/components/add_model/build_auto_router_test_targets.test.ts","./src/components/add_model/build_complexity_router_config.test.ts","./src/components/add_model/classifierprompteditorstate.test.ts","./src/components/add_model/complexity_router_keywords.test.ts","./src/components/add_model/complexity_router_tiers.test.ts","./src/components/add_model/heuristic_scoring_knobs.test.ts","./src/components/add_model/tier_rows.test.ts","./src/components/add_model/tier_set_actions.test.ts","./src/components/chat/types.ts","./src/components/chat/usechathistory.ts","./src/contexts/chatshellcontext.tsx","./node_modules/dayjs/locale/types.d.ts","./node_modules/dayjs/locale/index.d.ts","./node_modules/dayjs/index.d.ts","./src/components/chat/conversationlist.tsx","./src/components/chat/chatshell.tsx","./src/components/chat/chatshell.serverrootpath.test.ts","./src/components/chat/usechathistory.test.ts","./src/components/claude_code_plugins/helpers.ts","./src/components/claude_code_plugins/helpers.test.ts","./src/components/common_components/formrules.ts","./src/components/common_components/routersettingspayload.ts","./src/components/common_components/routersettingspayload.test.ts","./node_modules/zod/v3/helpers/typealiases.d.cts","./node_modules/zod/v3/helpers/util.d.cts","./node_modules/zod/v3/zoderror.d.cts","./node_modules/zod/v3/locales/en.d.cts","./node_modules/zod/v3/errors.d.cts","./node_modules/zod/v3/helpers/parseutil.d.cts","./node_modules/zod/v3/helpers/enumutil.d.cts","./node_modules/zod/v3/helpers/errorutil.d.cts","./node_modules/zod/v3/helpers/partialutil.d.cts","./node_modules/zod/v3/standard-schema.d.cts","./node_modules/zod/v3/types.d.cts","./node_modules/zod/v3/external.d.cts","./node_modules/zod/v3/index.d.cts","./node_modules/@hookform/resolvers/zod/dist/zod.d.ts","./node_modules/@hookform/resolvers/zod/dist/index.d.ts","./src/lib/forms/usezodform.ts","./src/components/add_model/accessgrouptagscombobox.tsx","./src/components/add_model/modelchoicecombobox.tsx","./src/components/add_model/routerconfigbuilder.tsx","./src/components/edit_auto_router/edit_auto_router_modal.tsx","./src/components/edit_auto_router/build_updated_complexity_router_config.test.ts","./src/components/edit_auto_router/edit_auto_router_modal.test.ts","./src/components/email_events/email_event_settings.tsx","./src/components/email_events/index.ts","./src/components/guardrails/types.ts","./src/components/key_team_helpers/modelmaxbudgeteditor.test.ts","./src/components/key_team_helpers/filter_helpers.ts","./src/components/key_team_helpers/filter_helpers.test.ts","./src/components/key_team_helpers/modelmaxbudgetpayload.ts","./src/components/key_team_helpers/modelmaxbudgetpayload.test.ts","./src/components/key_team_helpers/transform_key_info.tsx","./src/components/key_team_helpers/transform_key_info.test.ts","./src/components/key_team_helpers/useseededstate.ts","./src/components/key_team_helpers/usemodelmaxbudgetfield.ts","./src/components/llm_calls/mcp_tool_blocks.ts","./src/components/llm_calls/mcp_tool_blocks.test.ts","./src/components/mcp_server_management/effectivemcpservers.test.ts","./src/components/mcp_server_management/mcpentitlement.ts","./src/components/model_add/credential_form_helpers.ts","./src/components/model_add/credential_form_helpers.test.ts","./src/components/model_dashboard/types.ts","./src/components/organisms/createkeypayload.test.ts","./src/components/organisms/regeneratekeypayload.ts","./src/components/organisms/regeneratekeypayload.test.ts","./src/components/organisms/utils.test.ts","./src/components/organization/org-settings/schema.ts","./src/components/organization/org-create/mapper.ts","./src/components/organization/org-create/mapper.test.ts","./src/components/organization/org-settings/mapper.ts","./src/components/organization/org-settings/mapper.test.ts","./src/components/team/teammodelaccess.ts","./src/components/permissions/inheritedgrants.ts","./src/components/permissions/inheritedgrants.test.ts","./src/components/publicmodelhub/publicmodelhubfilters.ts","./src/components/publicmodelhub/publicmodelhubfilters.test.ts","./src/components/publicmodelhubtablecolumns.tsx","./src/components/publicmodelhub/usepublicmodelhublist.ts","./src/components/publicmodelhub/usepublicmodelhubfacets.ts","./src/components/routing_groups/routinggrouppayload.ts","./src/components/routing_groups/routinggrouppayload.test.ts","./src/components/routing_groups/strategy.ts","./src/components/shared/charts/colors.ts","./node_modules/@types/d3-time/index.d.ts","./node_modules/@types/d3-scale/index.d.ts","./node_modules/victory-vendor/d3-scale.d.ts","./node_modules/recharts/types/shape/dot.d.ts","./node_modules/recharts/types/component/text.d.ts","./node_modules/recharts/types/zindex/zindexlayer.d.ts","./node_modules/recharts/types/cartesian/getcartesianposition.d.ts","./node_modules/recharts/types/component/label.d.ts","./node_modules/recharts/types/cartesian/cartesianaxis.d.ts","./node_modules/recharts/types/util/scale/customscaledefinition.d.ts","./node_modules/redux/dist/redux.d.ts","./node_modules/immer/dist/immer.d.ts","./node_modules/redux-thunk/dist/redux-thunk.d.ts","./node_modules/@reduxjs/toolkit/dist/uncheckedindexed.ts","./node_modules/@reduxjs/toolkit/dist/index.d.mts","./node_modules/recharts/types/state/cartesianaxisslice.d.ts","./node_modules/recharts/types/synchronisation/types.d.ts","./node_modules/recharts/types/chart/types.d.ts","./node_modules/recharts/types/component/defaulttooltipcontent.d.ts","./node_modules/recharts/types/context/brushupdatecontext.d.ts","./node_modules/recharts/types/state/chartdataslice.d.ts","./node_modules/recharts/types/state/types/linesettings.d.ts","./node_modules/recharts/types/state/types/scattersettings.d.ts","./node_modules/@types/d3-path/index.d.ts","./node_modules/@types/d3-shape/index.d.ts","./node_modules/victory-vendor/d3-shape.d.ts","./node_modules/recharts/types/shape/curve.d.ts","./node_modules/recharts/types/component/labellist.d.ts","./node_modules/recharts/types/component/defaultlegendcontent.d.ts","./node_modules/recharts/types/util/payload/getuniqpayload.d.ts","./node_modules/recharts/types/util/useelementoffset.d.ts","./node_modules/recharts/types/component/legend.d.ts","./node_modules/recharts/types/state/legendslice.d.ts","./node_modules/recharts/types/state/types/stackedgraphicalitem.d.ts","./node_modules/recharts/types/util/stacks/stacktypes.d.ts","./node_modules/recharts/types/util/scale/rechartsscale.d.ts","./node_modules/recharts/types/util/chartutils.d.ts","./node_modules/recharts/types/state/selectors/areaselectors.d.ts","./node_modules/recharts/types/animation/easing.d.ts","./node_modules/recharts/types/animation/matchby.d.ts","./node_modules/recharts/types/animation/animateditems.d.ts","./node_modules/recharts/types/cartesian/arearevealshape.d.ts","./node_modules/recharts/types/cartesian/area.d.ts","./node_modules/recharts/types/state/types/areasettings.d.ts","./node_modules/recharts/types/shape/rectangle.d.ts","./node_modules/recharts/types/cartesian/bar.d.ts","./node_modules/recharts/types/util/barutils.d.ts","./node_modules/recharts/types/state/types/barsettings.d.ts","./node_modules/recharts/types/state/types/radialbarsettings.d.ts","./node_modules/recharts/types/util/svgpropertiesnoevents.d.ts","./node_modules/recharts/types/util/useuniqueid.d.ts","./node_modules/recharts/types/state/types/piesettings.d.ts","./node_modules/recharts/types/state/types/radarsettings.d.ts","./node_modules/recharts/types/state/graphicalitemsslice.d.ts","./node_modules/recharts/types/state/tooltipslice.d.ts","./node_modules/recharts/types/state/optionsslice.d.ts","./node_modules/recharts/types/state/layoutslice.d.ts","./node_modules/recharts/types/util/ifoverflow.d.ts","./node_modules/recharts/types/util/resolvedefaultprops.d.ts","./node_modules/recharts/types/cartesian/referenceline.d.ts","./node_modules/recharts/types/state/referenceelementsslice.d.ts","./node_modules/recharts/types/state/brushslice.d.ts","./node_modules/recharts/types/state/rootpropsslice.d.ts","./node_modules/recharts/types/state/polaraxisslice.d.ts","./node_modules/recharts/types/state/polaroptionsslice.d.ts","./node_modules/recharts/types/cartesian/linedrawshape.d.ts","./node_modules/recharts/types/cartesian/line.d.ts","./node_modules/recharts/types/shape/symbols.d.ts","./node_modules/recharts/types/util/constants.d.ts","./node_modules/recharts/types/util/scatterutils.d.ts","./node_modules/recharts/types/cartesian/scatter.d.ts","./node_modules/recharts/types/cartesian/errorbar.d.ts","./node_modules/recharts/types/state/errorbarslice.d.ts","./node_modules/recharts/types/state/zindexslice.d.ts","./node_modules/recharts/types/state/eventsettingsslice.d.ts","./node_modules/recharts/types/state/renderedticksslice.d.ts","./node_modules/recharts/types/state/store.d.ts","./node_modules/recharts/types/cartesian/getticks.d.ts","./node_modules/recharts/types/cartesian/cartesiangrid.d.ts","./node_modules/recharts/types/state/selectors/combiners/combinedisplayedstackeddata.d.ts","./node_modules/recharts/types/state/selectors/selecttooltipaxistype.d.ts","./node_modules/recharts/types/types.d.ts","./node_modules/recharts/types/hooks.d.ts","./node_modules/recharts/types/state/selectors/axisselectors.d.ts","./node_modules/recharts/types/component/dots.d.ts","./node_modules/recharts/types/util/typeddatakey.d.ts","./node_modules/recharts/types/util/types.d.ts","./node_modules/recharts/types/container/surface.d.ts","./node_modules/recharts/types/container/layer.d.ts","./node_modules/recharts/types/component/cursor.d.ts","./node_modules/recharts/types/component/tooltip.d.ts","./node_modules/recharts/types/component/responsivecontainer.d.ts","./node_modules/recharts/types/component/cell.d.ts","./node_modules/recharts/types/component/customized.d.ts","./node_modules/recharts/types/shape/sector.d.ts","./node_modules/recharts/types/shape/polygon.d.ts","./node_modules/recharts/types/shape/cross.d.ts","./node_modules/recharts/types/polar/polargrid.d.ts","./node_modules/recharts/types/polar/defaultpolarradiusaxisprops.d.ts","./node_modules/recharts/types/polar/polarradiusaxis.d.ts","./node_modules/recharts/types/polar/defaultpolarangleaxisprops.d.ts","./node_modules/recharts/types/polar/polarangleaxis.d.ts","./node_modules/recharts/types/context/tooltipcontext.d.ts","./node_modules/recharts/types/polar/pie.d.ts","./node_modules/recharts/types/polar/radar.d.ts","./node_modules/recharts/types/util/radialbarutils.d.ts","./node_modules/recharts/types/polar/radialbar.d.ts","./node_modules/recharts/types/cartesian/brush.d.ts","./node_modules/recharts/types/cartesian/referencedot.d.ts","./node_modules/recharts/types/util/excludeeventprops.d.ts","./node_modules/recharts/types/util/svgpropertiesandevents.d.ts","./node_modules/recharts/types/cartesian/referencearea.d.ts","./node_modules/recharts/types/cartesian/barstack.d.ts","./node_modules/recharts/types/cartesian/xaxis.d.ts","./node_modules/recharts/types/cartesian/yaxis.d.ts","./node_modules/recharts/types/cartesian/zaxis.d.ts","./node_modules/recharts/types/chart/linechart.d.ts","./node_modules/recharts/types/chart/barchart.d.ts","./node_modules/recharts/types/chart/piechart.d.ts","./node_modules/recharts/types/chart/treemap.d.ts","./node_modules/recharts/types/chart/sankey.d.ts","./node_modules/recharts/types/chart/radarchart.d.ts","./node_modules/recharts/types/chart/scatterchart.d.ts","./node_modules/recharts/types/chart/areachart.d.ts","./node_modules/recharts/types/chart/radialbarchart.d.ts","./node_modules/recharts/types/chart/composedchart.d.ts","./node_modules/recharts/types/chart/sunburstchart.d.ts","./node_modules/recharts/types/shape/trapezoid.d.ts","./node_modules/recharts/types/cartesian/funnel.d.ts","./node_modules/recharts/types/chart/funnelchart.d.ts","./node_modules/recharts/types/util/global.d.ts","./node_modules/recharts/types/animation/animationhandle.d.ts","./node_modules/recharts/types/animation/timeoutcontroller.d.ts","./node_modules/recharts/types/animation/animationcontroller.d.ts","./node_modules/recharts/types/animation/useanimationcontroller.d.ts","./node_modules/recharts/types/zindex/defaultzindexes.d.ts","./node_modules/decimal.js-light/decimal.d.ts","./node_modules/recharts/types/util/scale/getnicetickvalues.d.ts","./node_modules/recharts/types/context/chartlayoutcontext.d.ts","./node_modules/recharts/types/util/getrelativecoordinate.d.ts","./node_modules/recharts/types/util/createcartesiancharts.d.ts","./node_modules/recharts/types/util/createpolarcharts.d.ts","./node_modules/recharts/types/util/datautils.d.ts","./node_modules/recharts/types/index.d.ts","./src/components/ui/chart.tsx","./src/components/shared/charts/chart_tooltip.tsx","./src/components/shared/charts/area_chart.tsx","./src/components/shared/charts/bar_chart.tsx","./src/components/shared/charts/chart_legend.tsx","./src/components/shared/charts/donut_chart.tsx","./src/components/shared/charts/line_chart.tsx","./src/components/shared/charts/index.ts","./src/components/team/memberformvalues.ts","./src/components/team/memberformvalues.test.ts","./src/components/team/tabvisibilityutils.ts","./src/components/team/tabvisibilityutils.test.ts","./src/components/team/teammodelaccess.test.ts","./src/components/team/usemyteammember.ts","./src/components/templates/estimatedoutputtokens.ts","./src/components/templates/estimatedoutputtokens.test.ts","./src/components/templates/keyeditfieldnormalizers.ts","./src/components/key_info_utils.tsx","./src/components/templates/keyeditformvalues.ts","./src/components/templates/keyeditformvalues.test.ts","./src/components/view_logs/constants.ts","./src/components/view_logs/logdetailrouting.ts","./src/components/view_logs/utils.ts","./src/components/view_logs/guardrailviewer/bedrockguardraildetails.tsx","./src/components/view_logs/guardrailviewer/__tests__/fixtures.ts","./src/components/view_logs/logdetailsdrawer/constants.ts","./src/components/view_logs/columns.tsx","./src/components/view_logs/logdetailsdrawer/classifytag.tsx","./node_modules/moment/ts3.1-typings/moment.d.ts","./src/components/view_logs/logdetailsdrawer/sidebartoggle.tsx","./src/components/view_logs/logdetailsdrawer/drawerheader.tsx","./src/components/view_logs/logdetailsdrawer/usekeyboardnavigation.ts","./src/components/view_logs/guardrailviewer/presidiodetectedentities.tsx","./src/components/view_logs/guardrailviewer/contentfilterdetails.tsx","./src/components/view_logs/guardrailviewer/compliancepanel.tsx","./src/components/view_logs/guardrailviewer/guardrailviewer.tsx","./src/components/view_logs/evalviewer/evalviewer.tsx","./src/components/view_logs/costbreakdownviewer.tsx","./src/components/view_logs/configinfomessage.tsx","./src/components/view_logs/vectorstoreviewer.tsx","./src/components/view_logs/logdetailsdrawer/truncatedvalue.tsx","./src/components/view_logs/logdetailsdrawer/tokenflow.tsx","./node_modules/react-json-view-lite/dist/datarenderer.d.ts","./node_modules/react-json-view-lite/dist/index.d.ts","./src/components/view_logs/logdetailsdrawer/jsonviewer.tsx","./src/components/view_logs/logdetailsdrawer/utils.ts","./src/components/view_logs/toolssection/types.ts","./src/components/view_logs/toolssection/utils.ts","./src/components/view_logs/toolssection/formattedtoolview.tsx","./src/components/view_logs/toolssection/jsontoolview.tsx","./src/components/view_logs/toolssection/toolexpandedcontent.tsx","./src/components/view_logs/toolssection/toolitem.tsx","./src/components/view_logs/toolssection/toolssection.tsx","./src/components/view_logs/toolssection/index.ts","./src/components/view_logs/logdetailsdrawer/prettymessagestypes.ts","./src/components/view_logs/logdetailsdrawer/prettymessagesutils.ts","./src/components/view_logs/logdetailsdrawer/sectionheader.tsx","./src/components/view_logs/logdetailsdrawer/collapsiblemessage.tsx","./src/components/view_logs/logdetailsdrawer/simpletoolcallblock.tsx","./src/components/view_logs/logdetailsdrawer/simplemessageblock.tsx","./src/components/view_logs/logdetailsdrawer/historytree.tsx","./src/components/view_logs/logdetailsdrawer/inputcard.tsx","./src/components/view_logs/logdetailsdrawer/outputcard.tsx","./src/components/view_logs/logdetailsdrawer/realtimeprettyview.tsx","./src/components/view_logs/logdetailsdrawer/prettymessagesview.tsx","./src/components/view_logs/logdetailsdrawer/logdetailcontent.tsx","./src/components/view_logs/logdetailsdrawer/logdetailsdrawer.tsx","./src/components/view_logs/logdetailsdrawer/index.ts","./src/components/view_logs/logdetailsdrawer/utils.test.ts","./src/components/view_logs/toolssection/utils.test.ts","./src/data/insultscomplianceprompts.ts","./src/data/financialcomplianceprompts.ts","./src/data/codeexecutioncomplianceprompts.ts","./src/data/claimscomplianceprompts.ts","./src/data/complianceprompts.ts","./src/data/canadianpiicomplianceprompts.ts","./src/hooks/mcpoauthutils.ts","./src/hooks/usevisitedtabs.ts","./src/hooks/useworker.ts","./src/hooks/policies/usedeletepolicyattachment.ts","./src/lib/assetpaths.test.ts","./tests/mocks/autorouterpresets.ts","./src/lib/autorouter_presets.test.ts","./src/lib/cva.config.test.ts","./src/lib/logotreatments.test.ts","./src/lib/toast.test.ts","./src/lib/forms/pickdirty.ts","./src/lib/forms/pickdirty.test.ts","./src/lib/forms/urlvalidation.ts","./src/lib/forms/urlvalidation.test.ts","./src/lib/http/api.sameorigin.test.ts","./src/lib/http/api.test.ts","./src/lib/http/client.test.ts","./src/lib/http/resolveapibase.test.ts","./src/lib/http/runtime.test.ts","./src/lib/http/searchtooltypes.test-d.ts","./src/utils/budgetutils.ts","./src/utils/capabilities.test.ts","./src/utils/cookieutils.test.ts","./src/utils/datautils.test.ts","./src/utils/entitylinks.test.ts","./src/utils/errorpatterns.ts","./src/utils/errorutils.ts","./src/utils/errorutils.test.ts","./src/utils/jwtutils.test.ts","./node_modules/date-fns/constants.d.ts","./node_modules/date-fns/locale/types.d.ts","./node_modules/date-fns/fp/types.d.ts","./node_modules/date-fns/types.d.ts","./node_modules/date-fns/add.d.ts","./node_modules/date-fns/addbusinessdays.d.ts","./node_modules/date-fns/adddays.d.ts","./node_modules/date-fns/addhours.d.ts","./node_modules/date-fns/addisoweekyears.d.ts","./node_modules/date-fns/addmilliseconds.d.ts","./node_modules/date-fns/addminutes.d.ts","./node_modules/date-fns/addmonths.d.ts","./node_modules/date-fns/addquarters.d.ts","./node_modules/date-fns/addseconds.d.ts","./node_modules/date-fns/addweeks.d.ts","./node_modules/date-fns/addyears.d.ts","./node_modules/date-fns/areintervalsoverlapping.d.ts","./node_modules/date-fns/clamp.d.ts","./node_modules/date-fns/closestindexto.d.ts","./node_modules/date-fns/closestto.d.ts","./node_modules/date-fns/compareasc.d.ts","./node_modules/date-fns/comparedesc.d.ts","./node_modules/date-fns/constructfrom.d.ts","./node_modules/date-fns/constructnow.d.ts","./node_modules/date-fns/daystoweeks.d.ts","./node_modules/date-fns/differenceinbusinessdays.d.ts","./node_modules/date-fns/differenceincalendardays.d.ts","./node_modules/date-fns/differenceincalendarisoweekyears.d.ts","./node_modules/date-fns/differenceincalendarisoweeks.d.ts","./node_modules/date-fns/differenceincalendarmonths.d.ts","./node_modules/date-fns/differenceincalendarquarters.d.ts","./node_modules/date-fns/differenceincalendarweeks.d.ts","./node_modules/date-fns/differenceincalendaryears.d.ts","./node_modules/date-fns/differenceindays.d.ts","./node_modules/date-fns/differenceinhours.d.ts","./node_modules/date-fns/differenceinisoweekyears.d.ts","./node_modules/date-fns/differenceinmilliseconds.d.ts","./node_modules/date-fns/differenceinminutes.d.ts","./node_modules/date-fns/differenceinmonths.d.ts","./node_modules/date-fns/differenceinquarters.d.ts","./node_modules/date-fns/differenceinseconds.d.ts","./node_modules/date-fns/differenceinweeks.d.ts","./node_modules/date-fns/differenceinyears.d.ts","./node_modules/date-fns/eachdayofinterval.d.ts","./node_modules/date-fns/eachhourofinterval.d.ts","./node_modules/date-fns/eachminuteofinterval.d.ts","./node_modules/date-fns/eachmonthofinterval.d.ts","./node_modules/date-fns/eachquarterofinterval.d.ts","./node_modules/date-fns/eachweekofinterval.d.ts","./node_modules/date-fns/eachweekendofinterval.d.ts","./node_modules/date-fns/eachweekendofmonth.d.ts","./node_modules/date-fns/eachweekendofyear.d.ts","./node_modules/date-fns/eachyearofinterval.d.ts","./node_modules/date-fns/endofday.d.ts","./node_modules/date-fns/endofdecade.d.ts","./node_modules/date-fns/endofhour.d.ts","./node_modules/date-fns/endofisoweek.d.ts","./node_modules/date-fns/endofisoweekyear.d.ts","./node_modules/date-fns/endofminute.d.ts","./node_modules/date-fns/endofmonth.d.ts","./node_modules/date-fns/endofquarter.d.ts","./node_modules/date-fns/endofsecond.d.ts","./node_modules/date-fns/endoftoday.d.ts","./node_modules/date-fns/endoftomorrow.d.ts","./node_modules/date-fns/endofweek.d.ts","./node_modules/date-fns/endofyear.d.ts","./node_modules/date-fns/endofyesterday.d.ts","./node_modules/date-fns/_lib/format/formatters.d.ts","./node_modules/date-fns/_lib/format/longformatters.d.ts","./node_modules/date-fns/format.d.ts","./node_modules/date-fns/formatdistance.d.ts","./node_modules/date-fns/formatdistancestrict.d.ts","./node_modules/date-fns/formatdistancetonow.d.ts","./node_modules/date-fns/formatdistancetonowstrict.d.ts","./node_modules/date-fns/formatduration.d.ts","./node_modules/date-fns/formatiso.d.ts","./node_modules/date-fns/formatiso9075.d.ts","./node_modules/date-fns/formatisoduration.d.ts","./node_modules/date-fns/formatrfc3339.d.ts","./node_modules/date-fns/formatrfc7231.d.ts","./node_modules/date-fns/formatrelative.d.ts","./node_modules/date-fns/fromunixtime.d.ts","./node_modules/date-fns/getdate.d.ts","./node_modules/date-fns/getday.d.ts","./node_modules/date-fns/getdayofyear.d.ts","./node_modules/date-fns/getdaysinmonth.d.ts","./node_modules/date-fns/getdaysinyear.d.ts","./node_modules/date-fns/getdecade.d.ts","./node_modules/date-fns/_lib/defaultoptions.d.ts","./node_modules/date-fns/getdefaultoptions.d.ts","./node_modules/date-fns/gethours.d.ts","./node_modules/date-fns/getisoday.d.ts","./node_modules/date-fns/getisoweek.d.ts","./node_modules/date-fns/getisoweekyear.d.ts","./node_modules/date-fns/getisoweeksinyear.d.ts","./node_modules/date-fns/getmilliseconds.d.ts","./node_modules/date-fns/getminutes.d.ts","./node_modules/date-fns/getmonth.d.ts","./node_modules/date-fns/getoverlappingdaysinintervals.d.ts","./node_modules/date-fns/getquarter.d.ts","./node_modules/date-fns/getseconds.d.ts","./node_modules/date-fns/gettime.d.ts","./node_modules/date-fns/getunixtime.d.ts","./node_modules/date-fns/getweek.d.ts","./node_modules/date-fns/getweekofmonth.d.ts","./node_modules/date-fns/getweekyear.d.ts","./node_modules/date-fns/getweeksinmonth.d.ts","./node_modules/date-fns/getyear.d.ts","./node_modules/date-fns/hourstomilliseconds.d.ts","./node_modules/date-fns/hourstominutes.d.ts","./node_modules/date-fns/hourstoseconds.d.ts","./node_modules/date-fns/interval.d.ts","./node_modules/date-fns/intervaltoduration.d.ts","./node_modules/date-fns/intlformat.d.ts","./node_modules/date-fns/intlformatdistance.d.ts","./node_modules/date-fns/isafter.d.ts","./node_modules/date-fns/isbefore.d.ts","./node_modules/date-fns/isdate.d.ts","./node_modules/date-fns/isequal.d.ts","./node_modules/date-fns/isexists.d.ts","./node_modules/date-fns/isfirstdayofmonth.d.ts","./node_modules/date-fns/isfriday.d.ts","./node_modules/date-fns/isfuture.d.ts","./node_modules/date-fns/islastdayofmonth.d.ts","./node_modules/date-fns/isleapyear.d.ts","./node_modules/date-fns/ismatch.d.ts","./node_modules/date-fns/ismonday.d.ts","./node_modules/date-fns/ispast.d.ts","./node_modules/date-fns/issameday.d.ts","./node_modules/date-fns/issamehour.d.ts","./node_modules/date-fns/issameisoweek.d.ts","./node_modules/date-fns/issameisoweekyear.d.ts","./node_modules/date-fns/issameminute.d.ts","./node_modules/date-fns/issamemonth.d.ts","./node_modules/date-fns/issamequarter.d.ts","./node_modules/date-fns/issamesecond.d.ts","./node_modules/date-fns/issameweek.d.ts","./node_modules/date-fns/issameyear.d.ts","./node_modules/date-fns/issaturday.d.ts","./node_modules/date-fns/issunday.d.ts","./node_modules/date-fns/isthishour.d.ts","./node_modules/date-fns/isthisisoweek.d.ts","./node_modules/date-fns/isthisminute.d.ts","./node_modules/date-fns/isthismonth.d.ts","./node_modules/date-fns/isthisquarter.d.ts","./node_modules/date-fns/isthissecond.d.ts","./node_modules/date-fns/isthisweek.d.ts","./node_modules/date-fns/isthisyear.d.ts","./node_modules/date-fns/isthursday.d.ts","./node_modules/date-fns/istoday.d.ts","./node_modules/date-fns/istomorrow.d.ts","./node_modules/date-fns/istuesday.d.ts","./node_modules/date-fns/isvalid.d.ts","./node_modules/date-fns/iswednesday.d.ts","./node_modules/date-fns/isweekend.d.ts","./node_modules/date-fns/iswithininterval.d.ts","./node_modules/date-fns/isyesterday.d.ts","./node_modules/date-fns/lastdayofdecade.d.ts","./node_modules/date-fns/lastdayofisoweek.d.ts","./node_modules/date-fns/lastdayofisoweekyear.d.ts","./node_modules/date-fns/lastdayofmonth.d.ts","./node_modules/date-fns/lastdayofquarter.d.ts","./node_modules/date-fns/lastdayofweek.d.ts","./node_modules/date-fns/lastdayofyear.d.ts","./node_modules/date-fns/_lib/format/lightformatters.d.ts","./node_modules/date-fns/lightformat.d.ts","./node_modules/date-fns/max.d.ts","./node_modules/date-fns/milliseconds.d.ts","./node_modules/date-fns/millisecondstohours.d.ts","./node_modules/date-fns/millisecondstominutes.d.ts","./node_modules/date-fns/millisecondstoseconds.d.ts","./node_modules/date-fns/min.d.ts","./node_modules/date-fns/minutestohours.d.ts","./node_modules/date-fns/minutestomilliseconds.d.ts","./node_modules/date-fns/minutestoseconds.d.ts","./node_modules/date-fns/monthstoquarters.d.ts","./node_modules/date-fns/monthstoyears.d.ts","./node_modules/date-fns/nextday.d.ts","./node_modules/date-fns/nextfriday.d.ts","./node_modules/date-fns/nextmonday.d.ts","./node_modules/date-fns/nextsaturday.d.ts","./node_modules/date-fns/nextsunday.d.ts","./node_modules/date-fns/nextthursday.d.ts","./node_modules/date-fns/nexttuesday.d.ts","./node_modules/date-fns/nextwednesday.d.ts","./node_modules/date-fns/parse/_lib/types.d.ts","./node_modules/date-fns/parse/_lib/setter.d.ts","./node_modules/date-fns/parse/_lib/parser.d.ts","./node_modules/date-fns/parse/_lib/parsers.d.ts","./node_modules/date-fns/parse.d.ts","./node_modules/date-fns/parseiso.d.ts","./node_modules/date-fns/parsejson.d.ts","./node_modules/date-fns/previousday.d.ts","./node_modules/date-fns/previousfriday.d.ts","./node_modules/date-fns/previousmonday.d.ts","./node_modules/date-fns/previoussaturday.d.ts","./node_modules/date-fns/previoussunday.d.ts","./node_modules/date-fns/previousthursday.d.ts","./node_modules/date-fns/previoustuesday.d.ts","./node_modules/date-fns/previouswednesday.d.ts","./node_modules/date-fns/quarterstomonths.d.ts","./node_modules/date-fns/quarterstoyears.d.ts","./node_modules/date-fns/roundtonearesthours.d.ts","./node_modules/date-fns/roundtonearestminutes.d.ts","./node_modules/date-fns/secondstohours.d.ts","./node_modules/date-fns/secondstomilliseconds.d.ts","./node_modules/date-fns/secondstominutes.d.ts","./node_modules/date-fns/set.d.ts","./node_modules/date-fns/setdate.d.ts","./node_modules/date-fns/setday.d.ts","./node_modules/date-fns/setdayofyear.d.ts","./node_modules/date-fns/setdefaultoptions.d.ts","./node_modules/date-fns/sethours.d.ts","./node_modules/date-fns/setisoday.d.ts","./node_modules/date-fns/setisoweek.d.ts","./node_modules/date-fns/setisoweekyear.d.ts","./node_modules/date-fns/setmilliseconds.d.ts","./node_modules/date-fns/setminutes.d.ts","./node_modules/date-fns/setmonth.d.ts","./node_modules/date-fns/setquarter.d.ts","./node_modules/date-fns/setseconds.d.ts","./node_modules/date-fns/setweek.d.ts","./node_modules/date-fns/setweekyear.d.ts","./node_modules/date-fns/setyear.d.ts","./node_modules/date-fns/startofday.d.ts","./node_modules/date-fns/startofdecade.d.ts","./node_modules/date-fns/startofhour.d.ts","./node_modules/date-fns/startofisoweek.d.ts","./node_modules/date-fns/startofisoweekyear.d.ts","./node_modules/date-fns/startofminute.d.ts","./node_modules/date-fns/startofmonth.d.ts","./node_modules/date-fns/startofquarter.d.ts","./node_modules/date-fns/startofsecond.d.ts","./node_modules/date-fns/startoftoday.d.ts","./node_modules/date-fns/startoftomorrow.d.ts","./node_modules/date-fns/startofweek.d.ts","./node_modules/date-fns/startofweekyear.d.ts","./node_modules/date-fns/startofyear.d.ts","./node_modules/date-fns/startofyesterday.d.ts","./node_modules/date-fns/sub.d.ts","./node_modules/date-fns/subbusinessdays.d.ts","./node_modules/date-fns/subdays.d.ts","./node_modules/date-fns/subhours.d.ts","./node_modules/date-fns/subisoweekyears.d.ts","./node_modules/date-fns/submilliseconds.d.ts","./node_modules/date-fns/subminutes.d.ts","./node_modules/date-fns/submonths.d.ts","./node_modules/date-fns/subquarters.d.ts","./node_modules/date-fns/subseconds.d.ts","./node_modules/date-fns/subweeks.d.ts","./node_modules/date-fns/subyears.d.ts","./node_modules/date-fns/todate.d.ts","./node_modules/date-fns/transpose.d.ts","./node_modules/date-fns/weekstodays.d.ts","./node_modules/date-fns/yearstodays.d.ts","./node_modules/date-fns/yearstomonths.d.ts","./node_modules/date-fns/yearstoquarters.d.ts","./node_modules/date-fns/index.d.ts","./src/utils/keyexpiryutils.ts","./src/utils/keyexpiryutils.test.ts","./src/utils/keyupdateutils.ts","./src/utils/keyupdateutils.test.ts","./src/utils/licenseutils.test.ts","./src/utils/localstorageutils.test.ts","./src/utils/maskedsecretutils.ts","./src/utils/mcpheaderutils.ts","./src/utils/mcpheaderutils.test.ts","./src/utils/mcptokenstore.test.ts","./src/utils/mcptoolcrudclassification.test.ts","./src/utils/modelpermissions.test.ts","./src/utils/pkce.ts","./src/utils/promptcacheusage.test.ts","./src/utils/proxyutils.test.ts","./node_modules/dayjs/plugin/utc.d.ts","./src/utils/ptudatetime.ts","./src/utils/ptudatetime.test.ts","./src/utils/ptuvalidation.ts","./src/utils/ptumodelinfo.ts","./src/utils/ptumodelinfo.test.ts","./src/utils/ptuvalidation.test.ts","./src/utils/returnurlutils.test.ts","./src/utils/roles.test.ts","./src/utils/searchutils.ts","./src/utils/searchutils.test.ts","./src/utils/tabroutes.test.ts","./src/utils/teamutils.test.ts","./src/utils/textutils.test.ts","./src/utils/uihref.test.ts","./node_modules/@types/json-schema/index.d.ts","./node_modules/@eslint/core/dist/cjs/types.d.cts","./node_modules/eslint/lib/types/use-at-your-own-risk.d.ts","./node_modules/eslint/lib/types/index.d.ts","./tests/fetch-location-rule.test.ts","./tests/fieldorientation.ts","./node_modules/vitest/dist/environments.d.ts","./tests/jsdomfetchenv.ts","./scripts/lint-budget-lib.mjs","./tests/lint-budget-lib.test.ts","./tests/setup.unit.ts","./node_modules/@testing-library/jest-dom/types/matchers.d.ts","./node_modules/@testing-library/jest-dom/types/jest.d.ts","./node_modules/@testing-library/jest-dom/types/index.d.ts","./tests/setuptests.ts","./scripts/eslint-rules/filename-pascal-case.mjs","./tests/eslint-rules/filename-pascal-case.test.ts","./scripts/eslint-rules/no-ad-hoc-z-index.mjs","./tests/eslint-rules/no-ad-hoc-z-index.test.ts","./scripts/eslint-rules/no-complex-jsx-arrow.mjs","./tests/eslint-rules/no-complex-jsx-arrow.test.ts","./scripts/eslint-rules/no-large-inline-object-arg.mjs","./tests/eslint-rules/no-large-inline-object-arg.test.ts","./scripts/eslint-rules/no-long-condition-chain.mjs","./tests/eslint-rules/no-long-condition-chain.test.ts","./scripts/eslint-rules/no-noop-hover-variant.mjs","./tests/eslint-rules/no-noop-hover-variant.test.ts","./tests/mocks/complexityscorerdefaults.ts","./node_modules/next/dist/compiled/@next/font/dist/types.d.ts","./node_modules/next/dist/compiled/@next/font/dist/google/index.d.ts","./node_modules/next/font/google/index.d.ts","./node_modules/nuqs/dist/adapters/next/app.d.ts","./src/contexts/authcontext.tsx","./src/contexts/reactqueryprovider.tsx","./src/components/ui/sonner.tsx","./src/app/layout.tsx","./src/components/ui/breadcrumb.tsx","./src/components/shared/toolbarseparator.tsx","./src/components/navbar/blogdropdown/blogdropdown.tsx","./src/components/navbar/docslink/docslink.tsx","./src/components/ui/button-group.tsx","./src/components/navbar/communityengagementbuttons/communityengagementbuttons.tsx","./src/components/navbar/notificationsbell/notificationsbell.tsx","./src/contexts/pluginmodecontext.tsx","./src/components/navbar/viewswitcher.tsx","./src/components/themetoggle/themetoggle.tsx","./src/components/navbar/workerdropdown/workerdropdown.tsx","./src/components/dashboardheader.tsx","./src/components/navbar/userdropdown/userdropdown.tsx","./src/components/navbar.tsx","./src/components/common_components/loadingscreen.tsx","./src/app/(dashboard)/components/sidebarprovider.tsx","./src/components/debugwarningbanner.tsx","./src/components/norediswarningbanner.tsx","./src/components/licenseexpirybanner.tsx","./node_modules/@types/unist/index.d.ts","./node_modules/@types/hast/index.d.ts","./node_modules/vfile-message/lib/index.d.ts","./node_modules/vfile-message/index.d.ts","./node_modules/vfile/lib/index.d.ts","./node_modules/vfile/index.d.ts","./node_modules/unified/lib/callable-instance.d.ts","./node_modules/trough/lib/index.d.ts","./node_modules/trough/index.d.ts","./node_modules/unified/lib/index.d.ts","./node_modules/unified/index.d.ts","./node_modules/@types/mdast/index.d.ts","./node_modules/mdast-util-to-hast/lib/state.d.ts","./node_modules/mdast-util-to-hast/lib/footer.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/blockquote.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/break.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/code.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/delete.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/emphasis.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/footnote-reference.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/heading.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/html.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/image-reference.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/image.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/inline-code.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/link-reference.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/link.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/list-item.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/list.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/paragraph.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/root.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/strong.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/table.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/table-cell.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/table-row.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/text.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/thematic-break.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/index.d.ts","./node_modules/mdast-util-to-hast/lib/index.d.ts","./node_modules/mdast-util-to-hast/index.d.ts","./node_modules/remark-rehype/lib/index.d.ts","./node_modules/remark-rehype/index.d.ts","./node_modules/react-markdown/lib/index.d.ts","./node_modules/react-markdown/index.d.ts","./node_modules/micromark-util-types/index.d.ts","./node_modules/micromark-extension-gfm-footnote/lib/html.d.ts","./node_modules/micromark-extension-gfm-footnote/lib/syntax.d.ts","./node_modules/micromark-extension-gfm-footnote/index.d.ts","./node_modules/micromark-extension-gfm-strikethrough/lib/html.d.ts","./node_modules/micromark-extension-gfm-strikethrough/lib/syntax.d.ts","./node_modules/micromark-extension-gfm-strikethrough/index.d.ts","./node_modules/micromark-extension-gfm/index.d.ts","./node_modules/mdast-util-from-markdown/lib/types.d.ts","./node_modules/mdast-util-from-markdown/lib/index.d.ts","./node_modules/mdast-util-from-markdown/index.d.ts","./node_modules/mdast-util-to-markdown/lib/types.d.ts","./node_modules/mdast-util-to-markdown/lib/index.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/blockquote.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/break.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/code.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/definition.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/emphasis.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/heading.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/html.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/image.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/image-reference.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/inline-code.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/link.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/link-reference.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/list.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/list-item.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/paragraph.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/root.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/strong.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/text.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/thematic-break.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/index.d.ts","./node_modules/mdast-util-to-markdown/index.d.ts","./node_modules/mdast-util-gfm-footnote/lib/index.d.ts","./node_modules/mdast-util-gfm-footnote/index.d.ts","./node_modules/markdown-table/index.d.ts","./node_modules/mdast-util-gfm-table/lib/index.d.ts","./node_modules/mdast-util-gfm-table/index.d.ts","./node_modules/mdast-util-gfm/lib/index.d.ts","./node_modules/mdast-util-gfm/index.d.ts","./node_modules/remark-gfm/lib/index.d.ts","./node_modules/remark-gfm/index.d.ts","./src/components/userbanner.tsx","./src/app/(dashboard)/layout.tsx","./src/app/(dashboard)/agentcontrolplaneview.test.tsx","./src/app/(dashboard)/layout.test.tsx","./src/components/shared/pageheader.tsx","./src/app/(dashboard)/hooks/useteams.tsx","./src/components/shared/badgelink.tsx","./src/components/ui/hover-card.tsx","./src/components/common_components/defaultproxyadmintag.tsx","./src/components/common_components/labeledfield.tsx","./src/components/templates/keyinfoheader.tsx","./src/components/shared/advanced_date_picker.tsx","./src/components/shared/summarycard.tsx","./src/components/shared/savingstiles.tsx","./src/components/templates/keysavingstab.tsx","./src/components/shared/paginatedmultiselect.tsx","./src/components/common_components/team_multi_select.tsx","./src/components/common_components/userdropdown.tsx","./src/app/(dashboard)/cost-optimization/_components/shadowevalstartform.tsx","./src/app/(dashboard)/cost-optimization/_components/shadowevalsection.tsx","./src/app/(dashboard)/cost-optimization/_components/tierturnschart.tsx","./src/app/(dashboard)/cost-optimization/_components/autorouterbenchmarkstab.tsx","./src/components/templates/keyautorouterusagetab.tsx","./src/components/common_components/autorotationview.tsx","./src/components/common_components/deleteresourcemodal.tsx","./src/components/common_components/routersettingssummary.tsx","./src/components/logging_settings_view.tsx","./src/components/permissions/vectorstorepermissions.tsx","./src/components/permissions/mcpserverpermissions.tsx","./src/components/permissions/agentpermissions.tsx","./src/components/object_permissions_view.tsx","./src/components/organisms/regeneratekeymodal.tsx","./src/components/shared/errorutils.tsx","./src/components/guardrails/guardrailselector.tsx","./src/components/policies/policyselector.tsx","./src/components/templates/keyeditviewcontrols.tsx","./src/components/team/editloggingsettings.tsx","./src/components/templates/key_edit_view.tsx","./src/components/templates/key_info_view.tsx","./src/components/virtualkeyspage/keytablecolumns.tsx","./src/components/virtualkeyspage/virtualkeystable.tsx","./src/app/(dashboard)/api-keys/apikeysdashboard.tsx","./src/app/(dashboard)/page.tsx","./src/app/(dashboard)/page.test.tsx","./src/components/modelselect/modelselect.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupsmodal/accessgroupbaseform.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupsmodal/accessgroupeditmodal.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupsdetailspage.tsx","./src/app/(dashboard)/access-groups/_components/access-group-create/accessgroupcreatedialog.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupstablecolumns.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupstable.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupspage.tsx","./src/app/(dashboard)/access-groups/page.tsx","./tests/test-utils.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupsdetailspage.test.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupspage.test.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupsmodal/accessgroupeditmodal.integration.test.tsx","./src/app/(dashboard)/access-groups/_components/access-group-create/accessgroupcreatedialog.test.tsx","./src/components/constants.tsx","./src/components/scim.tsx","./src/components/settings/adminsettings/loggingsettings/loggingsettings.tsx","./src/components/shared/passwordinput.tsx","./src/components/settings/adminsettings/ssosettings/modals/basessosettingsform.tsx","./src/components/settings/adminsettings/ssosettings/modals/addssosettingsmodal.tsx","./src/components/settings/adminsettings/ssosettings/modals/deletessosettingsmodal.tsx","./src/components/settings/adminsettings/ssosettings/modals/editssosettingsmodal.tsx","./src/components/settings/adminsettings/ssosettings/redactablefield.tsx","./src/components/settings/adminsettings/ssosettings/rolemappings.tsx","./src/components/settings/adminsettings/ssosettings/ssosettingsemptyplaceholder.tsx","./src/components/settings/adminsettings/ssosettings/ssosettingsloadingskeleton.tsx","./src/components/settings/adminsettings/ssosettings/ssosettings.tsx","./src/components/settings/adminsettings/uisettings/pagevisibilitysettings.tsx","./src/components/settings/adminsettings/uisettings/uisettings.tsx","./src/components/settings/adminsettings/userbannersettings/userbannersettings.tsx","./src/components/settings/adminsettings/cyberark/cyberarkemptyplaceholder.tsx","./src/components/settings/adminsettings/cyberark/editcyberarkmodal.tsx","./src/components/settings/adminsettings/cyberark/cyberark.tsx","./src/components/settings/adminsettings/hashicorpvault/edithashicorpvaultmodal.tsx","./src/components/settings/adminsettings/hashicorpvault/hashicorpvaultemptyplaceholder.tsx","./src/components/settings/adminsettings/hashicorpvault/hashicorpvault.tsx","./src/components/settings/adminsettings/pluginsettings/pluginsettings.tsx","./src/components/ssomodals.tsx","./src/components/uiaccesscontrolform.tsx","./src/app/(dashboard)/admin-panel/_components/adminpanel.tsx","./src/app/(dashboard)/admin-panel/page.tsx","./src/app/(dashboard)/admin-panel/_components/adminpanel.test.tsx","./src/app/(dashboard)/agents/_components/agentformkit.tsx","./src/app/(dashboard)/agents/_components/cost_config_fields.tsx","./src/app/(dashboard)/agents/_components/agent_form_fields.tsx","./src/app/(dashboard)/agents/_components/agent_card_discovery.tsx","./src/app/(dashboard)/agents/_components/dynamic_agent_form_fields.tsx","./src/app/(dashboard)/agents/_components/add_agent_form.tsx","./src/app/(dashboard)/agents/_components/agent_virtual_keys.tsx","./src/app/(dashboard)/agents/_components/agent_cost_view.tsx","./src/app/(dashboard)/agents/_components/agent_info.tsx","./src/app/(dashboard)/agents/_components/agentstablecolumns.tsx","./src/app/(dashboard)/agents/_components/agentstable.tsx","./src/app/(dashboard)/agents/_components/agentspanel.tsx","./src/app/(dashboard)/agents/page.tsx","./src/app/(dashboard)/agents/_components/agentspanel.test.tsx","./src/app/(dashboard)/agents/_components/agentstable.test.tsx","./src/app/(dashboard)/agents/_components/add_agent_form.integration.test.tsx","./src/app/(dashboard)/agents/_components/add_agent_form.test.tsx","./src/app/(dashboard)/agents/_components/agent_card_discovery.test.tsx","./src/app/(dashboard)/agents/_components/agent_cost_view.test.tsx","./src/app/(dashboard)/agents/_components/agent_info.integration.test.tsx","./src/app/(dashboard)/agents/_components/agent_info.test.tsx","./src/app/(dashboard)/agents/_components/agent_virtual_keys.test.tsx","./src/app/(dashboard)/api-keys/apikeysdashboard.test.tsx","./src/app/(dashboard)/api-keys/page.tsx","./src/app/(dashboard)/api-reference/_components/doclink.tsx","./src/app/(dashboard)/api-reference/_components/apireferenceview.tsx","./src/components/deprecationbanner.tsx","./src/app/(dashboard)/api-reference/page.tsx","./src/app/(dashboard)/api-reference/_components/apireferenceview.test.tsx","./src/app/(dashboard)/budgets/_components/budget_modal.tsx","./src/app/(dashboard)/budgets/_components/budgettablecolumns.tsx","./src/app/(dashboard)/budgets/_components/budgettable.tsx","./src/app/(dashboard)/budgets/_components/edit_budget_modal.tsx","./src/app/(dashboard)/budgets/_components/budget_panel.tsx","./src/app/(dashboard)/budgets/page.tsx","./src/app/(dashboard)/budgets/_components/budgettable.test.tsx","./src/app/(dashboard)/budgets/_components/budget_modal.integration.test.tsx","./src/app/(dashboard)/budgets/_components/budget_panel.test.tsx","./src/app/(dashboard)/budgets/_components/edit_budget_modal.integration.test.tsx","./src/app/(dashboard)/caching/_components/response_time_indicator.tsx","./src/app/(dashboard)/caching/_components/cache_health.tsx","./src/app/(dashboard)/caching/_components/cache_settings/redistypeselector.tsx","./src/app/(dashboard)/caching/_components/cache_settings/cacheformfield.tsx","./src/app/(dashboard)/caching/_components/cache_settings/cachefieldsection.tsx","./src/app/(dashboard)/caching/_components/cache_settings/index.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredisformfield.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredisfieldsection.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredistypeselector.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/index.tsx","./src/app/(dashboard)/caching/_components/errordrilldown.tsx","./src/app/(dashboard)/caching/_components/cache_dashboard.tsx","./src/app/(dashboard)/caching/page.tsx","./src/app/(dashboard)/caching/_components/errordrilldown.test.tsx","./src/app/(dashboard)/caching/_components/cache_dashboard.test.tsx","./src/app/(dashboard)/caching/_components/cache_health.test.tsx","./src/app/(dashboard)/caching/_components/cache_settings/redistypeselector.test.tsx","./src/app/(dashboard)/caching/_components/cache_settings/index.integration.test.tsx","./src/app/(dashboard)/caching/_components/cache_settings/index.test.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredistypeselector.test.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/index.integration.test.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/index.test.tsx","./src/components/shared/paginationstatusalerts.tsx","./src/app/(dashboard)/cost-optimization/_components/usagetab.tsx","./src/app/(dashboard)/cost-optimization/_components/promptcompressiontab.tsx","./src/components/router_settings/index.tsx","./node_modules/openai/_shims/manual-types.d.ts","./node_modules/openai/_shims/auto/types.d.ts","./node_modules/openai/streaming.d.ts","./node_modules/openai/error.d.ts","./node_modules/openai/_shims/multipartbody.d.ts","./node_modules/openai/uploads.d.ts","./node_modules/openai/core.d.ts","./node_modules/openai/_shims/index.d.ts","./node_modules/openai/pagination.d.ts","./node_modules/openai/resources/shared.d.ts","./node_modules/openai/resources/batches.d.ts","./node_modules/openai/resources/chat/completions/messages.d.ts","./node_modules/openai/resources/chat/completions/completions.d.ts","./node_modules/openai/resources/completions.d.ts","./node_modules/openai/resources/embeddings.d.ts","./node_modules/openai/resources/files.d.ts","./node_modules/openai/resources/images.d.ts","./node_modules/openai/resources/models.d.ts","./node_modules/openai/resources/moderations.d.ts","./node_modules/openai/resources/audio/speech.d.ts","./node_modules/openai/resources/audio/transcriptions.d.ts","./node_modules/openai/resources/audio/translations.d.ts","./node_modules/openai/resources/audio/audio.d.ts","./node_modules/openai/resources/beta/threads/messages.d.ts","./node_modules/openai/resources/beta/threads/runs/steps.d.ts","./node_modules/openai/resources/beta/threads/runs/runs.d.ts","./node_modules/openai/lib/eventstream.d.ts","./node_modules/openai/lib/assistantstream.d.ts","./node_modules/openai/resources/beta/threads/threads.d.ts","./node_modules/openai/resources/beta/assistants.d.ts","./node_modules/openai/resources/chat/completions.d.ts","./node_modules/openai/lib/abstractchatcompletionrunner.d.ts","./node_modules/openai/lib/chatcompletionstream.d.ts","./node_modules/openai/lib/responsesparser.d.ts","./node_modules/openai/resources/responses/input-items.d.ts","./node_modules/openai/lib/responses/eventtypes.d.ts","./node_modules/openai/lib/responses/responsestream.d.ts","./node_modules/openai/resources/responses/responses.d.ts","./node_modules/openai/lib/parser.d.ts","./node_modules/openai/lib/chatcompletionstreamingrunner.d.ts","./node_modules/openai/lib/jsonschema.d.ts","./node_modules/openai/lib/runnablefunction.d.ts","./node_modules/openai/lib/chatcompletionrunner.d.ts","./node_modules/openai/resources/beta/chat/completions.d.ts","./node_modules/openai/resources/beta/chat/chat.d.ts","./node_modules/openai/resources/beta/realtime/sessions.d.ts","./node_modules/openai/resources/beta/realtime/transcription-sessions.d.ts","./node_modules/openai/resources/beta/realtime/realtime.d.ts","./node_modules/openai/resources/beta/beta.d.ts","./node_modules/openai/resources/containers/files/content.d.ts","./node_modules/openai/resources/containers/files/files.d.ts","./node_modules/openai/resources/containers/containers.d.ts","./node_modules/openai/resources/graders/grader-models.d.ts","./node_modules/openai/resources/evals/runs/output-items.d.ts","./node_modules/openai/resources/evals/runs/runs.d.ts","./node_modules/openai/resources/evals/evals.d.ts","./node_modules/openai/resources/fine-tuning/methods.d.ts","./node_modules/openai/resources/fine-tuning/alpha/graders.d.ts","./node_modules/openai/resources/fine-tuning/alpha/alpha.d.ts","./node_modules/openai/resources/fine-tuning/checkpoints/permissions.d.ts","./node_modules/openai/resources/fine-tuning/checkpoints/checkpoints.d.ts","./node_modules/openai/resources/fine-tuning/jobs/checkpoints.d.ts","./node_modules/openai/resources/fine-tuning/jobs/jobs.d.ts","./node_modules/openai/resources/fine-tuning/fine-tuning.d.ts","./node_modules/openai/resources/graders/graders.d.ts","./node_modules/openai/resources/uploads/parts.d.ts","./node_modules/openai/resources/uploads/uploads.d.ts","./node_modules/openai/resources/vector-stores/files.d.ts","./node_modules/openai/resources/vector-stores/file-batches.d.ts","./node_modules/openai/resources/vector-stores/vector-stores.d.ts","./node_modules/openai/index.d.ts","./node_modules/openai/resource.d.ts","./node_modules/openai/resources/chat/chat.d.ts","./node_modules/openai/resources/chat/completions/index.d.ts","./node_modules/openai/resources/chat/index.d.ts","./node_modules/openai/resources/index.d.ts","./node_modules/openai/index.d.mts","./src/components/molecules/models/providerlogo.tsx","./src/components/settings/routersettings/fallbacks/editfallbacks.tsx","./src/components/settings/routersettings/fallbacks/fallbacks.tsx","./src/components/routing_groups/routinggroupusagepanel.tsx","./src/components/routing_groups/routinggroupstablecolumns.tsx","./src/components/routing_groups/routinggroupstable.tsx","./src/components/routing_groups/routinggroupmodal.tsx","./src/components/routing_groups/index.tsx","./src/app/(dashboard)/router-settings/_components/general_settings.tsx","./src/app/(dashboard)/cost-optimization/_components/cacheleakagecard.tsx","./src/app/(dashboard)/cost-optimization/_components/promptcachingtab.tsx","./src/app/(dashboard)/cost-optimization/_components/costoptimizationview.tsx","./src/app/(dashboard)/cost-optimization/page.tsx","./src/app/(dashboard)/cost-optimization/_components/autorouterbenchmarkstab.test.tsx","./src/app/(dashboard)/cost-optimization/_components/cacheleakagecard.test.tsx","./src/app/(dashboard)/cost-optimization/_components/costoptimizationview.activity.test.tsx","./src/app/(dashboard)/cost-optimization/_components/costoptimizationview.test.tsx","./src/app/(dashboard)/cost-optimization/_components/promptcachingtab.test.tsx","./src/app/(dashboard)/cost-optimization/_components/promptcompressiontab.integration.test.tsx","./src/app/(dashboard)/cost-optimization/_components/shadowevalsection.test.tsx","./src/app/(dashboard)/cost-optimization/_components/tierturnschart.test.tsx","./src/app/(dashboard)/cost-optimization/_components/usagetab.test.tsx","./src/app/(dashboard)/cost-optimization/_components/usedailyactivityrange.integration.test.tsx","./src/app/(dashboard)/cost-optimization/_components/usedailyactivityrange.test.tsx","./src/app/(dashboard)/cost-tracking/page.tsx","./src/app/(dashboard)/cost-tracking/_components/add_margin_form.test.tsx","./src/app/(dashboard)/cost-tracking/_components/add_provider_form.integration.test.tsx","./src/app/(dashboard)/cost-tracking/_components/add_provider_form.test.tsx","./src/app/(dashboard)/cost-tracking/_components/cost_tracking_settings.integration.test.tsx","./src/app/(dashboard)/cost-tracking/_components/cost_tracking_settings.test.tsx","./src/app/(dashboard)/cost-tracking/_components/how_it_works.test.tsx","./src/app/(dashboard)/cost-tracking/_components/provider_discount_table.test.tsx","./src/app/(dashboard)/cost-tracking/_components/provider_margin_table.test.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/index.test.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_cost_results.test.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_dropdown.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/patternmodal.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/custompatternmodal.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/keywordmodal.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/patterntable.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/keywordtable.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentcategoryconfiguration.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/thresholdinput.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/competitorintentconfiguration.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfilterconfiguration.tsx","./src/app/(dashboard)/guardrails/_components/guardrailformfield.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_optional_params.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_provider_fields.tsx","./src/app/(dashboard)/guardrails/_components/llm_judge/llmjudgefields.tsx","./src/app/(dashboard)/guardrails/_components/pii_components.tsx","./src/app/(dashboard)/guardrails/_components/pii_configuration.tsx","./src/app/(dashboard)/guardrails/_components/tool_permission/toolpermissionruleseditor.tsx","./src/app/(dashboard)/guardrails/_components/add_guardrail_form.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtablecolumns.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_table.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/categorytable.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfilterdisplay.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfiltermanager.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_info.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtestresults.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtestpanel.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtestplayground.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden_card.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden_detail.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden.tsx","./src/app/(dashboard)/guardrails/_components/teamguardrailstab.tsx","./src/app/(dashboard)/guardrails/_components/guardrailspanel.tsx","./src/app/(dashboard)/guardrails/page.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtestpanel.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtestplayground.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtestresults.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrailspanel.test.tsx","./src/app/(dashboard)/guardrails/_components/teamguardrailstab.integration.test.tsx","./src/app/(dashboard)/guardrails/_components/teamguardrailstab.test.tsx","./src/app/(dashboard)/guardrails/_components/add_guardrail_form.characterization.test.tsx","./src/app/(dashboard)/guardrails/_components/add_guardrail_form.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden_card.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden_detail.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_info.characterization.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_info.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_info_helpers.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_provider_fields.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_table.test.tsx","./src/app/(dashboard)/guardrails/_components/pii_components.test.tsx","./src/app/(dashboard)/guardrails/_components/pii_configuration.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/competitorintentconfiguration.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfilterconfiguration.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfilterdisplay.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfiltermanager.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfiltertables.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/custompatternmodal.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/keywordmodal.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/patternmodal.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/tagsinput.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/thresholdinput.test.tsx","./src/app/(dashboard)/guardrails/_components/custom_code/customcodemodal.test.tsx","./src/app/(dashboard)/guardrails/_components/tool_permission/toolpermissionruleseditor.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/evaluationsettingsmodal.tsx","./src/components/guardrailsmonitor/calcpopover.tsx","./src/components/guardrailsmonitor/metriccard.tsx","./src/components/guardrailsmonitor/unpricednote.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailusagebreakdown.tsx","./src/components/guardrailsmonitor/logviewer.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardraildetail.tsx","./src/app/(dashboard)/guardrails-monitor/_components/scorechart.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailsoverview.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailsmonitorview.tsx","./src/components/shared/adminonlynotice.tsx","./src/app/(dashboard)/guardrails-monitor/page.tsx","./src/app/(dashboard)/guardrails-monitor/page.integration.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/evaluationsettingsmodal.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailconfig.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailconfig.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardraildetail.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailusagebreakdown.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailsmonitorview.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailsoverview.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/scorechart.test.tsx","./src/app/(dashboard)/hooks/usetabrouting.test.tsx","./src/app/(dashboard)/hooks/common/useresourcelist.test.tsx","./src/components/email_settings.tsx","./src/components/msteamssettings.tsx","./src/components/alerting/dynamic_form.tsx","./src/components/alerting/alerting_settings.tsx","./src/components/cloudzerocosttracking/cloudzeroemptyplaceholder.tsx","./src/components/cloudzerocosttracking/cloudzeroformcontrols.tsx","./src/components/cloudzerocosttracking/cloudzerocreatemodal.tsx","./src/components/cloudzerocosttracking/cloudzeroupdatemodal.tsx","./src/components/cloudzerocosttracking/cloudzerointegrationsettings.tsx","./src/components/cloudzerocosttracking/cloudzerocosttracking.tsx","./src/components/settings/loggingandalerts/loggingcallbacks/loggingcallbackstablecolumns.tsx","./src/components/settings/loggingandalerts/loggingcallbacks/loggingcallbackstable.tsx","./src/components/settings.tsx","./src/app/(dashboard)/logging-and-alerts/page.tsx","./src/components/deletedkeyspage/deletedkeystable/deletedkeystablecolumns.tsx","./src/components/deletedkeyspage/deletedkeystable/deletedkeystable.tsx","./src/components/deletedkeyspage/deletedkeyspage.tsx","./src/components/deletedteamspage/deletedteamstable/deletedteamstablecolumns.tsx","./src/components/deletedteamspage/deletedteamstable/deletedteamstable.tsx","./src/components/deletedteamspage/deletedteamspage.tsx","./src/components/view_logs/auditlogstablecolumns.tsx","./src/components/view_logs/auditlogstable.tsx","./src/components/view_logs/auditlogdrawer/auditlogdrawer.tsx","./src/components/view_logs/auditlogspanel.tsx","./src/components/view_logs/log_filter_logic.tsx","./src/components/view_logs/logs_utils.tsx","./src/components/view_logs/logstabletoolbar.tsx","./src/components/view_logs/requestlogsfilters.tsx","./src/components/view_logs/typebadges.tsx","./src/components/view_logs/requestlogstablecolumns.tsx","./src/components/view_logs/requestlogstable.tsx","./src/components/view_logs/requestlogspanel.tsx","./src/components/view_logs/index.tsx","./src/app/(dashboard)/logs/page.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpstandardssettings.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpsubmissionstab.tsx","./src/app/(dashboard)/mcp-servers/_components/mcptoolsettablecolumns.tsx","./src/app/(dashboard)/mcp-servers/_components/mcptoolsetstab.tsx","./src/app/(dashboard)/mcp-servers/_components/awssigv4fields.tsx","./src/app/(dashboard)/mcp-servers/_components/openapibyokfields.tsx","./src/app/(dashboard)/mcp-servers/_components/tokenendpointauthmethodfield.tsx","./src/app/(dashboard)/mcp-servers/_components/upstreamtokenheaderfield.tsx","./src/app/(dashboard)/mcp-servers/_components/oauthformfields.tsx","./src/app/(dashboard)/mcp-servers/_components/truepassthroughwarning.tsx","./src/app/(dashboard)/mcp-servers/_components/dcrbridgetoggle.tsx","./src/app/(dashboard)/mcp-servers/_components/passthroughauthorizesection.tsx","./src/app/(dashboard)/mcp-servers/_components/tokenexchangeformfields.tsx","./src/app/(dashboard)/mcp-servers/_components/idjagformfields.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_config.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_connection_status.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_tool_configuration.tsx","./src/app/(dashboard)/mcp-servers/_components/stdioconfiguration.tsx","./src/app/(dashboard)/mcp-servers/_components/mcppermissionmanagement.tsx","./src/app/(dashboard)/mcp-servers/_components/openapiquickpicker.tsx","./src/app/(dashboard)/mcp-servers/_components/openapiformsection.tsx","./src/app/(dashboard)/mcp-servers/_components/mcplogoselector.tsx","./src/app/(dashboard)/mcp-servers/_components/envvarssection.tsx","./src/hooks/usemcpoauthflow.tsx","./src/hooks/usetestmcpconnection.tsx","./src/app/(dashboard)/mcp-servers/_components/createmcpserver.tsx","./src/app/(dashboard)/mcp-servers/_components/importmcpservers.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_connect.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpservercard.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_display.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_view.tsx","./src/components/settings/adminsettings/mcpsemanticfiltersettings/mcpsemanticfiltertestpanel.tsx","./src/components/settings/adminsettings/mcpsemanticfiltersettings/mcpsemanticfiltersettings.tsx","./src/components/settings/adminsettings/mcptoolsearchsettings/mcptoolsearchsettings.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpnetworksettings.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_discovery.tsx","./src/components/mcp_tools/byokcredentialmodal.tsx","./src/app/(dashboard)/mcp-servers/_components/userenvvarsmodal.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_servers.tsx","./src/app/(dashboard)/mcp-servers/_components/toolargumentsform.tsx","./src/app/(dashboard)/mcp-servers/_components/tooltestpanel.tsx","./src/hooks/usetoolsoauthflow.tsx","./src/hooks/useusermcpoauthflow.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_tools.tsx","./src/app/(dashboard)/mcp-servers/_components/index.tsx","./src/app/(dashboard)/mcp-servers/page.tsx","./src/app/(dashboard)/mcp-servers/_components/createmcpserver.integration.test.tsx","./src/app/(dashboard)/mcp-servers/_components/createmcpserver.permissions.integration.test.tsx","./src/app/(dashboard)/mcp-servers/_components/envvarssection.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcplogoselector.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpnetworksettings.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpformtestharness.tsx","./src/app/(dashboard)/mcp-servers/_components/mcppermissionmanagement.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpservercard.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpstandardssettings.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcptoolsettablecolumns.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcptoolsetstab.integration.test.tsx","./src/app/(dashboard)/mcp-servers/_components/oauthformfields.test.tsx","./src/app/(dashboard)/mcp-servers/_components/openapiquickpicker.test.tsx","./src/app/(dashboard)/mcp-servers/_components/passthroughauthorizesection.test.tsx","./src/app/(dashboard)/mcp-servers/_components/tooltestpanel.test.tsx","./src/app/(dashboard)/mcp-servers/_components/truepassthroughwarning.test.tsx","./src/app/(dashboard)/mcp-servers/_components/userenvvarsmodal.integration.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpformstore.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_connect.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_connection_status.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_discovery.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_config.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_display.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.integration.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_view.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_servers.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_tool_configuration.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_tools.test.tsx","./src/app/(dashboard)/mcp-servers/_components/utils.test.tsx","./src/app/(dashboard)/memory/_components/memorydetaildrawer.tsx","./src/app/(dashboard)/memory/_components/memoryeditmodal.tsx","./src/app/(dashboard)/memory/_components/memorytablecolumns.tsx","./src/app/(dashboard)/memory/_components/memorytable.tsx","./src/app/(dashboard)/memory/_components/memoryview.tsx","./src/app/(dashboard)/memory/page.tsx","./src/app/(dashboard)/memory/page.integration.test.tsx","./src/app/(dashboard)/memory/_components/memorydetaildrawer.test.tsx","./src/app/(dashboard)/memory/_components/memoryeditmodal.integration.test.tsx","./src/app/(dashboard)/memory/_components/memorytable.test.tsx","./src/app/(dashboard)/memory/_components/memoryview.test.tsx","./src/components/aihub/agenthubtablecolumns.tsx","./src/components/aihub/forms/makeagentpublicform.tsx","./src/components/aihub/mcphubtablecolumns.tsx","./src/components/aihub/forms/makemcppublicform.tsx","./src/components/model_filters.tsx","./src/components/aihub/forms/makemodelpublicform.tsx","./src/components/aihub/modelhubtablecolumns.tsx","./src/components/common_components/iconactionbutton/baseactionbutton.tsx","./src/components/common_components/iconactionbutton/tableiconactionbuttons/tableiconactionbutton.tsx","./src/components/aihub/usefullinksmanagement.tsx","./src/components/aihub/skillhubtablecolumns.tsx","./src/components/claude_code_plugins/skill_detail.tsx","./src/components/aihub/skillhubdashboard.tsx","./src/components/claude_code_plugins/makeskillpublicform.tsx","./src/components/chat_ui/codesnippets.tsx","./src/components/public_model_hub.tsx","./src/components/aihub/modelhubtable.tsx","./src/app/(dashboard)/model-hub-table/page.tsx","./src/components/molecules/cost_optimization_feedback_banner.tsx","./src/components/add_model/auto_router_connection_test.tsx","./src/components/model_add/reuse_credentials.tsx","./src/components/update_model_credentials_modal.tsx","./src/components/shared/form/utcdatetimeinput.tsx","./src/components/add_model/cache_control_settings.tsx","./src/components/modelinfoeditform.tsx","./src/components/view_model/model_name_display.tsx","./src/components/model_info_view.tsx","./src/components/common_components/user_search_modal.tsx","./src/components/shared/form/labelwithhint.tsx","./src/components/team/guardrailsselect.tsx","./src/components/common_components/metadatakeyvaluefields.tsx","./src/components/guardrailsettingsview.tsx","./src/components/search_tools/searchtoolselector.tsx","./src/components/team/editmembership.tsx","./src/components/team/permission_definitions.tsx","./src/components/team/member_permissions.tsx","./src/components/team/myusertab.tsx","./src/components/common_components/membertable.tsx","./src/components/team/teammembertab.tsx","./src/components/team/teamvirtualkeystable.tsx","./src/components/team/teaminfo.tsx","./src/components/model_dashboard/modelsettingsmodal/modelsettingsmodal.tsx","./src/app/(dashboard)/models-and-endpoints/components/modelstablecolumns.tsx","./src/app/(dashboard)/models-and-endpoints/components/allmodelstable.tsx","./src/app/(dashboard)/models-and-endpoints/components/allmodelstab.tsx","./src/app/(dashboard)/models-and-endpoints/panels/allmodelspanel.tsx","./src/components/add_model/handle_add_auto_router_submit.tsx","./src/components/add_model/autorouterroutingtest.tsx","./src/components/add_model/add_auto_router_tab.tsx","./src/app/(dashboard)/models-and-endpoints/components/autorouters/autorouterstablecolumns.tsx","./src/app/(dashboard)/models-and-endpoints/components/autorouters/autorouterstable.tsx","./src/app/(dashboard)/models-and-endpoints/components/autorouters/autorouterspanel.tsx","./src/app/(dashboard)/models-and-endpoints/panels/autorouterstabpanel.tsx","./src/components/add_model/advanced_settings.tsx","./src/components/add_model/conditional_public_model_name.tsx","./src/components/add_model/litellm_model_name.tsx","./src/components/add_model/handle_add_model_submit.tsx","./src/components/add_model/model_connection_test.tsx","./src/components/add_model/provider_specific_fields.tsx","./src/components/add_model/add_model_modes.tsx","./src/components/add_model/addmodelform.tsx","./src/app/(dashboard)/models-and-endpoints/panels/addmodelpanel.tsx","./src/components/model_add/credentialmodal.tsx","./src/components/model_add/credentialstablecolumns.tsx","./src/components/model_add/credentialstable.tsx","./src/components/model_add/credentialspanel.tsx","./src/app/(dashboard)/models-and-endpoints/panels/llmcredentialspanel.tsx","./src/components/key_value_input.tsx","./src/components/query_param_input.tsx","./src/components/route_preview.tsx","./src/components/common_components/passthroughsecuritysection.tsx","./src/components/common_components/passthroughguardrailssection.tsx","./src/components/add_pass_through.tsx","./src/components/pass_through_info.tsx","./src/components/passthroughsettings/passthroughendpointstablecolumns.tsx","./src/components/passthroughsettings/passthroughendpointstable.tsx","./src/components/passthroughsettings/passthroughsettings.tsx","./src/app/(dashboard)/models-and-endpoints/panels/passthroughpanel.tsx","./src/components/model_dashboard/healthcheckstablecolumns.tsx","./src/components/model_dashboard/healthcheckstable.tsx","./src/components/model_dashboard/healthcheckcomponent.tsx","./src/app/(dashboard)/models-and-endpoints/panels/healthstatuspanel.tsx","./src/app/(dashboard)/models-and-endpoints/components/modelretrysettingstab.tsx","./src/app/(dashboard)/models-and-endpoints/panels/modelretrysettingspanel.tsx","./src/components/model_group_alias_settings.tsx","./src/app/(dashboard)/models-and-endpoints/panels/modelgroupaliaspanel.tsx","./src/app/(dashboard)/models-and-endpoints/components/accessgroupbudgetmodal.tsx","./src/app/(dashboard)/models-and-endpoints/components/accessgroupbudgetcolumns.tsx","./src/app/(dashboard)/models-and-endpoints/panels/accessgroupbudgetspanel.tsx","./src/components/price_data_reload.tsx","./src/app/(dashboard)/models-and-endpoints/components/pricedatamanagementtab.tsx","./src/app/(dashboard)/models-and-endpoints/panels/pricedatapanel.tsx","./src/app/(dashboard)/models-and-endpoints/page.tsx","./src/app/(dashboard)/models-and-endpoints/page.test.tsx","./src/app/(dashboard)/models-and-endpoints/components/allmodelstab.test.tsx","./src/app/(dashboard)/models-and-endpoints/components/allmodelstable.test.tsx","./src/app/(dashboard)/models-and-endpoints/components/modelretrysettingstab.test.tsx","./src/app/(dashboard)/models-and-endpoints/components/pricedatamanagementtab.test.tsx","./src/app/(dashboard)/models-and-endpoints/components/autorouters/autorouterspanel.test.tsx","./src/app/(dashboard)/models-and-endpoints/panels/accessgroupbudgetspanel.integration.test.tsx","./src/app/(dashboard)/models-and-endpoints/panels/addmodelpanel.integration.test.tsx","./src/app/(dashboard)/models-and-endpoints/panels/autorouterstabpanel.test.tsx","./src/app/(dashboard)/models-and-endpoints/panels/healthstatuspanel.test.tsx","./src/components/view_user_spend.tsx","./src/components/usagepage/components/entityusage/topkeyview.tsx","./src/app/(dashboard)/old-usage/_components/usage.tsx","./src/app/(dashboard)/old-usage/page.tsx","./src/app/(dashboard)/old-usage/_components/usage.test.tsx","./src/components/common_components/filters/filterinput.tsx","./src/components/common_components/filters/filtersbutton.tsx","./src/components/common_components/filters/resetfiltersbutton.tsx","./src/app/(dashboard)/organizations/organizationfilters.tsx","./src/app/(dashboard)/organizations/organizationfilters.test.tsx","./src/components/organization/org-settings/orgsettingsform.tsx","./src/components/organization/org-create/orgcreatedialog.tsx","./src/components/organization/organization_view.tsx","./src/app/(dashboard)/organizations/_components/organizationstablecolumns.tsx","./src/app/(dashboard)/organizations/_components/organizationstable.tsx","./src/app/(dashboard)/organizations/_components/organizationspanel.tsx","./src/app/(dashboard)/organizations/page.tsx","./src/app/(dashboard)/organizations/_components/organizationspanel.test.tsx","./src/app/(dashboard)/organizations/_components/organizationstable.test.tsx","./src/components/llm_calls/chat_completion.tsx","./src/app/(dashboard)/playground/components/complianceui/complianceui.tsx","./node_modules/uuid/dist/max.d.ts","./node_modules/uuid/dist/nil.d.ts","./node_modules/uuid/dist/types.d.ts","./node_modules/uuid/dist/parse.d.ts","./node_modules/uuid/dist/stringify.d.ts","./node_modules/uuid/dist/v1.d.ts","./node_modules/uuid/dist/v1tov6.d.ts","./node_modules/uuid/dist/v35.d.ts","./node_modules/uuid/dist/v3.d.ts","./node_modules/uuid/dist/v4.d.ts","./node_modules/uuid/dist/v5.d.ts","./node_modules/uuid/dist/v6.d.ts","./node_modules/uuid/dist/v6tov1.d.ts","./node_modules/uuid/dist/v7.d.ts","./node_modules/uuid/dist/validate.d.ts","./node_modules/uuid/dist/version.d.ts","./node_modules/uuid/dist/index.d.ts","./src/components/mcp_tools/mcptoolargumentsform.tsx","./src/components/tag_management/tagselector.tsx","./src/app/(dashboard)/playground/llm_calls/a2a_send_message.tsx","./node_modules/@anthropic-ai/sdk/internal/builtin-types.d.mts","./node_modules/form-data/index.d.ts","./node_modules/@types/node-fetch/externals.d.ts","./node_modules/@types/node-fetch/index.d.ts","./node_modules/@anthropic-ai/sdk/internal/types.d.mts","./node_modules/@anthropic-ai/sdk/internal/headers.d.mts","./node_modules/@anthropic-ai/sdk/internal/shim-types.d.mts","./node_modules/@anthropic-ai/sdk/core/streaming.d.mts","./node_modules/@anthropic-ai/sdk/internal/request-options.d.mts","./node_modules/@anthropic-ai/sdk/internal/utils/log.d.mts","./node_modules/@anthropic-ai/sdk/resources/shared.d.mts","./node_modules/@anthropic-ai/sdk/core/error.d.mts","./node_modules/@anthropic-ai/sdk/internal/parse.d.mts","./node_modules/@anthropic-ai/sdk/core/api-promise.d.mts","./node_modules/@anthropic-ai/sdk/core/pagination.d.mts","./node_modules/@anthropic-ai/sdk/internal/uploads.d.mts","./node_modules/@anthropic-ai/sdk/internal/to-file.d.mts","./node_modules/@anthropic-ai/sdk/core/uploads.d.mts","./node_modules/@anthropic-ai/sdk/core/resource.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/environments.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/files.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/models.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/user-profiles.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/agents/versions.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/agents/agents.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memories.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memory-versions.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memory-stores.d.mts","./node_modules/@anthropic-ai/sdk/internal/decoders/line.d.mts","./node_modules/@anthropic-ai/sdk/internal/decoders/jsonl.d.mts","./node_modules/@anthropic-ai/sdk/error.d.mts","./node_modules/@anthropic-ai/sdk/resources/messages/batches.d.mts","./node_modules/@anthropic-ai/sdk/resources/messages/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/messages.d.mts","./node_modules/@anthropic-ai/sdk/lib/parser.d.mts","./node_modules/@anthropic-ai/sdk/lib/messagestream.d.mts","./node_modules/@anthropic-ai/sdk/resources/messages/messages.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/messages/batches.d.mts","./node_modules/@anthropic-ai/sdk/lib/beta-parser.d.mts","./node_modules/@anthropic-ai/sdk/lib/betamessagestream.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/agents/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/messages/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/sessions/events.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/sessions/sessions.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/sessions/resources.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/sessions/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/skills/versions.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/skills/skills.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/skills/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/vaults/credentials.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/vaults/vaults.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/vaults/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta.d.mts","./node_modules/@anthropic-ai/sdk/lib/tools/betarunnabletool.d.mts","./node_modules/@anthropic-ai/sdk/resources.d.mts","./node_modules/@anthropic-ai/sdk/lib/tools/compactioncontrol.d.mts","./node_modules/@anthropic-ai/sdk/lib/tools/betatoolrunner.d.mts","./node_modules/@anthropic-ai/sdk/lib/tools/toolerror.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/messages/messages.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/beta.d.mts","./node_modules/@anthropic-ai/sdk/resources/completions.d.mts","./node_modules/@anthropic-ai/sdk/resources/models.d.mts","./node_modules/@anthropic-ai/sdk/resources/index.d.mts","./node_modules/@anthropic-ai/sdk/client.d.mts","./node_modules/@anthropic-ai/sdk/index.d.mts","./src/app/(dashboard)/playground/llm_calls/anthropic_messages.tsx","./src/app/(dashboard)/playground/llm_calls/audio_speech.tsx","./src/app/(dashboard)/playground/llm_calls/audio_transcriptions.tsx","./src/app/(dashboard)/playground/llm_calls/embeddings_api.tsx","./src/app/(dashboard)/playground/llm_calls/image_edits.tsx","./src/app/(dashboard)/playground/llm_calls/image_generation.tsx","./src/components/llm_calls/responses_api.tsx","./src/app/(dashboard)/playground/llm_calls/interactions_api.tsx","./src/app/(dashboard)/playground/components/chat_ui/additionalmodelsettings.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatcomposer.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatimageupload.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatimageutils.tsx","./src/app/(dashboard)/playground/components/chat_ui/codeinterpretertool.tsx","./src/app/(dashboard)/playground/components/chat_ui/endpointselector.tsx","./src/app/(dashboard)/playground/components/chat_ui/endpointutils.tsx","./src/app/(dashboard)/playground/components/chat_ui/filepreviewcard.tsx","./src/app/(dashboard)/playground/components/chat_ui/a2ametrics.tsx","./src/app/(dashboard)/playground/components/chat_ui/audiorenderer.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatimagerenderer.tsx","./src/app/(dashboard)/playground/components/chat_ui/codeinterpreteroutput.tsx","./src/components/chat_ui/mcpeventsdisplay.tsx","./src/components/chat_ui/reasoningcontent.tsx","./src/app/(dashboard)/playground/components/chat_ui/responsesimageutils.tsx","./src/app/(dashboard)/playground/components/chat_ui/responsesimagerenderer.tsx","./src/app/(dashboard)/playground/components/chat_ui/searchresultsdisplay.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatmessagebubble.tsx","./src/app/(dashboard)/playground/components/chat_ui/responsesimageupload.tsx","./src/app/(dashboard)/playground/components/chat_ui/sessionmanagement.tsx","./src/app/(dashboard)/playground/components/chat_ui/realtimeplayground.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatui.tsx","./src/app/(dashboard)/playground/components/chat_ui/agentbuilderview.tsx","./src/app/(dashboard)/playground/components/compareui/components/messagedisplay.tsx","./src/app/(dashboard)/playground/components/compareui/components/unifiedselector.tsx","./src/app/(dashboard)/playground/components/compareui/components/comparisonpanel.tsx","./src/app/(dashboard)/playground/components/compareui/components/messageinput.tsx","./src/app/(dashboard)/playground/components/compareui/compareui.tsx","./src/app/(dashboard)/playground/page.tsx","./src/app/(dashboard)/playground/page.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/additionalmodelsettings.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/agentbuilderview.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/audiorenderer.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatcomposer.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatimageutils.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatmessagebubble.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatui.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/codeinterpreteroutput.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/endpointselector.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/endpointutils.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/filepreviewcard.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/realtimeplayground.test.tsx","./src/app/(dashboard)/playground/components/compareui/compareui.test.tsx","./src/app/(dashboard)/playground/components/compareui/components/comparisonpanel.test.tsx","./src/app/(dashboard)/playground/components/compareui/components/messagedisplay.test.tsx","./src/app/(dashboard)/playground/components/compareui/components/messageinput.test.tsx","./src/app/(dashboard)/playground/components/compareui/components/modelselector.tsx","./src/app/(dashboard)/playground/components/compareui/components/modelselector.test.tsx","./src/app/(dashboard)/playground/components/compareui/components/unifiedselector.test.tsx","./src/app/(dashboard)/playground/llm_calls/anthropic_messages.test.tsx","./src/app/(dashboard)/playground/llm_calls/audio_speech.test.tsx","./src/app/(dashboard)/playground/llm_calls/audio_transcriptions.test.tsx","./src/app/(dashboard)/playground/llm_calls/embeddings_api.test.tsx","./src/app/(dashboard)/policies/_components/policytablecolumns.tsx","./src/app/(dashboard)/policies/_components/policytable.tsx","./src/app/(dashboard)/policies/_components/pipeline_flow_builder.tsx","./src/app/(dashboard)/policies/_components/policy_info.tsx","./src/app/(dashboard)/policies/_components/add_policy_form.tsx","./src/app/(dashboard)/policies/_components/impact_popover.tsx","./src/app/(dashboard)/policies/_components/attachmenttablecolumns.tsx","./src/app/(dashboard)/policies/_components/attachmenttable.tsx","./src/app/(dashboard)/policies/_components/impact_preview_alert.tsx","./src/app/(dashboard)/policies/_components/tokenselect.tsx","./src/app/(dashboard)/policies/_components/add_attachment_form.tsx","./src/app/(dashboard)/policies/_components/policy_test_panel.tsx","./src/app/(dashboard)/policies/_components/policy_templates.tsx","./src/app/(dashboard)/policies/_components/guardrail_selection_modal.tsx","./src/app/(dashboard)/policies/_components/template_parameter_modal.tsx","./src/app/(dashboard)/policies/_components/ai_suggestion_modal.tsx","./src/app/(dashboard)/policies/_components/index.tsx","./src/app/(dashboard)/policies/page.tsx","./src/app/(dashboard)/policies/_components/attachmenttable.test.tsx","./src/app/(dashboard)/policies/_components/policytable.test.tsx","./src/app/(dashboard)/policies/_components/add_attachment_form.test.tsx","./src/app/(dashboard)/policies/_components/add_policy_form.test.tsx","./src/app/(dashboard)/policies/_components/ai_suggestion_modal.test.tsx","./src/app/(dashboard)/policies/_components/guardrail_selection_modal.test.tsx","./src/app/(dashboard)/policies/_components/impact_popover.test.tsx","./src/app/(dashboard)/policies/_components/impact_preview_alert.test.tsx","./src/app/(dashboard)/policies/_components/index.test.tsx","./src/app/(dashboard)/policies/_components/pipeline_flow_builder.test.tsx","./src/app/(dashboard)/policies/_components/policy_info.test.tsx","./src/app/(dashboard)/policies/_components/policy_templates.test.tsx","./src/app/(dashboard)/policies/_components/policy_test_panel.test.tsx","./src/app/(dashboard)/policies/_components/template_parameter_modal.test.tsx","./src/app/(dashboard)/projects/_components/projectmodals/createprojectmodal.tsx","./src/app/(dashboard)/projects/_components/projectmodals/editprojectmodal.tsx","./src/app/(dashboard)/projects/_components/projectkeystablecolumns.tsx","./src/app/(dashboard)/projects/_components/projectkeystable.tsx","./src/app/(dashboard)/projects/_components/projectkeyssection.tsx","./src/app/(dashboard)/projects/_components/projectdetailspage.tsx","./src/app/(dashboard)/projects/_components/projectstablecolumns.tsx","./src/app/(dashboard)/projects/_components/projectstable.tsx","./src/app/(dashboard)/projects/_components/projectspage.tsx","./src/app/(dashboard)/projects/page.tsx","./src/app/(dashboard)/projects/_components/projectdetailspage.test.tsx","./src/app/(dashboard)/projects/_components/projectkeyssection.test.tsx","./src/app/(dashboard)/projects/_components/projectkeystable.test.tsx","./src/app/(dashboard)/projects/_components/projectspage.test.tsx","./src/app/(dashboard)/projects/_components/projectstable.test.tsx","./src/app/(dashboard)/projects/_components/projectmodals/createprojectmodal.integration.test.tsx","./src/app/(dashboard)/projects/_components/projectmodals/createprojectmodal.test.tsx","./src/app/(dashboard)/projects/_components/projectmodals/editprojectmodal.integration.test.tsx","./src/app/(dashboard)/projects/_components/projectmodals/editprojectmodal.test.tsx","./src/app/(dashboard)/projects/_components/projectmodals/projectbaseform.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_utils.tsx","./src/app/(dashboard)/prompts/_components/prompttablecolumns.tsx","./src/app/(dashboard)/prompts/_components/prompttable.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/promptcodesnippets.tsx","./src/app/(dashboard)/prompts/_components/prompt_info.tsx","./src/app/(dashboard)/prompts/_components/add_prompt_form.tsx","./src/app/(dashboard)/prompts/_components/tool_modal.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/prompteditorheader.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/modelconfigcard.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/toolscard.tsx","./src/app/(dashboard)/prompts/_components/variable_textarea.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/developermessagecard.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/promptmessagescard.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/variableinput.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/emptystate.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/messagebubble.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/messagelist.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/variablewarning.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/messageinput.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/index.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/publishmodal.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/dotpromptviewtab.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/versionhistorysidepanel.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/index.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view.tsx","./src/app/(dashboard)/prompts/_components/index.tsx","./src/app/(dashboard)/prompts/page.tsx","./src/app/(dashboard)/prompts/_components/prompttable.test.tsx","./src/app/(dashboard)/prompts/_components/add_prompt_form.integration.test.tsx","./src/app/(dashboard)/prompts/_components/index.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_info.test.tsx","./src/app/(dashboard)/prompts/_components/tool_modal.test.tsx","./src/app/(dashboard)/prompts/_components/variable_textarea.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/developermessagecard.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/modelconfigcard.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/promptcodesnippets.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/prompteditorheader.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/promptmessagescard.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/publishmodal.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/toolscard.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/versionhistorysidepanel.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/emptystate.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/messagebubble.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/messageinput.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/messagelist.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/variableinput.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/index.test.tsx","./src/app/(dashboard)/router-settings/page.tsx","./src/app/(dashboard)/router-settings/_components/general_settings.test.tsx","./src/app/(dashboard)/search-tools/_components/searchconnectiontest.tsx","./src/app/(dashboard)/search-tools/_components/createsearchtools.tsx","./src/app/(dashboard)/search-tools/_components/searchtooltablecolumns.tsx","./src/app/(dashboard)/search-tools/_components/searchtooltable.tsx","./src/app/(dashboard)/search-tools/_components/searchtooltester.tsx","./src/app/(dashboard)/search-tools/_components/searchtoolview.tsx","./src/app/(dashboard)/search-tools/_components/searchtools.tsx","./src/app/(dashboard)/search-tools/_components/index.tsx","./src/app/(dashboard)/search-tools/page.tsx","./src/app/(dashboard)/search-tools/_components/createsearchtools.integration.test.tsx","./src/app/(dashboard)/search-tools/_components/createsearchtools.test.tsx","./src/app/(dashboard)/search-tools/_components/searchconnectiontest.test.tsx","./src/app/(dashboard)/search-tools/_components/searchtooltable.test.tsx","./src/app/(dashboard)/search-tools/_components/searchtooltester.test.tsx","./src/app/(dashboard)/search-tools/_components/searchtoolview.test.tsx","./src/app/(dashboard)/search-tools/_components/searchtools.integration.test.tsx","./src/app/(dashboard)/search-tools/_components/searchtools.test.tsx","./src/app/(dashboard)/skills/_components/add_plugin_form.tsx","./src/app/(dashboard)/skills/_components/plugintablecolumns.tsx","./src/app/(dashboard)/skills/_components/plugintable.tsx","./src/app/(dashboard)/skills/_components/claudecodepluginspanel.tsx","./src/app/(dashboard)/skills/page.tsx","./src/app/(dashboard)/skills/_components/claudecodepluginspanel.test.tsx","./src/app/(dashboard)/skills/_components/plugintable.test.tsx","./src/app/(dashboard)/skills/_components/add_plugin_form.test.tsx","./src/app/(dashboard)/tag-management/_components/tag_info.tsx","./src/app/(dashboard)/tag-management/_components/tagtablecolumns.tsx","./src/app/(dashboard)/tag-management/_components/tagtable.tsx","./src/app/(dashboard)/tag-management/_components/components/createtagmodal.tsx","./src/app/(dashboard)/tag-management/_components/index.tsx","./src/app/(dashboard)/tag-management/page.tsx","./src/app/(dashboard)/tag-management/_components/tagtable.test.tsx","./src/app/(dashboard)/tag-management/_components/index.test.tsx","./src/app/(dashboard)/tag-management/_components/tag_info.integration.test.tsx","./src/app/(dashboard)/tag-management/_components/components/createtagmodal.test.tsx","./src/components/team/availableteamstablecolumns.tsx","./src/components/team/availableteamstable.tsx","./src/components/team/availableteamspanel.tsx","./src/components/teamssosettings.tsx","./src/components/teamspage/teamtablecolumns.tsx","./src/components/teamspage/teamstable.tsx","./src/components/teams.tsx","./src/app/(dashboard)/teams/page.tsx","./src/components/toolpolicies/policyselect.tsx","./src/components/tooldetail.tsx","./src/components/toolpolicies/toolpoliciestablecolumns.tsx","./src/components/toolpolicies/toolpoliciestable.tsx","./src/components/toolpolicies/toolpoliciespanel.tsx","./src/components/toolpoliciesview.tsx","./src/app/(dashboard)/tool-policies/page.tsx","./src/app/(dashboard)/transform-request/transformrequestpanel.tsx","./src/app/(dashboard)/transform-request/transformrequestpanel.test.tsx","./src/app/(dashboard)/transform-request/page.tsx","./src/app/(dashboard)/ui-theme/uithemesettings.tsx","./src/app/(dashboard)/ui-theme/uithemesettings.test.tsx","./src/app/(dashboard)/ui-theme/page.tsx","./src/components/usagepage/components/keymodelusageview.tsx","./src/components/activity_metrics.tsx","./src/components/cloudzero_export_modal.tsx","./src/components/shared/chart_loader.tsx","./src/components/per_user_usage.tsx","./src/components/user_agent_activity.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/components/endpointusagebarchart.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/components/endpointusagelinechart.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/components/endpointusagetable.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/endpointusage.tsx","./src/app/(dashboard)/usage/_components/components/modelviewtoggle.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/topmodelview.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/teamuserspendcard.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/entityusage.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/spendbyprovider.tsx","./src/app/(dashboard)/usage/_components/components/usageaichatpanel.tsx","./src/app/(dashboard)/usage/_components/components/usageviewselect/usageviewselect.tsx","./src/app/(dashboard)/usage/_components/components/usagepageview.tsx","./src/app/(dashboard)/usage/page.tsx","./src/app/(dashboard)/usage/_components/components/usageaichatpanel.test.tsx","./src/app/(dashboard)/usage/_components/components/usagepageview.test.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/endpointusage.test.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/components/endpointusagebarchart.test.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/components/endpointusagelinechart.test.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/components/endpointusagetable.test.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/entityusage.test.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/spendbyprovider.test.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/topmodelview.test.tsx","./src/app/(dashboard)/usage/_components/components/usageviewselect/usageviewselect.test.tsx","./src/app/(dashboard)/users/_components/user_edit_view.tsx","./src/app/(dashboard)/users/_components/bulkeditusers.tsx","./src/components/bulk_create_users_button.tsx","./src/app/(dashboard)/users/_components/default-user-settings/defaultusersettingsform.tsx","./src/app/(dashboard)/users/_components/view_users/userstablecolumns.tsx","./src/app/(dashboard)/users/_components/view_users/userstable.tsx","./src/app/(dashboard)/users/_components/view_users/user_info_view.tsx","./src/app/(dashboard)/users/_components/view_users.tsx","./src/app/(dashboard)/users/_components/index.tsx","./src/app/(dashboard)/users/page.tsx","./src/app/(dashboard)/users/_components/bulkeditusers.test.tsx","./src/app/(dashboard)/users/_components/user_edit_view.test.tsx","./src/app/(dashboard)/users/_components/view_users.test.tsx","./src/app/(dashboard)/users/_components/default-user-settings/defaultusersettingsform.test.tsx","./src/app/(dashboard)/users/_components/view_users/userstable.test.tsx","./src/app/(dashboard)/users/_components/view_users/user_info_view.integration.test.tsx","./src/app/(dashboard)/users/_components/view_users/user_info_view.test.tsx","./src/components/vector_store_providers.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoretablecolumns.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoretable.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoreform.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoretester.tsx","./src/app/(dashboard)/vector-stores/_components/vector_store_info.tsx","./src/app/(dashboard)/vector-stores/_components/documentstablecolumns.tsx","./src/app/(dashboard)/vector-stores/_components/documentstable.tsx","./src/app/(dashboard)/vector-stores/_components/s3vectorsconfig.tsx","./src/app/(dashboard)/vector-stores/_components/createvectorstore.tsx","./src/app/(dashboard)/vector-stores/_components/testvectorstoretab.tsx","./src/app/(dashboard)/vector-stores/_components/index.tsx","./src/app/(dashboard)/vector-stores/page.tsx","./src/app/(dashboard)/vector-stores/_components/createvectorstore.characterization.test.tsx","./src/app/(dashboard)/vector-stores/_components/createvectorstore.test.tsx","./src/app/(dashboard)/vector-stores/_components/documentstable.test.tsx","./src/app/(dashboard)/vector-stores/_components/indexestable.test.tsx","./src/app/(dashboard)/vector-stores/_components/s3vectorsconfig.test.tsx","./src/app/(dashboard)/vector-stores/_components/testvectorstoretab.test.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoreform.integration.test.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoreform.test.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoretable.test.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoretester.test.tsx","./src/app/(dashboard)/vector-stores/_components/index.test.tsx","./src/app/(dashboard)/vector-stores/_components/vector_store_info.integration.test.tsx","./src/app/(dashboard)/vector-stores/_components/vector_store_info.test.tsx","./src/app/(dashboard)/workflows/workflowruns.tsx","./src/app/(dashboard)/workflows/workflowruns.test.tsx","./src/app/(dashboard)/workflows/page.tsx","./src/app/(dashboard)/workflows/page.integration.test.tsx","./src/app/chat/layout.tsx","./src/app/chat/layout.test.tsx","./src/components/chat/chatmessages.tsx","./src/components/chat/mcpconnectpicker.tsx","./src/app/chat/page.tsx","./src/app/chat/page.integration.test.tsx","./src/components/chat/keyspanel.tsx","./src/app/chat/api-keys/page.tsx","./src/components/chat/mcpcredentialstab.tsx","./src/app/chat/credentials/page.tsx","./src/components/chat/mcpappspanel.tsx","./src/components/chat/connectflowbanner.tsx","./src/components/chat/connectflowsurface.tsx","./src/app/chat/integrations/page.tsx","./src/components/chat/logspanel.tsx","./src/app/chat/logs/page.tsx","./src/components/chat/usagepanel.tsx","./src/app/chat/usage/page.tsx","./src/app/connect/layout.tsx","./src/app/connect/layout.test.tsx","./src/app/connect/page.tsx","./src/app/connect/page.test.tsx","./src/app/login/loginpage.tsx","./src/app/login/loginpage.integration.test.tsx","./src/app/login/loginpage.test.tsx","./src/app/login/page.tsx","./src/app/mcp/oauth/callback/page.tsx","./src/app/model_hub/page.tsx","./src/app/model_hub_table/page.tsx","./src/app/onboarding/onboardingerrorview.tsx","./src/app/onboarding/onboardingerrorview.test.tsx","./src/app/onboarding/onboardingloadingview.tsx","./src/app/onboarding/onboardingformbody.tsx","./src/app/onboarding/onboardingform.tsx","./src/app/onboarding/onboardingform.test.tsx","./src/app/onboarding/onboardingformbody.integration.test.tsx","./src/app/onboarding/onboardingformbody.test.tsx","./src/app/onboarding/onboardingloadingview.test.tsx","./src/app/onboarding/page.tsx","./src/components/betabadge.test.tsx","./src/components/createuserbutton.test.tsx","./src/components/dashboardheader.test.tsx","./src/components/debugwarningbanner.test.tsx","./src/components/deprecationbanner.test.tsx","./src/components/guardrailsettingsview.test.tsx","./src/components/helplink.test.tsx","./src/components/licenseexpirybanner.test.tsx","./src/components/norediswarningbanner.test.tsx","./src/components/scim.test.tsx","./src/components/ssomodals.test.tsx","./src/components/sidebarusagecard.test.tsx","./src/components/teamssosettings.test.tsx","./src/components/teams.test.tsx","./src/components/tooldetail.test.tsx","./src/components/toolpoliciesview.test.tsx","./src/components/uiaccesscontrolform.integration.test.tsx","./src/components/uiaccesscontrolform.unit.test.tsx","./src/components/userbanner.test.tsx","./src/components/activity_metrics.test.tsx","./src/components/add_pass_through.integration.test.tsx","./src/components/bulk_create_users_button.test.tsx","./src/components/cloudzero_export_modal.integration.test.tsx","./src/components/email_settings.test.tsx","./src/components/key_info_utils.test.tsx","./src/components/key_value_input.test.tsx","./src/components/leftnav.test.tsx","./src/components/logging_settings_view.test.tsx","./src/components/model_info_view.test.tsx","./src/components/navbar.test.tsx","./src/components/onboarding_link.test.tsx","./src/components/pass_through_info.integration.test.tsx","./src/components/per_user_usage.test.tsx","./src/components/price_data_reload.test.tsx","./src/components/provider_info_helpers.test.tsx","./src/components/public_model_hub.test.tsx","./src/components/query_param_input.test.tsx","./src/components/route_preview.test.tsx","./src/components/settings.test.tsx","./src/components/update_model_credentials_modal.test.tsx","./src/components/user_agent_activity.test.tsx","./src/components/vector_store_providers.test.tsx","./src/components/aihub/agenthubtablecolumns.test.tsx","./src/components/aihub/mcphubtablecolumns.test.tsx","./src/components/aihub/modelhubtable.test.tsx","./src/components/aihub/modelhubtablecolumns.test.tsx","./src/components/aihub/skillhubtablecolumns.test.tsx","./src/components/aihub/usefullinksmanagement.test.tsx","./src/components/aihub/forms/makeagentpublicform.test.tsx","./src/components/aihub/forms/makemcppublicform.test.tsx","./src/components/aihub/forms/makemodelpublicform.test.tsx","./src/components/cloudzerocosttracking/cloudzerocosttracking.test.tsx","./src/components/cloudzerocosttracking/cloudzerocreatemodal.integration.test.tsx","./src/components/cloudzerocosttracking/cloudzerocreatemodal.test.tsx","./src/components/cloudzerocosttracking/cloudzeroemptyplaceholder.test.tsx","./src/components/cloudzerocosttracking/cloudzerointegrationsettings.test.tsx","./src/components/cloudzerocosttracking/cloudzeroupdatemodal.integration.test.tsx","./src/components/cloudzerocosttracking/cloudzeroupdatemodal.test.tsx","./src/components/deletedkeyspage/deletedkeyspage.test.tsx","./src/components/deletedkeyspage/deletedkeystable/deletedkeystable.test.tsx","./src/components/deletedteamspage/deletedteamspage.test.tsx","./src/components/deletedteamspage/deletedteamstable/deletedteamstable.test.tsx","./src/components/entityusageexport/entityusageexportmodal.test.tsx","./src/components/entityusageexport/exportformatselector.test.tsx","./src/components/entityusageexport/exportsummary.test.tsx","./src/components/entityusageexport/exporttypeselector.test.tsx","./src/components/entityusageexport/usageexportheader.test.tsx","./src/components/guardrailsmonitor/metriccard.test.tsx","./src/components/modelselect/modelselect.test.tsx","./src/components/navbar/viewswitcher.test.tsx","./src/components/navbar/blogdropdown/blogdropdown.test.tsx","./src/components/navbar/communityengagementbuttons/communityengagementbuttons.test.tsx","./src/components/navbar/docslink/docslink.test.tsx","./src/components/navbar/notificationsbell/notificationsbell.test.tsx","./src/components/navbar/userdropdown/userdropdown.test.tsx","./src/components/navbar/workerdropdown/workerdropdown.test.tsx","./src/components/passthroughsettings/passthroughendpointstable.test.tsx","./src/components/passthroughsettings/passthroughsettings.test.tsx","./src/components/settings/adminsettings/cyberark/cyberark.test.tsx","./src/components/settings/adminsettings/cyberark/editcyberarkmodal.test.tsx","./src/components/settings/adminsettings/hashicorpvault/edithashicorpvaultmodal.test.tsx","./src/components/settings/adminsettings/hashicorpvault/hashicorpvault.test.tsx","./src/components/settings/adminsettings/hashicorpvault/hashicorpvaultemptyplaceholder.test.tsx","./src/components/settings/adminsettings/loggingsettings/loggingsettings.test.tsx","./src/components/settings/adminsettings/mcpsemanticfiltersettings/mcpsemanticfiltersettings.test.tsx","./src/components/settings/adminsettings/mcpsemanticfiltersettings/mcpsemanticfiltertestpanel.test.tsx","./src/components/settings/adminsettings/mcptoolsearchsettings/mcptoolsearchsettings.test.tsx","./src/components/settings/adminsettings/pluginsettings/pluginsettings.integration.test.tsx","./src/components/settings/adminsettings/ssosettings/redactablefield.test.tsx","./src/components/settings/adminsettings/ssosettings/rolemappings.test.tsx","./src/components/settings/adminsettings/ssosettings/ssosettings.test.tsx","./src/components/settings/adminsettings/ssosettings/ssosettingsemptyplaceholder.test.tsx","./src/components/settings/adminsettings/ssosettings/ssosettingsloadingskeleton.test.tsx","./src/components/settings/adminsettings/ssosettings/modals/addssosettingsmodal.test.tsx","./src/components/settings/adminsettings/ssosettings/modals/basessosettingsform.test.tsx","./src/components/settings/adminsettings/ssosettings/modals/deletessosettingsmodal.test.tsx","./src/components/settings/adminsettings/ssosettings/modals/editssosettingsmodal.integration.test.tsx","./src/components/settings/adminsettings/ssosettings/modals/editssosettingsmodal.test.tsx","./src/components/settings/adminsettings/uisettings/pagevisibilitysettings.test.tsx","./src/components/settings/adminsettings/uisettings/uisettings.test.tsx","./src/components/settings/adminsettings/userbannersettings/userbannersettings.test.tsx","./src/components/settings/loggingandalerts/loggingcallbacks/loggingcallbackstable.test.tsx","./src/components/settings/routersettings/fallbacks/addfallbacks.test.tsx","./src/components/settings/routersettings/fallbacks/addfallbacksmodal.test.tsx","./src/components/settings/routersettings/fallbacks/editfallbacks.test.tsx","./src/components/settings/routersettings/fallbacks/fallbackselectionform.test.tsx","./src/components/settings/routersettings/fallbacks/fallbacks.test.tsx","./src/components/sidebaraccountmenu/sidebaraccountmenu.test.tsx","./src/components/teamspage/teamstable.test.tsx","./src/components/themetoggle/themetoggle.test.tsx","./src/components/toolpolicies/policyselect.test.tsx","./src/components/toolpolicies/toolpoliciespanel.test.tsx","./src/components/toolpolicies/toolpoliciestable.test.tsx","./src/components/toolpolicies/toolpoliciestablecolumns.test.tsx","./src/components/usagepage/components/keymodelusageview.test.tsx","./src/components/usagepage/components/entityusage/topkeyview.test.tsx","./src/components/virtualkeyspage/virtualkeystable.test.tsx","./src/components/add_model/addmodelform.test.tsx","./src/components/add_model/autorouterroutingtest.test.tsx","./src/components/add_model/classifierprompteditor.integration.test.tsx","./src/components/add_model/complexityrouterconfig.test.tsx","./src/components/add_model/heuristicscoringconfig.test.tsx","./src/components/add_model/openingprompteditor.test.tsx","./src/components/add_model/routerconfigbuilder.test.tsx","./src/components/add_model/semantickeywordmatching.test.tsx","./src/components/add_model/stallescalationconfig.test.tsx","./src/components/add_model/tiermodeleffortrows.test.tsx","./src/components/add_model/add_auto_router_tab.test.tsx","./tests/mounted-form-host.tsx","./src/components/add_model/advanced_settings.test.tsx","./src/components/add_model/auto_router_connection_test.test.tsx","./src/components/add_model/cache_control_settings.test.tsx","./src/components/add_model/conditional_public_model_name.test.tsx","./src/components/add_model/handle_add_model_submit.test.tsx","./src/components/add_model/litellm_model_name.test.tsx","./src/components/add_model/model_connection_test.test.tsx","./src/components/add_model/provider_specific_fields.test.tsx","./src/components/agent_management/agentselector.test.tsx","./src/components/alerting/dynamic_form.integration.test.tsx","./src/components/chat/chatshell.test.tsx","./src/components/chat/connectflowbanner.test.tsx","./src/components/chat/connectflowsurface.test.tsx","./src/components/chat/logspanel.test.tsx","./src/components/chat/mcpappspanel.test.tsx","./src/components/chat/mcpconnectpicker.test.tsx","./src/components/chat_ui/codesnippets.test.tsx","./src/components/chat_ui/reasoningcontent.test.tsx","./src/components/chat_ui/responsemetrics.test.tsx","./src/components/common_components/defaultproxyadmintag.test.tsx","./src/components/common_components/deleteresourcemodal.test.tsx","./src/components/common_components/keylifecyclesettings.test.tsx","./src/components/common_components/labeledfield.test.tsx","./src/components/common_components/loadingscreen.test.tsx","./src/components/common_components/metadatakeyvaluefields.test.tsx","./src/components/common_components/modelaliasmanager.test.tsx","./src/components/common_components/modelselector.test.tsx","./src/components/common_components/mountedformfield.test.tsx","./src/components/common_components/newbadge.tsx","./src/components/common_components/newbadge.test.tsx","./src/components/common_components/organizationdropdown.test.tsx","./src/components/common_components/passthroughguardrailssection.test.tsx","./src/components/common_components/premiumloggingsettings.test.tsx","./src/components/common_components/ratelimittypeformitem.test.tsx","./src/components/common_components/routersettingsaccordion.test.tsx","./src/components/common_components/routersettingssummary.test.tsx","./src/components/common_components/userdropdown.test.tsx","./src/components/common_components/routersettingswiring.test.tsx","./src/components/common_components/team_dropdown.test.tsx","./src/components/common_components/team_multi_select.test.tsx","./src/components/common_components/user_search_modal.test.tsx","./src/components/common_components/filters/filterinput.test.tsx","./src/components/common_components/filters/filtersbutton.test.tsx","./src/components/common_components/filters/resetfiltersbutton.test.tsx","./src/components/common_components/iconactionbutton/baseactionbutton.test.tsx","./src/components/common_components/iconactionbutton/tableiconactionbuttons/tableiconactionbutton.test.tsx","./src/components/email_events/email_event_settings.test.tsx","./src/components/guardrails/guardrailselector.test.tsx","./src/components/key_team_helpers/budgetfallbackseditor.test.tsx","./src/components/key_team_helpers/modelmaxbudgeteditor.integration.test.tsx","./src/components/key_team_helpers/tagratelimiteditor.test.tsx","./src/components/key_team_helpers/fetch_available_models_team_key.test.tsx","./src/components/llm_calls/chat_completion.test.tsx","./src/components/llm_calls/fetch_models.test.tsx","./src/components/llm_calls/responses_api.test.tsx","./src/components/mcp_server_management/mcpserverselector.test.tsx","./src/components/mcp_server_management/mcptoolpermissions.test.tsx","./src/components/mcp_tools/byokcredentialmodal.test.tsx","./src/components/mcp_tools/mcptoolargumentsform.test.tsx","./src/components/mcp_tools/types.test.tsx","./src/components/model_add/credentialmodal.test.tsx","./src/components/model_add/credentialspanel.test.tsx","./src/components/model_add/credentialstable.test.tsx","./src/components/model_add/reuse_credentials.test.tsx","./src/components/model_dashboard/healthcheckcomponent.test.tsx","./src/components/model_dashboard/healthcheckstable.test.tsx","./src/components/model_dashboard/modelsettingsmodal/modelsettingsmodal.test.tsx","./src/components/molecules/cost_optimization_feedback_banner.test.tsx","./src/components/molecules/logo/logo.test.tsx","./src/components/molecules/models/providerlogo.test.tsx","./src/components/organisms/regeneratekeymodal.integration.test.tsx","./src/components/organisms/regeneratekeymodal.test.tsx","./src/components/organisms/create_key_button.integration.test.tsx","./src/components/organisms/create_key_button.test.tsx","./src/components/organization/organization_view.test.tsx","./src/components/organization/org-create/orgcreatedialog.test.tsx","./src/components/organization/org-settings/orgsettingsform.test.tsx","./src/components/permissions/agentpermissions.test.tsx","./src/components/permissions/mcpserverpermissions.test.tsx","./src/components/policies/policyselector.test.tsx","./src/components/router_settings/latencybasedconfiguration.test.tsx","./src/components/router_settings/reliabilityretriessection.test.tsx","./src/components/router_settings/routersettingsform.test.tsx","./src/components/router_settings/routingstrategyselector.test.tsx","./src/components/router_settings/tagfilteringtoggle.test.tsx","./src/components/router_settings/index.test.tsx","./src/components/routing_groups/routinggroupmodal.test.tsx","./src/components/routing_groups/routinggroupstable.test.tsx","./src/components/routing_groups/index.integration.test.tsx","./src/components/search_tools/searchtoolselector.test.tsx","./src/components/shared/alert.test.tsx","./src/components/shared/badgelink.test.tsx","./src/components/shared/copybutton.test.tsx","./src/components/shared/createdkeydisplay.test.tsx","./src/components/shared/entitylink.test.tsx","./src/components/shared/inheritedbudgethint.test.tsx","./src/components/shared/meter.test.tsx","./src/components/shared/multiselect.test.tsx","./src/components/shared/pageheader.test.tsx","./src/components/shared/paginatedmultiselect.test.tsx","./src/components/shared/paginatedsearchselect.test.tsx","./src/components/shared/paginationstatusalerts.test.tsx","./src/components/shared/searchselect.test.tsx","./src/components/shared/sidebar.test.tsx","./src/components/shared/toolbarseparator.test.tsx","./src/components/shared/advanced_date_picker.test.tsx","./src/components/shared/chart_loader.test.tsx","./src/components/shared/datatable/datatable.test-d.tsx","./src/components/shared/datatable/datatable.test.tsx","./src/components/shared/datatable/datatablefilterdrawer.test.tsx","./src/components/shared/datatable/datatablepagination.test.tsx","./src/components/shared/datatable/datatablerowselection.test.tsx","./src/components/shared/datatable/datatablesortheader.test.tsx","./src/components/shared/datatable/datatabletoolbar.test.tsx","./src/components/shared/charts/area_chart.test.tsx","./src/components/shared/charts/bar_chart.test.tsx","./src/components/shared/charts/chart_legend.test.tsx","./src/components/shared/charts/chart_tooltip.test.tsx","./src/components/shared/charts/donut_chart.test.tsx","./src/components/shared/charts/line_chart.test.tsx","./src/components/shared/form/formfield.test.tsx","./src/components/shared/table_cells/autoroutertag.test.tsx","./src/components/shared/table_cells/date_cell.test.tsx","./src/components/shared/table_cells/id_cell.test.tsx","./src/components/shared/table_cells/identity_cell.test.tsx","./src/components/shared/table_cells/models_cell.test.tsx","./src/components/shared/table_cells/money_cell.test.tsx","./src/components/shared/table_cells/spend_budget_cell.test.tsx","./src/components/shared/table_cells/status_badge.test.tsx","./src/components/tag_management/tagselector.test.tsx","./src/components/team/availableteamspanel.test.tsx","./src/components/team/editmembership.integration.test.tsx","./src/components/team/editmembership.test.tsx","./src/components/team/loggingsettings.test.tsx","./src/components/team/myusertab.test.tsx","./src/components/team/teaminfo.test.tsx","./src/components/team/teammembertab.test.tsx","./src/components/team/teamvirtualkeystable.test.tsx","./src/components/team/member_permissions.test.tsx","./src/components/team/permission_definitions.test.tsx","./src/components/templates/keyautorouterusagetab.integration.test.tsx","./src/components/templates/keyinfoheader.test.tsx","./src/components/templates/keyinfoview.handlekeyupdate.test.tsx","./src/components/templates/keysavingstab.integration.test.tsx","./src/components/templates/key_edit_view.test.tsx","./src/components/templates/key_info_view.budget_display.test.tsx","./src/components/templates/key_info_view.test.tsx","./src/components/ui/alert-dialog.test.tsx","./src/components/ui/avatar.test.tsx","./src/components/ui/badge.test.tsx","./src/components/ui/breadcrumb.test.tsx","./src/components/ui/button.test.tsx","./src/components/ui/chart.test.tsx","./src/components/ui/field.test.tsx","./src/components/ui/ref-forwarding.test.tsx","./src/components/ui/select.test.tsx","./src/components/ui/tooltip.test.tsx","./src/components/ui/ui-loading-spinner.test.tsx","./src/components/vector_store_management/vectorstoreselector.test.tsx","./src/components/view_logs/auditlogspanel.test.tsx","./src/components/view_logs/auditlogstable.test.tsx","./src/components/view_logs/configinfomessage.test.tsx","./src/components/view_logs/costbreakdownviewer.test.tsx","./src/components/view_logs/requestlogsfilters.test.tsx","./src/components/view_logs/requestlogspanel.test.tsx","./src/components/view_logs/requestlogstablecolumns.test.tsx","./src/components/view_logs/typebadges.test.tsx","./src/components/view_logs/index.integration.test.tsx","./src/components/view_logs/index.test.tsx","./src/components/view_logs/log_filter_logic.test.tsx","./src/components/view_logs/logs_utils.test.tsx","./src/components/view_logs/auditlogdrawer/auditlogdrawer.test.tsx","./src/components/view_logs/guardrailviewer/bedrockguardraildetails.test.tsx","./src/components/view_logs/guardrailviewer/guardrailviewer.test.tsx","./src/components/view_logs/guardrailviewer/presidiodetectedentities.test.tsx","./src/components/view_logs/logdetailsdrawer/classifytag.test.tsx","./src/components/view_logs/logdetailsdrawer/collapsiblemessage.test.tsx","./src/components/view_logs/logdetailsdrawer/drawerheader.test.tsx","./src/components/view_logs/logdetailsdrawer/historytree.test.tsx","./src/components/view_logs/logdetailsdrawer/inputcard.test.tsx","./src/components/view_logs/logdetailsdrawer/jsonviewer.test.tsx","./src/components/view_logs/logdetailsdrawer/logdetailcontent.test.tsx","./src/components/view_logs/logdetailsdrawer/logdetailsdrawer.test.tsx","./src/components/view_logs/logdetailsdrawer/outputcard.test.tsx","./src/components/view_logs/logdetailsdrawer/prettymessagesview.test.tsx","./src/components/view_logs/logdetailsdrawer/realtimeprettyview.test.tsx","./src/components/view_logs/logdetailsdrawer/routingdecisioncard.test.tsx","./src/components/view_logs/logdetailsdrawer/sectionheader.test.tsx","./src/components/view_logs/logdetailsdrawer/simplemessageblock.test.tsx","./src/components/view_logs/logdetailsdrawer/simpletoolcallblock.test.tsx","./src/components/view_logs/logdetailsdrawer/tokenflow.test.tsx","./src/components/view_logs/logdetailsdrawer/truncatedvalue.test.tsx","./src/components/view_logs/toolssection/toolssection.test.tsx","./src/contexts/pluginmodecontext.test.tsx","./src/hooks/usemcpoauthflow.test.tsx","./src/hooks/usesyntaxtheme.test.tsx","./src/hooks/usetoolsoauthflow.test.tsx","./src/hooks/policies/usedeletepolicyattachment.test.tsx","./src/lib/forms/usezodform.test.tsx","./tests/createkeypage.expiredtoken.test.tsx","./tests/top_key_view.test.tsx","./.next/dev/types/cache-life.d.ts","./.next/dev/types/validator.ts","./node_modules/@types/d3-array/index.d.ts","./node_modules/@types/d3-color/index.d.ts","./node_modules/@types/d3-ease/index.d.ts","./node_modules/@types/d3-interpolate/index.d.ts","./node_modules/@types/d3-timer/index.d.ts","./node_modules/@types/ms/index.d.ts","./node_modules/@types/debug/index.d.ts","./node_modules/@types/estree-jsx/index.d.ts","./node_modules/@types/json5/index.d.ts","./node_modules/@types/use-sync-external-store/index.d.ts"],"fileIdsList":[[97,143,484,485,486,487],[97,143],[97,143,226,528,531,2703,2811,2852,2862,2894,2908,2919,2923,2930,2947,3050,3062,3105,3147,3172,3192,3239,3275,3298,3373,3387,3400,3528,3570,3594,3631,3652,3662,3675,3684,3696,3703,3706,3709,3728,3748,3768,3784,3786,3790,3793,3795,3799,3801,3803,3804,3806,3811,3812,3813,3814,3824],[97,143,529,530,531],[97,143,3425,3429,3430,3433,3434,3436,3438,3439,3442,3461,3486,3487,3488,3489],[97,143,3429,3437,3490],[97,143,3435],[97,143,3433,3437,3438,3490],[97,143,3490],[97,143,3431,3490],[97,143,3440,3441],[97,143,3436],[97,143,3436,3438,3439,3442,3459,3490],[97,143,3453],[97,143,3433,3439,3490],[97,143,3425,3429,3430,3432],[97,143,176],[97,143,3425],[97,138,143,3428],[97,143,3425,3433,3490],[97,143,3433,3490],[97,143,3485,3490],[97,143,3433,3455,3463,3485,3490],[97,143,3433,3455,3458,3459,3490],[97,143,3461,3490],[97,143,3479],[97,143,3433,3464,3479,3480,3482,3491],[97,143,3481],[97,143,3489],[97,143,3478],[97,143,3433,3438,3439,3443,3448,3486],[97,143,3448,3449],[97,143,3433,3439,3443,3449,3486],[97,143,3443,3444,3445,3446,3447,3449,3452,3469,3473,3476,3485],[97,143,3433,3438,3439,3443,3486],[97,143,3433,3438,3439,3442,3443,3486],[97,143,3444,3445,3446,3447,3465,3466,3467,3471,3474,3477,3486],[97,143,3450,3451,3452],[97,143,3433,3438,3439,3443,3450,3451,3486],[97,143,3433,3438,3439,3443,3450,3486],[97,143,3433,3438,3439,3443,3454,3461,3485,3486],[97,143,3462,3485],[97,143,3432,3433,3438,3443,3461,3462,3463,3464,3483,3484,3485,3486],[97,143,3432,3433,3438,3439,3443,3486],[97,143,3468,3469,3470],[97,143,3433,3438,3439,3443,3469,3486],[97,143,3433,3438,3439,3443,3449,3468,3470,3486],[97,143,3472,3473],[97,143,3433,3438,3439,3442,3443,3472,3486],[97,143,3475,3476],[97,143,3433,3438,3439,3443,3475,3486],[97,143,3432,3433,3438,3443,3461,3486,3487],[97,143,3435,3461,3486,3487,3488],[97,143,3457],[97,143,3433,3435,3438,3439,3443,3454,3461],[97,143,3456,3461],[97,143,3432,3433,3438,3443,3456,3459,3460,3461],[85,97,143,645,650],[97,143,646,650,651,652,653,654],[97,143,646,650,651,652,653],[85,97,143,642,643,645,646,649],[85,97,143,645,646,647,650],[85,97,143,642,643,645],[97,143,704,705],[97,143,708,709,710,711,712,713,714,716,717,718],[97,143,707,708,709,710,711,712,713,714,716,717],[85,97,143,226,643,706,707],[85,97,143,707,715],[97,143,722,723,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,749,751],[97,143,722,723,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,749,750],[85,97,143,645,727,728],[85,97,143,645],[85,97,143,721],[85,97,143],[85,97,143,645,753],[85,97,143,645,647,753],[97,143,753,754,755,756],[97,143,753,754,755],[97,143,758],[85,97,143,642,643,645,727],[97,143,764],[97,143,760,761,762],[97,143,760,761],[85,97,143,645,647,760],[97,143,648,766,767,768],[97,143,648,766,767],[85,97,143,645,647,648],[85,97,143,642,643,645,649],[85,97,143,647,648],[85,97,143,645,648],[85,97,143,645,728],[85,97,143,645,647],[97,143,730,732,733,734,735,736,737,738,739,740,741,742,744,745,746,749,770,771,772,773,774,775,776,777,778,779,781],[97,143,730,732,733,734,735,736,737,738,739,740,741,742,744,745,746,749,750,770,771,772,773,774,775,776,777,778,779,780],[85,97,143,645,727],[85,97,143,645,647,665,728],[85,97,143,699],[85,97,143,642,643,720],[97,143,748],[97,143,783,784,791,792,793,794,795,796,797,798,799,800,801,802,804,807,810,811,812],[97,143,747,783,784,791,792,793,794,795,796,797,798,799,800,801,802,804,807,810,811],[97,143,226,644,790,809],[85,97,143,810],[85,97,143,226],[97,143,814,815],[97,143,814],[97,143,706,709,710,711,712,713,714,715,717,817],[97,143,705,706,709,710,711,712,713,714,715,717],[85,97,143,645,647,665],[85,97,143,226,642,643,703,705],[97,143,704],[85,97,143,647,664,665,673,699,703,706,1029],[85,97,143,645,705],[85,97,143,819],[97,143,820,821],[97,143,819,820],[97,143,823,824,825,826,827,828,830,832,833,834,835,836,837,838,839,840],[97,143,705,823,824,825,826,827,828,830,832,833,834,835,836,837,838,839],[85,97,143,645,647,665,831],[85,97,143,226,642,643,703,705,831],[85,97,143,829,830],[85,97,143,645,829,831],[85,97,143,645,647,727],[97,143,727,842,843,844,845,846,847,848],[97,143,727,842,843,844,845,846,847],[85,97,143,645,726],[85,97,143,647,727],[97,143,850,851,852],[97,143,850,851],[85,97,143,694],[85,97,143,665,672,694],[85,97,143,645,676],[85,97,143,643,647,664,694,703],[85,97,143,672,694],[97,143,694],[85,97,143,687],[97,143,642,694],[97,143,672,694],[97,143,643,673,694],[97,143,683,694],[85,97,143,645,672,683,694],[97,143,682,694],[85,97,143,672,688,694],[97,143,644,664,673,703],[85,97,143,687,694],[97,143,670,672,674,677,678,679,680,684,685,686,689,690,691,692,693,694,695,696,697,698],[97,143,683],[85,97,143,643,670,672,673,674,677,678,679,680,683,684,685,686,689,690,691,692,693,695,699],[97,143,681,703],[85,97,143,642,643,645,724],[97,143,725],[97,143,644,655,719,726,752,757,759,763,765,769,780,782,809,813,816,818,822,841,849,853,855,857,859,866,881,891,896,912,925,932,936,938,946,966,976,980,987,1002,1004,1006,1014,1026,1028],[97,143,854],[85,97,143,645,849],[97,143,642],[85,97,143,725,727],[97,143,641],[85,97,143,644],[85,97,143,645,675],[85,97,143,645,790],[97,143,783,784,790,791,792,793,794,795,796,797,798,799,800,801,802,804,805,806,807,808],[97,143,747,783,784,789,790,791,792,793,794,795,796,797,798,799,800,801,802,804,805,806,807],[85,97,143,226,642,643,703,785,786,787,788,789],[85,97,143,785,790],[97,143,785],[85,97,143,645,647,664,665,672,673,699,703,790,809],[85,97,143,226,790,803],[85,97,143,785],[85,97,143,645,789],[97,143,856],[85,97,143,790],[97,143,858],[97,143,860,861,862,863,864,865],[97,143,860,861,862,863,864],[85,97,143,645,860],[97,143,867,868,869,870,871,872,873,874,875,876,877,878,879,880],[97,143,867,868,869,870,871,872,873,874,875,876,877,878,879],[85,97,143,645,647,728],[85,97,143,645,883],[97,143,883,884,885,886,887,888,889,890],[97,143,883,884,885,886,887,888,889],[85,97,143,642,643,645,727,882],[97,143,893,894,895],[97,143,747,893,894],[85,97,143,645,893],[85,97,143,642,643,645,727,892],[97,143,900,901,902,903,904,905,906,907,908,909,910,911],[97,143,899,900,901,902,903,904,905,906,907,908,909,910],[85,97,143,226,642,643,703,899],[97,143,898],[85,97,143,647,664,665,673,699,703,897,900,912,1029],[85,97,143,645,899],[97,143,915,917,918,919,920,921,922,923,924],[97,143,914,915,917,918,919,920,921,922,923],[85,97,143,916],[85,97,143,226,642,643,703,914],[97,143,913],[85,97,143,647,664,673,699,703,915,1029],[85,97,143,645,914],[97,143,926,927,928,929,930,931],[97,143,926,927,928,929,930],[85,97,143,645,926],[97,143,937],[97,143,933,934,935],[97,143,933,934],[85,97,143,645,647,933],[85,97,143,645,939],[97,143,939,940,941,942,943,944,945],[97,143,939,940,941,942,943,944],[97,143,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965],[97,143,747,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964],[97,143,747],[85,97,143,645,967],[97,143,967,968,969,970,971,973,974,975],[97,143,967,968,969,970,971,973,974],[85,97,143,645,967,972],[97,143,977,978,979],[97,143,977,978],[85,97,143,642,644,645,727],[85,97,143,645,977],[97,143,981,982,983,984,985,986],[97,143,981,982,983,984,985],[85,97,143,645,981,982],[85,97,143,645,982],[85,97,143,645,647,981,982],[85,97,143,642,643,645,981],[97,143,989],[97,143,988,989,990,991,992,993,994,995,996,997,998,999,1000,1001],[97,143,988,989,990,991,992,993,994,995,996,997,998,999,1000],[85,97,143,645,728,989],[85,97,143,990],[85,97,143,645,647,989],[85,97,143,988],[97,143,1005],[97,143,1003],[85,97,143,645,1008],[97,143,1007,1008,1009,1010,1011,1012,1013],[97,143,645,1007,1008,1009,1010,1011,1012],[85,97,143,645,780],[97,143,1017,1018,1019,1020,1021,1022,1023,1024,1025],[97,143,1016,1017,1018,1019,1020,1021,1022,1023,1024],[85,97,143,226,642,643,703,1016],[97,143,1015],[85,97,143,647,664,673,699,703,1017,1026,1029],[85,97,143,645,1016],[85,97,143,643],[97,143,645,1027],[97,143,671,700,701,702],[85,97,143,670],[85,97,143,642,643,647,664,665,701],[97,143,645,647,673,699,700],[85,97,143,666,699],[97,143,656],[97,143,657],[97,143,657,658,660,661,662,663],[97,143,660],[85,97,143,226,660],[97,143,659,660],[97,143,2668],[97,143,666],[97,143,667,668],[85,97,143,669],[97,143,1702,1703,1704,1705,1706,1707,1708,1709,1710,1711,1712,1713,1714,1715,1716,1717,1718,1719,1720,1721,1722,1723,1724,1725,1726,1727,1728,1729,1730,1731,1732,1733,1734,1735,1736,1737,1738,1739,1740,1741,1742,1743,1744,1745,1746,1747,1748,1749,1750,1751,1752,1753,1754,1755,1756,1757,1758,1759,1760,1761,1762,1763,1764,1765,1766,1767,1768,1769,1770,1771,1772,1773,1774,1775,1776,1777,1778,1779,1780,1781,1782,1783,1784,1785,1786,1787,1788,1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1800,1801,1802,1803,1804,1805,1806,1807,1808,1809,1810,1811,1812,1813,1814,1815,1816,1817,1818,1819,1820,1821,1822,1823,1824,1825,1826,1827,1828,1829,1830,1831,1832,1833,1834,1835,1836,1837,1838,1839,1840,1841,1842,1843,1844,1845,1846,1847,1848,1849,1850,1851,1852,1853,1854,1855,1856,1857,1858,1859,1860,1861,1862,1863,1864,1865,1866,1867,1868,1869,1870,1871,1872,1873,1874,1875,1876,1877,1878,1879,1880,1881,1882,1883,1884,1885,1886,1887,1888,1889,1890,1891,1892,1893,1894,1895,1896,1897,1898,1899,1900,1901,1902,1903,1904,1905,1906,1907,1908,1909,1910,1911,1912,1913,1914,1915,1916,1917,1918,1919,1920,1921,1922,1923,1924,1925,1926,1927,1928,1929,1930,1931],[97,143,2082],[97,143,1082,1301,2081],[97,143,656,2141,2142,2143,2144],[97,143,226],[97,143,1438,1446],[97,143,625],[97,143,1447,1448,1449,1450,1451],[97,143,1446,1448],[97,143,1447,1448],[85,97,143,1445,1446,1447],[85,97,143,226,626],[97,143,627],[97,143,1438,1441],[97,143,1432,1438,1439,1440,1441,1442,1443,1444],[97,143,1438],[85,97,143,1173],[97,143,1434],[97,143,1434,1435,1436,1437],[97,143,1433],[97,143,1154],[97,143,1139,1162],[97,143,1162],[97,143,1162,1173],[97,143,1148,1162,1173],[97,143,1153,1162,1173],[97,143,1143,1162],[97,143,1151,1162,1173],[97,143,1149],[97,143,1139,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,1153,1154,1155,1156,1157,1158,1159,1160,1161,1162,1163,1164,1165,1166,1167,1168,1169,1170,1171,1172],[97,143,1152],[97,143,1139,1140,1141,1142,1143,1144,1145,1146,1147,1149,1150,1152,1154,1155,1156,1157,1158,1159,1160,1161],[97,143,1385],[97,143,1382,1383,1384,1385,1386,1389,1390,1391,1392,1393,1394,1395,1396],[97,143,1381],[97,143,1388],[97,143,1382,1383,1384],[97,143,1382,1383],[97,143,1385,1386,1388],[97,143,1383],[97,143,2680],[97,143,2679],[85,97,143,196,460,1397,1398],[97,143,1652],[97,143,1639,1640,1641],[97,143,1634,1635,1636],[97,143,1612,1613,1614,1615],[97,143,1578,1652],[97,143,1578],[97,143,1578,1579,1580,1581,1626],[97,143,1616],[97,143,1611,1617,1618,1619,1620,1621,1622,1623,1624,1625],[97,143,1626],[97,143,1577],[97,143,1630,1632,1633,1651,1652],[97,143,1630,1632],[97,143,1627,1630,1652],[97,143,1637,1638,1642,1643,1648],[97,143,1631,1633,1643,1651],[97,143,1650,1651],[97,143,1627,1631,1633,1649,1650],[97,143,1631,1652],[97,143,1629],[97,143,1629,1631,1652],[97,143,1627,1628],[97,143,1644,1645,1646,1647],[97,143,1633,1652],[97,143,1588],[97,143,1582,1589],[97,143,1582,1583,1584,1585,1586,1587,1588,1589,1590,1591,1592,1593,1594,1595,1596,1597,1598,1599,1600,1601,1602,1603,1604,1605,1606,1607,1608,1609,1610],[97,143,1608,1652],[97,143,600,601],[97,143,4158],[97,143,2131],[97,143,2154],[97,143,4162],[97,143,546,547,4164],[97,143,2723],[97,143,157,184,191,3426,3427],[97,140,143],[97,142,143],[143],[97,143,148,176],[97,143,144,149,154,162,173,184],[97,143,144,145,154,162],[92,93,94,97,143],[97,143,146,185],[97,143,147,148,155,163],[97,143,148,173,181],[97,143,149,151,154,162],[97,142,143,150],[97,143,151,152],[97,143,153,154],[97,142,143,154],[97,143,154,155,156,173,184],[97,143,154,155,156,169,173,176],[97,143,151,154,157,162,173,184],[97,143,154,155,157,158,162,173,181,184],[97,143,157,159,173,181,184],[95,96,97,98,99,100,101,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190],[97,143,154,160],[97,143,161,184,189],[97,143,151,154,162,173],[97,143,163],[97,143,164],[97,142,143,165],[97,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190],[97,143,167],[97,143,168],[97,143,154,169,170],[97,143,169,171,185,187],[97,143,154,173,174,176],[97,143,175,176],[97,143,173,174],[97,143,177],[97,140,143,173,178],[97,143,154,179,180],[97,143,179,180],[97,143,148,162,173,181],[97,143,182],[97,143,162,183],[97,143,157,168,184],[97,143,148,185],[97,143,173,186],[97,143,161,187],[97,143,188],[97,138,143],[97,138,143,154,156,165,173,176,184,187,189],[97,143,173,190],[97,143,173,191],[85,89,97,143,192,193,194,195,196,479,524],[85,89,97,143,192,193,194,195,460,479,524],[85,89,97,143,192,193,195,196,479,524],[85,97,143,196,460,461],[85,97,143,196,460],[85,97,143,1369],[85,89,97,143,193,194,195,196,479,524],[85,89,97,143,192,194,195,196,479,524],[83,84,97,143],[97,143,533,538,539,541],[97,143,587,588],[97,143,539,541,581,582,583],[97,143,539],[97,143,539,541,581],[97,143,539,581],[97,143,594],[97,143,534,594,595],[97,143,534,594],[97,143,534,540],[97,143,535],[97,143,534,535,536,538],[97,143,534],[97,143,1030,1032],[97,143,1030],[97,143,2383],[97,143,2381,2383],[97,143,2381],[97,143,2383,2447,2448],[97,143,2383,2450],[97,143,2383,2451],[97,143,2468],[97,143,2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394,2395,2396,2397,2398,2399,2400,2401,2402,2403,2404,2405,2406,2407,2408,2409,2410,2411,2412,2413,2414,2415,2416,2417,2418,2419,2420,2421,2422,2423,2424,2425,2426,2427,2428,2429,2430,2431,2432,2433,2434,2435,2436,2437,2438,2439,2440,2441,2442,2443,2444,2445,2446,2449,2450,2451,2452,2453,2454,2455,2456,2457,2458,2459,2460,2461,2462,2463,2464,2465,2466,2467,2469,2470,2471,2472,2473,2474,2475,2476,2477,2478,2479,2480,2481,2482,2483,2484,2485,2486,2487,2488,2489,2490,2491,2492,2493,2494,2495,2496,2497,2498,2499,2500,2501,2502,2503,2504,2505,2506,2507,2508,2509,2510,2511,2512,2513,2514,2515,2516,2517,2518,2519,2520,2521,2522,2523,2524,2525,2526,2527,2528,2529,2530,2531,2532,2533,2534,2535,2536,2537,2538,2539,2540,2541,2542,2543,2545,2546,2547,2548,2549,2550,2551,2552,2553,2554,2555,2556,2557,2558,2559,2560,2561,2562,2563,2564,2569,2570,2571,2572,2573,2574,2575,2576,2577,2578,2579,2580,2581,2582,2583,2584,2585,2586,2587,2588,2589,2590,2591,2592,2593,2594,2595,2596,2597,2598,2599,2600,2601,2602,2603,2604,2605,2606,2607,2608,2609,2610,2611,2612,2613,2614,2615,2616,2617,2618,2619,2620,2621,2622,2623,2624,2625,2626,2627,2628,2629,2630,2631,2632,2633,2634,2635,2636],[97,143,2383,2544],[97,143,2383,2448,2568],[97,143,2381,2565,2566],[97,143,2567],[97,143,2383,2565],[97,143,2380,2381,2382],[97,143,2058],[97,143,2057],[97,143,2059],[97,143,546,547,2669,2670,4164],[97,143,2671],[97,143,619,620],[97,143,619,620,621,622],[97,143,619,621],[97,143,619],[97,143,157,173,191],[97,143,574,575],[97,143,2767,2770,2773,2775,2776,2777],[97,143,2734,2762,2767,2770,2773,2775,2777],[97,143,2734,2762,2767,2770,2773,2777],[97,143,2800,2801,2805],[97,143,2777,2800,2802,2805],[97,143,2777,2800,2802,2804],[97,143,2734,2762,2777,2800,2802,2803,2805],[97,143,2802,2805,2806],[97,143,2777,2800,2802,2805,2807],[97,143,2724,2734,2735,2736,2760,2761,2762],[97,143,2724,2735,2762],[97,143,2724,2734,2735,2762],[97,143,2737,2738,2739,2740,2741,2742,2743,2744,2745,2746,2747,2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2759],[97,143,2724,2728,2734,2736,2762],[97,143,2778,2779,2799],[97,143,2734,2762,2800,2802,2805],[97,143,2734,2762],[97,143,2780,2781,2782,2783,2784,2785,2786,2787,2788,2789,2790,2791,2792,2793,2794,2795,2796,2797,2798],[97,143,2723,2734,2762],[97,143,2767,2768,2769,2773,2777],[97,143,2767,2770,2773,2777],[97,143,2767,2770,2771,2772,2777],[97,143,482],[97,143,430,493,494],[97,143,201,202,204,216,240,355,366,475],[97,143,204,235,236,237,239,475],[97,143,204,372,374,376,377,379,475,477],[97,143,204,238,275,475],[97,143,202,204,215,216,222,228,233,354,355,356,365,475,477],[97,143,475],[97,143,211,217,236,256,351],[97,143,204],[97,143,197,211,217],[97,143,383],[97,143,380,381,383],[97,143,380,382,475],[97,143,157,256,454,472],[97,143,157,327,330,346,351,472],[97,143,157,299,472],[97,143,359],[97,143,358,359,360],[97,143,358],[91,97,143,157,197,204,216,222,228,234,236,240,241,254,255,322,352,353,366,475,479],[97,143,201,204,238,275,372,373,378,475,527],[97,143,238,527],[97,143,201,255,425,475,527],[97,143,527],[97,143,204,238,239,527],[97,143,375,527],[97,143,241,354,357,364],[85,97,143,430],[97,143,168,211,226],[97,143,211,226],[85,97,143,296],[85,97,143,217,226,430],[97,143,211,282,296,297,509,516],[97,143,281,510,511,512,513,515],[97,143,332],[97,143,332,333],[97,143,215,217,284,285],[97,143,217,291,292],[97,143,217,286,294],[97,143,291],[97,143,209,217,284,285,286,287,288,289,290,291,294],[97,143,217,284,291,292,293,295],[97,143,217,285,287,288],[97,143,285,287,290,292],[97,143,514],[97,143,217],[85,97,143,205,503],[85,97,143,184],[85,97,143,238,273],[85,97,143,238,366],[97,143,271,276],[85,97,143,272,481],[97,143,2696],[85,89,97,143,157,192,193,194,195,196,479,523],[97,143,157,217],[97,143,157,216,221,302,319,361,362,366,422,424,475,476],[97,143,254,363],[97,143,479],[97,143,203],[85,97,143,208,211,427,443,445],[97,143,168,211,427,442,443,444,526],[97,143,436,437,438,439,440,441],[97,143,438],[97,143,442],[97,143,226,390,391,393],[85,97,143,217,384,385,386,387,392],[97,143,390,392],[97,143,388],[97,143,389],[85,97,143,226,272,481],[85,97,143,226,480,481],[85,97,143,226,481],[97,143,319,320],[97,143,320],[97,143,157,476,481],[97,143,349],[97,142,143,348],[97,143,211,217,223,225,327,340,344,346,424,427,464,465,472,476],[97,143,217,266,288],[97,143,327,338,341,346],[85,97,143,208,211,327,330,346,349,383,431,432,433,434,435,446,447,448,449,450,451,452,453,527],[97,143,208,211,236,327,334,335,336,339,340],[97,143,173,217,236,338,345,427,428,472],[97,143,342],[97,143,157,168,205,217,221,231,263,264,267,319,322,387,422,423,464,475,476,477,479,527],[97,143,208,209,211],[97,143,327],[97,142,143,236,263,264,321,322,323,324,325,326,476],[97,143,346],[97,142,143,210,211,221,225,261,327,334,335,336,337,338,341,342,343,344,345,465],[97,143,157,261,262,334,476,477],[97,143,236,264,319,322,327,424,476],[97,143,157,475,477],[97,143,157,173,472,476,477],[97,143,157,168,197,211,216,223,225,228,231,238,258,263,264,265,266,267,302,303,305,308,310,313,314,315,316,318,366,422,424,472,475,476,477],[97,143,157,173],[97,143,204,205,206,234,472,473,474,479,481,527],[97,143,201,202,475],[97,143,395],[97,143,157,173,184,213,379,383,384,385,386,387,393,394,527],[97,143,168,184,197,211,213,225,228,264,303,308,318,319,372,399,400,401,408,411,412,422,424,472,475],[97,143,228,234,241,254,264,322,475],[97,143,157,184,205,216,225,264,406,472,475],[97,143,426],[97,143,157,395,409,410,419],[97,143,472,475],[97,143,324,465],[97,143,225,263,366,481],[97,143,157,168,203,308,368,372,401,408,411,414,472],[97,143,157,241,254,372,415],[97,143,204,265,366,417,475,477],[97,143,157,184,387,475],[97,143,157,238,265,366,367,368,377,395,416,418,475],[91,97,143,157,263,421,479,481],[97,143,317,422],[97,143,157,168,211,214,216,217,223,225,231,240,241,254,264,267,303,305,315,318,319,366,399,400,401,402,404,407,422,424,472,481],[97,143,157,173,241,408,413,419,472],[97,143,244,245,246,247,248,249,250,251,252,253],[97,143,258,309],[97,143,311],[97,143,309],[97,143,311,312],[97,143,157,215,216,217,221,222,476],[97,143,157,168,203,205,223,227,263,266,267,301,422,472,477,479,481],[97,143,157,168,184,207,214,215,225,227,264,420,465,471,476],[97,143,334],[97,143,335],[97,143,217,228,464],[97,143,336],[97,143,210],[97,143,212,224],[97,143,157,212,216,223],[97,143,219,224],[97,143,220],[97,143,212,213],[97,143,212,268],[97,143,212],[97,143,214,258,307],[97,143,306],[97,143,211,213,214],[97,143,214,304],[97,143,211,213],[97,143,263,366],[97,143,464],[97,143,157,184,223,225,229,263,366,421,424,427,428,429,455,456,459,463,465,472,476],[97,143,277,280,282,283,296,297],[85,97,143,194,195,196,226,457,458],[85,97,143,194,195,196,226,457,458,462],[97,143,350],[97,143,236,257,262,263,327,328,329,330,331,333,346,347,349,352,421,424,475,477],[97,143,296],[97,143,157,301,472],[97,143,301],[97,143,157,223,269,298,300,302,421,472,479,481],[97,143,277,278,279,280,282,283,296,297,480],[91,97,143,157,168,184,212,213,225,231,263,264,267,366,419,420,422,472,475,476,479],[97,143,208,211,218],[97,143,262,264,396,399],[97,143,262,397,466,467,468,469,470],[97,143,157,258,475],[97,143,157],[97,143,261,346],[97,143,260],[97,143,262,315],[97,143,259,261,475],[97,143,157,207,262,396,397,398,472,475,476],[85,97,143,211,217,295],[85,97,143,209],[97,143,199,200],[85,97,143,205],[85,97,143,211,281],[85,91,97,143,263,267,479,481],[97,143,205,503,504],[85,97,143,276],[85,97,143,168,184,203,270,272,274,275,481],[97,143,211,238,476],[97,143,211,403],[85,97,143,155,157,168,201,203,276,374,479,480],[85,97,143,192,193,194,195,196,479,524],[85,86,87,88,89,97,143],[97,143,148],[97,143,369,370,371],[97,143,369],[85,89,97,143,157,159,168,191,192,193,194,195,196,197,203,231,236,414,442,477,478,481,524],[97,143,489],[97,143,491],[97,143,495],[97,143,2697],[97,143,497],[97,143,499,500,501],[97,143,505],[90,97,143,483,488,490,492,496,498,502,506,508,518,519,521,525,526,527,528],[97,143,507],[97,143,517],[97,143,272],[97,143,520],[97,142,143,262,396,397,399,466,467,469,470,522,524],[97,143,191],[85,97,143,1658],[85,97,143,1657],[97,143,1657,1660],[97,143,2961,2962,2967],[97,143,2963,2964,2966,2968],[97,143,2967],[97,143,2964,2966,2967,2968,2969,2971,2973,2974,2975,2976,2977,2978,2979,2983,2998,3009,3012,3016,3024,3025,3027,3030,3033,3036],[97,143,2967,2974,2987,2991,3000,3002,3003,3004,3031],[97,143,2967,2968,2984,2985,2986,2987,2989,2990],[97,143,2991,2992,2999,3002,3031],[97,143,2967,2968,2973,2992,3004,3031],[97,143,2968,2991,2992,2993,2999,3002,3031],[97,143,2964],[97,143,2970,2991,2998,3004],[97,143,2998],[97,143,2967,2987,2994,2996,2998,3031],[97,143,2991,2998,2999],[97,143,3000,3001,3003],[97,143,3031],[97,143,2980,2981,2982,3032],[97,143,2967,2968,3032],[97,143,2963,2967,2981,2983,3032],[97,143,2967,2981,2983,3032],[97,143,2967,2969,2970,2971,3032],[97,143,2967,2969,2970,2984,2985,2986,2988,2989,3032],[97,143,2989,2990,3005,3008,3032],[97,143,3004,3032],[97,143,2967,2991,2992,2993,2999,3000,3002,3003,3032],[97,143,2970,3006,3007,3008,3032],[97,143,2967,3032],[97,143,2967,2969,2970,2990,3032],[97,143,2963,2967,2969,2970,2984,2985,2986,2988,2989,2990,3032],[97,143,2967,2969,2970,2985,3032],[97,143,2963,2967,2970,2984,2986,2988,2989,2990,3032],[97,143,2970,2973,3032],[97,143,2973],[97,143,2963,2967,2969,2970,2972,2973,2974,3032],[97,143,2972,2973],[97,143,2967,2969,2973,3032],[97,143,3033,3034],[97,143,2963,2967,2973,2974,3032],[97,143,2967,2969,3011,3032],[97,143,2967,2969,3010,3032],[97,143,2967,2969,2970,2998,3013,3015,3032],[97,143,2967,2969,3015,3032],[97,143,2967,2969,2970,2998,3014,3032],[97,143,2967,2968,2969,3032],[97,143,3018,3032],[97,143,2967,3013,3032],[97,143,3020,3032],[97,143,2967,2969,3032],[97,143,3017,3019,3021,3023,3032],[97,143,2967,2969,3017,3022,3032],[97,143,3013,3032],[97,143,2998,3032],[97,143,2970,2971,2974,2975,2976,2977,2978,2979,2983,2998,3009,3012,3016,3024,3025,3027,3030,3035],[97,143,2967,2969,2998,3032],[97,143,2963,2967,2969,2970,2994,2995,2997,2998,3032],[97,143,2967,2976,3026,3032],[97,143,2967,2969,3028,3030,3032],[97,143,2967,2969,3030,3032],[97,143,2967,2969,2970,3028,3029,3032],[97,143,2968],[97,143,2965,2967,2968],[97,143,1337],[97,143,628,1337,1338],[97,143,568],[97,143,566,568],[97,143,557,565,566,567,569,571],[97,143,555],[97,143,558,563,568,571],[97,143,554,571],[97,143,558,559,562,563,564,571],[97,143,558,559,560,562,563,571],[97,143,555,556,557,558,559,563,564,565,567,568,569,571],[97,143,571],[97,143,553,555,556,557,558,559,560,562,563,564,565,566,567,568,569,570],[97,143,553,571],[97,143,558,560,561,563,564,571],[97,143,562,571],[97,143,563,564,568,571],[97,143,556,566],[97,143,1387],[85,97,143,1064],[97,143,1064,1065,1066,1067,1068,1071,1072,1073,1074,1075,1076,1077,1080,1081],[97,143,1064],[97,143,1069,1070],[85,97,143,1061,1064],[97,143,1058,1059,1061],[97,143,1054,1057,1059,1061],[97,143,1058,1061],[85,97,143,1049,1050,1051,1054,1055,1056,1058,1059,1060,1061],[97,143,1051,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063],[97,143,1058],[97,143,1052,1058,1059],[97,143,1052,1053],[97,143,1057,1059,1060],[97,143,1057],[97,143,1049,1054,1057,1059,1060],[85,97,143,1054,1057,1058,1059],[97,143,1078,1079],[85,97,143,2318],[85,97,143,2317],[97,143,2765],[85,97,143,2724,2733,2762,2764],[85,97,143,2169,2170,2217],[97,143,2262,2263],[97,143,2169],[97,143,2217],[85,97,143,2264],[85,97,143,2136,2146,2149,2151,2157,2158,2165,2167,2168,2170,2171,2172,2174,2214,2217],[85,97,143,2157,2217],[85,97,143,2136,2146,2149,2151,2156,2158,2167,2169,2170,2171,2175,2177,2178,2214,2217],[85,97,143,2167,2175,2219],[85,97,143,2150,2217],[85,97,143,2135,2136,2138,2146,2217],[85,97,143,2136,2146,2167,2208,2217],[85,97,143,2136,2176,2197,2201,2217],[85,97,143,2149,2158,2170,2171,2184,2185,2217,2258],[97,143,2135,2217],[97,143,2146,2217],[85,97,143,2136,2146,2149,2151,2157,2158,2170,2171,2196,2214,2217],[85,97,143,2136,2138,2175,2188,2241],[85,97,143,2134,2136,2138,2188],[85,97,143,2136,2138,2166,2188,2189,2217],[85,97,143,2136,2146,2149,2153,2157,2158,2170,2171,2185,2198,2200,2214,2217],[85,97,143,2140,2146,2217],[85,97,143,2140,2146,2214,2217],[85,97,143,2217],[85,97,143,2217,2274],[85,97,143,2175,2185,2217],[85,97,143,2135,2185,2217],[85,97,143,2185,2217],[85,97,143,2147],[85,97,143,2136,2185,2217],[85,97,143,2134,2136,2217],[85,97,143,2135,2136,2137,2217],[85,97,143,2135,2136,2138,2217,2274],[85,97,143,2159,2160,2161],[85,97,143,2146,2148,2149,2160,2185,2217,2220],[97,143,2207,2217],[97,143,2146,2147,2166,2212,2214,2217],[97,143,2134,2135,2136,2138,2139,2140,2146,2147,2149,2157,2158,2159,2162,2166,2168,2169,2170,2171,2172,2173,2175,2176,2185,2188,2190,2196,2197,2198,2200,2201,2202,2209,2212,2213,2214,2217,2218,2219,2221,2222,2223,2224,2225,2226,2227,2228,2230,2232,2234,2235,2236,2237,2238,2239,2242,2243,2244,2245,2246,2247,2248,2249,2250,2251,2252,2253,2254,2255,2256,2257,2258,2259,2260,2261,2262,2263,2264,2265,2266,2268,2269,2270,2271,2272,2273],[85,97,143,2136,2149,2151,2158,2170,2171,2180,2182,2184,2199,2217,2233,2274],[85,97,143,2136,2140,2146,2189,2217,2231],[85,97,143,2136,2146],[85,97,143,2136,2140,2146,2189,2217,2229],[85,97,143,2136,2158,2166,2170,2171,2181,2189,2217],[85,97,143,2136,2146,2149,2151,2156,2158,2167,2170,2171,2214,2217,2225,2233,2236],[85,97,143,2156,2217],[85,97,143,2169,2217],[97,143,2141,2145,2217],[97,143,2139,2140,2141,2145,2214,2217],[97,143,2141,2145,2150],[97,143,2141,2145,2184,2202,2217],[97,143,2141,2145,2146,2151,2152,2153,2174,2178,2179,2182,2183,2217],[97,143,2141,2145,2159,2162,2217],[97,143,2141,2145,2185,2217],[97,143,2141,2145,2146],[97,143,2141,2145],[97,143,2141,2142,2145,2146,2188,2190],[97,143,2141,2142,2145,2146,2217],[97,143,2141,2145,2147,2173,2217],[97,143,2165,2184,2207,2217],[97,143,2146,2151,2164,2165,2166,2184,2191,2194,2203,2207,2209,2210,2211,2213,2217],[97,143,2146,2151,2164,2165],[97,143,2207],[97,143,2145,2146,2151,2163,2184,2185,2186,2187,2191,2192,2193,2194,2195,2203,2204,2205,2206],[97,143,2141,2145,2146,2148,2149,2184,2217],[97,143,2151,2164,2173,2184,2217],[97,143,2164,2177,2184],[97,143,2151,2184,2217],[85,97,143,2149,2180,2181,2184,2217],[97,143,2184],[97,143,2164,2184],[97,143,2149,2151,2184,2217],[97,143,2167,2184,2217],[97,143,2185,2217],[85,97,143,2175,2176,2217],[97,143,2149,2156,2163,2165,2166,2185,2214,2217],[85,97,143,2149,2173,2176,2197,2201,2217,2221,2244,2245,2246,2259],[85,97,143,2149,2217,2221,2230,2232,2234,2235,2237],[85,97,143,2217,2237,2274],[97,143,2146,2217,2267],[97,143,2140,2217],[85,97,143,2184,2198,2199,2201,2217],[97,143,2156,2164,2167,2184],[85,97,143,2180,2240],[85,97,143,2133,2134,2135,2138,2139,2140,2146,2147,2148,2151,2169,2173,2180,2214,2215,2216,2274],[97,143,2141],[97,143,2774,2807,2808],[97,143,2809],[97,143,2762,2763],[97,143,2724,2728,2733,2734,2762],[97,143,547,579,580],[97,143,173,191,405],[97,143,537],[97,143,2730],[97,110,114,143,184],[97,110,143,173,184],[97,105,143],[97,107,110,143,181,184],[97,143,162,181],[97,105,143,191],[97,107,110,143,162,184],[97,102,103,106,109,143,154,173,184],[97,110,117,143],[97,102,108,143],[97,110,131,132,143],[97,106,110,143,176,184,191],[97,131,143,191],[97,104,105,143,191],[97,110,143],[97,104,105,106,107,108,109,110,111,112,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,132,133,134,135,136,137,143],[97,110,125,143],[97,110,117,118,143],[97,108,110,118,119,143],[97,109,143],[97,102,105,110,143],[97,110,114,118,119,143],[97,114,143],[97,108,110,113,143,184],[97,102,107,110,117,143],[97,143,173],[97,105,110,131,143,189,191],[97,143,2728,2732],[97,143,2723,2728,2729,2731,2733],[97,143,3405,3406,3407,3408,3409,3410,3411,3413,3414,3415,3416,3417,3418,3419,3420],[97,143,3407],[97,143,3407,3412],[97,143,2725],[97,143,2726,2727],[97,143,2723,2726,2728],[97,143,2132],[97,143,2155],[97,143,591,592],[97,143,591],[97,143,543],[97,143,154,155,157,158,159,162,173,181,184,190,191,543,544,545,547,548,550,551,552,572,573,577,578,579,580],[97,143,543,544,545,549],[97,143,545],[97,143,576],[97,143,547,580],[97,143,542,611,616],[97,143,584,603,604,616],[97,143,534,541,584,596,597,616],[97,143,606],[97,143,585],[97,143,534,542,584,586,596,605,616],[97,143,589],[97,143,146,155,173,534,539,541,580,584,586,589,590,593,596,598,599,602,605,607,608,610,616],[97,143,584,603,604,605,616],[97,143,580,609,610],[97,143,584,586,593,596,598,616],[97,143,189,599],[97,143,146,155,173,534,539,541,580,584,585,586,589,590,593,596,597,598,599,602,603,604,605,606,607,608,609,610,616],[97,143,585,586],[97,143,146,155,173,189,533,534,539,541,542,580,584,585,586,589,590,593,596,597,598,599,602,603,604,605,606,607,608,609,610,615,616,617,618,623],[97,143,2071,2072],[97,143,2069,2070,2071,2073,2074,2079],[97,143,2070,2071],[97,143,2079],[97,143,2080],[97,143,2071],[97,143,2069,2070,2071,2074,2075,2076,2077,2078],[97,143,2069,2070,2081],[97,143,1301],[97,143,1301,1304],[97,143,1294,1301,1302,1303,1304,1305,1306,1307,1308],[97,143,1309],[97,143,1301,1302],[97,143,1301,1303],[97,143,1247,1249,1250,1251,1252],[97,143,1247,1249,1251,1252],[97,143,1247,1249,1251],[97,143,1247,1249,1250,1252],[97,143,1247,1249,1252],[97,143,1247,1248,1249,1250,1251,1252,1253,1254,1294,1295,1296,1297,1298,1299,1300],[97,143,1249,1252],[97,143,1246,1247,1248,1250,1251,1252],[97,143,1249,1295,1299],[97,143,1249,1250,1251,1252],[97,143,1310],[97,143,1251],[97,143,1255,1256,1257,1258,1259,1260,1261,1262,1263,1264,1265,1266,1267,1268,1269,1270,1271,1272,1273,1274,1275,1276,1277,1278,1279,1280,1281,1282,1283,1284,1285,1286,1287,1288,1289,1290,1291,1292,1293],[97,143,164,226],[85,97,143,226,624,628,1399,1653,2858],[85,97,143,226,628,631,1035,1036,1037,1039,1044,1045,1110,1312,1313,1340,1348,1421,1426,1501,1954,2084,2854],[97,143,226,624,1313],[97,143,226,639,1312],[97,143,226,1311],[97,143,226,624,1399,1421,1422,1653,2857,2863],[85,97,143,226,639,1035,1039,1048,1092,1115,1207,1235,1348,1361,1422,2816,2818,2856],[97,143,226,1036,1037,1039,1044,1045,1082,1311,1348,1426,1501,1954,2854],[97,143,226,624,1421,1653,2856,2863],[85,97,143,226,631,1035,1110,1421,1425,2084,2855],[97,143,226,624,1399,1421,1653,2861,2863],[85,97,143,226,1035,1038,1039,1106,1109,1245,1421,1424,2814,2834,2857,2858,2860],[85,97,143,226,1039,1174,1176,1188,1245,2859],[97,143,226,1034,1035,1039,1174,1176,1188,1204,1245,1362],[97,143,226,1109,2861],[97,143,226,624,1399,1653,2893],[85,97,143,226,631,1035,1036,1039,1044,1092,1109,1110,1178,1214,1311,1348,1954,1956,2084,2868,2869,2870,2872,2880,2882,2883,2886,2889,2890,2891,2892],[97,143,226,1109,1222,2893],[97,143,226,624,1399,2811],[85,97,143,226,624,1214,1399,1653,2863,2901],[85,97,143,226,624,1214,1399,1653,2901],[85,97,143,226,631,1035,1036,1037,1039,1041,1043,1044,1045,1048,1082,1089,1094,1102,1109,1110,1115,1203,1214,1215,1315,1316,1354,1361,1951,1963,1967,1968,2843,2871,2896,2898,2899,2900],[85,97,143,226,624,1214,1399,1653,2863,2899],[85,97,143,226,1035,1036,1037,1039,1048,1094,1101,1115,1183,1214,1316,1361,1452,1956],[85,97,143,226,624,1318,1399,2863,2903],[85,97,143,226,1318],[97,143,226,624,1316],[97,143,226,1214],[85,97,143,226,1035,1036,1037,1039,1044,1045,1082,1094,1315,2896,2897],[85,97,143,226,624,628,1214,1399,1653,2904],[85,97,143,226,624,1214,1318,1399,2904],[85,97,143,226,631,1034,1035,1036,1039,1043,1044,1048,1082,1092,1214,1215,1315,1316,1318,1319,1348,1361,1487,1501,1963,1967,2848,2896,2898,2899,2900,2902,2903],[97,143,226,624,1214,1318,1319],[97,143,226,1214,1318],[85,97,143,226,624,1215,1399,1653,2863,2902],[85,97,143,226,1035,1039,1048,1215],[85,97,143,226,1036,1039,1040,1044,1048,1082,1101],[85,97,143,226,624,1214,1399,1653,2907],[85,97,143,226,631,1035,1039,1106,1214,1215,1318,1347,1956,2901,2904,2906],[97,143,226,624,1318,1399,1653,2906],[85,97,143,226,1038,1039,1048,1094,1174,1176,1188,1318,2662,2905],[97,143,226,1034,1035,1039,1115,1174,1176,1188,1204,1318,1362],[85,97,143,226,1036,1315,2896],[85,97,143,226,1036,1037,1044,1045,1214,1315,2871,2896,2897],[97,143,226,1109,1217,2907],[97,143,226,624,1399,2851],[85,97,143,226,518,1109,1215,1217,1972,2850],[85,97,143,226,1109,2718,2851],[97,143,226,624,1399,1653,2921],[85,97,143,226,1348,1372,2920],[85,97,143,226,1034,1039],[97,143,226,1109,1222,2921,2922],[85,97,143,226,624,1399,1653,2863,2925],[85,97,143,226,631,1035,1036,1039,1044,1045,1101,1110,1311,1321,1455,1954,2084],[85,97,143,226,624,628,630,1399,1653,2929],[85,97,143,226,631,1035,1039,1106,1109,1323,1348,1369,1371,1455,2814,2834,2925,2927,2928],[97,143,226,624,1321],[97,143,226,624,630,1193,1399,1454,1455,1653,2863,2927],[85,97,143,226,630,1036,1039,1042,1183,1188,1430,1454,1455,2926],[97,143,226,1034,1035,1039,1174,1176,1188,1204,1362,1455,1700],[85,97,143,226,624,639,1399,1653,2863,2928],[85,97,143,226,631,1035,1036,1039,1044,1045,1082,1101,1110,1321,1455,1954],[97,143,226,1109,2929],[85,97,143,226,624,1399,2863,2946],[85,97,143,226,631,1035,1039,1040,1092,1214,1348,1456,2006,2282,2821,2936,2940,2944,2945],[85,97,143,226,624,1399,1653,2863,2936],[85,97,143,226,1035,1039,1348,2935],[85,97,143,226,1324,1325,2938],[85,97,143,226,1036,1037,1040,1045,1082,1094,1324,1325,1954,2871],[97,143,226,624,1324,1325],[97,143,226,1324],[97,143,226,624,1399,1653,2940],[85,97,143,226,631,1035,1039,1082,1090,1101,1214,1324,1325,2937,2938,2939],[97,143,226,624,1399,2937],[85,97,143,226,1045],[85,97,143,226,1327,1328,2941],[85,97,143,226,1036,1037,1082,1094,1327,1328,1954,2871],[85,97,143,226,624,1327,1399,1653,2863,2943],[85,97,143,226,1045,1327],[97,143,226,624,1087,1328],[97,143,226,1087,1203,1327],[97,143,226,624,628,631,1214,1399,1653,2944],[97,143,226,624,628,631,1087,1214,1399,1653,2944],[85,97,143,226,631,1035,1082,1203,1327,1328,1361,1476,2942,2943],[85,97,143,226,624,1399,2945],[85,97,143,226,639,1035,1039,1092,2276,2282],[97,143,226,1109,2946],[85,97,143,226,1109,1214,1242],[97,143,226,624,1330],[97,143,226,639],[85,97,143,226,624,628,630,1189,1330,1343,1399,2831],[85,97,143,226,630,1043,1045,1048,1092,1115,1178,1189,1193,1330,1333,1342,1343,1348,2821,2829,2830],[97,143,226,624,1332,1342,1399,3047],[85,97,143,226,1039,1048,1092,1178,1193,1333,1342,1348,2821],[97,143,226,624,1214,1332,1333],[97,143,226,1193,1214,1332],[85,97,143,226,624,628,1399,3049],[85,97,143,226,1039,1342,1348,1414,2814,2831,2957,2958,2959,3048],[97,143,226,624,1335],[97,143,226,624,1399,3048],[85,97,143,226,631,1214,1342,3046,3047],[97,143,226,624,1399,1653,2959],[85,97,143,226,631,1035,1036,1039,1044,1048,1092,1094,1214,1311,1335,1361,1954,2084],[85,97,143,226,624,630,1345,1399,1487,1653,2829,2863],[85,97,143,226,630,1035,1039,1048,1092,1109,1115,1178,1333,1345,2828],[85,97,143,226,1035,1036,1041,1042,1045,1091,1092,1109,1189,1345,1487,1508,1560,2825,2826,2827],[85,97,143,226,624,1189,1330,1399,2830],[85,97,143,226,1092,1132,1134,1136,1189,1330,2282],[97,143,226,624,1214,1332,1399,1653,2958],[85,97,143,226,1092,1214,1333,1342,1348,1414,2282,2821,2823],[97,143,226,624,1340,1343],[97,143,226,1214,1340,1342],[97,143,226,624,1342,1399],[97,143,226,624,1214,1342,1399],[85,97,143,226,1106,1214,1332,1341],[97,143,226,624,1345],[97,143,226,628,631,639,1109,1340],[97,143,226,1109,3049],[85,97,143,226,624,1349,1358,1399,1653,2863],[85,97,143,226,1035,1036,1039,1040,1044,1048,1102,1349,1352,1354],[97,143,226,624,1349,1356,1399,1653,2863],[85,97,143,226,624,1349,1352,1356,1399,1653,2863],[85,97,143,226,1035,1036,1038,1039,1040,1044,1048,1349,1352,1354],[85,97,143,226,624,1378,1399,1653,2863],[85,97,143,226,1035,1039,1090,1094,1101,1110,1347,1348,1349,1355,1356,1357,1358,1367,1368,1373,1375,1376,1377],[85,97,143,226,624,1373,1399,1653,2863],[85,97,143,226,1036,1042,1372],[97,143,226,1349,1355,1356,1357,1358,1373,1374,1375,1376,1378],[85,97,143,226,624,1359,1367,1399,1653,2863],[85,97,143,226,1035,1036,1039,1041,1102,1178,1359,1365,1366],[85,97,143,226,624,1349,1359,1365,1399,1653,2863],[85,97,143,226,1035,1039,1043,1092,1115,1178,1193,1349,1359,1361,1364],[85,97,143,226,624,1359,1363,1364,1653,2863],[85,97,143,226,1035,1039,1359,1362,1363],[97,143,226,624,1349,1359,1363],[97,143,226,1193,1349,1359],[97,143,226,1349],[97,143,226,624,1349,1359,1366,1399],[85,97,143,226,1214,1349,1359],[85,97,143,226,624,1355,1399,1653,2863],[85,97,143,226,1035,1036,1039,1349,1350,1352,1354],[97,143,226,624,1374],[97,143,226,1352],[85,97,143,226,624,1352,1357,1399,1653,2863],[97,143,226,624,631,1214,1377,1399],[85,97,143,226,631,1214],[97,143,226,624,631,1375,1399],[85,97,143,226,631,1214,1349,1352,1374],[97,143,226,624,631,1376,1399],[97,143,226,1109,1379],[97,143,226,624,1399,1653,3136],[85,97,143,226,1035,1037,1039,1041,1090,1110],[97,143,226,624,1399,1653,3150],[85,97,143,226,1035,1036,1037,1039,1042,1045,1094],[97,143,226,624,628,1399,1482,1653,3142],[85,97,143,226,628,1035,1039,1115,1203,1214,1348,1361,1482,2016,3136,3138,3140,3141],[97,143,226,624,1214,1653,1659,2863,3145],[85,97,143,226,1214,1661,2006,2821,3142,3144],[97,143,226,624,1399,1482,1653,3144],[85,97,143,226,1035,1039,1174,1176,1188,1191,1199,1361,1482,2017,2814,3136,3137,3138,3139,3143],[97,143,226,624,1399,1482,1653,3140],[85,97,143,226,1039,1174,1176,1188,1194,1199,1482,2017,3137,3138,3139],[85,97,143,226,624,1399,2863,3143],[85,97,143,226,1092,2282],[97,143,226,624,1399,2863,3147],[97,143,226,1109,1414,3145,3146],[85,97,143,226,624,1214,1399,1653,2863,3090],[85,97,143,226,624,1399,2863,3090],[85,97,143,226,631,1035,1036,1037,1040,1044,1045,1048,1082,1091,1110,1214,1354,1361,1406,3081,3082,3083,3084,3085,3086,3088,3089],[85,97,143,226,1035,1039,1045,1115,1174,1176,1188,1409],[85,97,143,226,624,1214,1399,1653,3081],[85,97,143,226,1044,1045,1092,1094,1214,1697,3080],[85,97,143,226,1035,1039,1040,1045,1092,1101,1115,1174,1176,1188,1214,1409],[97,143,226,624,1653,2863,3082],[85,97,143,226,631,1035,1039,1092,1214,1361,3074,3075,3076,3077,3078,3079,3081],[97,143,226,624,2863,3094],[85,97,143,226,1092,1115,3077,3078,3093],[97,143,226,624,1399,1653,3095],[85,97,143,226,1039,1043,1956,3081,3082,3094],[97,143,226,624,1653,2863,3077,3078,3079,3093],[97,143,226,624,1399,1653,3075],[85,97,143,226,1035,1036,1045,1110,1409],[97,143,226,624,1399,1653,3076],[85,97,143,226,1035,1036,1037,1045,1110,1409],[85,97,143,226,1035,1039,1045,1174,1176,1188,1409],[97,143,226,624,1399,1653,3074],[85,97,143,226,1035,1040,1045,1110,1409],[85,97,143,226,624,1399,1653,1697],[85,97,143,226,1040],[85,97,143,226,624,1399,1653,3080],[85,97,143,226,1036],[97,143,226,624,1214,1399,1410,1653],[85,97,143,226,631,1035,1036,1037,1039,1040,1045,1094,1101,1110,1214,1361],[97,143,226,1410],[97,143,226,624,1399,1407,1653,3102],[85,97,143,226,1038,1039,1407,3100,3101],[97,143,226,624,1399,1407,1653,3100],[85,97,143,226,1039,1354,1407],[97,143,226,624,1407],[97,143,226,1406],[97,143,226,624,1399,1407,3101],[85,97,143,226,1034,1035,1039,1354,1405,1407,3090],[97,143,226,624,1214,1399,1653,2863,3096],[97,143,226,624,1214,1399,1653,3096],[85,97,143,226,631,1035,1036,1037,1039,1043,1044,1045,1048,1082,1092,1115,1193,1214,1348,1354,1406,1410,3083,3084,3085,3088,3089,3095],[97,143,226,624,1406],[97,143,226,530],[85,97,143,226,1035,1036,1045,1091,1937,2871,3083],[85,97,143,226,624,1082,1399,1406,2863,3083,3085],[85,97,143,226,631,1036,1037,1044,1045,1091,1103,1214,1361,1406,1937,2871,3083],[97,143,226,624,1399,1653,2093,3092],[85,97,143,226,1039,1174,1176,1188,2093,3091],[85,97,143,226,1039,1044,1045,1048,1082],[85,97,143,226,624,1214,1406,1659,2863,3104],[85,97,143,226,631,1034,1035,1039,1106,1214,1348,1362,1406,1411,1661,2093,2834,3090,3092,3096,3099,3102,3103],[97,143,226,1034,1035,1039,1174,1176,1188,1204,1354,1362,1406,2093],[97,143,226,624,1399,1653,3098],[85,97,143,226,631,1035,1037,1039,1048,1361,3097],[97,143,226,624,1399,1653,3099],[85,97,143,226,631,1038,1039,1092,1214,1361,1406,2093,3098],[97,143,226,624,1399,1653,3097],[85,97,143,226,631,1035,1039,1092],[85,97,143,226,1035,1036,1038,1039,1040,1044,1045,1082,3083],[97,143,226,624,1399,2093,3087],[85,97,143,226,1035,1039,1040,1045,1048,1115,1183,2093],[97,143,226,624,1399,3088],[85,97,143,226,2093,3087],[97,143,226,624,1109,1214,1399,1653,2863,3103],[97,143,226,624,1109,1214,1399,2863,3103],[85,97,143,226,631,1035,1036,1037,1039,1044,1045,1048,1106,1109,1110,1214,1311,1452,1453,1484,1951,1954,2084,2363],[85,97,143,226,624,1399,1653,3089],[85,97,143,226,1035,1036,1037,1039,1043,1045,1048,1092],[97,143,226,1109,3104],[97,143,226,628,1106,1109,1214,1421],[85,97,143,226,624,628,1109,1214,1399,1421],[97,143,226,628,639,1106,1107,1109,1214],[97,143,226,628,1109,1214,1421],[85,97,143,226,624,628,1214,1318,1399,1426],[97,143,226,628,1106,1107,1109,1214,1318],[97,143,226,628,1107,1137,1214],[97,143,226,628,1107,1214],[97,143,226,628,1214],[97,143,226,624,1430],[97,143,226,1174,1176],[85,97,143,226,628,639,1107,1109,1174,1176,1214,1430,1454],[97,143,226,624,1399,1456],[97,143,226,639,1109,1340],[85,97,143,226,624,628,1399,1458],[85,97,143,226,624,628,1399,1460],[85,97,143,226,624,628,1399,1462],[85,97,143,226,624,628,1399,1464,1465],[97,143,226,628,1107,1214,1464],[97,143,226,624,1107],[85,97,143,226,624,628,1174,1176,1399,1454],[85,97,143,226,628,639,1174,1176,1452,1453],[97,143,226,630,1214],[97,143,226,628,1107,1109,1468],[97,143,226,628,1468,1470],[97,143,226,628,1469,1472],[97,143,226,628,1107,1109,1469],[97,143,226,628,1087,1107,1109,1214],[85,97,143,226,624,628,1214,1399,1477],[97,143,226,628,1107,1109,1214],[97,143,226,624,1399,1479],[97,143,226,639,1106,1109,1340],[85,97,143,226,624,628,1129,1214,1399],[97,143,226,624,1399,1482],[97,143,226,639,1340],[85,97,143,226,624,628,1214,1399,1485],[97,143,226,628,1109,1214,1215,1487],[85,97,143,226,624,628,1215,1399,1487],[97,143,226,628,1107,1109,1214,1215],[97,143,226,628,1109,1214,1487],[85,97,143,226,624,628,1214,1399,1491],[97,143,226,628,1109,1214],[85,97,143,226,624,628,1109,1214,1399,1497],[85,97,143,226,624,628,1109,1214,1399,1499],[85,97,143,226,628,1107,1109,1214],[85,97,143,226,624,628,1109,1214,1399,1501],[97,143,226,628,1086,1107,1109,1214],[97,143,226,628,639,1107,1109,1214],[97,143,226,628,639,1340,1505],[97,143,226,628,639,1106,1107,1109,1340],[85,97,143,226,624,628,1214,1399,1508],[85,97,143,226,624,628,1189,1214,1399],[85,97,143,226,624,628,1214,1399,1511],[97,143,226,628,1107,1108,1214],[85,97,143,226,624,628,1214,1218,1399],[85,97,143,226,624,628,1399,1514,1515],[97,143,226,628,1109,1214,1514],[85,97,143,226,624,628,1399,1514,1517],[85,97,143,226,624,628,1399,1514,1519],[97,143,226,628,1106,1109,1214,1514],[85,97,143,226,624,628,1399,1514],[97,143,226,628,1106,1107,1109,1214],[85,97,143,226,624,628,1399,1514,1522],[85,97,143,226,624,628,1214,1399,1524],[85,97,143,226,624,628,1399,1526],[97,143,226,628,1107,1221],[85,97,143,226,624,628,1399,1528],[97,143,226,628,1107,1109,1214,1531],[97,143,226,624,1399,1533],[97,143,226,624,1399,1535],[97,143,226,1109,1340,1533],[85,97,143,226,624,628,1214,1399,1537],[85,97,143,226,624,628,1214,1399,1539],[85,97,143,226,624,628,1109,1399,1541],[97,143,226,628,1109,1214,1526],[85,97,143,226,624,628,637,1214,1399,1544],[97,143,226,628,637,1107,1109,1214],[97,143,226,624,1546],[97,143,226,628,630,1107,1109,1214],[85,97,143,226,624,628,1214,1215,1216,1217,1399],[97,143,226,628,1106,1107,1109,1214,1215,1216],[85,97,143,226,624,628,1108,1214,1399],[85,97,143,226,624,628,1214,1399,1550,1551],[97,143,226,1550],[85,97,143,226,624,628,1214,1399,1550],[85,97,143,226,624,628,1214,1399,1554],[85,97,143,226,624,628,1109,1399],[85,97,143,226,624,628,634,636,1105,1109,1214,1399],[85,97,143,226,634,636,1105,1106,1108,1214],[97,143,226,1109,1219,1227],[85,97,143,226,1228],[97,143,226,624,1228,1229,1399],[97,143,226,624,1228,1233,1399],[97,143,226,624,1214,1219,1399],[97,143,226,1106,1109,1218],[97,143,226,634,1105,1222],[97,143,226,628,1214,1556],[85,97,143,226,624,628,1214,1399,1558],[85,97,143,226,624,628,1214,1399,1560],[97,143,226,624,1399,1419,1420],[85,97,143,226,518,1419],[85,97,143,226,1109,1215,1216],[97,143,226,624,1214,1399,2700,2811],[85,97,143,226,518,630,1206,1214,1224,2700,2711,2715,2717,2718,2719,2720,2721,2722,2810],[97,143,226,624,1242,1243],[97,143,226,1206],[97,143,226,1109,3171],[97,143,226,1109,3191],[85,97,143,226,1036,1039,1048,1084,1572,2066,2871],[97,143,226,624,633,1214,1399,1653,1654,3218],[97,143,226,624,1214,1399,1653,1654,3218],[85,97,143,226,530,631,633,1035,1036,1039,1045,1048,1082,1084,1086,1101,1106,1110,1214,1361,1562,1564,1565,1572,1574,2066,2871,3197,3198,3201,3202,3204,3205,3206,3207,3208,3209,3210,3211,3213,3214,3215,3216,3217],[97,143,226,624,632,1562],[97,143,226,632,1086],[97,143,226,624,1565],[97,143,226,1086,1564],[85,97,143,226,1039,1048,1084,1086,1094,1572],[97,143,226,1086,1567],[97,143,226,624,1086,1564,1565,1567,1568],[97,143,226,1086,1564,1565],[85,97,143,226,624,1082,1084,1399,1653,3215],[85,97,143,226,1035,1036,1038,1039,1045,1048,1082,1084,1572,1574,2066],[85,97,143,226,1036,1037,1039,1048,1084,1091,1572,2066,2871,3200],[97,143,226,624,1570],[85,97,143,226,631,1035,1037,1110,1214,1570,1956],[97,143,226,3232,3237],[85,97,143,226,624,1399,1653,3220],[85,97,143,226,1035,1039,1042,1092,1094,1193,1214,1348,1956],[85,97,143,226,624,1399,1653,3208],[85,97,143,226,1035,1039,1092,1101,1361,1956],[97,143,226,624,1086,1214,1399,1653,3229],[85,97,143,226,1034,1035,1038,1039,1086,1110,1177,1214,1351,3218],[97,143,226,624,1399,1653,3207],[85,97,143,226,1038,1039,1048,1086,1092,1101,1115],[97,143,226,624,1399,3223],[85,97,143,226,1086],[85,97,143,226,624,1086,1214,1399,3222],[85,97,143,226,624,631,632,1214,1399,1653,1654,3222],[85,97,143,226,631,632,633,1035,1036,1037,1039,1045,1048,1082,1084,1086,1091,1214,1348,1564,1567,1572,1574,1956,2066,2645,2871,3201,3202,3204,3205,3206,3207,3209,3210,3211,3214,3215,3216],[97,143,226,624,1086,1399,1653,3224],[85,97,143,226,632,1035,1039,1086,1092,1115,1193,1348,1564,3222,3223,3238],[85,97,143,226,624,628,1214,1399,1653,3232],[85,97,143,226,628,631,632,1035,1038,1039,1045,1048,1086,1106,1115,1214,1347,1348,1361,1499,1501,2351,3194,3196,3218,3219,3220,3221,3224,3226,3227,3228,3229,3230,3231],[85,97,143,226,624,1399,3209],[85,97,143,226,1034,1035,1036,1037,1038,1039,1092,1115,1183,1361,1564,1965],[97,143,226,624,628,633,1214,1399,3237],[85,97,143,226,628,632,633,1034,1035,1038,1039,1086,1092,1115,1214,1351,1361,2351,2645,3234,3235,3236],[97,143,226,624,1084,1572],[85,97,143,226,1082,1084],[85,97,143,226,624,1082,1084,1399,1574],[97,143,226,1082,1084],[85,97,143,226,1082,1084,1399],[85,97,143,226,624,1399,1653,3214],[85,97,143,226,530,1034,1038,1039,1048,1354],[97,143,226,624,1214,1399,1653,3228],[85,97,143,226,1035,1036,1039,1092,1115,1214,1361,2922],[85,97,143,226,624,1399,1653,3211,3245],[85,97,143,226,1035,1038,1039,1044,1048,1082,1084,1086,1091,1094,1101,1572,1574,1956,2066],[85,97,143,226,624,1086,1205,1399,3221],[85,97,143,226,1034,1035,1039,1048,1086,1115,1354,1362,1564],[97,143,226,624,1086,3193],[97,143,226,1086],[85,97,143,226,631,1039,1086,1214,3193],[85,97,143,226,624,628,1086,1214,1399,1501,1503,1653,3196],[85,97,143,226,628,631,1035,1036,1038,1039,1044,1086,1110,1174,1176,1188,1214,1311,1361,1501,1503,1954,2084,3195],[97,143,226,624,1086,1188,1399,1653,3195],[97,143,226,1034,1035,1039,1086,1174,1176,1188,1193,1204,1214,1362],[97,143,226,624,1575],[97,143,226,1086,1565],[85,97,143,226,624,1399,3201,3245],[85,97,143,226,1035,1036,1037,1039,1045,1048,1084,1086,1091,1572,2066,2871,3199,3200],[85,97,143,226,1036,1039,1048,1082,1084,1091,1094,1572],[85,97,143,226,1036,1039,1048,1084,1086,1572,1574,2066,3212],[97,143,226,624,1214,1399,1653,3212],[85,97,143,226,1034,1214,1361],[85,97,143,226,624,1399,3204,3245],[85,97,143,226,1035,1042,1084,1086,1183,1572,2871,3203],[85,97,143,226,1037,1039,1048,1084,1572,2066],[97,143,226,624,1399,1653],[85,97,143,226,1039,1045,1048,1084,1572],[85,97,143,226,1036,1039,1045,1048,1082,1084,1091,1572,2066,2871,3200],[85,97,143,226,1035,1036,1037,1039,1044,1045,1048,1082,1086,1361,1655,1954],[97,143,226,624,1086,1655],[97,143,226,1082,1086],[85,97,143,226,624,1086,1399,1653,2863,3234],[85,97,143,226,631,1035,1039,1048,1086,1351,1655,3233],[97,143,226,624,1086,1399,3202],[85,97,143,226,1039,1086,1956],[85,97,143,226,1036,1039,1048,1084,1572],[85,97,143,226,624,628,1086,1214,1399,1653,3231],[85,97,143,226,628,631,1035,1039,1044,1086,1110,1115,1203,1214,1311,1361,1954,1956,2084,2871],[97,143,226,624,1564],[97,143,226,1109,3238],[85,97,143,226,624,1214,1399,1653,3270],[85,97,143,226,1181,1214],[85,97,143,226,624,1214,1399,1653,3271],[85,97,143,226,1035,1036,1037,1039,1044,1048,1110,1214,1311,1954,2084],[85,97,143,226,624,1174,1176,1214,1399,1653,3273],[85,97,143,226,1039,1174,1176,1188,1214,3272],[97,143,226,1034,1035,1039,1174,1176,1204,1214,1362],[85,97,143,226,624,628,1174,1176,1214,1399,1653,3274],[85,97,143,226,628,631,1035,1039,1174,1176,1214,1452,1453,2834,3270,3271,3273],[97,143,226,624,1399,2863,3275],[97,143,226,1109,1414,2922,3146,3274],[97,143,226,1106,1109,3296,3297],[97,143,226,1034,1035,1039,1174,1176,1188,1204,1362,1505,1700],[85,97,143,226,1035,1039,1044,1048,1110,1311,1505,1507,1665,1700,1937,1954,2084],[97,143,226,624,1665],[97,143,226,1505,1507],[97,143,226,624,628,1109,1399,1653,3323,3325],[85,97,143,226,628,631,1039,1109,1174,1176,1189,1206,1214,1217,1452,1508,1673,2109,2834,3322,3323,3324],[97,143,226,624,1399,1653,2109,3324],[85,97,143,226,1034,1035,1039,1041,1045,1174,1176,1188,2109,2705,3323],[97,143,226,624,1667,1669],[97,143,226,1134,1189,1214,1667,1668],[97,143,226,624,1653,2863,3332],[85,97,143,226,631,1035,1039,1110,1189,1214,1662,1668,1669,2834,3329,3331],[85,97,143,226,1174,1176,1188,1204,1669,3330],[85,97,143,226,1034,1035,1039,1115,1174,1176,1188,1204,1362,1669,1671],[97,143,226,624,1671],[97,143,226,624,1399,1653,3363],[85,97,143,226,1035,1036,1039,1042,1045],[97,143,226,1035,1039,1094,1115,1174,1176,1188,1193,1204,2109,2817,3038,3306],[97,143,226,624,1399,3371],[85,97,143,226,1109,1508,3370],[97,143,226,624,1399,1659,1662],[85,97,143,226,1661],[97,143,226,624,628,1399,1653,3373],[85,97,143,226,628,1035,1039,1106,1109,1217,1230,1348,1550,1662,1664,1668,3299,3307,3321,3326,3333,3342,3347,3358,3362,3364,3366,3369,3372],[85,97,143,226,624,628,1399,1653,3369],[85,97,143,226,631,1039,1106,1109,1174,1176,1188,1505,1506,1507,2834,3367,3368],[97,143,226,624,1214,1653,2863,3342],[85,97,143,226,628,1082,1084,1109,1217,1352,1477,1508,3337,3341],[97,143,226,1662,1664,3324,3325],[97,143,226,624,1399,3333],[97,143,226,1106,1109,1217,1550,1668,3332],[97,143,226,624,1399,1659,3362],[85,97,143,226,1109,1174,1176,1189,1217,1508,1662,1673,3306,3361],[97,143,226,3346],[85,97,143,226,1109,1214,3365],[85,97,143,226,631,1109,1214,1530,1664,3363],[97,143,226,1109,3357],[97,143,226,3371],[85,97,143,226,1189],[97,143,226,624,1673],[85,97,143,226,624,1399,1653,2863,3386],[85,97,143,226,1035,1040,1045,1092,1178,1193,1201,1204,1214,1222,1227,1348,2282,2821,3384,3385],[97,143,226,1109,2922,3386],[85,97,143,226,624,628,1399,1659,3396,3398,3399],[85,97,143,226,628,631,1035,1189,1214,1218,1661,2834,3392,3395,3396,3398],[85,97,143,226,624,1214,1399,1653,3398],[85,97,143,226,1039,1174,1176,1188,1214,3397],[97,143,226,1034,1035,1039,1174,1176,1188,1204,1214,1362],[97,143,226,624,1399,1653,3392],[97,143,226,1039,3389,3390,3391],[97,143,226,1109,3399],[97,143,226,624,1399,2852],[85,97,143,226,518,1105,1214,1243,2700,2718,2851],[85,97,143,226,1035,1039,1048,1101],[97,143,226,624,1399,1653,3500],[85,97,143,226,1034,1036,1039,1048,1183,1237],[85,97,143,226,624,1399,1653,1679,3522],[85,97,143,226,631,1035,1036,1037,1039,1045,1086,1090,1091,1214,1347,1348,1361,1372,1679,2352,3404,3521],[97,143,226,624,1399,1684,3509],[85,97,143,226,1684],[97,143,226,624,1399,3501],[85,97,143,226,1034,1035,1038,1039,1048],[97,143,226,1675],[85,97,143,226,506,1039,1684,3503],[85,97,143,226,631,1035,1039,1048,1677],[97,143,226,624,1684,3503],[97,143,226,1684],[97,143,226,624,1399,1675,1684,3517],[85,97,143,226,1039,1086,1369,1371,1675,1683,1684,1687,2766,3508,3509,3510,3511,3512,3513,3515,3516],[97,143,226,624,1090,1399,1653,2863,3403,3521],[85,97,143,226,631,632,1035,1036,1039,1041,1045,1048,1086,1090,1091,1110,1206,1214,1237,1369,1371,1414,1452,1675,1676,1677,1679,1684,1685,1688,1969,2843,2844,3230,3295,3403,3421,3422,3423,3424,3492,3493,3494,3495,3496,3497,3498,3499,3500,3501,3502,3503,3504,3505,3506,3507,3512,3514,3517,3518,3519,3520],[97,143,226,624,1399,1653,3511],[85,97,143,226,1035,1039,1101,1214,1369,1371],[85,97,143,226,631,1039,1048,1094],[97,143,226,624,1399,1653,1676,3505],[85,97,143,226,1041,1676],[97,143,226,624,1090,1675,3506],[97,143,226,1090,1675],[97,143,226,624,1399,1653,3507],[97,143,226,1035,1039],[97,143,226,624,1399,1653,3520],[85,97,143,226,1035,1036,1039,1045,1214,1676],[85,97,143,226,1039,1684,3514],[85,97,143,226,1035,1039,1101,1684],[85,97,143,226,631,1035,1039,1048,1094,1675],[97,143,226,624,1677],[97,143,226,624,1399,1653,3403,3527],[85,97,143,226,631,1035,1036,1039,1045,1048,1090,1452,1453,1679,1680,1683,1684,3403,3421,3424,3502,3503,3525,3526],[97,143,226,624,1399,1653,1680,3525,3527],[85,97,143,226,1039,1043,1103,1183,1237,1680,1969,2843,3423,3523,3524,3527],[97,143,226,624,1399,1684,3523],[85,97,143,226,1039,1369,1371,1683,1684,2766,3510,3513,3516],[97,143,226,624,1399,1653,3526],[85,97,143,226,1035,1037,1039],[97,143,226,624,1399,1653,3546],[85,97,143,226,1036,1041],[97,143,226,624,1399,1653,1680,3524],[97,143,226,1040,1361,1680],[97,143,226,624,1679,1680],[97,143,226,1679],[85,97,143,226,1039,1214,1414,1689,1990,2349,2844,3403],[97,143,226,624,1399,1685],[85,97,143,226,1083,1086,1452,1683,1684],[85,97,143,226,1687],[97,143,226,1214,1684,3421],[97,143,226,624,1683,3492],[97,143,226,631,1086,1214,1682,1683,1684,2103,3491],[97,143,226,624,3037,3493],[97,143,226,631,1214,1676,3037],[97,143,226,624,3037,3494],[97,143,226,631,1214,3037],[97,143,226,624,3495],[97,143,226,631,1214],[97,143,226,624,1399,3528],[85,97,143,226,1109,1221,1348,2922,3404,3521,3522,3527],[85,97,143,226,624,1214,1399,1653,1689,2863,3563],[85,97,143,226,631,1035,1039,1043,1044,1048,1102,1109,1110,1214,1311,1361,1689,1690,1692,1954,2084,3561,3562],[97,143,226,624,1399,1653,1689,2863,3557],[85,97,143,226,631,1035,1036,1037,1039,1041,1043,1044,1048,1091,1102,1109,1110,1115,1203,1214,1311,1361,1689,1954,1956,2084,2093],[85,97,143,226,624,1399,1653,2863,3568],[85,97,143,226,1035,1036,1037,1039,1041,1048,1092,1110,1183,1214,1361],[85,97,143,226,624,1399,1653,1689,2863,3560],[85,97,143,226,1039,1174,1176,1188,1689,3559],[97,143,226,1034,1035,1039,1174,1176,1188,1193,1204,1362,1689,3558],[97,143,226,624,1690],[97,143,226,1689],[85,97,143,226,624,1399,1653,2863,3566],[85,97,143,226,1035,1039,1043,1110,1115,1183,1406],[85,97,143,226,624,1214,1653,1689,2863,3558],[85,97,143,226,1035,1039,1048,1115,1214,1237,1689],[97,143,226,624,1399,2863,3561],[85,97,143,226,1039,1115,1956],[85,97,143,226,624,1399,1653,2863,3569],[85,97,143,226,631,1035,1039,1106,1214,1348,1689,1956,2093,2354,2834,3554,3555,3556,3557,3560,3563,3564,3565,3566,3567,3568],[85,97,143,226,624,1399,1653,1689,2093,2863,3555],[85,97,143,226,631,1035,1036,1039,1041,1045,1214,1361,1689,2093,2349],[97,143,226,624,1214,1399,1653,1689,2863,3556],[85,97,143,226,1035,1039,1043,1092,1115,1177,1214,1689,1956,3555],[85,97,143,226,624,1214,1399,1653,2863,3565],[85,97,143,226,631,1035,1039,1092,1115,1177,1183,1214],[85,97,143,226,624,1214,1399,1653,2863,3564],[85,97,143,226,1035,1039,1040,1044,1082,1109,1115,1214,1361,1954,1956,3562],[85,97,143,226,624,1399,1653,1689,2863,3554],[85,97,143,226,1039,1174,1176,1188,1689,3553],[97,143,226,1034,1035,1039,1174,1176,1188,1204,1362,1689],[97,143,226,624,1692],[85,97,143,226,624,1399,1653,2863,3567],[85,97,143,226,1035,1036,1039,1041,1102,1110,1115,1214,1361],[97,143,226,1109,3569],[97,143,226,624,1514,1653,2863,3590],[85,97,143,226,1035,1039,1092,1115,1201,1203,1217,1235,1361,1519,2282,2818,3586,3589],[97,143,226,624,2863,3589],[85,97,143,226,1038,1039,1092,1174,1176,1487,3588],[97,143,226,624,1215,1653,2863,3588],[85,97,143,226,1039,1174,1176,1188,1215,3587],[97,143,226,1174,1176,1204,1207,1215,2818],[97,143,226,624,1214,1653,2863,3585],[97,143,226,624,1653,2863,3585],[85,97,143,226,631,1035,1039,1110,1361,1515,1694,1973,1974,2084],[97,143,226,624,1214,1514,1653,2863,3586],[97,143,226,624,1514,1653,2863,3586],[85,97,143,226,631,1035,1039,1110,1361,1514,1522,1694,1973,1974,2084],[85,97,143,226,624,1653,1694,1973,2084,2863],[85,97,143,226,1035,1036,1037,1038,1039,1041,1043,1044,1045,1082,1089,1094,1101,1109,1214,1215,1217,1694,1697,1954,1956,1972],[97,143,226,624,1973,1974],[97,143,226,1973],[97,143,226,624,1514,1653,1659,2863,3593],[85,97,143,226,1035,1038,1039,1217,1514,1661,2814,3585,3590,3592],[97,143,226,624,1514,1653,1659,2863,3592],[85,97,143,226,1039,1174,1176,1188,1514,1661,3591],[97,143,226,1039,1115,1174,1176,1177,1188,1204,1514],[97,143,226,1109,3593],[85,97,143,226,624,631,1214,1399,3610],[85,97,143,226,631,1035,1036,1039,1044,1045,1110,1214,1311,1361,1954,2084],[97,143,226,624,1214,1399,1653,2863,3630],[85,97,143,226,631,1035,1039,1045,1106,1214,1347,3607,3609,3610,3629],[97,143,226,1976,3628],[97,143,226,624,1399,3619],[85,97,143,226,1039],[97,143,226,624,1399,3624],[85,97,143,226,1035,1039,1979,1980,3618,3621,3622,3623],[97,143,226,624,1399,3620],[85,97,143,226,1039,1369,1371,1683,1979,2766],[97,143,226,624,1399,3623],[85,97,143,226,624,1399,3621],[85,97,143,226,1039,1979,3619,3620],[97,143,226,1683],[85,97,143,226,631,1214,1683,1977,1979],[97,143,226,624,1399,3618],[97,143,226,624,1399,3616],[85,97,143,226,1092,3615],[85,97,143,226,1976,1977],[85,97,143,226,631,1214,1976,1977,3611,3612,3613,3614,3616,3617,3624,3625,3626,3627],[97,143,226,624,1399,3613],[85,97,143,226,1035,1036,1039,1110,1933],[97,143,226,624,1399,1653,3608],[85,97,143,226,631,1035,1039,1045,1110,1348,1369,1371],[97,143,226,624,1399,3612],[85,97,143,226,1035,1036,1039,1045,1115,3608],[97,143,226,624,1399,3617],[85,97,143,226,1035,1039,1045,1092,1976,3615],[97,143,226,624,1399,3625],[85,97,143,226,1035,1036,1039,1110],[97,143,226,624,1399,1976,3614],[85,97,143,226,1035,1039,1092,1976],[97,143,226,624,1976,1977],[97,143,226,1976],[97,143,226,624,1110,1214,1653,2863,3627],[85,97,143,226,1035,1039,1115,1177,1214],[85,97,143,226,624,1214,1399,1653,3609],[85,97,143,226,631,1035,1039,1092,1110,1115,1178,1193,1214,1348,3605,3608],[97,143,226,1214,1977],[97,143,226,624,1214,1399,1653,3607],[85,97,143,226,1039,1174,1176,1188,1214,3605,3606],[97,143,226,1034,1035,1039,1174,1176,1188,1193,1204,1214,1352,1362,3605],[97,143,226,624,1399,3611],[85,97,143,226,1035,1110],[97,143,226,624,1399,3615],[85,97,143,226,1035,1036,1037,1039,1115,1237],[97,143,226,1109,2922,3630],[97,143,226,624,1214,1653,2863,3046],[85,97,143,226,1035,1036,1038,1039,1045,1092,1094,1178,1204,1214,1348,2960,3040,3045],[97,143,226,1109,3046],[97,143,226,624,628,1214,1399,1653,3655],[97,143,226,624,1399,3655],[85,97,143,226,530,628,631,1035,1036,1037,1039,1040,1044,1048,1082,1106,1110,1214,1311,1354,1361,1954,1981,1982,2084,2871,3654],[97,143,226,3660],[97,143,226,624,631,1214,1399,1653,3654],[85,97,143,226,631,1035,1039,1043,1214,1361],[97,143,226,624,1982],[97,143,226,1981],[97,143,226,624,628,1106,1214,1399,1653,1981,3660],[85,97,143,226,628,631,1035,1036,1037,1044,1045,1106,1110,1214,1311,1361,1954,1981,1982,2084,2834,2871,3655,3657,3659],[97,143,226,624,1399,1653,1981,2863,3657],[85,97,143,226,1039,1174,1176,1188,1981,3656],[97,143,226,1034,1035,1039,1174,1176,1188,1204,1362,1981],[97,143,226,624,631,1214,1399,1653,3658],[85,97,143,226,631,1035,1036,1039,1092,1214,1361],[97,143,226,624,1193,1399,1653,1981,3659],[85,97,143,226,1035,1039,1092,1193,1981,3658],[97,143,226,1109,3661],[85,97,143,226,624,631,1214,1399,2863,3671],[85,97,143,226,631,640,1035,1036,1037,1039,1040,1044,1048,1110,1214,1311,1361,1954,2064,2084],[97,143,226,624,640,1214,1399,1653,3674],[85,97,143,226,631,640,1035,1106,1214,1347,3292,3671,3673],[97,143,226,624,640,1399,1653,3673],[85,97,143,226,640,1039,1174,1176,1188,3672],[97,143,226,640,1034,1035,1039,1115,1174,1176,1188,1193,1204,1362,2064],[97,143,226,1109,3674],[97,143,226,624,1399,1653,3682],[85,97,143,226,1035,1036,1037,1039,1044,1048,1091,1101,1110,1311,1700,1937,1954,2084],[97,143,226,624,1214,1399,1653,3683],[85,97,143,226,631,637,1035,1039,1214,2834,3679,3681,3682],[97,143,226,624,637,1214,1399,1653,3679],[85,97,143,226,631,637,1035,1036,1037,1039,1044,1048,1089,1091,1092,1101,1115,1193,1214,1311,1700,1937,1954,1972,2084],[97,143,226,624,637,1204,1399,1653,3681],[85,97,143,226,637,1039,1174,1176,1188,3680],[97,143,226,637,1034,1035,1039,1115,1174,1176,1188,1204,1362],[97,143,226,1109,3683],[97,143,226,1109,3695],[97,143,226,1109,3702],[97,143,226,1109,3704],[97,143,226,624,631,1214,1399,1653,3704],[85,97,143,226,631,1035,1037,1039,1092,1214,1361],[97,143,226,1109,3707],[97,143,226,624,631,1399,1653,3707],[85,97,143,226,631,1035,1036,1042,1092,1214,1224,1361],[97,143,226,624,1332,1399,2863,3716],[85,97,143,226,1092,1332,2282],[97,143,226,624,1332,1399,2863,3717],[97,143,226,624,2863,3718],[85,97,143,226,1174,1176,1188,1201,1204,1332],[97,143,226,624,1399,3719],[85,97,143,226,1332,3716,3717,3718],[85,97,143,226,624,1214,1399,1560,1653,2815,3723],[85,97,143,226,1039,1048,1092,1174,1176,1188,1193,1204,1214,1227,1332,1341,1348,1354,1987,1988,2006,2007,2014,2038,2282,2815,2826,2827,2957,3385,3711,3719,3720,3721,3722],[97,143,226,1332,1986],[97,143,226,624,1988],[97,143,226,1193],[97,143,226,624,1399,3724],[85,97,143,226,1039,1048,1092,1094,1174,1176,1188,1193,1204,2282,3038,3713],[97,143,226,624,1214,1991],[97,143,226,1214,1990],[85,97,143,226,628,1035,1039,1092,1174,1176,1188,1204,1214,1991],[97,143,226,624,1399,1653,3721],[85,97,143,226,1188,1193,1204,1348,2282],[97,143,226,624,1984],[97,143,226,624,1399,2863,3725],[85,97,143,226,1035,1037,1040,1214,1361,2766],[85,97,143,226,624,1109,1214,1219,1399,1426,1479,1558,1560,1653,2863,3727],[85,97,143,226,637,1035,1039,1048,1092,1106,1109,1193,1214,1215,1219,1227,1332,1341,1348,1426,1479,1558,1956,1984,1986,2006,2014,2038,2282,2821,2827,2957,3384,3385,3711,3712,3713,3715,3719,3720,3721,3723,3724,3725,3726],[97,143,226,624,1399,1653,2863,3726],[85,97,143,226,1039,1045,1106,1115,1227],[97,143,226,624,1332,1341,1399],[85,97,143,226,1332],[97,143,226,1109,1217,1218,3727],[97,143,226,624,631,1214,1653,2863,3740],[85,97,143,226,631,1043,1091,1092,1109,1110,1178,1183,1204,1214,1937,3739],[85,97,143,226,624,628,631,1399,1653,1995,2854,3742],[85,97,143,226,628,631,1035,1036,1041,1044,1045,1082,1092,1177,1217,1340,1696,1954,1994,1995,2084,2854],[97,143,226,624,1994,1995],[97,143,226,639,1311,1994],[97,143,226,624,1994],[97,143,226,3746],[97,143,226,624,1214,1399,1653,2863,3739],[85,97,143,226,1035,1036,1037,1039,1044,1045,1046,1047,1048,1089,1091,1106,1183,1311,1700,1954,1963,1967,2084,2097,2101],[85,97,143,226,624,628,1399,1653,2863,3746],[85,97,143,226,628,631,1035,1106,1174,1176,1177,1214,1348,1452,1453,1661,1958,1959,2834,3740,3741,3742,3744,3745],[97,143,226,624,1399,1653,3745],[97,143,226,624,1399,1653,2106,3745],[85,97,143,226,631,1035,1039,1040,1044,1045,1048,1092,1106,1109,1110,1178,1193,1207,1214,1348,1501,1503,1700,1958,2106,2816,2834,2838,3739],[85,97,143,226,624,1174,1176,1214,1399,1653,3744],[85,97,143,226,1036,1039,1041,1174,1176,1188,1214,3743],[97,143,226,1034,1035,1039,1115,1174,1176,1188,1193,1204,1214,1362],[97,143,226,1109,1217,3747],[97,143,226,624,1090,1214,1399,1653,3765],[97,143,226,624,1214,1399,1653,3765],[85,97,143,226,631,1035,1036,1037,1039,1044,1045,1048,1092,1138,1214,1354,1361,1956,3421,3756,3763,3764],[97,143,226,624,1138,1399,1653,3763],[85,97,143,226,1039,1138,1188,3762],[97,143,226,1034,1035,1039,1138,1174,1176,1193,1204,1362],[97,143,226,624,1214,1399,1653,3767],[85,97,143,226,631,1035,1039,1106,1138,1210,1214,1348,2352,2834,3758,3759,3761,3765,3766],[85,97,143,226,631,1138,1209,1214],[97,143,226,624,1209,1210,1399,1653],[85,97,143,226,1039,1174,1176,1188,1208,1210],[97,143,226,1174,1176,1188,1204,1207,1210],[97,143,226,624,1090,1399,3764],[85,97,143,226,1036,1039,1040,1044,1048,1090,1956],[97,143,226,624,1138,1399,1653,3766],[85,97,143,226,1040,1092,1138,3760],[97,143,226,624,631,1214,1399,1653,3761],[85,97,143,226,624,1214,1399,1653,3761],[85,97,143,226,631,1035,1036,1037,1039,1040,1044,1045,1048,1092,1115,1138,1214,1311,1348,1352,1354,1954,2084,3756,3760],[97,143,226,624,631,1214,1399,1653,3759],[97,143,226,624,1214,1352,1399,1653,3756,3759],[85,97,143,226,631,1035,1036,1037,1038,1039,1040,1044,1045,1048,1082,1090,1110,1214,1311,1354,1954,1956,2084,3756],[97,143,226,624,1138,1399,1653,3758],[85,97,143,226,1039,1138,1174,1176,1188,3757],[97,143,226,1034,1035,1039,1138,1174,1176,1188,1193,1204,1362,3756],[97,143,226,624,631,1214,1399,1653,3760],[85,97,143,226,631,1035,1037,1039,1043,1092,1214,1361],[97,143,226,1109,3767],[97,143,226,624,1399,2863,3784],[97,143,226,1109,1414,2922,3146,3782],[97,143,226,624,1399,1653,3782],[85,97,143,226,1034,1035,1036,1039,1045,1048,1101,1174,1176,1181,1188,1214,1361],[97,143,226,2056,3792],[97,143,226,2056,3794],[85,97,143,226,2056,3798],[97,143,226,624,1399,3786],[85,97,143,226,518,1109,1206,1224,1550,2056,2061,2717],[97,143,226,2056,3800],[85,97,143,226,624,1399,2055,3790],[85,97,143,226,518,631,1035,1036,1039,1090,1177,1225,1237,1352,1683,2054,2056,2061,3498,3788,3789],[97,143,226,2056,3802],[97,143,226,624,1399,3804],[97,143,226,1109,1224,2717],[97,143,226,624,1399,3806],[85,97,143,226,1109,3798],[97,143,226,526,529,1370,2698,2699,2700,2701,2702],[97,143,226,624,628,1108,1214,1399,1653,3808],[97,143,226,624,628,634,636,1108,1214,1399,3808],[85,97,143,226,518,634,636,1035,1036,1039,1044,1045,1048,1092,1105,1108,1214,1311,1361,1494,1954,1956,2084,2353,2718,2871],[97,143,226,3808],[85,97,143,226,518,632],[85,97,143,226,518,3296],[85,97,143,226,518,3297],[85,97,143,226,624,1399,3815],[85,97,143,226,1035,1039,1105,1956],[85,97,143,226,624,1399,3819],[85,97,143,226,518,634,635,1214,1511,3815,3817,3818],[97,143,226,624,1399,1653,3818],[85,97,143,226,624,1399,1653,3818],[85,97,143,226,1034,1035,1036,1039,1044,1092,1311,1361,1954,1956,2084,2871],[85,97,143,226,624,1399,3817],[85,97,143,226,1361],[85,97,143,226,518,3819],[85,97,143,226,624,1193,1215,1332,1399,3711],[85,97,143,226,1039,1092,1101,1193,1215,1332,1986,2005,2038,2282,3710],[85,97,143,226,1036,1042,1092,1094,1102,1103,1132],[97,143,226,624,631,1090,1136,1137,1189,1214,1653,2356,2695,2863,3327,3329],[85,97,143,226,628,631,1035,1036,1039,1044,1045,1048,1082,1090,1092,1106,1110,1127,1128,1130,1132,1133,1134,1135,1136,1137,1189,1214,1311,1361,1428,1668,1951,1954,2040,2045,2084,2085,3300,3309,3327,3328],[97,143,226,624,1082,1084,1109,1214,1215,1352,1653,2863,3341],[85,97,143,226,1035,1039,1041,1044,1045,1048,1082,1084,1092,1094,1106,1109,1110,1129,1214,1215,1352,1524,1544,1668,1951,1956,2066,2085,3038,3309,3334,3335,3336,3338,3339,3340],[97,143,226,624,1399,3334,3953],[85,97,143,226,637,1036,1037,1039,1045,1048,1083,1084,1091,1094,1101,1215,1551,1969,2059,2066,2656,3303,3304,3309],[85,97,143,226,1036,1094,1132],[97,143,226,624,1214,2045,2863,3300],[85,97,143,226,1039,1214,2045],[97,143,226,624,1137,1189,2040],[97,143,226,1090,1132,1137,1189],[97,143,226,624,1136,1214,1653,2695,2863,3328],[85,97,143,226,1035,1037,1039,1115,1136,1211,1214,2043],[97,143,226,1136,2043],[97,143,226,1136,1214],[97,143,226,2045],[97,143,226,1133,1136],[97,143,226,1090,1093,1132,1133,1134,1135],[97,143,226,1130],[97,143,226,624,1399,1653,3304],[85,97,143,226,1035,1039,1042,1045,1048,1937],[85,97,143,226,1036,1039,1041,1042,1045,1048,1091,1092,1094,1097,1102,1112,1113,1114,1117,1118,1119,1120,1132,1134],[85,97,143,226,1036,1042,1094,1132],[97,143,226,624,1112,1132,1653,2695,2863],[85,97,143,226,631,1035,1037,1039,1109,1110,1111,1132,1214],[97,143,226,1111],[97,143,226,1039,1045,1048,1134],[85,97,143,226,1036,1042,1094,1136],[97,143,226,624,1093],[97,143,226,1135],[97,143,226,624,1132,1133,1134],[97,143,226,1090,1133,1135],[85,97,143,226,624,1132,1653,2695,2863],[85,97,143,226,1035,1036,1037,1038,1039,1041,1043,1048,1090,1091,1092,1094,1095,1096,1097,1098,1099,1100,1101,1104,1116,1121,1122,1123,1124,1125,1126,1127,1128,1130,1131,1133,1134,1135],[85,97,143,226,1039,1041,1042,1048,1102,1129,1130],[85,97,143,226,624,1082,1084,1399,1653,3335,3953],[85,97,143,226,1036,1048,1082,1084,1174,1176,1188,1352,2066],[85,97,143,226,1039,1048,1091],[97,143,226,631,1130,1136,1214],[97,143,226,624,3337],[97,143,226,631,1214,1352,2654],[97,143,226,624,1116,1132],[97,143,226,624,1114,1116,1117,1121,1132,1653,2695,2863],[85,97,143,226,1035,1036,1039,1042,1101,1103,1114,1115,1116,1132],[85,97,143,226,1035,1039,1045,1048,1091,1092,1093,1134],[97,143,226,624,1352,1399,3336,3953],[85,97,143,226,1036,1082,1084,1091,1352,2066,3309],[85,97,143,226,1094,1132],[97,143,226,624,631,1214,1399,3337,3338],[85,97,143,226,631,1035,1039,1043,1214,3337],[97,143,226,624,1113,1653,2863],[85,97,143,226,1035,1037,1045,1109,1110,1132,1133,1214],[97,143,226,624,628,1082,1084,1352,1399,3339,3953],[85,97,143,226,1035,1036,1037,1039,1045,1082,1084,1214,1352,1524,2066,2871,3309],[97,143,226,624,1399,1653,2087],[85,97,143,226,1035,1036,1037,1039,1041,1042,1043,1048,1090,1092,1101,1115],[97,143,226,624,1128,1653,2863],[85,97,143,226,1036,1039,1041,1048,1090,1094],[97,143,226,624,1124,1132,2863],[97,143,226,624,1133],[97,143,226,1132,1134,1135],[97,143,226,624,1125,1132],[97,143,226,1132,1133,1134,1135],[85,97,143,226,1097,1132],[97,143,226,624,1126],[85,97,143,226,1039,1045,1048,1134],[85,97,143,226,1133],[97,143,226,624,1214,1653,2863,3353],[85,97,143,226,631,1035,1036,1039,1045,1048,1082,1092,1094,1110,1214,1311,1361,1937,1954,1956,2084,3348,3349,3350,3351,3352,3357],[97,143,226,624,1399,1653,1698],[85,97,143,226,1091,1214],[97,143,226,624,1188,1399,1653,3281],[97,143,226,1034,1035,1039,1115,1174,1176,1188,1193,1204,1362],[97,143,226,624,1214,1399,3281,3282],[85,97,143,226,631,1034,1035,1039,1110,1115,1183,1214,3281],[97,143,226,624,1214,1399,3283,3284],[85,97,143,226,631,1034,1035,1039,1110,1115,1183,1214,3283],[97,143,226,624,1214,1399,3286],[85,97,143,226,631,1034,1035,1039,1110,1115,1183,1214,3285],[97,143,226,624,1188,1399,1653,3283],[97,143,226,624,1214,1653,2863,3297],[85,97,143,226,634,636,640,1035,1038,1039,1092,1105,1106,1110,1115,1174,1176,1188,1193,1214,1348,1369,1371,1550,2662,3281,3282,3283,3284,3285,3286,3287,3290,3293,3294,3296],[97,143,226,624,1188,1399,1653,3287],[85,97,143,226,640,1038,1039,1045,1174,1176,1188,3291,3292],[97,143,226,624,640,1188,1399,1653,3291],[97,143,226,640,1034,1035,1039,1115,1174,1176,1188,1193,1204,1362],[97,143,226,624,631,1214,1399,1653,3290],[85,97,143,226,508,631,1092,1106,1178,1214,1932,3289],[85,97,143,226,630,631,1214,3161],[85,97,143,226,624,1399,1653,3161],[85,97,143,226,1035,1036,1039,1082,1094,1115,1178],[97,143,226,624,1229,1230,1399],[97,143,226,1115,1229],[97,143,226,624,1399,1653,3741],[85,97,143,226,631,1035,1039,1110,1178,1214,1932,1957,1990],[85,97,143,226,1035,1039,1048,1101,1369,1371,1683,2054,2766,2809,3512,3513],[97,143,226,624,1205,2061],[97,143,226,624,1399,2061],[85,97,143,226,518,1035,1039,1043,1206,2056,2060],[97,143,226,624,1214,1399,3797],[85,97,143,226,1039,1214,3796],[97,143,226,624,628,1214,1399,3796,3798],[85,97,143,226,518,628,1214,3796,3797],[85,97,143,226,1035,1036,1039,1048,1110,1225,1347,2054,2059],[85,97,143,226,628,631,1035,1036,1039,1042,1110,1115,1177,1178,1214,1215,1957,2638],[97,143,226,624,1214,1399,2863,3800],[85,97,143,226,628,1035,1039,1110,1177,1178,1214,2303],[85,97,143,226,624,628,1086,1205,1214,1399,3796],[85,97,143,226,628,631,632,1035,1036,1039,1086,1177,1214,1348,1354,3236],[85,97,143,226,624,1086,1205,1214,1399,3789],[85,97,143,226,631,1039,1086,1094,1177,1214,1354],[85,97,143,226,628,631,1035,1039,1115,1177,1178,1214,1347],[97,143,226,1086,1683],[85,97,143,226,628,1035,1039,1177,1214],[97,143,226,624,1399,2055],[85,97,143,226,2054],[97,143,226,624,1675,3295],[97,143,226,1086,1675,1684],[85,97,143,226,1034,1039,1086,1101],[97,143,226,624,1399,3513],[85,97,143,226,1035,1039,1101,1369,1371,2766],[97,143,226,624,1399,1683],[85,97,143,226,1039,1048,1682],[97,143,226,624,640,2064],[97,143,226,640],[85,97,143,226,631,640,1034,1035,1039,1110,1115,1183,1214],[85,97,143,226,640,1034,1039,2064],[85,97,143,226,624,1399,1653,3712],[85,97,143,226,631,1035,1036,1039,1044,1045,1110,1214,1311,1361,1954,1956,2084,2871],[97,143,226,624,628,1399,3168],[85,97,143,226,628,1092,1107,1109,1465,3163,3165,3167],[97,143,226,624,628,1399,1653,3165],[97,143,226,624,628,1399,3165],[85,97,143,226,631,1035,1036,1044,1048,1109,1110,1311,1458,1954,2003,2084,3164],[97,143,226,624,1399,3163],[85,97,143,226,1038,1039,1048],[97,143,226,624,628,1399,1464,3167],[85,97,143,226,631,1035,1039,1043,1092,1109,1115,1347,1460,1462,1464,1465,1956,2834,3166],[97,143,226,624,2003],[97,143,226,624,628,1399,1464,1653,3166],[97,143,226,624,628,1399,1464,3166],[85,97,143,226,631,1035,1036,1044,1048,1109,1110,1311,1464,1465,1954,2003,2084,3164],[85,97,143,226,1039,1369,1371],[85,97,143,226,1039,1091,1177,1421],[85,97,143,226,1204,1932],[85,97,143,226,1036,1037,1039,1045,1048,1082,1083,1084,1214],[97,143,226,624,1399,2818],[97,143,226,1115],[97,143,226,624,1653,2834,2863],[85,97,143,226,1035,1038,1039,1092,1110,1956],[97,143,226,624,1399,3389],[85,97,143,226,1034,1038,1039,1452,1453],[97,143,226,624,1399,1653,3390],[85,97,143,226,1034,1035,1039],[97,143,226,624,1399,1653,3391],[85,97,143,226,1035,1039],[97,143,226,624,1399,1932,3288],[85,97,143,226,1034],[97,143,226,624,1399,1653,3289],[97,143,226,1048,1932,3288],[85,97,143,226,624,1082,1653,1701,2863],[85,97,143,226,1036,1039,1043,1045,1048,1094,1183],[97,143,226,624,1399,2819],[85,97,143,226,1034,1195,1235,2818],[97,143,226,624,1399,2718],[97,143,226,1034,1361],[85,97,143,226,1035,1039,1048,1178,1204,1214,3289],[85,97,143,226,624,1311,1399,1546,1653,2084,3311],[85,97,143,226,1035,1036,1039,1082,1177,1311,1546,1954],[97,143,155,164,226,624,1399,1653,1934],[85,97,143,226,631,1035,1036,1092,1178,1932,1933],[97,143,226,624,1399,1653,1933,2863],[85,97,143,226,1036,1039,1041,1090,1452],[85,97,143,226,624,1082,1084,1399,1653],[85,97,143,226,1044,1082],[97,143,226,624,1229,1399,3982],[97,143,226,624,1399,1653,1952],[85,97,143,226,1041,1214],[97,143,226,624,1214,1399,1653,3352],[85,97,143,226,1035,1039,1044,1048,1092,1697,1956,2843],[85,97,143,226,1092,1094],[85,97,143,155,164,226,624,1939,2863],[85,97,143,226,1115,1938],[85,97,143,226,1041,1514],[97,143,226,624,1653,1940,2863],[85,97,143,226,1039,1045,1048],[85,97,143,226,624,628,1090,1399,1945,1950],[85,97,143,226,628,1090,1214,1348,1452,1945,1947,1948,1949],[97,143,226,624,2067],[97,143,226,1950],[97,143,226,624,1399,2835],[97,143,226,1115,2067],[85,97,143,226,624,628,1399,1945,1947,1950,2067],[85,97,143,226,1178],[97,143,226,624,1215,1399,1653,1951,2863],[85,97,143,226,1215,1217,1696],[85,97,143,226,624,1217,1399,1653,2826],[85,97,143,226,1041,1217,2825],[97,143,226,624,1214,1399,1453,1653,3308],[85,97,143,226,1035,1039,1044,1045,1048,1082,1110,1214,1361,1696,1954,1956],[97,143,226,624,1214,1399,1560,1653,2827],[85,97,143,226,1041,1214,1560,1696],[97,143,226,624,628,631,1214,1218,1399,1653,1959,2673],[85,97,143,226,628,631,1035,1036,1037,1039,1044,1045,1048,1082,1089,1091,1101,1110,1183,1214,1218,1951,1954,1956,1958],[97,143,226,624,1399,2022,2715],[97,143,226,518,634,1105,1233,1242,2353,2704,2705,2706,2707,2709,2710,2712,2713,2714],[97,143,226,624,1220,2720,2863],[85,97,143,226,1039,1220,1956],[97,143,226,624,1399,1487,1653,2863,3175],[85,97,143,226,1039,1109,1174,1176,1487,1956,3174],[97,143,226,624,1399,1487,1653,2863,3174],[85,97,143,226,1039,1174,1176,1188,1487,3173],[97,143,226,1174,1176,1188,1204,1487],[97,143,226,624,1217,1399,1653,2863,3178],[85,97,143,226,1039,1109,1174,1176,1188,1217,1956,3177],[97,143,226,624,1217,1399,2863,3177],[85,97,143,226,1039,1174,1176,1188,1217,3176],[97,143,226,1174,1176,1188,1204,1217],[97,143,226,624,1399,1653,2922],[85,97,143,226,508,1039],[97,143,226,624,2088],[97,143,226,2088],[85,97,143,226,631,1035,1036,1039,1044,1048,1090,1093,1110,1116,1128,1130,1132,1133,1134,1135,1136,1214,1311,1361,1667,1954,2084,2085,2086,2087],[85,97,143,226,624,1399,1653,2091,2863],[85,97,143,226,614,631,638,1035,1043,1092,1177,1183,1214],[97,143,226,638,2091],[97,143,226,614],[85,97,143,226,624,1399,1653,2863,3159],[85,97,143,226,631,1035,1038,1039,1092,1214,2092],[97,143,226,624,1399,1653,2011,2012,2863],[85,97,143,226,631,1035,1039,1110,1177,1217,2005,2007,2008,2009,2010,2011],[97,143,226,624,2008,2863],[85,97,143,226,1045,2007],[97,143,226,2009,2863],[85,97,143,226,2006],[97,143,226,624,1653,2010,2863],[85,97,143,226,1102,2007],[97,143,226,2007,2012,2013],[97,143,226,1215,2006],[97,143,226,624,1653,2007,2013,2863],[85,97,143,226,1035,1039,1040,1215,2006,2007,2012],[97,143,226,624,1990,2006,2007,2011],[97,143,226,1193,1986,1990,2006,2007],[97,143,226,624,1214,1399,2843],[85,97,143,226,1091,1214,2093],[97,143,226,624,2863,3312],[85,97,143,226,1034,1039,1092,1115],[85,97,143,226,1039,1237,2017],[85,97,143,226,628,1035,1039,1214,1361,2016,2301,2303,2342],[85,97,143,226,2863,3138],[85,97,143,226,2017],[97,143,226,624,2017],[85,97,143,226,624,1368,1399,1653,2863],[97,143,226,624,2292],[97,143,226,624,1197],[97,143,226,624,1399,1653,1960],[85,97,143,226,1035,1039,1041,1091],[85,97,143,226,1035,1038,1045],[97,143,226,624,1089],[97,143,226,624,2095],[97,143,226,1214,1215],[85,97,143,226,614,1046,1047,1214],[97,143,226,624,1046,2863],[97,143,226,624,1046],[85,97,143,226,1035,1038,1039,1041,1044,1045],[97,143,226,624,2097],[97,143,226,1046],[85,97,143,226,624,1399,1653,1962],[85,97,143,226,1035,1036],[97,143,226,624,2099],[97,143,226,1215],[97,143,226,1046,2097,2101],[85,97,143,226,624,1399,1653,3348],[85,97,143,226,1035,1036,1039],[97,143,226,624,1106,1242,1399,2863],[85,97,143,226,508,518,1034,1035,1039,1106,1109,1115,1206,1214,1217,1219,1220,1223,1224,1225,1226,1227,1230,1238,1241],[85,97,143,226,624,1214,1399,2722],[85,97,143,226,1035,1039,1214,1239,1240,1956],[97,143,226,624,1683,3403],[97,143,226,1086,1214,1682,1683,1684,2991,3037],[97,143,226,624,1090,1214],[97,143,226,1089,1214],[97,143,226,624,1086,2103],[97,143,226,624,1683,1684,3498],[97,143,226,631,1086,1214,1682,1683,1684,1687,3037],[97,143,226,624,1399,2836],[85,97,143,226,1115,1354,1932,1936],[97,143,226,624,1086,1966],[97,143,226,1086,1311],[97,143,226,1086,1088],[97,143,226,624,1088,1399,1497,1501,1503,1653,1963,2863],[85,97,143,226,1088,1091,1497,1501,1503],[85,97,143,226,624,1086,1088,1214,1399,1653,1967,2863],[85,97,143,226,1086,1088,1102,1214,1361,1497,1501,1503,1964,1965,1966],[97,143,226,624,631,1086,1213,1399,1653,3230],[85,97,143,226,630,631,1039,1086,1094,1110,1340,2871],[85,97,143,226,1039,1183,1964],[85,97,143,226,624,1086,1399,1653,3422],[85,97,143,226,1036,1037,1039,1044,1045,1048,1082,1086,1954],[97,143,226,624,1086],[97,143,226,624,1352,2107],[97,143,226,624,628,1214,1352,1399,3343],[85,97,143,226,1035,1036,1041,1048,1082,1084,1110,1214,1352,1354,2066,2107,3309,3339],[97,143,226,624,628,631,1214,1399,1653,3346],[85,97,143,226,631,1035,1039,1106,1109,1214,1477,2644,2834,3343,3345],[97,143,226,624,1214,1399,1653,3345],[85,97,143,226,1039,1174,1176,1188,1214,3344],[97,143,226,1034,1035,1039,1174,1176,1188,1193,1204,1214,1352,1362],[97,143,226,624,1214,1653,2863,3301],[85,97,143,226,1035,1036,1044,1048,1110,1214,1311,1954,2084],[85,97,143,226,624,1174,1176,1399,1653,3361],[85,97,143,226,1035,1110,1174,1176,1214,1215,2376,3359,3360],[85,97,143,226,624,1174,1176,1399,1653,3359,3360],[85,97,143,226,1039,1174,1176,1188,1215,3359],[97,143,226,1034,1039,1174,1176,1188,1204,1215],[97,143,226,624,631,1399,1526,1541,1653,2842,2863,3322],[85,97,143,226,631,1035,1039,1044,1048,1082,1094,1110,1177,1526,1541,1954,2842],[85,97,143,226,1092,2662],[85,97,143,226,631,1092,1178,1214,1932],[85,97,143,226,624,628,631,1214,1399,1653,2695,3307],[85,97,143,226,628,631,637,1035,1039,1048,1083,1092,1110,1134,1189,1193,1214,1217,1348,1354,1508,1551,1667,1668,1673,1932,2045,2088,2644,2657,2834,3300,3301,3302,3305,3306],[85,97,143,226,637,1035,1036,1037,1039,1045,1048,1082,1094,1115,1214,1311,1361,1697,1937,1954,1969,2059,2083,2644,2654,2656,3303,3304],[97,143,226,624,1189,1214,1217,1218,1399,1558,1653,2854,2863],[97,143,226,1040,1048,1177,1189,1214,1217,1218,1558,2019],[97,143,226,624,2019],[97,143,226,624,1399,3299],[85,97,143,226,624,1352,1354,1399],[85,97,143,226,1034,1351,1352,1353],[85,97,143,226,624,1352,1399,3038],[85,97,143,226,1354],[85,97,143,226,631,1035,1038,1039,1092,1214],[85,97,143,226,624,634,1228,1653,2717,2863],[85,97,143,226,508,634,1034,1039,1105,1115,1206,1214,1220,1222,1224,1232,1233,2353,2706,2707,2709,2710,2712,2713,2714,2716],[97,143,226,624,1653,2706,2863],[85,97,143,226,1035,1039,1231,1362,1429,2022],[97,143,226,624,2709,2863],[85,97,143,226,1034,1035,1039,1048,1233,2708],[97,143,226,624,1399,2022,2707],[85,97,143,226,1039,2022],[97,143,226,624,1234],[85,97,143,226,1653,2710,2863],[85,97,143,226,1034,1035,1039,1115,1228,1237,1417],[97,143,226,624,1228,1653,2716,2863],[85,97,143,226,1034,1039,1043,1048,1094,1109,1115,1228,1231,1232,1233,1234,1235,1236,1237],[97,143,226,624,1399,2712],[85,97,143,226,518,1039,1206,1362,1550,2711],[97,143,226,624,1399,1653,2714],[85,97,143,226,1038,1039,1040,2353],[97,143,226,624,631,634,1206,1214],[97,143,226,630,631,634,636,637,638,639,640,1046,1047,1085,1086,1087,1088,1136,1137,1205,1210,1211,1212,1213,1215],[97,143,226,624,628,1220,2721,2863],[85,97,143,226,1039,1220],[85,97,143,226,1047,2120,2837,2838,2839],[97,143,226,624,1958],[85,97,143,226,631,1035,1110,1957],[97,143,226,624,631,1214,1215,1653,1972,2863],[97,143,226,624,1214,1972],[85,97,143,226,628,631,1035,1036,1037,1039,1041,1044,1045,1046,1048,1082,1084,1085,1089,1091,1094,1101,1102,1106,1109,1110,1115,1193,1214,1215,1218,1414,1487,1514,1544,1550,1696,1697,1698,1699,1700,1701,1934,1935,1937,1939,1940,1950,1951,1952,1953,1959,1960,1961,1962,1963,1967,1968,1969,1970,1971],[97,143,226,624,1970],[97,143,226,1046,1700,1936,1950,1961,1962],[97,143,226,624,1215,1653,2841,2863],[97,143,226,624,631,1215,1653,2841,2863],[85,97,143,226,631,1035,1036,1039,1044,1048,1082,1109,1110,1214,1215,1311,1954,1956,1957,2084,2111,2638],[97,143,226,624,2111],[97,143,226,624,1971],[97,143,226,624,2115],[97,143,226,639,1311,2114],[85,97,143,226,624,628,1399,1653,3395],[85,97,143,226,628,631,1035,1036,1037,1044,1045,1110,1218,1340,1954,1963,1969,2084,2114,2115,2854,3394],[97,143,226,624,1214,2117],[97,143,226,639,1214,1311,2114],[85,97,143,226,624,628,1214,1399,1653,3394],[85,97,143,226,628,631,1035,1036,1037,1044,1045,1214,1218,1340,1954,1963,1969,2084,2114,2117,2361,2854],[85,97,143,226,624,1218,1399,1653,2863,3396],[85,97,143,226,628,631,1035,1039,1092,1193,1204,1207,1214,1217,1218,1235,1348,2005,2352,2816,2840,3308,3314,3318,3394],[97,143,226,624,1106,1242,2000,2001],[97,143,226,1106,1242,2000],[97,143,226,624,1214,1653,2863,3354],[85,97,143,226,631,1035,1036,1037,1038,1039,1045,1082,1092,1094,1115,1214,1311,1348,1954,2084,3350,3351,3352],[97,143,226,624,1399,1653,3356,3357],[85,97,143,226,1039,1188,3355,3357],[85,97,143,226,1034,1035,1039,1115,1174,1176,1204,1362,3357],[97,143,226,624,1214,1399,1653,3356,3357],[85,97,143,226,631,1035,1214,3353,3354,3356],[97,143,226,624,1214,1399,1653,3714],[85,97,143,226,1174,1176,1188,1214,1348,2282],[97,143,226,624,1214,1399,1653,2839],[85,97,143,226,1048,1115,1214,1932,2120],[97,143,226,624,2119,2120],[97,143,226,2119],[97,143,226,624,1088,1214,1399,1653,2838],[85,97,143,226,1048,1086,1088,1115,1214,1932,1966,2120],[85,97,143,226,1115,1214,1932],[97,143,226,624,1214,1399,1689,2844,2863],[85,97,143,226,1091,1214,1414,1689],[97,143,226,624,631,1214,1399,1653,3370],[85,97,143,226,631,1034,1035,1038,1039,1043,1048,1092,1110,1115,1214,1347],[97,143,226,624,1352],[97,143,226,530,1351],[97,143,226,624,628,1174,1176,1214,1399,1653,2124,3296],[85,97,143,226,631,640,1039,1040,1048,1091,1092,1110,1115,1174,1176,1188,1214,1224,1348,1352,1675,1684,1932,2122,2124,2125,2126,2662,2717,3293,3295],[97,143,226,624,1174,1176,2122],[97,143,226,628,639,1214,2125],[85,97,143,226,1174,1176,1214,1454,2122,2124],[97,143,226,1115,1174,1176,1188,1204,1352,2122],[85,97,143,226,624,1399,1653,3348,3349],[85,97,143,226,1035,1036,1039,3348],[97,143,226,624,1399,3350],[85,97,143,226,1039,1092,1214],[97,143,226,624,631,1214,1653,2863,2960],[85,97,143,226,631,1035,1214,1945],[97,143,226,624,1399,1941],[97,143,226,624,1399,1942],[97,143,226,624,1399,1653,1945],[85,97,143,226,1941,1942,1943,1944],[97,143,226,624,1399,1653,1943],[97,143,226,624,1399,1653,1944],[85,97,143,226,1094],[97,143,226,624,631,1399,1531,1532,1653,3045],[85,97,143,226,631,1035,1038,1039,1092,1109,1110,1189,1222,1528,1531,1532,3043,3044],[97,143,226,624,1531,1653,2863,3044],[85,97,143,226,1035,1036,1037,1040,1044,1045,1082,1110,1311,1531,1954,2084,2127],[97,143,226,624,1531,2127],[97,143,226,1531],[97,143,226,624,1399,1531,1653,3043],[85,97,143,226,1039,1174,1176,1188,1531,3041,3042],[97,143,226,1034,1035,1039,1174,1176,1188,1204,1362,1531,2129],[85,97,143,226,1039,1348,1372,1531,2129],[97,143,226,624,631,1214,1399,1653,2863,2869],[85,97,143,226,631,1035,1036,1039,1043,1044,1092,1214,1311,1361,1954,1956,1957,2084,2842],[97,143,226,624,1214,1653,2863,3313],[85,97,143,226,1034,1040,1214],[97,143,226,624,1082,1214,1399,1653,3171],[85,97,143,226,631,1035,1036,1040,1044,1082,1092,1094,1110,1178,1214,1348,1354,2033,2834,2842,3159,3160,3162,3168,3170],[97,143,226,624,1399,1653,2863,2886],[85,97,143,226,631,1035,1039,1092,1109,1177,1468,1470,1471,1474,1956,2023,2834,2884,2885],[97,143,226,624,1399,1470,1474,1653,2863,2885],[85,97,143,226,631,1035,1036,1043,1044,1109,1110,1311,1361,1470,1474,1954,2023,2084,2871],[97,143,226,624,1399,1472,1475,1653,2863,2887],[85,97,143,226,631,1035,1036,1043,1044,1109,1110,1311,1361,1472,1475,1954,2024,2084,2871],[97,143,226,624,1399,1653,2863,2889],[85,97,143,226,631,1035,1039,1092,1109,1177,1469,1472,1473,1475,1956,2024,2834,2887,2888],[97,143,226,624,1399,1653,2888],[97,143,226,624,631,1399,1526,1543,1653,2842,2863,2870],[85,97,143,226,631,1035,1036,1038,1039,1044,1048,1082,1092,1094,1177,1361,1526,1543,1954,2842],[85,97,143,226,624,1090,1399,1495,1496,1653,3226],[85,97,143,226,631,1035,1036,1039,1041,1044,1048,1082,1090,1092,1094,1103,1177,1361,1495,1496,1954,1956,2025,3225],[85,97,143,226,624,1399,1653,2025,3225],[97,143,226,1035,1037,1039,1092,1348,1933,1956,2025],[97,143,226,624,631,1214,2025],[85,97,143,226,624,1399,1504,3227],[85,97,143,226,631,1035,1036,1037,1039,1041,1044,1048,1082,1090,1092,1103,1177,1361,1504,1954,1956,2027],[97,143,226,624,2027],[97,143,226,1504],[97,143,226,624,1399,1653,2890],[85,97,143,226,1035,1036,1038,1039,1044,1092,1109,1110,1178,1214,1361,1954,2029,2084],[97,143,226,624,1399,2863,2873],[85,97,143,226,631,1035,1110,1361,1537,2031,2842,2872],[97,143,226,624,1399,1653,2673,2863,2872],[85,97,143,226,1036,1037,1044,1045,1082,1183,1311,1354,1954,2030,2084,2871],[97,143,226,624,628,1399,2874],[85,97,143,226,631,1537,1539,2031,2834,2842],[97,143,226,624,1399,1537,1539,1653,2863,2875],[97,143,226,624,631,1399,1537,1539,2031,2842,2872,2875],[85,97,143,226,631,1035,1110,1361,1537,1539,2031,2842,2872],[97,143,226,624,1399,1653,2863,2876],[97,143,226,624,1399,1539,2863,2877],[97,143,226,1039,1043,1092,1174,1176,1188,1203,1539,2030],[97,143,226,624,628,1399,2880],[85,97,143,226,1035,1039,1092,1115,1193,1354,1539,2030,2031,2873,2874,2875,2876,2877,2878,2879],[97,143,226,624,1399,2878],[97,143,226,624,1399,2863,2879],[97,143,226,1039,1092,1177],[97,143,226,624,1539,2031],[97,143,226,1539],[97,143,226,624,1653,2863,2881],[85,97,143,226,1035,1039,1101,1115,1183,2001],[97,143,226,624,631,1399,2882],[97,143,226,631,1043,1092,1094,1109,1177,1550,1554,1956,2881],[97,143,226,624,1214,1399,1556,1557,2863,2883],[85,97,143,226,631,1035,1037,1042,1045,1092,1094,1109,1177,1214,1556,1557,1956,2810],[97,143,226,624,1399,1653,3170],[85,97,143,226,1035,1039,1188,2033,3169],[97,143,226,1034,1035,1039,1174,1176,1204,1362,2033],[97,143,226,624,631,1090,1399,1653,1949],[85,97,143,226,631,1035,1090,1361,1946,1947,1948],[97,143,226,624,1399,1946],[85,97,143,226,1039,1110],[97,143,226,624,628,1090,1399,1653,3039],[85,97,143,226,628,631,1035,1039,1090,1946,1947],[85,97,143,226,1039,1041,1091],[97,143,226,624,628,1090,1214,1399,1653,3040],[85,97,143,226,631,1039,1048,1106,1178,1214,1508,1949,2834,3037,3038,3039],[97,143,226,624,631,1399,1653,1947,1948],[85,97,143,226,631,1035,1039,1348,1947],[97,143,226,624,1399,1653,2821],[85,97,143,226,1034,1035,1039,2006,2303],[97,143,226,624,1399,1956],[85,97,143,226,1034,1955],[97,143,226,624,1399,1653,2816],[85,97,143,226,1034,1115,1195],[97,143,226,624,1399,3713],[85,97,143,226,624,1399,2277],[85,97,143,226,1034,2130,2274,2275,2276],[85,97,143,226,624,1399,2278],[85,97,143,226,624,1399,2279],[85,97,143,226,2130,2276],[85,97,143,226,624,1399,2276],[85,97,143,226,2274],[85,97,143,226,624,1399,2280],[97,143,226,2130,2276,2277,2278,2279,2280,2281],[85,97,143,226,624,1399,2281],[97,143,226,624,1235,1653,2863],[97,143,226,624,631,1399,1653,1968],[85,97,143,226,631,1035,1957],[85,97,143,226,1174,1175,1176],[97,143,226,1174,1176,1180],[85,97,143,226,624,1174,1176,1180,1185,1187,1399,1653,2863],[85,97,143,226,1034,1039,1174,1175,1176,1177,1178,1179],[85,97,143,226,624,1174,1176,1180,1182,1186,1399,1653],[85,97,143,226,1035,1042,1174,1176,1181],[97,143,226,624,1179,1399,1653],[97,143,226,1034,1035,1039,1045],[85,97,143,226,624,1174,1176,1188,1399,1653],[97,143,226,1174,1176,1183],[85,97,143,226,624,1174,1176,1187,1399,1653,2863],[85,97,143,226,809,1034,1039,1174,1176],[85,97,143,226,624,1174,1176,1180,1186,1399,1653],[85,97,143,226,1034,1035,1036,1039,1115,1174,1176,1185],[97,143,226,809,1035,1039,1174,1176],[97,143,226,1175,1176,1179,1180,1182,1184,1185,1186,1187],[85,97,143,226,1174,1176],[97,143,226,624,1195,1399,1653],[85,97,143,226,518,1034,1039],[85,97,143,226,624,1036,1082,1311,1399,1653,1954,2083],[85,97,143,226,1039,1048],[85,97,143,226,1034,1036,2059,2653],[97,143,226,624,1200,1399,1653],[97,143,226,1048,1193,1214,1215],[97,143,226,624,1201,1399],[85,97,143,226,866,1033,1034],[97,143,226,624,1091,1399,1653],[97,143,226,624,2814,2863],[85,97,143,226,2705],[85,97,143,226,624,1041,1399,1653,2825],[85,97,143,226,1039,1040,1041,1695],[85,97,143,226,624,1041,1399,1653,1696,2863],[97,143,226,624,1399,2957],[97,143,226,1035,1039,1956],[85,97,143,226,1038,1039],[85,97,143,226,1193,1332,1333,2822],[97,143,226,624,1041,1399,1653,2863],[97,143,226,624,1226,1399],[85,97,143,226,759,1033,1034],[85,97,143,226,1039,1092,1237],[97,143,226,624,1189,1190,1399],[85,97,143,226,1034,1039,1115,1189],[85,97,143,226,1048],[97,143,226,624,1192,1399],[97,143,226,1191],[97,143,226,624,1193,1194,1399,1653],[85,97,143,226,1034,1039,1191,1193],[97,143,226,624,1196,1399,1653],[85,97,143,226,1034,1039,1195],[97,143,226,1190,1191,1192,1194,1196,1198,1199,1202,1203],[97,143,226,624,1198,1399,1653],[97,143,226,1089,1115,1191,1197],[97,143,226,624,1199,1399],[97,143,226,624,1202,1399],[97,143,226,1193,1200,1201],[97,143,226,624,1203,1399,1653],[85,97,143,226,1034,1115,1191,1195],[97,143,226,624,1399,2705],[97,143,226,1034,1043],[85,97,143,226,1452,1453],[97,143,226,624,1228,1238,1653,2863],[85,97,143,226,1034,1035,1039,1043,1094,1109,1115,1220,1228,1229,1231,1232,1233,1234,1235,1236,1237],[85,97,143,226,624,628,1214,1239,1241,1399,1653],[97,143,226,628,1035,1039,1101,1201,1214,1239,1240],[97,143,226,624,631,1214,1399,1653,2872,2891],[85,97,143,226,631,1035,1044,1082,1110,1214,2842,2872],[97,143,226,624,1399,3423],[85,97,143,226,637,1091,1214],[97,143,226,624,1214,1399,1653,2863,3689,3691],[85,97,143,226,631,1214,3689,3690],[85,97,143,226,1039,1174,1176,1188,3689],[97,143,226,1034,1035,1039,1174,1176,1188,1204,1362],[85,97,143,226,1938],[97,143,226,624,1399,2863,3314],[85,97,143,226,1035,1036,1044,1045,1091,1110,1311,1361,1700,1937,1954,2084,2283],[85,97,143,226,1039,1040],[85,97,143,155,164,226,624,1653,1938,2863],[85,97,143,226,1035,1036,1038,1039,1043,1045,1048,1092,1115,1354,1932,1936,1937],[97,143,226,624,1214,1399,2863,3316],[85,97,143,226,631,1035,1039,1092,1178,1183,1214,3315],[97,143,226,624,2283],[97,143,226,624,2288,2863,3317],[85,97,143,226,1039,1048,1092,1115,1193,2288,2371],[97,143,226,624,3315],[97,143,226,624,2285],[97,143,226,624,631,1086,1189,1214,1217,1218,1399,1421,1487,1501,1503,1546,1558,1653,1966,2863,3321],[85,97,143,226,628,631,1035,1036,1037,1039,1041,1044,1047,1048,1082,1086,1089,1091,1092,1094,1101,1106,1109,1115,1129,1193,1203,1207,1214,1218,1311,1348,1361,1414,1421,1501,1503,1546,1697,1698,1699,1700,1932,1934,1935,1937,1950,1954,1963,1966,1967,1969,2084,2119,2120,2285,2289,2352,2640,2816,2834,2836,2840,2846,2854,3308,3309,3310,3311,3312,3313,3314,3316,3317,3319,3320],[97,143,226,624,1106,1109,1399,1550,1653,2863,3319,3321],[85,97,143,226,1039,1048,1106,1109,1193,1204,1214,1550,3318,3321],[97,143,226,624,2119],[97,143,226,624,1214,1215,1487,1653,2863,3320],[85,97,143,226,1036,1039,1048,1089,1115,1174,1176,1188,1197,1204,1214,1215,1235,1452,1453,1487,2817,2818,2848],[85,97,143,226,624,628,631,1089,1214,1399,1546,1653,1659,2863,3695],[85,97,143,226,628,630,631,1035,1036,1037,1039,1041,1044,1048,1089,1094,1101,1106,1110,1214,1215,1217,1218,1311,1348,1414,1546,1661,1697,1698,1699,1700,1934,1935,1937,1939,1950,1954,1963,1967,1969,2084,2119,2814,2834,2854,3309,3311,3313,3321,3691,3692,3694],[97,143,226,624,1215,1217,2034],[97,143,226,1214,1215,1217,1990],[85,97,143,226,624,1215,1217,1399,1653,2863,3694],[85,97,143,226,1035,1036,1039,1041,1109,1174,1176,1188,1215,1217,1218,1452,1453,2034,3693],[97,143,226,1034,1035,1039,1174,1176,1177,1188,1193,1204,1214,1215,1362],[85,97,143,226,624,631,1214,1653,2863,3692],[85,97,143,226,631,1035,1036,1038,1039,1040,1089,1092,1115,1214,1218,1361,1700,1952,2854],[97,143,226,624,2289],[85,97,143,226,624,1046,1214,1215,1399,1653,2847,2863],[85,97,143,226,631,637,1035,1036,1037,1044,1045,1046,1048,1089,1091,1094,1106,1214,1215,1218,1227,1361,1514,1550,1697,1698,1699,1700,1701,1935,1936,1937,1940,1950,1952,1954,1960,1961,1962,1963,1967,1969,1972,2067,2084,2102,2289,2291,2293,2843,2844,2845,2846],[97,143,226,624,1109,1214,1215,1218,1399,1653,2815,2848,2863],[97,143,226,624,628,1109,1214,1215,1342,1399,1501,1503,1653,2815,2848,2863],[85,97,143,226,628,631,1035,1039,1092,1106,1109,1110,1115,1193,1195,1200,1207,1214,1215,1218,1342,1348,1487,1490,1491,1501,1503,1514,1550,1936,2067,2106,2292,2640,2815,2816,2820,2824,2832,2833,2834,2835,2836,2840,2841,2842,2847],[97,143,226,624,1399,2832,2863],[85,97,143,226,1342,2831],[97,143,226,624,2293],[97,143,226,1215,1311,1936,2289,2291,2292],[85,97,143,226,1039,1045,1048,1082,1937,1954,2293],[97,143,226,624,1399,1653,2820],[85,97,143,226,1035,1039,1043,1048,1115,1195,1207,1235,1362,2817,2818,2819],[85,97,143,226,624,628,631,1106,1399,2848],[97,143,226,624,1332,1342,1399,2824],[85,97,143,226,1092,1106,1333,1342,1348,2282,2821,2823],[97,143,226,624,1370,1399,1653,2713],[85,97,143,226,1035,1039,1370],[97,143,226,624,628,1214,1399,1653,3698],[85,97,143,226,628,1035,1039,1040,1115,1214,1361,1951,2016,3141,3697],[97,143,226,624,2863,3697],[85,97,143,226,1034,1045],[85,97,143,226,624,628,631,1214,1399,1653,2863,3701],[85,97,143,226,628,631,1214,1414,2036,3138,3700],[85,97,143,226,624,1214,1399,1653,2863,3700],[85,97,143,226,1039,1045,1174,1176,1188,1214,3697,3699],[97,143,226,624,1174,1176,1214,1399,1653,3699],[97,143,226,1048,1174,1176,1188,1204,1214,3697],[85,97,143,226,624,1399,1653,2863,3702],[85,97,143,226,1414,3698,3701],[97,143,226,624,1035,1347,1399,1653],[85,97,143,226,719,1034,1035],[85,97,143,226,1033,1034],[97,143,226,624,1236,1399],[85,97,143,226,757,1034],[97,143,226,624,1115,1399],[97,143,226,859,1028,1033,1034],[97,143,226,624,1399,2704],[97,143,226,859,1028,1033,1034,1043],[85,97,143,226,624,1035,1399],[97,143,226,759,1033,1034],[85,97,143,226,624,1399,2275],[85,97,143,226,1034,2274],[97,143,226,763,1034,1039],[97,143,226,769],[85,97,143,226,1029,1034,1035,1038,1039],[85,97,143,226,818,1034,1035,1039],[85,97,143,226,809,1034,1039],[85,97,143,226,624,1044,1399,2673],[85,97,143,226,1033,1034,1042,1043],[97,143,226,925,1034],[85,97,143,226,1033,1034,1035,1036,1037],[85,97,143,226,912,1034],[97,143,226,936,938,1034],[85,97,143,226,624,1035,1036,1037,1042,1043,1092,1115,1177,1178,1361,1399,2275],[85,97,143,226,946,1034],[97,143,226,624,1045,1399,1653],[85,97,143,226,966,1034,1039],[97,143,226,780,1034],[97,143,226,1034],[97,143,226,976,1034],[97,143,226,629,1039,1370],[97,143,226,980,1034],[97,143,226,987,1033,1034],[97,143,226,624,1048,1399,1653],[85,97,143,226,1026,1034,1039],[97,143,226,624,1361,1399],[85,97,143,226,1034,1360],[97,143,226,624,1214,1399,1653,2892],[85,97,143,226,631,1035,1036,1039,1044,1045,1048,1082,1214,1311,1361,1954,2084],[97,143,226,624,1214],[97,143,226,624,631,1214,1399,1653,3302],[85,97,143,226,631,1035,1039,1044,1110,1214,1311,1361,1954,1956,2084,2871],[97,143,226,624,1109,1214,1215,1399,1653,2099,3385],[85,97,143,226,1042,1048,1102,1109,1188,1193,1204,1214,1332,1932,2099,2282,2848],[97,143,226,624,1332,1399,1653,3710],[85,97,143,226,1092,1174,1176,1188,1193,1204,1332,2282],[97,143,226,1986],[97,143,226,1332],[97,143,226,624,2038],[97,143,226,624,1214,1399,3715],[85,97,143,226,1040,1048,1092,1214,1348,2006,2282,3713,3714],[97,143,226,624,1214,1399,1556,2810,2863],[85,97,143,226,1035,1039,1214,1556,1956,2766,2809],[97,143,226,624,1091,1138,1399,1969],[85,97,143,226,1091,1138,1214],[97,143,226,624,1352,3756],[97,143,226,530,1352],[97,143,226,624,1399,1653,2303,3179,3181],[85,97,143,226,1035,1039,1181,1203,1235,2303,2818,3179],[97,143,226,624,628,1214,1399,1653,2863,3182],[85,97,143,226,628,1174,1176,1214,1351,1452,1453,3179,3180,3181],[97,143,226,624,1174,1176,1399,1653,3179,3180],[85,97,143,226,1036,1039,1045,1174,1176,1188,3179],[97,143,226,1174,1176,1204,2818],[97,143,226,624,1399,2313],[85,97,143,226,624,1653,2312,2863],[85,97,143,226,1039,1101,1193],[85,97,143,226,1039,1048,1092,1115,1178],[97,143,226,2298],[85,97,143,226,624,2298,2299,2863],[85,97,143,226,1048,1214],[85,97,143,226,624,1653,2299,2310,2863],[85,97,143,226,1048,1193,2298,2307,2308,2309],[85,97,143,226,624,1653,2299,2307,2863],[97,143,226,624,1399,1653,2863,3191],[85,97,143,226,1348,1361,1414,3175,3178,3182,3190],[85,97,143,226,624,628,1174,1176,1214,1215,1399,2095,2303,3183],[97,143,226,628,1106,1174,1176,1214,1215,2095,2301,2303,2868],[97,143,226,624,1399,2302],[97,143,226,1034,1115],[85,97,143,226,624,1399,1653,2332],[85,97,143,226,1039,1101],[97,143,226,624,1399,2301,2305,2863],[85,97,143,226,1035,1039,1048,1115,1204,1352,2300,2301,2302,2303,2304],[85,97,143,226,624,1399,1653,2329,2335],[85,97,143,226,1039,1101,2329,2334],[97,143,226,2340,2341],[85,97,143,226,624,1399,1653,2329,2336],[85,97,143,226,631,2329,2331,2332,2334,2335],[97,143,226,624,1370,1399,2318,2319],[97,143,226,526,1370,2300,2318],[97,143,226,624,1399,1653,2301,2340],[85,97,143,226,1035,1039,1048,1092,1101,1115,1193,1211,1348,1361,1682,2300,2301,2303,2310,2311,2312,2313,2314,2315,2316,2319,2320,2328,2339],[97,143,226,624,628,1204,1214,1399,2301,2341],[85,97,143,226,628,1039,1181,1193,1204,1214,1348,1493,2295,2297,2300,2301,2302,2304,2305,2306,2320,2340],[85,97,143,226,624,1399,1653,2329,2337],[85,97,143,226,631,2300,2329,2331,2334],[97,143,226,2329],[85,97,143,226,624,1399,2339],[97,143,226,2330,2336,2337,2338],[85,97,143,226,624,1399,1653,2338],[85,97,143,226,1039,1048,1115,2331],[85,97,143,226,624,1211,1399],[97,143,226,1034,1039,1115],[97,143,226,624,1399,1653,2331],[97,143,226,1034,1035,1039,1048],[97,143,226,1034,1035,1039],[85,97,143,226,624,1399,2334],[97,143,226,1034,2329,2333],[85,97,143,226,624,1399,2333],[97,143,226,1034,2329],[97,143,226,624,1399,2316],[97,143,226,624,1399,2315],[97,143,226,1048,1235,2300],[85,97,143,226,2300,2301],[97,143,226,624,2320],[97,143,226,624,2303,3184],[97,143,226,2303],[85,97,143,226,1035,1036,1039,1094,1237,2295,2303,3184],[85,97,143,226,624,1189,1399,1485,1533,1535,1653,2295,2863,3183,3186],[85,97,143,226,1036,1040,1041,1045,1188,1189,1215,1485,1533,1535,1696,2295,3183],[97,143,226,624,628,1214,1399,1452,1453,1653,1659,2301,2303,2863,3190],[85,97,143,226,628,1174,1176,1188,1204,1214,1215,1452,1453,2296,2301,2303,2342,2848,3183,3185,3189],[85,97,143,226,1039,1174,1176,1188,1215,2301,3183,3186,3188],[97,143,226,624,1188,1399,1653,2301,3188],[97,143,226,1174,1176,1188,1193,1204,1352,2295,2301,3187],[97,143,226,1178,2321],[97,143,226,2321,2322,2327],[97,143,226,2321],[85,97,143,226,1348,2321,2323,2324],[85,97,143,226,1034,1039,1115,2321,2325],[97,143,226,624,1399,1653,2301,2322,2327],[85,97,143,226,1039,1101,2301,2322,2326],[97,143,226,624,2301,2322],[97,143,226,2301,2321],[97,143,226,624,1399,3187],[97,143,226,2295],[85,97,143,226,1039,1101,1352],[85,97,143,226,1109,1193,1214],[97,143,226,1039,1174,1176,1177,1188,1200,1204,1214,1215,2817,2818],[85,97,143,226,624,1214,1215,1399,1487,1488,1653,1659,2815,2849,2850,2863],[85,97,143,226,1036,1039,1041,1174,1176,1188,1215,1217,1218,1452,1453,1487,1488,1661,2814,2848,2849],[85,97,143,226,634,635,636,1106,1214],[85,97,143,226,518,2054,2055],[97,143,226,624,1399,2711],[85,97,143,226,630,1214],[97,143,226,628],[85,97,143,226,1214],[97,143,226,2349],[97,143,226,2345,2346,2347,2348,2350],[85,97,143,226,624,628,631,1214,1399,2354],[97,143,226,628,631,1214],[97,143,226,624,632,1214,1399,3216],[85,97,143,226,631,632,1214,2377,2650],[85,97,143,226,624,1369,1370,1371,1399],[85,97,143,226,1369,1370],[85,97,143,226,1086,1214],[97,143,226,624,1214,1399,3235],[85,97,143,226,631,632,633,1214,2351,2377,2650],[85,97,143,226,631,632,1214,2351,2377,2650],[85,97,143,226,1108,1214],[97,143,226,624,1351],[97,143,226,1205,1212],[97,143,226,624,1127,1128,1137,2356],[97,143,226,1093,1127,1128,1132,1134,1135,1136],[97,143,226,624,1034],[97,143,226,1030,1031,1033],[97,143,226,624,1082,1399,2361],[97,143,226,1082],[97,143,226,624,2363],[85,97,143,226,624,1311,1399,1653,2084],[97,143,226,1082,1301,2083],[97,143,226,624,1213,1340],[97,143,226,630,639,1212,1213,1338,1339],[97,143,226,624,630],[97,143,226,624,1212],[97,143,226,624,1213],[97,143,226,1212],[97,143,226,624,639,1981,1982],[97,143,226,624,1353],[97,143,226,1351],[97,143,226,624,630,631],[85,97,143,226,629,630],[97,143,226,2059],[97,143,226,624,1106,1227],[97,143,226,1106],[97,143,226,624,633,634],[97,143,226,633],[97,143,226,624,631,1193],[97,143,226,631],[97,143,226,624,1207],[97,143,226,624,2377],[97,143,226,624,635,636],[97,143,226,635],[97,143,226,624,2638],[97,143,226,2637],[97,143,226,624,2640],[97,143,226,624,1240],[97,143,226,624,1228],[97,143,226,624,2645],[97,143,226,624,633],[97,143,226,632],[97,143,226,624,1964],[97,143,226,624,1214,1668],[97,143,226,1106,1214],[97,143,226,624,1682],[97,143,226,624,1214,1221],[97,143,226,624,2059,2654],[97,143,226,2059,2653],[97,143,226,624,2059,2653,2657],[97,143,226,2059,2654,2656],[97,143,226,624,2656],[97,143,226,1105],[97,143,226,624,1106,1214],[97,143,226,624,2662],[97,143,226,624,1419],[97,143,226,624,1215,2005],[97,143,226,624,1083],[97,143,226,624,1205,1206],[97,143,226,1205],[85,97,143,226,624,628,1105,1399,2700,2852],[97,143,226,2671,2683],[97,143,226,2671,2685],[97,143,226,2671,2687],[97,143,226,2671,2689],[97,143,226,2671,2691],[97,143,226,2671,2693],[97,143,226,624,2671],[97,143,226,624],[97,143,226,2674],[97,143,226,624,2676],[97,143,155,164,226,624,1137],[97,143,226,624,1399],[85,97,143,226,624,628,1399,1653,1659],[97,143,226,624,1109,1332,2863,3385],[97,143,164,226,612]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"8fd575e12870e9944c7e1d62e1f5a73fcf23dd8d3a321f2a2c74c20d022283fe","impliedFormat":1},{"version":"2ab096661c711e4a81cc464fa1e6feb929a54f5340b46b0a07ac6bbf857471f0","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"df83c2a6c73228b625b0beb6669c7ee2a09c914637e2d35170723ad49c0f5cd4","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"87dc0f382502f5bbce5129bdc0aea21e19a3abbc19259e0b43ae038a9fc4e326","affectsGlobalScope":true,"impliedFormat":1},{"version":"b1cb28af0c891c8c96b2d6b7be76bd394fddcfdb4709a20ba05a7c1605eea0f9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2fef54945a13095fdb9b84f705f2b5994597640c46afeb2ce78352fab4cb3279","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac77cb3e8c6d3565793eb90a8373ee8033146315a3dbead3bde8db5eaf5e5ec6","affectsGlobalScope":true,"impliedFormat":1},{"version":"56e4ed5aab5f5920980066a9409bfaf53e6d21d3f8d020c17e4de584d29600ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ece9f17b3866cc077099c73f4983bddbcb1dc7ddb943227f1ec070f529dedd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a6282c8827e4b9a95f4bf4f5c205673ada31b982f50572d27103df8ceb8013c","affectsGlobalScope":true,"impliedFormat":1},{"version":"1c9319a09485199c1f7b0498f2988d6d2249793ef67edda49d1e584746be9032","affectsGlobalScope":true,"impliedFormat":1},{"version":"e3a2a0cee0f03ffdde24d89660eba2685bfbdeae955a6c67e8c4c9fd28928eeb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811c71eee4aa0ac5f7adf713323a5c41b0cf6c4e17367a34fbce379e12bbf0a4","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"60037901da1a425516449b9a20073aa03386cce92f7a1fd902d7602be3a7c2e9","affectsGlobalScope":true,"impliedFormat":1},{"version":"d4b1d2c51d058fc21ec2629fff7a76249dec2e36e12960ea056e3ef89174080f","affectsGlobalScope":true,"impliedFormat":1},{"version":"22adec94ef7047a6c9d1af3cb96be87a335908bf9ef386ae9fd50eeb37f44c47","affectsGlobalScope":true,"impliedFormat":1},{"version":"196cb558a13d4533a5163286f30b0509ce0210e4b316c56c38d4c0fd2fb38405","affectsGlobalScope":true,"impliedFormat":1},{"version":"73f78680d4c08509933daf80947902f6ff41b6230f94dd002ae372620adb0f60","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5239f5c01bcfa9cd32f37c496cf19c61d69d37e48be9de612b541aac915805b","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"7e29f41b158de217f94cb9676bf9cbd0cd9b5a46e1985141ed36e075c52bf6ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"814d5c7384f3ca276e9dc4bcfde5545801a3ea0bfae09916b3336774e662fd1b","impliedFormat":1},{"version":"acd8fd5090ac73902278889c38336ff3f48af6ba03aa665eb34a75e7ba1dccc4","impliedFormat":1},{"version":"d6258883868fb2680d2ca96bc8b1352cab69874581493e6d52680c5ffecdb6cc","impliedFormat":1},{"version":"1b61d259de5350f8b1e5db06290d31eaebebc6baafd5f79d314b5af9256d7153","impliedFormat":1},{"version":"f258e3960f324a956fc76a3d3d9e964fff2244ff5859dcc6ce5951e5413ca826","impliedFormat":1},{"version":"643f7232d07bf75e15bd8f658f664d6183a0efaca5eb84b48201c7671a266979","impliedFormat":1},{"version":"21da358700a3893281ce0c517a7a30cbd46be020d9f0c3f2834d0a8ad1f5fc75","impliedFormat":1},{"version":"70521b6ab0dcba37539e5303104f29b721bfb2940b2776da4cc818c07e1fefc1","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"ba481bca06f37d3f2c137ce343c7d5937029b2468f8e26111f3c9d9963d6568d","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d9ef24f9a22a88e3e9b3b3d8c40ab1ddb0853f1bfbd5c843c37800138437b61","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"e2677634fe27e87348825bb041651e22d50a613e2fdf6a4a3ade971d71bac37e","impliedFormat":1},{"version":"7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419","impliedFormat":1},{"version":"8c0bcd6c6b67b4b503c11e91a1fb91522ed585900eab2ab1f61bba7d7caa9d6f","impliedFormat":1},{"version":"8cd19276b6590b3ebbeeb030ac271871b9ed0afc3074ac88a94ed2449174b776","affectsGlobalScope":true,"impliedFormat":1},{"version":"696eb8d28f5949b87d894b26dc97318ef944c794a9a4e4f62360cd1d1958014b","impliedFormat":1},{"version":"3f8fa3061bd7402970b399300880d55257953ee6d3cd408722cb9ac20126460c","impliedFormat":1},{"version":"35ec8b6760fd7138bbf5809b84551e31028fb2ba7b6dc91d95d098bf212ca8b4","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"68bd56c92c2bd7d2339457eb84d63e7de3bd56a69b25f3576e1568d21a162398","affectsGlobalScope":true,"impliedFormat":1},{"version":"3e93b123f7c2944969d291b35fed2af79a6e9e27fdd5faa99748a51c07c02d28","impliedFormat":1},{"version":"9d19808c8c291a9010a6c788e8532a2da70f811adb431c97520803e0ec649991","impliedFormat":1},{"version":"87aad3dd9752067dc875cfaa466fc44246451c0c560b820796bdd528e29bef40","impliedFormat":1},{"version":"4aacb0dd020eeaef65426153686cc639a78ec2885dc72ad220be1d25f1a439df","impliedFormat":1},{"version":"f0bd7e6d931657b59605c44112eaf8b980ba7f957a5051ed21cb93d978cf2f45","impliedFormat":1},{"version":"8db0ae9cb14d9955b14c214f34dae1b9ef2baee2fe4ce794a4cd3ac2531e3255","affectsGlobalScope":true,"impliedFormat":1},{"version":"15fc6f7512c86810273af28f224251a5a879e4261b4d4c7e532abfbfc3983134","impliedFormat":1},{"version":"58adba1a8ab2d10b54dc1dced4e41f4e7c9772cbbac40939c0dc8ce2cdb1d442","impliedFormat":1},{"version":"641942a78f9063caa5d6b777c99304b7d1dc7328076038c6d94d8a0b81fc95c1","impliedFormat":1},{"version":"714435130b9015fae551788df2a88038471a5a11eb471f27c4ede86552842bc9","impliedFormat":1},{"version":"855cd5f7eb396f5f1ab1bc0f8580339bff77b68a770f84c6b254e319bbfd1ac7","impliedFormat":1},{"version":"5650cf3dace09e7c25d384e3e6b818b938f68f4e8de96f52d9c5a1b3db068e86","impliedFormat":1},{"version":"1354ca5c38bd3fd3836a68e0f7c9f91f172582ba30ab15bb8c075891b91502b7","affectsGlobalScope":true,"impliedFormat":1},{"version":"27fdb0da0daf3b337c5530c5f266efe046a6ceb606e395b346974e4360c36419","impliedFormat":1},{"version":"2d2fcaab481b31a5882065c7951255703ddbe1c0e507af56ea42d79ac3911201","impliedFormat":1},{"version":"a192fe8ec33f75edbc8d8f3ed79f768dfae11ff5735e7fe52bfa69956e46d78d","impliedFormat":1},{"version":"ca867399f7db82df981d6915bcbb2d81131d7d1ef683bc782b59f71dda59bc85","affectsGlobalScope":true,"impliedFormat":1},{"version":"372413016d17d804e1d139418aca0c68e47a83fb6669490857f4b318de8cccb3","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e043a1bc8fbf2a255bccf9bf27e0f1caf916c3b0518ea34aa72357c0afd42ec","impliedFormat":1},{"version":"b4f70ec656a11d570e1a9edce07d118cd58d9760239e2ece99306ee9dfe61d02","impliedFormat":1},{"version":"3bc2f1e2c95c04048212c569ed38e338873f6a8593930cf5a7ef24ffb38fc3b6","impliedFormat":1},{"version":"6e70e9570e98aae2b825b533aa6292b6abd542e8d9f6e9475e88e1d7ba17c866","impliedFormat":1},{"version":"f9d9d753d430ed050dc1bf2667a1bab711ccbb1c1507183d794cc195a5b085cc","impliedFormat":1},{"version":"9eece5e586312581ccd106d4853e861aaaa1a39f8e3ea672b8c3847eedd12f6e","impliedFormat":1},{"version":"47ab634529c5955b6ad793474ae188fce3e6163e3a3fb5edd7e0e48f14435333","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"fad4e3c207fe23922d0b2d06b01acbfb9714c4f2685cf80fd384c8a100c82fd0","affectsGlobalScope":true,"impliedFormat":1},{"version":"74cf591a0f63db318651e0e04cb55f8791385f86e987a67fd4d2eaab8191f730","impliedFormat":1},{"version":"5eab9b3dc9b34f185417342436ec3f106898da5f4801992d8ff38ab3aff346b5","impliedFormat":1},{"version":"12ed4559eba17cd977aa0db658d25c4047067444b51acfdcbf38470630642b23","affectsGlobalScope":true,"impliedFormat":1},{"version":"f3ffabc95802521e1e4bcba4c88d8615176dc6e09111d920c7a213bdda6e1d65","impliedFormat":1},{"version":"809821b8a065e3234a55b3a9d7846231ed18d66dd749f2494c66288d890daf7f","impliedFormat":1},{"version":"ae56f65caf3be91108707bd8dfbccc2a57a91feb5daabf7165a06a945545ed26","impliedFormat":1},{"version":"a136d5de521da20f31631a0a96bf712370779d1c05b7015d7019a9b2a0446ca9","impliedFormat":1},{"version":"c3b41e74b9a84b88b1dca61ec39eee25c0dbc8e7d519ba11bb070918cfacf656","affectsGlobalScope":true,"impliedFormat":1},{"version":"4737a9dc24d0e68b734e6cfbcea0c15a2cfafeb493485e27905f7856988c6b29","affectsGlobalScope":true,"impliedFormat":1},{"version":"36d8d3e7506b631c9582c251a2c0b8a28855af3f76719b12b534c6edf952748d","impliedFormat":1},{"version":"1ca69210cc42729e7ca97d3a9ad48f2e9cb0042bada4075b588ae5387debd318","impliedFormat":1},{"version":"f5ebe66baaf7c552cfa59d75f2bfba679f329204847db3cec385acda245e574e","impliedFormat":1},{"version":"ed59add13139f84da271cafd32e2171876b0a0af2f798d0c663e8eeb867732cf","affectsGlobalScope":true,"impliedFormat":1},{"version":"b7c5e2ea4a9749097c347454805e933844ed207b6eefec6b7cfd418b5f5f7b28","impliedFormat":1},{"version":"b1810689b76fd473bd12cc9ee219f8e62f54a7d08019a235d07424afbf074d25","impliedFormat":1},{"version":"2beff543f6e9a9701df88daeee3cdd70a34b4a1c11cb4c734472195a5cb2af54","impliedFormat":1},{"version":"2e07abf27aa06353d46f4448c0bbac73431f6065eef7113128a5cd804d0c384d","impliedFormat":1},{"version":"be1cc4d94ea60cbe567bc29ed479d42587bf1e6cba490f123d329976b0fe4ee5","impliedFormat":1},{"version":"66be1299a7a3129ceb488b340c291cf575bebb0e337f92e169dec38231472e34","impliedFormat":1},{"version":"9894dafe342b976d251aac58e616ac6df8db91fb9d98934ff9dd103e9e82578f","impliedFormat":1},{"version":"413df52d4ea14472c2fa5bee62f7a40abd1eb49be0b9722ee01ee4e52e63beb2","impliedFormat":1},{"version":"db6d2d9daad8a6d83f281af12ce4355a20b9a3e71b82b9f57cddcca0a8964a96","impliedFormat":1},{"version":"446a50749b24d14deac6f8843e057a6355dd6437d1fac4f9e5ce4a5071f34bff","impliedFormat":1},{"version":"182e9fcbe08ac7c012e0a6e2b5798b4352470be29a64fdc114d23c2bab7d5106","impliedFormat":1},{"version":"2f4e6b4d39426a1b85ecf4bdeb9dddbf4d9b3397d95d8555d46f925c9519ec7d","impliedFormat":1},{"version":"78a2869ad0cbf3f9045dda08c0d4562b7e1b2bfe07b19e0db072f5c3c56e9584","impliedFormat":1},{"version":"89d5d28d4f57e000b836ac273079be1b75710e28ce14750d081fb420d37e2ca5","impliedFormat":1},{"version":"fd4e24ccff3966390600d7f5d6aa1fed5a512e92ada735ea5fbc933d313ad3d3","impliedFormat":1},{"version":"b7cddfe1aa6b86b5fad3c9ccb30d05b3ccb165aebbf112f48d2d8a5f69dd98b1","impliedFormat":1},{"version":"a86f82d646a739041d6702101afa82dcb935c416dd93cbca7fd754fd0282ce1f","impliedFormat":1},{"version":"ad0d1d75d129b1c80f911be438d6b61bfa8703930a8ff2be2f0e1f8a91841c64","impliedFormat":1},{"version":"bd2c7ada3dee03653d3f601011d30072194bc3970cd93208f9588fbdc0c69347","impliedFormat":1},{"version":"e480da45d32313e7174b265674da504f075f59ef326852f0c5a5d863b438ae85","impliedFormat":1},{"version":"ad54850f61fcf5d014e11be80d2f46fea9265cfa7e77456da876f7833ef81769","impliedFormat":1},{"version":"6f7c9e8bd2b5b6a080b07080065f94900bd3c7e5ebbd3047bc33fcce2fab1dd8","impliedFormat":1},{"version":"3e7efde639c6a6c3edb9847b3f61e308bf7a69685b92f665048c45132f51c218","impliedFormat":1},{"version":"df45ca1176e6ac211eae7ddf51336dc075c5314bc5c253651bae639defd5eec5","impliedFormat":1},{"version":"8a0e762ceb20c7e72504feef83d709468a70af4abccb304f32d6b9bac1129b2c","impliedFormat":1},{"version":"da5950ee2a90721df6f3fba45f5d05308f7e4c35835392215dd2cd404505e2de","impliedFormat":1},{"version":"ce75b1aebb33d510ff28af960a9221410a3eaf7f18fc5f21f9404075fba77256","impliedFormat":1},{"version":"f42d5fed19610d485c646a0c430e768115567d078c7fc855c57b0c578b3d6cd3","impliedFormat":1},{"version":"ee8df1cb8d0faaca4013a1b442e99130769ce06f438d18d510fed95890067563","impliedFormat":1},{"version":"d5630f2ad9b4541e5ce891648121022f9412ecdca1820baa1f0104f70fd7eff7","impliedFormat":1},{"version":"4d15375ab13497104bc8fe56fdef2b5fd6853f29255737d23a33fa306ff7fd69","impliedFormat":1},{"version":"2cd3fc1d0d6a1e85baffd2d4f50f5efb192b5446eef567e97c94765402f0aad4","impliedFormat":1},{"version":"e4cbf2f1e89ecccaddd2c045e600ae41b732295953fb06247c7dcbc2d281ed30","impliedFormat":1},{"version":"6dcedaef57dff0d79a05ab0ab602cde74db803d1e765468bf91263786a383e1b","impliedFormat":1},{"version":"8c1697d90c394a6fd955b98eae01238eff628e129b987a68aea10f898a48e7da","impliedFormat":1},{"version":"7580e62139cb2b44a0270c8d01abcbfcba2819a02514a527342447fa69b34ef1","impliedFormat":1},{"version":"b838d4c72740eb0afd284bf7575b74c624b105eff2e8c7b4aeead57e7ac320ff","impliedFormat":1},{"version":"f374cb24e93e7798c4d9e83ff872fa52d2cdb36306392b840a6ddf46cb925cb6","impliedFormat":1},{"version":"d10d63718e1646c2279e3b33831f82c60e31f622b2b7020f1196409ca4c09242","impliedFormat":1},{"version":"106c6025f1d99fd468fd8bf6e5bda724e11e5905a4076c5d29790b6c3745e50c","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"148679c6d0f449210a96e7d2e562d589e56fcde87f843a92808b3ff103f1a774","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"02436d7e9ead85e09a2f8e27d5f47d9464bced31738dec138ca735390815c9f0","impliedFormat":1},{"version":"f8d5ff8eafd37499f2b6a98659dd9b45a321de186b8db6b6142faed0fea3de77","impliedFormat":1},{"version":"c86fe861cf1b4c46a0fb7d74dffe596cf679a2e5e8b1456881313170f092e3fa","impliedFormat":1},{"version":"a22dd55aa4d39906252000ab8e8a1b83b195eef7f4274eb51e457c1f11cf6580","impliedFormat":1},{"version":"540cc83ab772a2c6bc509fe1354f314825b5dba3669efdfbe4693ecd3048e34f","impliedFormat":1},{"version":"121b0696021ab885c570bbeb331be8ad82c6efe2f3b93a6e63874901bebc13e3","impliedFormat":1},{"version":"612d9da66bb046a9c1e2e8d026245ded881fc4b9f98cbfae714415d57ee0ae0b","impliedFormat":1},{"version":"32c2ad9494dad5d11b0564a619fee18f388db6c1e9e2cd3c360b3122549691eb","impliedFormat":1},{"version":"6c301d40aec56a74ec7bd7324e31a728dadf9bfba3e96def02938d3d973534ec","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"aa14cee20aa0db79f8df101fc027d929aec10feb5b8a8da3b9af3895d05b7ba2","impliedFormat":1},{"version":"493c700ac3bd317177b2eb913805c87fe60d4e8af4fb39c41f04ba81fae7e170","impliedFormat":1},{"version":"aeb554d876c6b8c818da2e118d8b11e1e559adbe6bf606cc9a611c1b6c09f670","impliedFormat":1},{"version":"acf5a2ac47b59ca07afa9abbd2b31d001bf7448b041927befae2ea5b1951d9f9","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"d71291eff1e19d8762a908ba947e891af44749f3a2cbc5bd2ec4b72f72ea795f","impliedFormat":1},{"version":"c0480e03db4b816dff2682b347c95f2177699525c54e7e6f6aa8ded890b76be7","impliedFormat":1},{"version":"25a5f6fd3a2243c859eddc99ab5fba11d970af2fe7a5df9c32b7668f76f97b01","impliedFormat":1},{"version":"8d207e1f9d2c30d6f77dfa693f3827c3fbf0d89240297e10bdfe1041d433df68","impliedFormat":1},{"version":"b620391fe8060cf9bedc176a4d01366e6574d7a71e0ac0ab344a4e76576fcbb8","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"2652448ac55a2010a1f71dd141f828b682298d39728f9871e1cdf8696ef443fd","impliedFormat":1},{"version":"d682336018141807fb602709e2d95a192828fcb8d5ba06dda3833a8ea98f69e3","impliedFormat":1},{"version":"6124e973eab8c52cabf3c07575204efc1784aca6b0a30c79eb85fe240a857efa","impliedFormat":1},{"version":"0d891735a21edc75df51f3eb995e18149e119d1ce22fd40db2b260c5960b914e","impliedFormat":1},{"version":"3b414b99a73171e1c4b7b7714e26b87d6c5cb03d200352da5342ab4088a54c85","impliedFormat":1},{"version":"4fbd3116e00ed3a6410499924b6403cc9367fdca303e34838129b328058ede40","impliedFormat":1},{"version":"9c82171d836c47486074e4ca8e059735bf97b205e70b196535b5efd40cbe1bc5","impliedFormat":1},{"version":"48dcc919f76c040a999c0d46d2bf25ab089645ca21b837f120b222f56a86cd76","impliedFormat":1},{"version":"2f9c89cbb29d362290531b48880a4024f258c6033aaeb7e59fbc62db26819650","impliedFormat":1},{"version":"a365c4d3bed3be4e4e20793c999c51f5cd7e6792322f14650949d827fbcd170f","impliedFormat":1},{"version":"c5426dbfc1cf90532f66965a7aa8c1136a78d4d0f96d8180ecbfc11d7722f1a5","impliedFormat":1},{"version":"65a15fc47900787c0bd18b603afb98d33ede930bed1798fc984d5ebb78b26cf9","impliedFormat":1},{"version":"9d202701f6e0744adb6314d03d2eb8fc994798fc83d91b691b75b07626a69801","impliedFormat":1},{"version":"de9d2df7663e64e3a91bf495f315a7577e23ba088f2949d5ce9ec96f44fba37d","impliedFormat":1},{"version":"c7af78a2ea7cb1cd009cfb5bdb48cd0b03dad3b54f6da7aab615c2e9e9d570c5","impliedFormat":1},{"version":"1ee45496b5f8bdee6f7abc233355898e5bf9bd51255db65f5ff7ede617ca0027","impliedFormat":1},{"version":"273782b8454e78f6a8b30d2cfbf6860499c930595095fcc1689637115f0eddda","affectsGlobalScope":true,"impliedFormat":1},{"version":"3fbdd025f9d4d820414417eeb4107ffa0078d454a033b506e22d3a23bc3d9c41","affectsGlobalScope":true,"impliedFormat":1},{"version":"dba114fb6a32b355a9cfc26ca2276834d72fe0e94cd2c3494005547025015369","impliedFormat":1},{"version":"a8f8e6ab2fa07b45251f403548b78eaf2022f3c2254df3dc186cb2671fe4996d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa6c12a7c0f6b84d512f200690bfc74819e99efae69e4c95c4cd30f6884c526e","impliedFormat":1},{"version":"f1c32f9ce9c497da4dc215c3bc84b722ea02497d35f9134db3bb40a8d918b92b","impliedFormat":1},{"version":"b73c319af2cc3ef8f6421308a250f328836531ea3761823b4cabbd133047aefa","affectsGlobalScope":true,"impliedFormat":1},{"version":"e433b0337b8106909e7953015e8fa3f2d30797cea27141d1c5b135365bb975a6","impliedFormat":1},{"version":"9f9bb6755a8ce32d656ffa4763a8144aa4f274d6b69b59d7c32811031467216e","impliedFormat":1},{"version":"5c32bdfbd2d65e8fffbb9fbda04d7165e9181b08dad61154961852366deb7540","impliedFormat":1},{"version":"ddff7fc6edbdc5163a09e22bf8df7bef75f75369ebd7ecea95ba55c4386e2441","impliedFormat":1},{"version":"0c05e9842ec4f8b7bfebfd3ca61604bb8c914ba8da9b5337c4f25da427a005f2","impliedFormat":1},{"version":"faed7a5153215dbd6ebe76dfdcc0af0cfe760f7362bed43284be544308b114cf","impliedFormat":1},{"version":"7029e566b8df176f703fb59fd437a38670c7a0e02c58b2d66dfb5b2e2b2defdb","impliedFormat":1},{"version":"7f2aa4d4989a82530aaac3f72b3dceca90e9c25bee0b1a327e8a08a1262435ad","impliedFormat":1},{"version":"d96b39301d0ded3f1a27b47759676a33a02f6f5049bfcbde81e533fd10f50dcb","impliedFormat":1},{"version":"e9f147ecca73d9346a4c073432843c159ccbe50bdcb678a78f6da10eae2cecf4","impliedFormat":1},{"version":"de061f7d72bd65c06fc1419f841dfdcb29a8e22fe6fa527d1e6eb20b897d4de0","impliedFormat":1},{"version":"663beafc2446079574570cba86e9b15f986f908ddb1b01274509970126fee945","impliedFormat":1},{"version":"a3102887d5058bf4cb5b37fa6964c09e9527c42053b3b5c642b89878620748de","impliedFormat":1},{"version":"0aaaa1727edd29673d85c9b26d7ca4d54e5407a48586903c51b48b7f7d196f61","impliedFormat":1},{"version":"d35bca0b261bff02635758c48e8ab99c61c420d0dfabbcf467e847171d876b7d","impliedFormat":1},{"version":"3bc12c40d90c342ff88a3d876996c555ed5cbee5fe8c3308a240b321f401ee46","impliedFormat":1},{"version":"ba130768aae855a5477e9e148e5c879548e6e7ccbcc56fd1934c8a18ea5b7569","impliedFormat":1},{"version":"2e4f37ffe8862b14d8e24ae8763daaa8340c0df0b859d9a9733def0eee7562d9","impliedFormat":1},{"version":"d38530db0601215d6d767f280e3a3c54b2a83b709e8d9001acb6f61c67e965fc","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"b499af2054a037a162b3b72cd886f48bbf32a3502c865c6e29fac7d2ab3ce0b5","impliedFormat":1},{"version":"b83cb14474fa60c5f3ec660146b97d122f0735627f80d82dd03e8caa39b4388c","impliedFormat":1},{"version":"48773ca557b0319c2ee62ae249cf52a81709e8be139920d6479a66274de7c4ed","impliedFormat":1},{"version":"7274fbffbd7c9589d8d0ffba68157237afd5cecff1e99881ea3399127e60572f","impliedFormat":1},{"version":"b73cbf0a72c8800cf8f96a9acfe94f3ad32ca71342a8908b8ae484d61113f647","impliedFormat":1},{"version":"bae6dd176832f6423966647382c0d7ba9e63f8c167522f09a982f086cd4e8b23","impliedFormat":1},{"version":"20865ac316b8893c1a0cc383ccfc1801443fbcc2a7255be166cf90d03fac88c9","impliedFormat":1},{"version":"c9958eb32126a3843deedda8c22fb97024aa5d6dd588b90af2d7f2bfac540f23","impliedFormat":1},{"version":"461d0ad8ae5f2ff981778af912ba71b37a8426a33301daa00f21c6ccb27f8156","impliedFormat":1},{"version":"e927c2c13c4eaf0a7f17e6022eee8519eb29ef42c4c13a31e81a611ab8c95577","impliedFormat":1},{"version":"fcafff163ca5e66d3b87126e756e1b6dfa8c526aa9cd2a2b0a9da837d81bbd72","impliedFormat":1},{"version":"70246ad95ad8a22bdfe806cb5d383a26c0c6e58e7207ab9c431f1cb175aca657","impliedFormat":1},{"version":"f00f3aa5d64ff46e600648b55a79dcd1333458f7a10da2ed594d9f0a44b76d0b","impliedFormat":1},{"version":"772d8d5eb158b6c92412c03228bd9902ccb1457d7a705b8129814a5d1a6308fc","impliedFormat":1},{"version":"802e797bcab5663b2c9f63f51bdf67eff7c41bc64c0fd65e6da3e7941359e2f7","impliedFormat":1},{"version":"b01bd582a6e41457bc56e6f0f9de4cb17f33f5f3843a7cf8210ac9c18472fb0f","impliedFormat":1},{"version":"8b4327413e5af38cd8cb97c59f48c3c866015d5d642f28518e3a891c469f240e","impliedFormat":1},{"version":"4cceef18d7f088e797a463e90b7a9dad10c6bc667724b7686e3e740ae00122be","impliedFormat":1},{"version":"7ee86fbb3754388e004de0ef9e6505485ddfb3be7640783d6d015711c03d302d","impliedFormat":1},{"version":"cc1954b539604b1e562319119ac7e888172208b32ca873f9a357a92c826bd046","impliedFormat":1},{"version":"a67b87d0281c97dfc1197ef28dfe397fc2c865ccd41f7e32b53f647184cc7307","impliedFormat":1},{"version":"771ffb773f1ddd562492a6b9aaca648192ac3f056f0e1d997678ff97dbb6bf9b","impliedFormat":1},{"version":"43e96a3d5d1411ab40ba2f61d6a3192e58177bcf3b133a80ad2a16591611726d","impliedFormat":1},{"version":"232f70c0cf2b432f3a6e56a8dc3417103eb162292a9fd376d51a3a9ea5fbbf6f","impliedFormat":1},{"version":"bb8f2dbc03533abca2066ce4655c119bff353dd4514375beb93c08590c03e023","impliedFormat":1},{"version":"706dd95827e7ebaabda91d5db2b755233e0952d98570e9c032b0f066a15c1177","affectsGlobalScope":true,"impliedFormat":1},{"version":"0b103e9abfe82d14c0ad06a55d9f91d6747154ef7cacc73cf27ecad2bfb3afcf","impliedFormat":1},{"version":"cd9304972e6d616197fb44fce00540a904f38b54306a1951b5dbeaf3c01ab5bd","impliedFormat":1},{"version":"77438e2c397a3db78407621cfc57241a305b310ddea2c185f1d555248297f587","impliedFormat":1},{"version":"120599fd965257b1f4d0ff794bc696162832d9d8467224f4665f713a3119078b","impliedFormat":1},{"version":"43ba4f2fa8c698f5c304d21a3ef596741e8e85a810b7c1f9b692653791d8d97a","impliedFormat":1},{"version":"5433f33b0a20300cca35d2f229a7fc20b0e8477c44be2affeb21cb464af60c76","impliedFormat":1},{"version":"db036c56f79186da50af66511d37d9fe77fa6793381927292d17f81f787bb195","impliedFormat":1},{"version":"a6805fcafed712aea7759f8bc731014f9d22738c1d6ef9d43b8091d1d48346d5","impliedFormat":1},{"version":"c49469a5349b3cc1965710b5b0f98ed6c028686aa8450bcb3796728873eb923e","impliedFormat":1},{"version":"4a889f2c763edb4d55cb624257272ac10d04a1cad2ed2948b10ed4a7fda2a428","impliedFormat":1},{"version":"7bb79aa2fead87d9d56294ef71e056487e848d7b550c9a367523ee5416c44cfa","impliedFormat":1},{"version":"d88ea80a6447d7391f52352ec97e56b52ebec934a4a4af6e2464cfd8b39c3ba8","impliedFormat":1},{"version":"142617b3cdf902b69c6464c9fbd942b60ab3e733ca18c032b19e0f7e2adbefe8","impliedFormat":1},{"version":"0b603555f1881f87256ffd6344d3e3ed6d466c2e701eabf381f28be8c2125892","impliedFormat":1},{"version":"897e4f7662488e3ecc79e743bdd3b78f13bdb69a97851afa5b440c4211e32ea9","impliedFormat":1},{"version":"e2e1c6d3b2d93add5200bd7bc1a8cccb4e446836b2111ece45db8683a2c765de","impliedFormat":1},{"version":"251b03d5cd243854ce870d9a9a39f491faf69898c5d6b5eee28cc7649c57417b","impliedFormat":1},{"version":"27ff4196654e6373c9af16b6165120e2dd2169f9ad6abb5c935af5abd8c7938c","impliedFormat":1},{"version":"2c4de79f406d137390608e8c0a44fba2ff8e00bacfcae7c9d1781fef10e9440d","impliedFormat":1},{"version":"07ba23a10465791be5d22deaf5ef7de7658774ddff53721e5ea17fedea1bc721","impliedFormat":1},{"version":"dca8c645c5afeb03b1ecedbf16323f33e7d0afaa6256c8e047e6e38087a97f53","impliedFormat":1},{"version":"775f181bd4a533d6f8b5e55ec1d9f1624559720ae8a70e9432258da26b38d27c","impliedFormat":1},{"version":"796273b2edc72e78a04e86d7c58ae94d370ab93a0ddf40b1aa85a37a1c29ecd7","impliedFormat":1},{"version":"5df15a69187d737d6d8d066e189ae4f97e41f4d53712a46b2710ff9f8563ec9f","impliedFormat":1},{"version":"7715134a0cf07dd41a9da2895d708625a3a303a0385e355ecaaf0b8bfaef2550","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"622694a8522b46f6310c2a9b5d2530dde1e2854cb5829354e6d1ff8f371cf469","impliedFormat":1},{"version":"cd8ce8d68567f62dd580b3c3c37777ac3f5b81944c7417f5ea83030eab533385","impliedFormat":1},{"version":"e5c939d896565dcac0f6fbdbada11284e7728ef26a069561c09aa5aa4a788393","impliedFormat":1},{"version":"9e2739b32f741859263fdba0244c194ca8e96da49b430377930b8f721d77c000","impliedFormat":1},{"version":"a9e6c0ff3f8186fccd05752cf75fc94e147c02645087ac6de5cc16403323d870","impliedFormat":1},{"version":"49af4b52f0d4d2304c5f2c6fe5fab3e153e0acc38830d0202821b877c097dd02","impliedFormat":1},{"version":"49c346823ba6d4b12278c12c977fb3a31c06b9ca719015978cb145eb86da1c61","impliedFormat":1},{"version":"bfac6e50eaa7e73bb66b7e052c38fdc8ccfc8dbde2777648642af33cf349f7f1","impliedFormat":1},{"version":"92f7c1a4da7fbfd67a2228d1687d5c2e1faa0ba865a94d3550a3941d7527a45d","impliedFormat":1},{"version":"f53b120213a9289d9a26f5af90c4c686dd71d91487a0aa5451a38366c70dc64b","impliedFormat":1},{"version":"e68b8e5a1df7c1be2bc105141456ecba70215806e1c28bfbc5c12bfce4be6e68","impliedFormat":1},{"version":"511c8f02329808d47d00b859c532ae9115590048b17325a946c74dac48428650","impliedFormat":1},{"version":"57d67b72e06059adc5e9454de26bbfe567d412b962a501d263c75c2db430f40e","impliedFormat":1},{"version":"b5f9e66625783eefcbe3d2da074b2e7ba2066d61ce3fc6ef4f22805ad946cab4","impliedFormat":1},{"version":"e37115962d284b9f7a37c2bdd2add50f88365dde41f5e0ff591ffc48a8ec7575","impliedFormat":1},{"version":"6459054aabb306821a043e02b89d54da508e3a6966601a41e71c166e4ea1474f","impliedFormat":1},{"version":"bb37588926aba35c9283fe8d46ebf4e79ffe976343105f5c6d45f282793352b2","impliedFormat":1},{"version":"f89488602bec98a142072fae7ea5ba99431a569ff580c64b7be39896474799d8","impliedFormat":1},{"version":"bbbc47961f39a57df103cf4ca3bb8f8732b4b6678a18225a0aa76d59c466956c","impliedFormat":1},{"version":"2e6114a7dd6feeef85b2c80120fdbfb59a5529c0dcc5bfa8447b6996c97a69f5","impliedFormat":1},{"version":"2ffb043dc5163458e473b7010859f86e01dc4edffcae0a93d885d028b426a546","impliedFormat":1},{"version":"c8f004e6036aa1c764ad4ec543cf89a5c1893a9535c80ef3f2b653e370de45e6","impliedFormat":1},{"version":"dd80b1e600d00f5c6a6ba23f455b84a7db121219e68f89f10552c54ba46e4dc9","impliedFormat":1},{"version":"b064c36f35de7387d71c599bfcf28875849a1dbc733e82bd26cae3d1cd060521","impliedFormat":1},{"version":"05c7280d72f3ed26f346cbe7cbbbb002fb7f15739197cbbee6ab3fd1a6cb9347","impliedFormat":1},{"version":"8de9fe97fa9e00ec00666fa77ab6e91b35d25af8ca75dabcb01e14ad3299b150","impliedFormat":1},{"version":"04b7b2e0832dfd3c31e81df3975e8d8fda28e7ff999b0aa2932608a8f6661d5c","impliedFormat":1},{"version":"ca2d34c6ed5cbd3070b8b6f32f42ae54adcc6499c1e4b99f0a5798b3f27cc653","impliedFormat":1},{"version":"9ec68995e66dd6b9dac834bf5ae85fde802714ea2e82151a5d1d53ef01b463ef","impliedFormat":1},{"version":"5c4d626b4902f2ef8a1cc146d761d276cef988016dc674e3b98fbad70e64bc9f","impliedFormat":1},{"version":"fdfaa0aad899524962e2955287b5b991ffe3be50f64e02eb60c933ca44644a94","impliedFormat":1},{"version":"53c972a0f9bc3a4ec70fff7314123ea8cfcf75b3703046f767d2dc1eea87b2fb","impliedFormat":1},{"version":"f974e4a06953682a2c15d5bd5114c0284d5abf8bc0fe4da25cb9159427b70072","impliedFormat":1},{"version":"50256e9c31318487f3752b7ac12ff365c8949953e04568009c8705db802776fb","impliedFormat":1},{"version":"7d73b24e7bf31dfb8a931ca6c4245f6bb0814dfae17e4b60c9e194a631fe5f7b","impliedFormat":1},{"version":"d130c5f73768de51402351d5dc7d1b36eaec980ca697846e53156e4ea9911476","impliedFormat":1},{"version":"413586add0cfe7369b64979d4ec2ed56c3f771c0667fbde1bf1f10063ede0b08","impliedFormat":1},{"version":"06472528e998d152375ad3bd8ebcb69ff4694fd8d2effaf60a9d9f25a37a097a","impliedFormat":1},{"version":"7303b45138d2511035056a5901a1490ebdcbf055cbb1276f8629c5121cbe733e","impliedFormat":1},{"version":"27f874cd5327507eeff699a74567f60c1215b94509f4308633a7b01922471ed2","impliedFormat":1},{"version":"a401617604fa1f6ce437b81689563dfdc377069e4c58465dbd8d16069aede0a5","impliedFormat":1},{"version":"2c6cf04bc525caf6546e859e8ef10bfb9573837ec0bc5ec7b53a7b1b8ca72781","impliedFormat":1},{"version":"8695dec09ad439b0ceef3776ea68a232e381135b516878f0901ed2ea114fd0fe","impliedFormat":1},{"version":"304b44b1e97dd4c94697c3313df89a578dca4930a104454c99863f1784a54357","impliedFormat":1},{"version":"0a437ae178f999b46b6153d79095b60c42c996bc0458c04955f1c996dc68b971","impliedFormat":1},{"version":"74b2a5e5197bd0f2e0077a1ea7c07455bbea67b87b0869d9786d55104006784f","impliedFormat":1},{"version":"4a7baeb6325920044f66c0f8e5e6f1f52e06e6d87588d837bdf44feb6f35c664","impliedFormat":1},{"version":"87cc05fe13108f02e12da7e3efd8e360fef78d96a0c9e11408ea1b1b9fb3e03d","impliedFormat":1},{"version":"1abbf67c218d23c2ce76887caac2df6c7dab3d97ba2b65348432b876f510002a","impliedFormat":1},{"version":"1a82deef4c1d39f6882f28d275cad4c01f907b9b39be9cbc472fcf2cf051e05b","impliedFormat":1},{"version":"4b20fcf10a5413680e39f5666464859fc56b1003e7dfe2405ced82371ebd49b6","impliedFormat":1},{"version":"c06ef3b2569b1c1ad99fcd7fe5fba8d466e2619da5375dfa940a94e0feea899b","impliedFormat":1},{"version":"f7d628893c9fa52ba3ab01bcb5e79191636c4331ee5667ecc6373cbccff8ae12","impliedFormat":1},{"version":"2467b00d963828f540f4acd7910f4c04cfe4b489550e6bb682212f65583bca5b","impliedFormat":1},{"version":"854e50b93090b3f8fd6e355b074e1d24dce1ae0240f1ce46563e35fea210a6d5","impliedFormat":99},{"version":"5a16e93d5d53d987dddda1ec606c9821f6bd31d1bdf0635e05e3841312cefa8b","impliedFormat":1},{"version":"a6dba407fc287f1e25454e75028c91bbc00675f2d1c4e8b3edcc36c08611a486","impliedFormat":1},{"version":"d663134457d8d669ae0df34eabd57028bddc04fc444c4bc04bc5215afc91e1f4","impliedFormat":1},{"version":"e91f7b1344577a02f051b9b471f33044fef8334a76dc9e1de003d17595a5219b","impliedFormat":1},{"version":"c0723195c85e19656d6b5b9fdb81d3f3403c1ae4679e722c6ea058c516b38d12","impliedFormat":1},{"version":"b55eb9f72166093b5460d34b34f5d8699c968de3bc3fc696e40f2c93f2ebf650","impliedFormat":1},{"version":"71d9eb4c4e99456b78ae182fb20a5dfc20eb1667f091dbb9335b3c017dd1c783","impliedFormat":1},{"version":"cfa846a7b7847a1d973605fbb8c91f47f3a0f0643c18ac05c47077ebc72e71c7","impliedFormat":1},{"version":"1594da19968752a22b2ac48c2d0e60575700e745c577a8a4a676b841238ad5bb","impliedFormat":1},{"version":"e0cee12109e0a10a4c3d6769fcc7644b7c1ea7f52365bea51728f5af29f8a137","impliedFormat":1},{"version":"7d4254b4c6c67a29d5e7f65e67d72540480ac2cfb041ca484847f5ae70480b62","impliedFormat":1},{"version":"3536968defef8a75514f547ead5e2e9c1e984820290ec9b00c5fdfb6ef786535","impliedFormat":1},{"version":"d83773870080c30a230e322ce13a9c6f3398e8dacea4ea8a83e26370f3bac23e","impliedFormat":1},{"version":"dcfeaf98d66314fec29a9076c4290e45d0b196a65827becc19138e9c7b855f37","impliedFormat":1},{"version":"6849fe9210fe4946d5f085bfed36758f33dc6ae15a751338d178dd4daa017c46","impliedFormat":1},{"version":"888cda0fa66d7f74e985a3f7b1af1f64b8ff03eb3d5e80d051c3cbdeb7f32ab7","impliedFormat":1},{"version":"60681e13f3545be5e9477acb752b741eae6eaf4cc01658a25ec05bff8b82a2ef","impliedFormat":1},{"version":"ffae4e1e06aa848a1e4bcef162cd1c48e5909b26223515981310af9c036bdfc7","impliedFormat":1},{"version":"a57b1802794433adec9ff3fed12aa79d671faed86c49b09e02e1ac41b4f1d33a","impliedFormat":1},{"version":"34e16eb7c31768a11a08aebcfb3d70d7b8f0b016197e98d8419e566ceae6d6c8","impliedFormat":1},{"version":"f94ec1f7e4b709d26960306c9082a7a1b728a6e13089346aa48ba57c74cbf47e","impliedFormat":1},{"version":"9a11cb4033405e96c247cd5aa29790212aaffdd127869e8a5219103f0b389fd5","impliedFormat":1},{"version":"01479d9d5a5dda16d529b91811375187f61a06e74be294a35ecce77e0b9e8d6c","impliedFormat":1},{"version":"aff5213585cb72e94054dfe17250ff315f3569b3919d1ef1ad235f37c4ee894e","impliedFormat":1},{"version":"fb2ea35e1be6388d722d7725e2b49c697d34d9c890c3b96758faaeb86d35cef8","impliedFormat":1},{"version":"ce0df82a9ae6f914ba08409d4d883983cc08e6d59eb2df02d8e4d68309e7848b","impliedFormat":1},{"version":"1a4dc28334a926d90ba6a2d811ba0ff6c22775fcc13679521f034c124269fd40","impliedFormat":1},{"version":"f05315ff85714f0b87cc0b54bcd3dde2716e5a6b99aedcc19cad02bf2403e08c","impliedFormat":1},{"version":"5fad3b31fc17a5bc58095118a8b160f5260964787c52e7eb51e3d4fcf5d4a6f0","impliedFormat":1},{"version":"72105519d0390262cf0abe84cf41c926ade0ff475d35eb21307b2f94de985778","impliedFormat":1},{"version":"456006a6975b26c0a1785feddae165f6d307e2d601ffde27e21fc4a790e448a4","impliedFormat":1},{"version":"c857e0aae3f5f444abd791ec81206020fbcc1223e187316677e026d1c1d6fe08","impliedFormat":1},{"version":"ccf6dd45b708fb74ba9ed0f2478d4eb9195c9dfef0ff83a6092fa3cf2ff53b4f","impliedFormat":1},{"version":"1fe0d18b111e1145a7e7601855bccd4ca20f24e3b9a5aba6bb1fa9d1a7059170","impliedFormat":1},{"version":"5632c3c26d420c063eebe64c45b1248b9492a67bf44f1d0c57e9dc8f6cf449bb","impliedFormat":1},{"version":"0df5aa619ab12993a39ea6dae062ee46eadbb4d738916460e636ada52bced75b","impliedFormat":1},{"version":"8fca3039857709484e5893c05c1f9126ab7451fa6c29e19bb8c2411a2e937345","impliedFormat":1},{"version":"35069c2c417bd7443ae7c7cafd1de02f665bf015479fec998985ffbbf500628c","impliedFormat":1},{"version":"10ab7be91f87ebe8916b62cf28af2e45b5601fc7b0e311adf838f912c6b31dd8","impliedFormat":1},{"version":"bc636fbc08e0979ceb7eb0731a33000283d77a33b62e1f71ee65be50394e40ba","impliedFormat":1},{"version":"7e0b7f91c5ab6e33f511efc640d36e6f933510b11be24f98836a20a2dc914c2d","impliedFormat":1},{"version":"045b752f44bf9bbdcaffd882424ab0e15cb8d11fa94e1448942e338c8ef19fba","impliedFormat":1},{"version":"2894c56cad581928bb37607810af011764a2f511f575d28c9f4af0f2ef02d1ab","impliedFormat":1},{"version":"0a72186f94215d020cb386f7dca81d7495ab6c17066eb07d0f44a5bf33c1b21a","impliedFormat":1},{"version":"75bbd3be047d539988a0ff0b56384ef7a6a25f3b676ad96bee547d44c31622a7","impliedFormat":1},{"version":"42960001a776b089ade681ab5cfddc936e0afb0615133ec1841f3dee89d3e1bf","impliedFormat":1},{"version":"0aedb02516baf3e66b2c1db9fef50666d6ed257edac0f866ea32f1aa05aa474f","impliedFormat":1},{"version":"da47712b394d944328245482603bc6f416d3949b67c9392279caab595076b510","affectsGlobalScope":true,"impliedFormat":1},{"version":"37d0071d8f0a06dc55c2c5e0ec3391affd4fd107c53410bf358196ec0bf3923f","impliedFormat":1},{"version":"b213dad76ca37fd552274c9499056e1c0d9c1bd38a55bb7f68b22ba6b84c3ad7","impliedFormat":1},{"version":"c30436b130b6218b7714314dc41d3f459590db4bdf099eecd51cb1bda32109a8","impliedFormat":1},{"version":"20fa37b636fdcc1746ea0738f733d0aed17890d1cd7cb1b2f37010222c23f13e","impliedFormat":1},{"version":"d90b9f1520366d713a73bd30c5a9eb0040d0fb6076aff370796bc776fd705943","impliedFormat":1},{"version":"bc03c3c352f689e38c0ddd50c39b1e65d59273991bfc8858a9e3c0ebb79c023b","impliedFormat":1},{"version":"19df3488557c2fc9b4d8f0bac0fd20fb59aa19dec67c81f93813951a81a867f8","affectsGlobalScope":true,"impliedFormat":1},{"version":"b25350193e103ae90423c5418ddb0ad1168dc9c393c9295ef34980b990030617","affectsGlobalScope":true,"impliedFormat":1},{"version":"bef86adb77316505c6b471da1d9b8c9e428867c2566270e8894d4d773a1c4dc2","impliedFormat":1},{"version":"5a49adaef698b7ad7e6127949fa1b0bbd3d46b7cbd11c54e392a4dcdd51f5190","impliedFormat":1},{"version":"6ee598cdfdd0fa52039dca135b3dfff7b49035dc13292143e0a93843e3861967","impliedFormat":1},{"version":"27be6622e2922a1b412eb057faa854831b95db9db5035c3f6d4b677b902ab3b7","impliedFormat":1},{"version":"5c634644d45a1b6bc7b05e71e05e52ec04f3d73d9ac85d5927f647a5f965181a","impliedFormat":1},{"version":"2489bf04d77dc025ba67f49f1a56eb24b9db477d5ff88123d887e163ed1776aa","impliedFormat":1},{"version":"63a7595a5015e65262557f883463f934904959da563b4f788306f699411e9bac","impliedFormat":1},{"version":"4ba137d6553965703b6b55fd2000b4e07ba365f8caeb0359162ad7247f9707a6","impliedFormat":1},{"version":"0b77b819b5417775fccb20c678293cf614c054a5b1a65421a5b933a9124ba998","impliedFormat":1},{"version":"eb5acb58487367e502d994b57e2c58255d8241f481ea8efa8e79af23af3f41c2","impliedFormat":1},{"version":"9252d498a77517aab5d8d4b5eb9d71e4b225bbc7123df9713e08181de63180f6","impliedFormat":1},{"version":"b1f1d57fde8247599731b24a733395c880a6561ec0c882efaaf20d7df968c5af","impliedFormat":1},{"version":"5757b78830c681b3124af568b94c269259ea5e8171a4316508ef67310c2ed1ed","impliedFormat":1},{"version":"35e6379c3f7cb27b111ad4c1aa69538fd8e788ab737b8ff7596a1b40e96f4f90","impliedFormat":1},{"version":"1fffe726740f9787f15b532e1dc870af3cd964dbe29e191e76121aa3dd8693f2","impliedFormat":1},{"version":"5a3ea721d03a361ccbdd7390ccd75f6e84cbca3a3f01f4b331ecc9af31890c49","impliedFormat":1},{"version":"e7dfaee4af38d45b1cab8a1ee0b3bc1f85ddcf64545ed391d675d78ae6526274","affectsGlobalScope":true,"impliedFormat":1},{"version":"e8daa443eaf9a27fd382cc1f8ebe30330c0f4d89511cfb469166874806751d35","impliedFormat":1},{"version":"af48e58339188d5737b608d41411a9c054685413d8ae88b8c1d0d9bfabdf6e7e","impliedFormat":1},{"version":"616775f16134fa9d01fc677ad3f76e68c051a056c22ab552c64cc281a9686790","impliedFormat":1},{"version":"65c24a8baa2cca1de069a0ba9fba82a173690f52d7e2d0f1f7542d59d5eb4db0","impliedFormat":1},{"version":"f9fe6af238339a0e5f7563acee3178f51db37f32a2e7c09f85273098cee7ec49","impliedFormat":1},{"version":"1de8c302fd35220d8f29dea378a4ae45199dc8ff83ca9923aca1400f2b28848a","impliedFormat":1},{"version":"77e71242e71ebf8528c5802993697878f0533db8f2299b4d36aa015bae08a79c","impliedFormat":1},{"version":"98a787be42bd92f8c2a37d7df5f13e5992da0d967fab794adbb7ee18370f9849","impliedFormat":1},{"version":"332248ee37cca52903572e66c11bef755ccc6e235835e63d3c3e60ddda3e9b93","impliedFormat":1},{"version":"94e8cc88ae2ef3d920bb3bdc369f48436db123aa2dc07f683309ad8c9968a1e1","impliedFormat":1},{"version":"4545c1a1ceca170d5d83452dd7c4994644c35cf676a671412601689d9a62da35","impliedFormat":1},{"version":"320f4091e33548b554d2214ce5fc31c96631b513dffa806e2e3a60766c8c49d9","impliedFormat":1},{"version":"a2d648d333cf67b9aeac5d81a1a379d563a8ffa91ddd61c6179f68de724260ff","impliedFormat":1},{"version":"d90d5f524de38889d1e1dbc2aeef00060d779f8688c02766ddb9ca195e4a713d","impliedFormat":1},{"version":"07ed3ddab975995eea41b22f3010506fb9f5fb301d04820b07d7a1aee5477d7c","impliedFormat":1},{"version":"969d8b0965849f4bae7cab0ba90bd1e1220e95999c2c6f01117fa7500901c017","impliedFormat":1},{"version":"6ec840ee5e2bc103f557fe38b1d585ee250540468713d7634ee066de372bf332","impliedFormat":1},{"version":"b0309e1eda99a9e76f87c18992d9c3689b0938266242835dd4611f2b69efe456","impliedFormat":1},{"version":"47699512e6d8bebf7be488182427189f999affe3addc1c87c882d36b7f2d0b0e","impliedFormat":1},{"version":"6ceb10ca57943be87ff9debe978f4ab73593c0c85ee802c051a93fc96aaf7a20","impliedFormat":1},{"version":"1de3ffe0cc28a9fe2ac761ece075826836b5a02f340b412510a59ba1d41a505a","impliedFormat":1},{"version":"e46d6cc08d243d8d0d83986f609d830991f00450fb234f5b2f861648c42dc0d8","impliedFormat":1},{"version":"1c0a98de1323051010ce5b958ad47bc1c007f7921973123c999300e2b7b0ecc0","impliedFormat":1},{"version":"ff863d17c6c659440f7c5c536e4db7762d8c2565547b2608f36b798a743606ca","impliedFormat":1},{"version":"5412ad0043cd60d1f1406fc12cb4fb987e9a734decbdd4db6f6acf71791e36fe","impliedFormat":1},{"version":"ad036a85efcd9e5b4f7dd5c1a7362c8478f9a3b6c3554654ca24a29aa850a9c5","impliedFormat":1},{"version":"fedebeae32c5cdd1a85b4e0504a01996e4a8adf3dfa72876920d3dd6e42978e7","impliedFormat":1},{"version":"e297c0a524edee7677939122f90027bfbe5f2698939d9a85728e5044b39c7124","impliedFormat":1},{"version":"cdf21eee8007e339b1b9945abf4a7b44930b1d695cc528459e68a3adc39a622e","impliedFormat":1},{"version":"bc9ee0192f056b3d5527bcd78dc3f9e527a9ba2bdc0a2c296fbc9027147df4b2","impliedFormat":1},{"version":"b62381cae176db34f003cc6172ee8f3e0122014889d66391aa73698105cf4934","impliedFormat":1},{"version":"1d9c0a9a6df4e8f29dc84c25c5aa0bb1da5456ebede7a03e03df08bb8b27bae6","impliedFormat":1},{"version":"84380af21da938a567c65ef95aefb5354f676368ee1a1cbb4cae81604a4c7d17","impliedFormat":1},{"version":"1af3e1f2a5d1332e136f8b0b95c0e6c0a02aaabd5092b36b64f3042a03debf28","impliedFormat":1},{"version":"30d8da250766efa99490fc02801047c2c6d72dd0da1bba6581c7e80d1d8842a4","impliedFormat":1},{"version":"03566202f5553bd2d9de22dfab0c61aa163cabb64f0223c08431fb3fc8f70280","impliedFormat":1},{"version":"41eb514d9ce0a6e87957f08a4b7af70d93f87637f37dee706e2d92a6601c25a9","impliedFormat":1},{"version":"e7765aa8bcb74a38b3230d212b4547686eb9796621ffb4367a104451c3f9614f","impliedFormat":1},{"version":"1de80059b8078ea5749941c9f863aa970b4735bdbb003be4925c853a8b6b4450","impliedFormat":1},{"version":"1d079c37fa53e3c21ed3fa214a27507bda9991f2a41458705b19ed8c2b61173d","impliedFormat":1},{"version":"5bf5c7a44e779790d1eb54c234b668b15e34affa95e78eada73e5757f61ed76a","impliedFormat":1},{"version":"5835a6e0d7cd2738e56b671af0e561e7c1b4fb77751383672f4b009f4e161d70","impliedFormat":1},{"version":"4b7f74b772140395e7af67c4841be1ab867c11b3b82a51b1aeb692822b76c872","impliedFormat":1},{"version":"7bd01f0f28cd3aeb2046274d85208e245965f6f2948edf4f7b2057bcf9f22ccc","impliedFormat":99},{"version":"d2f2cf2b8cc92bea913cda4a076e0f790b23a21e84f989d12f0116a7fe3906e0","impliedFormat":99},{"version":"6de125ea94866c736c6d58d68eb15272cf7d1020a5b459fea1c660027eca9a90","affectsGlobalScope":true,"impliedFormat":1},{"version":"f5b20bc288ee49989c95b20847fc93b96bf61cc0845598897a6a53a967dd7d07","affectsGlobalScope":true,"impliedFormat":1},{"version":"064ac1c2ac4b2867c2ceaa74bbdce0cb6a4c16e7c31a6497097159c18f74aa7c","impliedFormat":1},{"version":"3dc14e1ab45e497e5d5e4295271d54ff689aeae00b4277979fdd10fa563540ae","impliedFormat":1},{"version":"d3b315763d91265d6b0e7e7fa93cfdb8a80ce7cdd2d9f55ba0f37a22db00bdb8","impliedFormat":1},{"version":"b789bf89eb19c777ed1e956dbad0925ca795701552d22e68fd130a032008b9f9","impliedFormat":1},{"version":"db2d933d8101f90deeec6698e70f1e14729495c5daab3199f4cdf0ac78a87bdf","affectsGlobalScope":true},"7ad303e40d4fddf44f156129e397511953a71481c5cfd86b1862649aaaf240cc",{"version":"04471dc55f802c29791cc75edda8c4dd2a121f71c2401059da61eff83099e8ab","impliedFormat":99},{"version":"5c54a34e3d91727f7ae840bfe4d5d1c9a2f93c54cb7b6063d06ee4a6c3322656","impliedFormat":99},{"version":"db4da53b03596668cf6cc9484834e5de3833b9e7e64620cf08399fe069cd398d","impliedFormat":99},{"version":"ac7c28f153820c10850457994db1462d8c8e462f253b828ad942a979f726f2f9","impliedFormat":99},{"version":"f9b028d3c3891dd817e24d53102132b8f696269309605e6ed4f0db2c113bbd82","impliedFormat":99},{"version":"fb7c8d90e52e2884509166f96f3d591020c7b7977ab473b746954b0c8d100960","impliedFormat":99},{"version":"0bff51d6ed0c9093f6955b9d8258ce152ddb273359d50a897d8baabcb34de2c4","impliedFormat":99},{"version":"ef13c73d6157a32933c612d476c1524dd674cf5b9a88571d7d6a0d147544d529","impliedFormat":99},{"version":"13918e2b81c4288695f9b1f3dcc2468caf0f848d5c1f3dc00071c619d34ff63a","impliedFormat":99},{"version":"120a80aa556732f684db3ed61aeff1d6671e1655bd6cba0aa88b22b88ac9a6b1","affectsGlobalScope":true,"impliedFormat":99},{"version":"a7ca8df4f2931bef2aa4118078584d84a0b16539598eaadf7dce9104dfaa381c","impliedFormat":1},{"version":"10073cdcf56982064c5337787cc59b79586131e1b28c106ede5bff362f912b70","impliedFormat":99},{"version":"72950913f4900b680f44d8cab6dd1ea0311698fc1eefb014eb9cdfc37ac4a734","impliedFormat":1},{"version":"751764bb94219b4ce8f5475dc35d3de2e432fea01a0c9610cd7f69ad05e398c6","impliedFormat":1},{"version":"ee70b8037ecdf0de6c04f35277f253663a536d7e38f1539d270e4e916d225a3f","affectsGlobalScope":true,"impliedFormat":1},{"version":"a660aa95476042d3fdcc1343cf6bb8fdf24772d31712b1db321c5a4dcc325434","impliedFormat":1},{"version":"36977c14a7f7bfc8c0426ae4343875689949fb699f3f84ecbe5b300ebf9a2c55","impliedFormat":1},{"version":"ff0a83c9a0489a627e264ffcb63f2264b935b20a502afa3a018848139e3d8575","impliedFormat":99},{"version":"161c8e0690c46021506e32fda85956d785b70f309ae97011fd27374c065cac9b","affectsGlobalScope":true,"impliedFormat":1},{"version":"f582b0fcbf1eea9b318ab92fb89ea9ab2ebb84f9b60af89328a91155e1afce72","impliedFormat":1},{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"52dcc257df5119fb66d864625112ce5033ac51a4c2afe376a0b299d2f7f76e4a","impliedFormat":1},{"version":"e5bab5f871ef708d52d47b3e5d0aa72a08ee7a152f33931d9a60809711a2a9a3","impliedFormat":1},{"version":"e16dc2a81595736024a206c7d5c8a39bfe2e6039208ef29981d0d95434ba8fcf","impliedFormat":1},{"version":"cc4a4903fb698ca1d961d4c10dce658aa3a479faf40509d526f122b044eaf6a4","impliedFormat":1},{"version":"19ee8416e6473ed6c7adb868fa796b5653cf0fa2a337658e677eaa0d134388c3","impliedFormat":1},{"version":"1328ab4e442614b28cdb3d4b414cf68325c0da0dca07287a338d0654b7a00261","impliedFormat":1},{"version":"a039dc21f045919f3cbee2ec13812cc6cc3eebc99dae4be00973230f468d19a6","impliedFormat":1},{"version":"3fbe57af01460e49dcd29df55d6931e1672bc6f1be0fb073d11410bc16f9037d","impliedFormat":1},{"version":"f760be449e8562ec5c09bb5187e8e1eabf3c113c0c58cddda53ef8c69f3e2131","impliedFormat":1},{"version":"44325ed13294fce6ab825b82947bbeed2611db7dad9d9135260192f375e5a189","impliedFormat":1},{"version":"e392e8fb5b514eafc585601c1d781485aa6dd6a320e75daf1064a4c6918a1b45","impliedFormat":1},{"version":"46e4a36e8ddbdfb4e7330e11c81c970dc8b218611df9183d39c41c5f8c653b55","impliedFormat":1},{"version":"3cc8a3d123b6b232d48d34b51b785f9da8d193f5b5817fa521fcd2f3b9315c55","impliedFormat":1},{"version":"6332f565867cf4a740a70e30f31cefba37ef7cebcf74f22eab8d744fde6d193e","impliedFormat":1},{"version":"2977b7884aedc895a1d0c9c210c7cf3272c29d6959a08a6fa3ff71e0aff08175","impliedFormat":1},{"version":"17f2922d41ddd032830a91371c948cd9ce903b35c95adca72271a54584f19b0b","impliedFormat":1},{"version":"3eed76ede2a1a14d7c9bb0a642041282dcc264811139d3dd275c9fe14efc9840","impliedFormat":1},{"version":"354a7f8e1287d9d6b7561bc97fdd8cbc2f7c1dd79e4cb37b942e8a5cfaff1085","impliedFormat":1},{"version":"8d369483f0c2b9ee388129cfdb6a43bc8112b377e86a41884bd06e19ce04f4c1","impliedFormat":99},{"version":"960bd764c62ac43edc24eaa2af958a4b4f1fa5d27df5237e176d0143b36a39c6","affectsGlobalScope":true,"impliedFormat":1},{"version":"3fd8a5aefd8c3feb3936ca66f5aa89dff7bf6e6537b4158dbd0f6e0d65ed3b9e","impliedFormat":1},{"version":"a18642ddf216f162052a16cba0944892c4c4c977d3306a87cb673d46abbb0cbf","impliedFormat":1},{"version":"41c41c6e90133bb2a14f7561f29944771886e5535945b2b372e2f6ed6987746e","impliedFormat":1},{"version":"4ec16d7a4e366c06a4573d299e15fe6207fc080f41beac5da06f4af33ea9761e","impliedFormat":1},{"version":"59f8dc89b9e724a6a667f52cdf4b90b6816ae6c9842ce176d38fcc973669009e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e4af494f7a14b226bbe732e9c130d8811f8c7025911d7c58dd97121a85519715","impliedFormat":1},{"version":"cbb1c5ba5dbabe42c19ca31b83e48fec95895484fe1d1a8fb649b69ea224c5b8","impliedFormat":99},{"version":"45cec9a1ba6549060552eead8959d47226048e0b71c7d0702ae58b7e16a28912","impliedFormat":99},{"version":"6907b09850f86610e7a528348c15484c1e1c09a18a9c1e98861399dfe4b18b46","impliedFormat":99},{"version":"12deea8eaa7a4fc1a2908e67da99831e5c5a6b46ad4f4f948fd4759314ea2b80","impliedFormat":99},{"version":"f0a8b376568a18f9a4976ecb0855187672b16b96c4df1c183a7e52dc1b5d98e8","impliedFormat":99},{"version":"8124828a11be7db984fcdab052fd4ff756b18edcfa8d71118b55388176210923","impliedFormat":99},{"version":"092944a8c05f9b96579161e88c6f211d5304a76bd2c47f8d4c30053269146bc8","impliedFormat":99},{"version":"b34b5f6b506abb206b1ea73c6a332b9ee9c8c98be0f6d17cdbda9430ecc1efab","impliedFormat":99},{"version":"75d4c746c3d16af0df61e7b0afe9606475a23335d9f34fcc525d388c21e9058b","impliedFormat":99},{"version":"fa959bf357232201c32566f45d97e70538c75a093c940af594865d12f31d4912","impliedFormat":99},{"version":"d2c52abd76259fc39a30dfae70a2e5ce77fd23144457a7ff1b64b03de6e3aec7","impliedFormat":99},{"version":"e6233e1c976265e85aa8ad76c3881febe6264cb06ae3136f0257e1eab4a6cc5a","impliedFormat":99},{"version":"f73e2335e568014e279927321770da6fe26facd4ac96cdc22a56687f1ecbb58e","impliedFormat":99},{"version":"317878f156f976d487e21fd1d58ad0461ee0a09185d5b0a43eedf2a56eb7e4ea","impliedFormat":99},{"version":"324ac98294dab54fbd580c7d0e707d94506d7b2c3d5efe981a8495f02cf9ad96","impliedFormat":99},{"version":"9ec72eb493ff209b470467e24264116b6a8616484bca438091433a545dfba17e","impliedFormat":99},{"version":"d6ee22aba183d5fc0c7b8617f77ee82ecadc2c14359cc51271c135e23f6ed51f","impliedFormat":99},{"version":"49747416f08b3ba50500a215e7a55d75268b84e31e896a40313c8053e8dec908","impliedFormat":99},{"version":"5e91172586d1be7d1508d1a40c8bf76161f6f4179d1c25158a20599f9fb26a66","impliedFormat":99},{"version":"09d215b379a93f8cbb6caf9e9f5d7b4d48042295ee697e7e4771288c7917a21e","impliedFormat":99},{"version":"427fe2004642504828c1476d0af4270e6ad4db6de78c0b5da3e4c5ca95052a99","impliedFormat":1},{"version":"2eeffcee5c1661ddca53353929558037b8cf305ffb86a803512982f99bcab50d","impliedFormat":99},{"version":"9afb4cb864d297e4092a79ee2871b5d3143ea14153f62ef0bb04ede25f432030","affectsGlobalScope":true,"impliedFormat":99},{"version":"891694d3694abd66f0b8872997b85fd8e52bc51632ce0f8128c96962b443189f","impliedFormat":99},{"version":"69bf2422313487956e4dacf049f30cb91b34968912058d244cb19e4baa24da97","impliedFormat":99},{"version":"971a2c327ff166c770c5fb35699575ba2d13bba1f6d2757309c9be4b30036c8e","impliedFormat":99},{"version":"4f45e8effab83434a78d17123b01124259fbd1e335732135c213955d85222234","impliedFormat":99},{"version":"7bd51996fb7717941cbe094b05adc0d80b9503b350a77b789bbb0fc786f28053","impliedFormat":99},{"version":"b62006bbc815fe8190c7aee262aad6bff993e3f9ade70d7057dfceab6de79d2f","impliedFormat":99},{"version":"ab80d2cb170a92c61ca8c822d0db0fc50eb15c7c6cf1a24cb01852a5a15a7db8","impliedFormat":99},{"version":"210955af8573671abebb6e1391d134d82fbcff130aa68922ea1e839f8f48a0ad","impliedFormat":99},{"version":"78a8f2e95e2904914c509e4bbc68e626f8b8ff8f30ca96cffc7a795fa17394f5","impliedFormat":99},{"version":"7bbff6783e96c691a41a7cf12dd5486b8166a01b0c57d071dbcfca55c9525ec4","impliedFormat":99},{"version":"061446b67af18b541c723104f25aa94667dd438c050fc873f3c02a7b5a9a3ef0","signature":"b8ee70929b7bfa2ced6aded5f38945440e9ff6809c61d2972b59aaecf88c254c"},{"version":"8ef0b457802d1883c0c185f90610a8aaf33283250633a31853de01bb45565b7b","signature":"b730dbc27807d6a94494d69e0154827379b8ed4606f3dd3a4584a1e2242b1e53"},{"version":"d689a82dec12ec02e1c558870a50f71000e06f173151fcdff0458c587dbf016f","impliedFormat":99},{"version":"e58c0b5226aff07b63be6ac6e1bec9d55bc3d2bda3b11b9b68cccea8c24ae839","affectsGlobalScope":true,"impliedFormat":99},{"version":"5a88655bf852c8cc007d6bc874ab61d1d63fba97063020458177173c454e9b4a","impliedFormat":99},{"version":"7e4dfae2da12ec71ffd9f55f4641a6e05610ce0d6784838659490e259e4eb13c","impliedFormat":99},{"version":"c30a41267fc04c6518b17e55dcb2b810f267af4314b0b6d7df1c33a76ce1b330","impliedFormat":1},{"version":"72422d0bac4076912385d0c10911b82e4694fc106e2d70added091f88f0824ba","impliedFormat":1},{"version":"da251b82c25bee1d93f9fd80c5a61d945da4f708ca21285541d7aff83ecb8200","impliedFormat":1},{"version":"64db14db2bf37ac089766fdb3c7e1160fabc10e9929bc2deeede7237e4419fc8","impliedFormat":1},{"version":"98b94085c9f78eba36d3d2314affe973e8994f99864b8708122750788825c771","impliedFormat":1},{"version":"c371ea3efec17691f019bca670c161bee2f4787b1f464bdbbb10c4117bf0a55d","impliedFormat":99},{"version":"5a2cdf6adeec348bbc876221be4367e8adff0bb78a5680ebd7d71e5c3bad6cc0","impliedFormat":99},{"version":"e004826eac62081f867c66dabd92d3ef7d126d93a70430a2c88429228c3ecc50","impliedFormat":99},{"version":"38d6857b58d2ac42442e396311c542062d4f0dad40f2adb496dd5fd0756ee400","impliedFormat":99},{"version":"34b7d1e2d15845cf08bcf5e3c01adbb92cea1ec27564ee249ba486cdfb28526c","impliedFormat":99},{"version":"64bc7684d633c835220935b80701168771e6ddc8c3d9145af8bb3a3ac7d0c59a","impliedFormat":99},{"version":"8cdbb0f1c8db096cfd876dcecbb38c54e495a4203af88417b762dd6f62daa1b6","signature":"2dc0c34bb13c5ca57ccd5a048d076fec5305f279d22010fc565a27b9a8d66ef5"},{"version":"598c32af38ceddfaf9699b9013ecf2e0b2df7b5d76795c9de010d5ff92c52ad5","signature":"e064b7ccad9850f3a78ba58a45e43e4b3eaf126cd2bd2979896b5885dea07f57"},{"version":"618bb47ac74b0ab39b90743cfe920a6e670b2d3bfa24f37d106b535c1c18ae37","signature":"4c8a45f845c330caf5dc2bc33da6e8e4f317035d08a20fd5f1da986bea511d60"},{"version":"9f50731b7a6739ad4d5d0e00b5d0be3650535cd74d92bf86ba3b81cf57000269","signature":"64be38d2ab0fa005245ad20baf0fc7899f1db575a219b4428e0fc3e550d02410"},{"version":"1d0628911b56f83b654ca0ba826d503b3605926e73b985e754513832eeab5592","signature":"45b43c38bc20ca8c0edcee887ed49ed8b771dbe78e1cb5b4e3ba794e853fa887"},{"version":"6d575d93896c413b308c3726eed99ddd17e821a00bdd2cc5929510b46fe64de4","impliedFormat":99},{"version":"1beebd50610b0c9701d2de263e0183ec22aad6c051d0e15ce9e6cce295c6a40b","signature":"3c49b34b1c62e5d74c637b63276c9acacb605689334f5450cc3f67f560ac0ecf"},{"version":"310c820b803950d18c0ed9376df2cd73def2f56cfcc993f9012008403cdd4843","signature":"6fb95390f4022e0327e4a170917a06de5caad8c8c563c8b00be3cd40a71c759e"},{"version":"4ed96213860296593b569b425eec8dfac37cb5bdaffbce2206c000dc673007c7","signature":"0fbe920fa2bb3439dfa680647a4ea264b7a8ea9bfa75e4cdd9ff2507d69df783"},"a7e69a45d5f2dc289b662813446d0cc7b2b972b05165e41a5501b2080aba0807",{"version":"4720053f6a578540743ee8f3c02278636ada05dfc7184b43433262c4aebbbff5","signature":"20f656d6480d8146a5128b53fee43e77e2851f98fd61b3da28f2d8a5560578b1"},{"version":"ddc62c8eb6b7fb8e8fd0f0f19809530b1e5ba5a131471a6eef65028d0d3b5a6e","impliedFormat":99},{"version":"802cbde8e06732ca0356927b4c9fbc39f5961df58a18b74fdc4e131269293a5d","impliedFormat":99},{"version":"480713ff75c24f445e3f159da28444406e1334730375c94d0aa24523c5e52e1c","impliedFormat":99},{"version":"3ac6eb2cafcb89a552a4923213c705f0fc3c2b50e466eae9dc1a540e3af18bc0","impliedFormat":99},{"version":"073f96a1cfddfedf8695401f8328a8e84a4d98fa5b08b4d894c3885069083cd6","impliedFormat":99},{"version":"66ba40fa928c2fada9a280a61c2b426dfbbfa69085f99913650ff72ddec75b1a","impliedFormat":99},{"version":"4d857105510df8011cfb5b3769dec55624a1df92e85d399cd03bc82bb89d090c","impliedFormat":99},{"version":"19a22f3446387435f13445a31e3d4eb65f132d8e6b7b060d249f0fb138cec698","impliedFormat":99},{"version":"ecc46b24349caeab20d889baf0a6f9d3beafa739a0f2c36afc107dceb15e7b2b","impliedFormat":99},{"version":"b887624859a2f03e78ae6018e96bd269b5318685f42adec4e93256ed7579c125","impliedFormat":99},{"version":"14191b461a91229ff4b388d15b9e15392a8a3af9bf11fa7d0d4cd31405178e75","impliedFormat":99},{"version":"23a564e852dc91b6e6f050584994b35156f6ee8a2d08c493dad04309046a8397","impliedFormat":99},{"version":"daf66c9de89f11011ef703af894970bb15985fd5a4156b8038e895ad4e4616a7","impliedFormat":99},{"version":"d59c3d0c3283c1878913fc2bc88d84160dbcdc69cf06f822ca7ffb39eefef13b","impliedFormat":99},{"version":"05128b72488ad970c2e30ae6b82c7ee232be49ce6def3b4dd56f62d8b7f7704c","impliedFormat":99},{"version":"9eb8e1320fc0ecbfba15c0f3452dfc1957543dfbd466aaf8b67ddb0f2ad0f217","impliedFormat":1},{"version":"4faca872dbd194a17b3ee267bd8ddc3daf3d16df96f4e43a02c7d9a862022c4f","impliedFormat":99},{"version":"87654de60b5cd8d91d59632ec576fa7e313b41c2540073d52814b6cf5bb739e6","impliedFormat":99},{"version":"144a4e5780b800c0553949169f50be285eccbdb0298afd83ef2ae03fef77e2d2","impliedFormat":99},{"version":"66aeb47bf8638d6767f7b4ff684c2d794391c981590073025e98f98e1afed499","impliedFormat":99},{"version":"cd5b0672c9699fe169d69efd65472a874de9d1e25fa8669a934f5f326bf0f025","impliedFormat":99},{"version":"4577621880c696b0aacec6ebd2dbf97ac178ee2e2bfaa0aa3a5260a798220ab4","impliedFormat":99},{"version":"26731910f98a56ed001d25d5167d85b1320def4ffbb76e1cc4b0c6484482a5e2","impliedFormat":99},{"version":"cbaadb95dcc68691900ffa857b3bd7eaa99eeb6c351afca15103560bc87f0d15","impliedFormat":99},{"version":"97b02501eb45f487174d5a0ff89b6a95690d50e9eae242e2162118edd5f2705c","impliedFormat":99},{"version":"bbea0619511648a92fe83d5c8eed6149106d7fbf3065310a1986d18598b83bbf","impliedFormat":99},{"version":"963ece6abb58542445eda863960cf053a98da8f4e8634b7a8826aa04f6f85a56","impliedFormat":99},{"version":"5ecea63968444d55f7c3cf677cbec9525db9229953b34f06be0386a24b0fffd2","impliedFormat":99},{"version":"1d226c1e6786584e97efede708d49f2dbd6f887905f16c785d5f09b300bc098d","impliedFormat":99},{"version":"07ff7d4360fbc945963d7a4a8105a5520d1681a00745c20a962fb36bf04452de","impliedFormat":99},{"version":"1b1c48c4d7cbe6f40616594c2a3f6f95bb1dcefd200a7e4167e47b67725b631a","impliedFormat":99},{"version":"62076be1e1e8b668a8ddcb803402f1aec725a31d592e8722ff39ad368d9cd472","impliedFormat":99},{"version":"2806e4d2a88e0461c3b0c8cd9e7bc8e927034690e33345aa0853439d67f801b4","impliedFormat":99},{"version":"b1d72bde8f54695b85883613af13295a615034b2829dde3a31bd3d2a40eb6bc4","impliedFormat":99},{"version":"26cfaec143443411bc7d5363f274f885ced430b8f4bee25a81f7827248848d7b","impliedFormat":99},{"version":"6870f32dc76ff6f8f6a419ce55add0a011909e8895252d7cca813835f431783f","impliedFormat":99},{"version":"29fcff21ff0ecbe700c7db7f719af2fb4822a08d6d703b6687822535e8bd3126","impliedFormat":99},{"version":"5597cbcd19e16f5c9148c76c914e158680de55849b625c2f6b69723f01f1007c","impliedFormat":99},{"version":"7864233f21a3bd04eb6dfa79103a6c1d0648cf17eb4c47cc7aef19d274dd639c","impliedFormat":99},{"version":"b27f7733758db8f462dadf0ee250056e370413028c99fc723c4a93baa54a7c1c","impliedFormat":99},{"version":"5bd7f6f573ac89ec20aaf326e79394da8a89fbff8a297aa864de9137ca045678","impliedFormat":99},{"version":"81b2bebeae6ec1e73b491fe22a82c7e2d3a8369271e622ed74b6e94ba108a475","impliedFormat":99},{"version":"d0e4a184f48eba140f30e8f770853b884e01694f6aff59b38d0be55b0410d397","impliedFormat":99},{"version":"1d34b3ef8e5926334d86d305477d3592d648adb41fa0110970a68059e13d45c0","impliedFormat":99},{"version":"791e26804cd328b19fc37f7903813e8e41892e70d5241dbe2c39fdb52fdd0c9a","impliedFormat":99},{"version":"02c2773eb8536a50f6e647483e78e8c2991fea8ac32ab69a37f9a24255401530","impliedFormat":99},{"version":"4ac6d584eada1621a7eaa4bfb3dd54e81c2c8a82c7ffdf421ae58d84c3a3490e","impliedFormat":99},{"version":"a522abad9b9b959a9c4bdb4e6bdad96e65d97da9385be13ffc8affc3669fd786","impliedFormat":99},{"version":"ae733b8a8fc9659e24821aa3797d25cfdc205bd31674227b49411ca4d54e510c","impliedFormat":99},{"version":"0aa9c5135c3a086d7c01d8d18409da6b01fd32f09a4a261048f8cb4653f22be1","impliedFormat":99},{"version":"1e185a3af4b4f3bd6fd52fde968f14dcf9a8cbbc4924237270e290e25d81fe40","impliedFormat":99},{"version":"ae42c6173cc8ad49d6ae21187d0bb7c7c65da10204f9e2614eeb83b29c58f4f7","impliedFormat":99},{"version":"050240464b97ffce2e353ccd5251660f5d3dcf9dc834f88504732ded7cfe926c","impliedFormat":99},{"version":"3f896952650454552b2584ef1e3dd072e97f8498908cd2ab25e6b0217e8bfeb2","impliedFormat":99},{"version":"3b3d0685f081f6a02cda029e4d1e1ba5f10690870c971e6697e0c2539501e835","impliedFormat":99},{"version":"5998b174ccb38a61393170f40448f80152ca5518f9d2048f5b5d3cbe0a9fbac2","impliedFormat":99},{"version":"6707d39d8afa069222d0674016d48c4772067eb671f9b62528a6cc8218fd5b40","impliedFormat":99},{"version":"481aab62f04afa6eab4e439fb4f39af392c5c51519f548897ff71e6bad0b6771","impliedFormat":99},{"version":"51de9d738596fcc085d13bdf86c0014f15d9b4e6986631c7be3df9d2f61590d8","impliedFormat":99},{"version":"8eca47167dadd486582ecd4e41f7fba6ae66cc4a4c5202f1f7acf34129a0dadf","impliedFormat":99},{"version":"29cc3322fd17fd1b55ea2150ad6f7cb37f0b587efca5696819cc5b6e95331bd4","impliedFormat":99},{"version":"d09b7414a64adc7cae660ecd6e8a222ad9fa58585dd2390eb0aaabfee812b354","impliedFormat":99},{"version":"769b6f9f1cd9471261d137513abc391a744a3c3a62f492491bcde520219fab53","impliedFormat":99},{"version":"49fdbd971a9b57df943498b37cf11c40fe09b2675493039a0b7841671f385108","impliedFormat":99},{"version":"faba6f3b673c89279d3b41a47e8ea2c850665eadfa1e2a56be4f50a6bf4356c6","impliedFormat":99},{"version":"5cbc3c3c6475704af132c35b095da392a03815baf2e9f2853178ff9b370b64d2","impliedFormat":99},{"version":"074209bc8fc6979cfc363d392a8babe62685adc61c62a8742ecdb86fb9b62ad0","impliedFormat":99},{"version":"6826e70645f65e77bcceb9230962687109301a4ad9d6dbb71a7785167d4a4b9e","impliedFormat":99},{"version":"3c8b637a833f97a085417e7d0024ac82f7fafc0834a4c61d5e48f8edd8da6c10","impliedFormat":99},{"version":"a3a4132d6c64f431b6d0cc890557c392f57eb43371bb73979ea38d27c86a1c4c","impliedFormat":99},{"version":"ec03be0777b98df75dcd97657ebfac0eb7a9153867aab050a591b6caadf1c2a2","impliedFormat":99},{"version":"c788aaea8be5712b40c3bd9cf589c9510930af7b2aa3d986125df0dedc569290","impliedFormat":99},{"version":"e7983072a038e512514c146b25e7e97a8a070ca3507950658ab1e96f6598957c","impliedFormat":99},{"version":"f2333bb4a221631fe506a0354fffb808507d4e5f6fe2c85b69890618226f7d9b","impliedFormat":99},{"version":"6c9114366ff07ee8f5c3cd4ba94ad189a098ce8368040909d605fb38e636d026","impliedFormat":99},{"version":"13e930c27d68ecfa906c24d599b10927b152030d07da0fa0889fd4fddc5b4115","impliedFormat":99},{"version":"b4e61f4f522304f7fce1038590ca1f6d091d58aff84833861848f8157732d8db","impliedFormat":99},{"version":"8c86f563e8bcefb0b5b1ac62e5a27ee6a2a9b775e72dea5793823edeb24d36e9","impliedFormat":99},{"version":"d9134c8daef2565f20b72171f634800efb204eba63b03142d5dae5f36088e95a","impliedFormat":99},{"version":"faf9a217d8d237b02ab6d95508d8736ae431bbeb38d98885eb5b8fb6dbe48cec","impliedFormat":99},{"version":"e702ed1fd1dcb24ec2634901441fd156449f75458359c771074cbe7675e86614","impliedFormat":99},{"version":"bbb044421875fc84b7d2f2aac4fb14499687cae5a5063da51bfa28c58239bcfb","impliedFormat":99},{"version":"74b564cd3da8f83d5e472a5b0cc53bf7e276b25576097cb89e6f67caf95b12dc","impliedFormat":99},{"version":"3705ba677801103461ff0a06d34b6b2149072952365e55d8266969978dd33154","impliedFormat":99},{"version":"cca68a7703ec3717b6d4c287884fc79ba811f894c472718126010418cd306aa7","impliedFormat":99},{"version":"590708a598f58b156518493c563df1d03040d3b2b7f75fe614e1ada06dbb44dc","impliedFormat":99},{"version":"dc2c32ad9c49a7c3e56a18f3f42933e91474bc26ecc2ea47cf533818a54e6471","impliedFormat":99},{"version":"e061e898ffe9970c067278f5a7462665e2706e7cc6ce2362276eca1c92c128f7","impliedFormat":99},{"version":"e5ee49966285e5afa0dd2db7f66acf1e8a9e1d0bc5724b03b67be92ea7819bfc","impliedFormat":99},{"version":"4db2be160aa80fecd367876f8cf1aa197cd1f296e5f82ed8d8b961d9ececb204","impliedFormat":99},{"version":"5a8e4a5e571755e265bd6a840d8ab48eeb1ca2e35487d96bbd601ed296e2d1b3","impliedFormat":99},{"version":"d93cac0bbb7e1fe241f4b0493cd47466df00d9f1c51a53b69e5442456cb4d102","impliedFormat":99},{"version":"9de94cfccea0da314e8554d6b2f1f01a1b63fa4c79dc24b54277e86b918e9d6f","impliedFormat":99},{"version":"b4f7f4e2e4d0e668ab7cfd94ae5b72b6c690eafeac0e7a6d2218b16afdf7432f","impliedFormat":99},{"version":"5e8d925c0b8f6f91ac0af131a83f72683f88d80a61f5eea37d8883afbe8f74fa","impliedFormat":99},{"version":"056e9235afb474b7b2ffb6df16ff331f5238027b185367ca745103eb228fe57b","impliedFormat":99},{"version":"2eb77a708b1d812a8b0a57a6a12cbdb659bf43acf839b21b8996dbbd511d6e53","impliedFormat":99},{"version":"9a2d7fe034d084982a18ed744a3e0748f4768fdc5b9f2cdbf5f190e5226b54a4","impliedFormat":99},{"version":"1cbf7a0290d370c2843e79344bd494a10d267b3e0323bb77cf1b34a36ecf4200","impliedFormat":99},{"version":"2dd580520217749fd86cd77b8e48075a6c2ff32339e2334aef676bd3800f345f","impliedFormat":99},{"version":"939fdf70427033c0a05d112c2b03e8e31f037b8f2ac4617df680107162ffb423","impliedFormat":99},{"version":"9bc7a3d724ff20d2429d94e087c276b9256946b2cd66c9f9bce79ab54ec9c115","impliedFormat":99},{"version":"aa813b5adf5ecf364ddcab7bc6652db73d5c4e43ee5f6ccfdc7737f6d3184667","impliedFormat":99},{"version":"e5fcc46e6fc608a77c7efea569e56e3cb02491a9fc0d74f49e784d0a4a6aee14","impliedFormat":99},{"version":"fd413d87e8bf7a8e523c70d194b2c3279016d1ec733a9db43640cc1e0cabde6f","impliedFormat":99},{"version":"126cab464ce86f9c155c0b79f9c38fa906c422ac02c856ff9874051ca35ceb14","impliedFormat":99},{"version":"4d84f055621f07107b6e882b0cb79848106d08899bde344eb6ad0c9bc3539eae","impliedFormat":99},{"version":"25ada8b073df8f9b669aa007ee66298095904b83236652ee940d827c2ed5fe9b","impliedFormat":99},{"version":"24bb5860d0b4310843a2ce164c113315db19861f3fae4f2a56727ca9b98dc4b4","impliedFormat":99},{"version":"b62d96002ec0c8710d0e99aa3175434e1df0f22f5a09291b19e5ec05e8a877e6","impliedFormat":99},{"version":"221c86478853bcca59d83ce0eb2832e575779f2244a9a0176971de55c45b9690","impliedFormat":99},{"version":"7b8940dddb145146d5e62f9d817d5cb9f54345cd17bb91a363c293dd5216a377","impliedFormat":99},{"version":"7d331ed732ddb23a5e04eb12716cff50491ba01b712f4810df496c174547403f","impliedFormat":99},{"version":"a444b1d18b18c90477babf60511e8348ab9d591698205ed1bf12f3a0bf5862e0","impliedFormat":99},{"version":"08ca4dca79ba1cc23d4610ddec493102d3fdef6bb57f025d99b1cba9759f71b3","impliedFormat":99},{"version":"39c2d0f3d8d82809c02668743fb19a50e66f05d4336d48765946e4a051d0579f","impliedFormat":99},{"version":"495e122ec7cd8b18150ec1191e48edda4e23b2587022e59805571ddf8a3b516a","impliedFormat":99},{"version":"9277cadea8fcd4c10616d7667f521274c5fc6cef385861f6962ef880db3e612d","impliedFormat":99},{"version":"20b20c535eb79b2a4a62229abf83f0fcdd3dc1f041fc3c588dbe01e3a7666ef9","impliedFormat":99},{"version":"e98970286b6514c67e3b0f916f23f8bb81ad6fbe3b5ef1f2bb013272e9ccb00a","impliedFormat":99},{"version":"09c1c46f10e01ae7399f2fb391178be7ddd42d70dc6a3abc41d80ccf73badad9","impliedFormat":99},{"version":"31c9882e1d08811f5821ea24554c0bd8a0d97fb7efc661ef76393a28d9a8eb26","impliedFormat":99},{"version":"7c9aecf4946da6395949f23bacaa6d7e9ce287f5aa65e50e69332ee5f4d1960f","impliedFormat":99},{"version":"6701adb65ce407ccabefbaf20862daf55d52dbdb2a663c899d163cc6cbb59192","impliedFormat":99},{"version":"f73df64d28c41e3bc777eca2fb49cb5cda69b52c3786b32d4dc473f855fca42b","impliedFormat":99},{"version":"b342ffdc48ee317927f88cb38871b984b7edf94634428fcad875c7a9fe5515ae","impliedFormat":99},{"version":"247fa787c809e9036079d3f4bf429f5c6e4d76a31647d5547e668fa25c46477e","impliedFormat":99},{"version":"f015d64096dbfde32ec9117706e6e1376e9ed0ea8534d17d1c4035262fb82ebe","impliedFormat":99},{"version":"0af38d2d00fc29764aead613ae52e263e235289ec9e2f365e909226e8b2df2a5","impliedFormat":99},{"version":"c610c569ccfdbcb03d9e531ac1be3ed944586e099bf4f756885fce2d5e1a680c","impliedFormat":99},{"version":"2062be175b1e4f6a0b6b21b4ad08c1e241833349fd82aae558000acb2a9c905b","impliedFormat":99},{"version":"32c98d5e98a05f108f4e405c853db481f83c5a1a9cd6c53870501d8248f9afad","impliedFormat":99},{"version":"c717d81d125641e3d95b30cb00d3c0179fdcb30c9e716c360aeb23c699e51321","impliedFormat":99},{"version":"9a96c65bc8d115c4cd1f6d61305013640593f0c0f869a2e6cebb7bbcdcd7313c","impliedFormat":99},{"version":"d5e101bf2eaafcf94b79c0a80a8b86e26ea0b24234f8f5b2c88b58cac0842a74","impliedFormat":99},{"version":"27caf95cace62037352d836d1c547a73363248289ba8b05205cb9eef146768ba","impliedFormat":99},{"version":"8ac1275f4eef836ace2b3779aa240cece0a7094cee65e3a56fc730a270695b0b","impliedFormat":99},{"version":"4dae97da440251bfb634edda1739b3cf39e66b56076e05d7b06bd3181a6fc500","impliedFormat":99},{"version":"66a183f89f492290d10baa4bc6840fac3a0212cd3e32f2230c1506eb6b1f84db","impliedFormat":99},{"version":"6576f83f333348274a02f3a9a048dfd9c0fbcc3515ec4e654def0ec5491a6261","impliedFormat":99},{"version":"f657e9bb81b35be0d298f305f4a6924c4b652692f9d48512038015e7eb79c7fa","impliedFormat":99},{"version":"5ea4c5fd9091e33b07825015ed1cce784854121cf42d337f0762f1d707ffacfa","impliedFormat":99},{"version":"0a6af3e7a2a63fec578ef9940ace9987eaa91450112efa665dd94cf26555463f","impliedFormat":99},{"version":"172423ba720956a2999c4e44a640d6b141c1c8646d96e8a88a333181eddb1eea","impliedFormat":99},{"version":"8d1ee20c4ca7a97ffb6c9b19049a1a9ebb34bfff32379261bc6295e82cb77abb","impliedFormat":99},{"version":"258436bc14be16b94eefec3da57b4eca7a3c1df633c79d4ccc35f18eaa9d8107","impliedFormat":99},{"version":"0ad0d843d93b5bf3fdaf79de4e159e28d6f9367a945970413205f345e9797cbc","impliedFormat":99},{"version":"f9a161a77ec523402d8d7dbaf9a04e9fc3d32d0b304dca4d7a86412bfdd1b1c9","impliedFormat":99},{"version":"ecaa337dd6eaa40a78934bc53a46455c969a9e2ec75e07da806552e5e1f5f575","impliedFormat":99},{"version":"747703dab2b5bfcb0f4372616373cbbe85a8a9e246bf4f2002252c54f79750a6","impliedFormat":99},{"version":"c09f4c7ec02ad3b5be269a3e220d69d3f16d43fe3843e2e75263344d3ce7981c","impliedFormat":99},{"version":"d351678cfdd7d86b5dbc0c75eaf66ada923f7ff1c76102508ac22f703cb9b927","impliedFormat":99},{"version":"b3d820765aa7672d9276e319e9a2b4d7a928b5dbfe34169e287bc2c0a03be70b","impliedFormat":99},{"version":"96ce9dcfef17a1945dbb4ec0ff2256f3847e813671bcba46381fa6673cf8b202","impliedFormat":99},{"version":"0d852b4958e9b9dee49676e33381e33280a0345bec8fde3f902b479bd0f69e37","impliedFormat":99},{"version":"1ee834bd1a5b21ee9f0f8e683ed8f46410f2548f5b81ae090c14fa41ebe3173e","impliedFormat":99},{"version":"fd875069349f1541cdbf2859ca8b0acdb81acaffeeb579f74dc08b332e8a2fc4","impliedFormat":99},{"version":"fb6994ae9a491ff440c5a78667f4d5783fb6c5827050db94f9ca7fb14f8ff260","impliedFormat":99},{"version":"3318f774e0fa8cd7decde2830e561c401e53057ea505c031f687966a16f4b32c","impliedFormat":99},{"version":"c130a5e599c565b49b02dfdaef22c5dc68bf648a9678339b44e8913d3d27ce71","impliedFormat":99},{"version":"06cb5fd4ff2e5cf532dfc6bbeae7b47ad7c2879909e6727ffdacc558115ebf0f","impliedFormat":99},{"version":"5cf2e81f262bee804fa9d50112c5288ed4224243b1837c653c9eec5a621a9b13","impliedFormat":99},{"version":"aeea67ef93786c8625e6c2840c5be41e6f6679f9890bc75628ac0a3cf8ea0c04","impliedFormat":99},{"version":"a37b86cc490287c9723338ed95965a938886313f0f912ba12f789462d8bad89c","impliedFormat":99},{"version":"41a073e65cbf693b4ca1f61f6847e16227d023cacfa75a84fb989efc3545cb19","impliedFormat":99},{"version":"e726badbad2c619272fe4fe528dd07cd5ef87bda456dc3656e4fd1bcc11976d0","impliedFormat":99},{"version":"60b0f3b27eed4652b4cf70ff359eecf92d1dadce962239812474436b4d608da6","impliedFormat":99},{"version":"4d7002dcc54793296ab4c4b1e28c00e99cdda63ef31b83ef616c58f8773c25bd","impliedFormat":99},{"version":"040dbae8a47533338afa394e6974e753b4bfc1895c322a3a715eb1be21eab5bf","impliedFormat":99},{"version":"a9d4d662f3494ab31e98c8193f20b0725a9488225df92bb4df2d9f96b5b7a166","impliedFormat":99},{"version":"6170c6827bcca40ead01d9a8e92e73049b82a0e595f1c11ef39bb98282781f7d","impliedFormat":99},{"version":"5dd074521b20eeb26c76fb3e1d0f85fb4bf26cd247c7dffbed08bd888a6d29d3","impliedFormat":99},{"version":"f3a8d4b406af14afba34488fec9b89859900a8df10510a23d9f1c2e8a116d3fd","impliedFormat":99},{"version":"b03aa91aef645f9856216a2223a47001a84954caf37b7ffb1d63d1327b4231fe","impliedFormat":99},{"version":"01b6435dae2508e231ded5ca79334075da7d6ca12d909765cb335211a90ba86e","impliedFormat":99},{"version":"75fc3992422a1d3b15788ee84656da98a10ce15ce5ba257a0df623a024a0d845","impliedFormat":99},{"version":"b6c5cede83853964b2f753d7e202613e1d461857cc3780a57a2a3d346c5afc0a","impliedFormat":99},{"version":"ffc4846043b7f71f310692e4bd38f349373981b832f907963e4bbdd4288f130e","impliedFormat":99},{"version":"54a730e06094b37f96436ccc8e736bb65b74d256439bf1663344e3fab16d2246","impliedFormat":99},{"version":"1389cb1ca8557f7380f983f00c337969542d6c932b1ba294b48f97f6fd1cb69e","impliedFormat":99},{"version":"85cfc4f1cd043b1df65ba7714d292ba7c6c79c9e288db0d4a9ea6a7b567a675b","impliedFormat":99},{"version":"74cc10ca21f4fc15188d7e7aafd66de5c34c82d011f0c9e02b05b9739e0fa31c","impliedFormat":99},{"version":"abe83f442f76121715241d0fc207d2c325510c6a4dfa6b07662f550c95c6a2a2","impliedFormat":99},{"version":"4b6de64797fd57745c2856f26b4c7de6be543f9335dfde7870a186c3541ff183","impliedFormat":99},{"version":"765122fafa15af14742c91619b7e30b36e5c38f01e6ad079d2c5ecd38a4fc45d","impliedFormat":99},{"version":"1194d3241ea56738d7d8e2b4908572a350cfe7a85b82ef89828ea32e20ab1803","impliedFormat":99},{"version":"6449789627c9555d2914c88498ab494cdc4f18e28a7426a1e74dcef3401f181d","impliedFormat":99},{"version":"17326f1b693cd3a0e89fdc1248097f0135adacbc072b0ed62cab9eecb1c21743","impliedFormat":99},{"version":"1b322be99b786ec951d3d14283aeddd32c3ab25033c4cb984b5224630317b232","impliedFormat":99},{"version":"c7523c0ac422da80b521031667dc06ca66d817ce5ac47f69db6fa98531febb26","impliedFormat":99},{"version":"19203771ab06e45e1524b7f608b332cad7143ba3ff473e302827a835ecd99dbc","impliedFormat":99},{"version":"3b7b9365174c24792ba2c762637b0bd5cbb8d88a72153e3f7f82d34e115d5647","impliedFormat":99},{"version":"7e3a2715195f927935488d7565bc30e7f540797776e1de208c64720d4ef87f77","impliedFormat":99},{"version":"5804ffbc65b78751fd510218b90827a7ca677ca34a45b4709a00783b658cbaba","impliedFormat":99},{"version":"0945a03ba41861ce8f75468e2bd1bfd424185418921fc2f55cac5eeeb5049c3d","impliedFormat":99},{"version":"246dc85745d220f0a1041d67bef89de1e02fabf49e6ce896bc1a345eab1fc507","impliedFormat":99},{"version":"616b74da95e0f9bca845458de4a8b25f12142b4a7b02e89882da05b4cc115802","impliedFormat":99},{"version":"b894722e4b4205a60154ee3d6fa8ecc3ffdfb92a7bd38936f666d3f00be6649c","impliedFormat":99},{"version":"1f7f05258c0992bd696cf00984e640011ae5477d7aac3b80fcf61bf27f42fe88","impliedFormat":99},{"version":"9129342b97e39ef2c9df4848dfe011329cef9b27e719c7913fd3859be5fc0cca","impliedFormat":99},{"version":"351edaf90b54a559e1759f7ceb54b7881079cba5f4d6dcf15bdb26f1877dd2c6","impliedFormat":99},{"version":"3f79205d951373afec1ca713cbda4be9816d97daa795a9e0a37fa3ae5429afbe","impliedFormat":99},{"version":"bcc4c8b5a39356915b8d366e3499a28adc89e2e0bffc02a108eaec1c4797a58e","impliedFormat":99},{"version":"5b2287eec9804a7fc7c6021ae0a7a92b0160750eb21604b77203589e2ad905f8","impliedFormat":99},{"version":"03870a19c7cbbad803b0ee2d69b777e12be7734e087ccfb0c862529a41cb493b","impliedFormat":99},{"version":"46a52d6ee42784826515dd6ab9f5afaab3a05dfb49ddd8298a2026b6c756b944","impliedFormat":99},{"version":"995f334b04df585cb2a77b74533441293ff1e1d4549c86dd5495494c1fc3969f","impliedFormat":99},{"version":"e83b7824f3d983e9b8c2785541579cd8d8c153e96959e71ab4f69bd83c71f953","impliedFormat":99},{"version":"7b0030262f3d2cc74ae1dd79f4990a7131c34935b2c177e6cfa17a88a6ea56ee","impliedFormat":99},{"version":"a923cde26c2e5431e455844ac5f31126d45976c85f347c7dfd2b9eba3e8ef63c","impliedFormat":99},{"version":"3b2738cfacb777ea1f53acdb26b4f4306fa3dbac7fc5d0f1c4750350d3f5741d","impliedFormat":99},{"version":"b95d11a17e57f0cd0ab04aa8148c8f0ca3a68f56c9a44ac9179cea8a6cccb546","impliedFormat":99},{"version":"7688f3196338007600eba7158240aaa15ad524ca42c204fdb3888446fd690086","impliedFormat":99},{"version":"514f33cfc8bf4a00d0603f6df438959657ce42f94e93e29df29fa9b58e7d54f9","impliedFormat":99},{"version":"7568cf2d6e505847c539e63406ddbde2ccc0f96f2e6c5f115a4b9774d0b55aad","impliedFormat":99},{"version":"d05bd9004c654c2583de473d77f047f03719e3e7bdbe62861371755208e36d59","impliedFormat":99},{"version":"74371225d6032ec7f73b46e736d9ff6ea3626be6fc7959e8b71fedff0bb75cf4","impliedFormat":99},{"version":"06dd247275efd44b3f91270763246700353f1add0945380bdbca8c90a517f9f1","impliedFormat":99},{"version":"0833e55be9920ff787cedb7ea623e97ac9bab28961e0e11aa4a56d36d6074dd2","impliedFormat":99},{"version":"92fbb2b6566fdefc6ba3f151299b2618bd1780cf26c2d0078dcd7f1bdc1c551e","impliedFormat":99},{"version":"3b5317db0574b276c1ecf6ebad9faa974f4e416786b682ed1f854cc85837c3df","impliedFormat":99},{"version":"5f27b1f1b03636451e90fc414bd8426a1db25ad438782354bea60f47d7efb9d6","impliedFormat":99},{"version":"53eb12cfe4c56afff32a3b8adec4fefefa12685c84202c8207351004d30c3b3a","impliedFormat":99},{"version":"05bc3698de467024d02654162f1eeb4edcb0ed9d855a96133572969a6f3675c4","impliedFormat":99},{"version":"a5ba4a306d8bc21ac2fef4e40e9076708dded0176aa21484f1f6da23a4d400e2","impliedFormat":99},{"version":"1c825d1f1bd9e70c306f6c16a0a6b76ccfe4be9350857831eba93e59b95fbb5b","impliedFormat":99},{"version":"b27224caf8db7ed9edf9b12368cedb963bbba3a9b5143c68dff53f5fb2351c96","impliedFormat":99},{"version":"eb3bfb8488f260946c5bbf5d9e730a6e23e0c4a568fbbbe782f3c365e0595dde","impliedFormat":99},{"version":"15de6ee96c8e0f6a78fed11e60c3a0f9b4535c1e6a802c55d65028d500e91e75","impliedFormat":99},{"version":"052f62cd94d56a5ca9d8ce7e68a2201fe8f399a12d7803be2619fd03dd36f1d9","impliedFormat":99},{"version":"06e98ec1e0428de740d985f3480b2e699826d5cd2fe2457f1265b32ff4797ae4","impliedFormat":99},{"version":"01b8daaa0be6124a730b7170c1bb1375f7ed6acf1b4b49c1389199b5ffb600e7","impliedFormat":99},{"version":"7b0d3cca9104d4d9f484ca0a64bf731ff1aea842c8a4bf93618814b1a8281992","impliedFormat":99},{"version":"e40aa12df628390fb3819a883c52c51ef94fd3998e74965fce6a38917a0530f4","impliedFormat":99},{"version":"0df397db19a2db183105dfe900d75798622677a5db73038608bd325f86a556ed","impliedFormat":99},{"version":"ab505b9c7ee7649920023b14384c71e3c542bc7535f51028dff27d70d2b1d6fd","impliedFormat":99},{"version":"29a9fb009bcc76c847dcf73d820d276d6353e5c6c4c016c847d51e42796f68f5","impliedFormat":99},{"version":"743751f2d8819fd7ac9d3ef6378614b6675d3101e42ddc18767901693621cb2f","impliedFormat":99},{"version":"e4a995fd487783122df0848df4c871dbb536e1636e8e7b6f6186d2993e9761e8","impliedFormat":99},{"version":"45da721f9a605485a439778c248dfbc6351341d87de448ec266b74185e090631","impliedFormat":99},{"version":"ec47a4e180f0cf61787ada2d4691a1cf4f7fd65482f6fa9e01444adff3cbd6eb","impliedFormat":99},{"version":"3209d42dcb86b35a13c127fc39981a644b61a1fb0e59524038d0f3bd7fe25768","impliedFormat":99},{"version":"c8c16f7fdc34f8bda36cd9827b11c065e94ae25473465b2b35aa71df336ecf63","impliedFormat":99},{"version":"db07a4e9f69cc9b58930c2d3a4ad1fd9f882794b92208d55ab057443081f649a","impliedFormat":99},{"version":"735a572ced293fa984b3675cce56091902a0529cef028fe016d9670e3a94dc8b","impliedFormat":99},{"version":"dcf0056dec8dc80fe76eac1e8c6fa778a2e4c094fe2d4b120e6f5bcabd820be8","impliedFormat":99},{"version":"6268a89f0ce2f857f6f7ada0045bf8dc990b449f648b51522c0a7d84d016fe85","impliedFormat":99},{"version":"efd3f26f59c3291a0998435ad54c67191b39b4cd0d451ac807afd8da86bc1996","impliedFormat":99},{"version":"19b70aecc85035f5faef7f3da8dcbf199af4ceccbce15a670950377b388c1d9c","impliedFormat":99},{"version":"d621382b4ad80cc27b2f670e44e0bb11a7e85cb0f6a0b043aa0c9b6b21b16a15","impliedFormat":99},{"version":"5c1f5f0c20f5171a182440cd0347dbb94e5c84f5976184f2f36dec92afbd9b9c","impliedFormat":99},{"version":"1a7e2345e3b20202800bc92adbf628d22a74902b8a5c87a6ce3c361d3ba314a9","impliedFormat":99},{"version":"eea9dc67b1bd75f72aad8483567241f5fdbe46436f018df7f0719e7ee5aa85da","impliedFormat":99},{"version":"5bf7ec4d84bfa8c29f32b7cde878e8ef4e11b1bbf0f4edbb9e851efbfdccbd2b","impliedFormat":99},{"version":"3a49927f72440d36c50e1b62f5cbc2f296253d151ea4e5484ecebc8bc461ad4f","impliedFormat":99},{"version":"ce293a2b914083388ff1de83875cc6e82792c5dc1e99c3be4b787f6b150516bf","impliedFormat":99},{"version":"f8d6e2784bb518d523898f614b8c0ae55341968c982d4617f08867b5d11cf354","impliedFormat":99},{"version":"1413f593b860e74f717f40bbf5c934fd77ee6cbbc630216954bc1a364d5d58a6","impliedFormat":99},{"version":"f7e358590496240e80dc08cd1b71ca492e4d27664bb403d3efbb9acef5075b40","impliedFormat":99},{"version":"656e30f229e3a05096b21a8d0b4a37cadda6201d74631fdfd6a6f52f0c158831","impliedFormat":99},{"version":"03206d1ab6b7f08b118786a903cf849768c8c927a21022df88fe63910ddc3433","impliedFormat":99},{"version":"702cb19c1b38ca1b2d158d765869b667b2c1e5ca0e62862b7792285055cb86d2","impliedFormat":99},{"version":"396f903b4d3bcc1d5a72580bb0a8d9f90c7dac5e481b81c2b58df80c968b64e5","impliedFormat":99},{"version":"0e8707f15586d91f92a120b4751048061e04fdee756246667158d4df0105dbe8","impliedFormat":99},{"version":"c37e7b3d6c0b5da08a46d028e980becdd8d48d7b32b7644209695d75d43f653c","impliedFormat":99},{"version":"fecc5365f9a1dd29cc8c582bc0427a7bf06a52c2a42cdb4b25012976628faa6e","impliedFormat":99},{"version":"0c073335c77c5ad0240a0303cae56c8be8da93e206591c5a5a8bd6a613d78d18","impliedFormat":99},{"version":"827c1178e5058f0aaa9047725b845d598f0f52871792441412059173f895597b","impliedFormat":99},{"version":"9e8ed20b5058a6f5f773f420c0efce5c8eb802c0af94cdb96b782cf2acf1b00b","impliedFormat":99},{"version":"8b0336c60458945b1fee185149fa4b5a512917aa171d4232b1f0805c3c12e31b","impliedFormat":99},{"version":"b15377bca02bd4d77f5d089fa0c7a13dc251b104de3b43f62dde6955cc8ef7e8","impliedFormat":99},{"version":"020409dbb29a4396e3c1c0732a0f8afa939e47c935182f6fd0603e21d5a6a8f2","impliedFormat":99},{"version":"bb259ffb75be8a11b1be05d135a561391f7123110d75074eeb5207be382ceb70","impliedFormat":99},{"version":"1f52c9be8dfb11cc31d9e2aa4f950ef56aa8eaef1b78949431882f70e10487de","impliedFormat":99},{"version":"1e685ffce849148fe9e9649189957078d9495608e9df42cfeab20367d2d70c75","impliedFormat":99},{"version":"97e30735672fbe25393231a53ab5e3b63d34e74d0697c59ffc034f9119c23d31","impliedFormat":99},{"version":"84ffde0a761e4b6cbf3cf90c97c4c01608962e8b55082f3705d29465a194f449","impliedFormat":99},{"version":"d874bdb89c1172b0eb109873d39175a5f210f5d853439e7eb250102622edb0d1","impliedFormat":99},{"version":"2b7e61a49cb27bbfc53fd5b888705290beb2d1fe78a8b433bac1ce7544113904","impliedFormat":99},{"version":"8b28a7039c2ccb5108bb3a3b771ca430db73c4ec9e47031303b8e87732a859a0","impliedFormat":99},{"version":"156eb4c6ef17eb61507364b320e2812cfc5afd862cb1baa251b2ab412384a2a9","impliedFormat":99},{"version":"9efc47a0e98346bfd4b386050634b4e150e6c41dd6d9b2bc1288e80a0f345390","impliedFormat":99},{"version":"2bd0a3ea02475382ae8e87d78e3be763dba251ddf9629664ce73c706b400dc94","impliedFormat":99},{"version":"20008d2327e19c4fd051a2c0ee88ea696d704bc6d7ad39988fc509d81c27a485","impliedFormat":99},{"version":"6cee28d40bc224e61f12e867140ab6d677a03a1defc9ade08b1bd60ab1c06524","impliedFormat":99},{"version":"3a0bb28315b2084f25a012275ef45e180ea80d9ca4bbc37665b9c67e912e998c","impliedFormat":99},{"version":"17662ae9763596c2ddaa833f9e326b3de9289098a71457ee18d2db9407cc681b","impliedFormat":99},{"version":"9442dcf95088615dd8ea58077ebed1f7d5dd662caca210b245a6b19f38984038","impliedFormat":99},{"version":"baf0ad4aa9df446c5b08370689dc08e23e112fdd1a022293676254fbb7897a47","impliedFormat":99},{"version":"e3a929f769e33c3001244a06d6a3e025083be64599c1e961aee31145d623e824","impliedFormat":99},{"version":"083493311f28114ab250a8f379798214e91f264dce121fa2140ae58376fc48c6","impliedFormat":99},{"version":"fd03b3ac929f2bcec6710176bbcdb34969d7f9810b01f65d19cbdac143a2c7d9","impliedFormat":99},{"version":"f3e2f84bdacbe962c856add41824ccfd66fba7b320753f6e9c6871cd6fd5133c","impliedFormat":99},{"version":"d70ae743099d2615ffab06760a3571a2beb01fcb27366cce4025544603a6081a","impliedFormat":99},{"version":"530fcba9474606ca2eca0b85f91b26d5e24c31431c27d20403928d51f9c1931f","impliedFormat":99},{"version":"c483babd94cb2effd09a918f5cacae5fbc8cdcb8b65b1a28cf07c2a9381f2a0d","impliedFormat":99},{"version":"c808470b50113d547da502f2380c6674fd41908d641663e5944a6070113469cd","impliedFormat":99},{"version":"f0568ac6f1c90cb01c4a2b3d14c0c6e734cfbfa34eebc57d789db55e7d0d34f1","impliedFormat":99},{"version":"0ae4ff7dd81505058a06f617152c94802f16fc7a8d2f768c8794f53f8be57178","impliedFormat":99},{"version":"3f61a28c42e990b337e084e92d7fa7df04f8a6b6699da3754dc59611d189b40e","impliedFormat":99},{"version":"73fbbf32113d791d019c474cf474344bb36d4c375f9622728163ad5640492a39","impliedFormat":99},{"version":"91b6fbc14c8a81bc1751cc033f55e0cb6f3b346653d51e30efb7995ecf969ed2","impliedFormat":99},{"version":"77e2fd9131fc81ffaffdc85a8ab553f869f2a67b236ceb95b85b9a1bd72b8823","impliedFormat":99},{"version":"330213ff23c7adbbb6f1b5ead11fb8dfb731c5c24f8c4a18586acaaa47e74077","impliedFormat":99},{"version":"a9f07992ccd51ff2a089628480d51364e19be7e5b22e04edd7e18a519c50e2fc","impliedFormat":99},{"version":"26f62f6b63fff6ad7abd3fc5d89d36f8c74f6ddb32d64795556d0ad3ac6b2d29","impliedFormat":99},{"version":"30c55932c3859c15cfb16c4cf3cda9c303588f3216f8b1ca205e2c41bf801402","impliedFormat":99},{"version":"b727fb19b28fdd8abf41b989f9ec0a6aae52cf07f3918386ad068b33d20c3468","impliedFormat":99},{"version":"d7fad08d42a437ea163bec1c3d08e5e4714a27636d89809602f04328a54a3fa4","impliedFormat":99},{"version":"22469dbd699381a169d6e02d5c080ba9d94b9d6567b7a5c41cb17f505e6a4ad7","impliedFormat":99},{"version":"44412e7238512522c472296f100c52c0accc20d3ee75db7aa503ad4d92b80754","impliedFormat":99},{"version":"27c4c4f9114b51cd89d2ba83e9fa60bacc6c29a1279f2f3b91d19c2f7b2c68ac","impliedFormat":99},{"version":"6acb809bb284648297faaefcb03e0e4500de5f78194a08b75512e13e5887829b","impliedFormat":99},{"version":"c31062874243eeb47ba70f53686f860d4c238bed5587af12ea4f73389ce2333c","impliedFormat":99},{"version":"b4f0992a1069bd5af311d02a49dae7aceb5e0400856449bd766b994267e2adba","impliedFormat":99},{"version":"adf2b0d2362e1b4c99336c56293ac3da8aa0d3ebbda67f963d4a0f3d3ef2a021","impliedFormat":99},{"version":"5fc3d9350eb34ad3cbcb1b69249161a33ffe19d7c0e72e6087c947046de6f756","impliedFormat":99},{"version":"b0418e08aab8aa9e4e406428964d2adf8187dd29f6cdaea32ede28fc36e86f56","impliedFormat":99},{"version":"be4147ddded6518b57942a23f89b50b772d841a97e22c93b70eddf901c7581d2","impliedFormat":99},{"version":"6b952ce628d71b1e1644cf8aea26a4de997596197158dc7b6e71ec356a8cf992","impliedFormat":99},{"version":"2f4dad0e02e51c0d630d46dd18b3a99a1d8c9f184af3e9d109027d8d11735f9f","impliedFormat":99},{"version":"41c5600e8662d67b2a149c2eebb422c80cc2337945f5b79dde92d41427499496","impliedFormat":99},{"version":"9c1e78acaead99ab9c612e54f5e16c0675cb6863627ec2dffa0c3d5651d53659","impliedFormat":99},{"version":"6df75e65602bbd54c977312ed62988e0c64423b046ed74ca126b529970233a2e","impliedFormat":99},{"version":"3ffc0815b3b1da65f6fc42a2a10aece2bda56d024cbcf7477b6380d4249ff8a1","impliedFormat":99},{"version":"7cb50c74ced03d93407f80f61840b52540cdc0ff7189ca603e6995306c2b25c2","impliedFormat":99},{"version":"a44dd85a5c1ba838eea01fd555504229ee74d97b1d237741598e7d97c0e857ca","impliedFormat":99},{"version":"aa7f2b3a9f4bb8a225b3a5e5c611b1a034ad76c3d870a1062e241485e3968e23","impliedFormat":99},{"version":"eb41d07bb7e2d527ac33c71146a3a4802a24d39defb6b8e4d707e5510074d076","impliedFormat":99},{"version":"405bf967f547561f6810f2903df5c5b3c7528d55917fcea0cf251951bedd879b","impliedFormat":99},{"version":"0060d5fcac50ed959be8765d1f5343eda5641109a62eba69696577e004b891d0","impliedFormat":99},{"version":"def4730fa85f358f1257bf2116242bec72080b1a0c70046d0c05ff7f90164707","impliedFormat":99},{"version":"5336f4657e6ffcc8bae26bd762b09b80ae6e3b67dce0a4b4aa99f5baab00c65a","impliedFormat":99},{"version":"8e728eefe8c7160465492dafb86f25085ede8c6b05e360dd2c7129955a155da8","impliedFormat":99},{"version":"c665cdd809976f388c82e21c47a040e5e19ba6cb953d0e0c1c38e1ce61f40922","impliedFormat":99},{"version":"c3bdb6cc2b1abe32815c4894c4d011d4ea80c79d0934d264b467cc6ec0051bcc","impliedFormat":99},{"version":"d40c02d227da200dd6be4e7d56ec2c560c08b9e24a4688a071f392b37953143a","impliedFormat":99},{"version":"01b9b0a56a739482aadb7da55886fa724bc2b557e9814ef4841d2262efb9846b","impliedFormat":99},{"version":"891117d566ab7e1a7798d83c58a20957c1703e92d5a351802081c643cf58faf0","impliedFormat":99},{"version":"6f925dbb5e83ba81d632287af1706945f435bfaec89258540eaae87817804c84","impliedFormat":99},{"version":"0fadf459265643344979f57c02e7ae5fdb5c70244fc9ccece5a1a977fe0b1fb8","impliedFormat":99},{"version":"0e77a1ed700a09eae143529750cc2eef65b8e28d76cf8a6eaf78b7f1afa24c63","impliedFormat":99},{"version":"5408800bf96b2cdd0d8d77e3d52f6848514efbf1590d96d9f8aa86c8ee95bbdf","impliedFormat":99},{"version":"b6e0ad0ba28715ae23a61b1192cdb24c06a909aa58b2048e64e56574aa4da7a8","impliedFormat":99},{"version":"c20b3e5d792dae26f5bbf8d1b73ddd16d9ddc336e32a301b9dd99c68a779f61e","impliedFormat":99},{"version":"d71713801d5419399f8edaaf0471dea5e578dd8b71eefde7abf387fb372feb1b","impliedFormat":99},{"version":"89b56bb82308d69d9ea109de95fff39ef64bcabd250688da972fcea05f50dad3","impliedFormat":99},{"version":"214f90578d41d0f5bf61b4d3de16b4671dc75fe893b803a483a4e7b96e80a1e7","impliedFormat":99},{"version":"32b6a2a6fc20f85513ffb0f35e455dfbaf058f65f063a10dda07ffe9592cd98f","impliedFormat":99},{"version":"8b2bdf89d903b856b52e4d416930701068a3522e9e8c2705602c6e7e2394e86d","impliedFormat":99},{"version":"c988c702e73a0ae03ff6d7868ebc2dd0497e921c3b7ea4fbde42aa781831b8a5","impliedFormat":99},{"version":"8409e2185704c03d12e1522dc4c7b137b6b7524e2fc1f9baee7581ee28fc3d86","impliedFormat":99},{"version":"95195cfacab74280a41490ca2c731fe499a37d7ffcaeac7dd2d9056cdc694623","impliedFormat":99},{"version":"9c8746b57866938dccd94775ccc3abe27e41d182b6f6d32ce82a1044084d3778","impliedFormat":99},{"version":"be71dce0024b565b17433b79dfb73c200bd087568e24e796d712cbd42eebf8cd","impliedFormat":99},{"version":"4d9081308548bde06c710ad7bc3af5e6d7e24538378a4c10eff2e769dec31bd5","impliedFormat":99},{"version":"09e693240afe609150a21882e64d8f34b664eea485d16ae78ac86cb3a47de3f9","impliedFormat":99},{"version":"89502a94ed72858e0018b65766f8deea38577994b7df9d406afc47224fc259c9","impliedFormat":99},{"version":"2c19973a0dad8e650d42349838ecc7bec9e181c28f7aacfc045eb3c0b8c7db19","impliedFormat":99},{"version":"851bba631a33a4413ce53ca3586b8a2d5799d0450207e8f7f9b594340e8d0af6","impliedFormat":99},{"version":"9772a1a4b6a4a8c16e2564c0d83848bc92c5410378710f9da8fb2d912ac32b57","impliedFormat":99},{"version":"ae66b8a49700f9b0e1e857eb7989a033392b92b5a19690c9ed7f8f403a1e219c","impliedFormat":99},{"version":"a095cd74b349b5c587c52343a00871d3a522d5d00614275a608e5c3ff690468f","impliedFormat":99},{"version":"ce2b17e7bb13676b9cfe8b9d71db509625851486b845475bf336e2c6f58a2cf7","impliedFormat":99},{"version":"68ff0025b0ff8a90165ae54d417191c8dddf93c794fd54fbfef6d4ea75f6ca82","impliedFormat":99},{"version":"6ff5a35137457c0c733501de9300f1801ae9abb33aea7bc9c6bf5e9d6d98cfc5","impliedFormat":99},{"version":"71128c986c2bd2554d203c724e897471277d96efae9d67721835e0174bb19a97","impliedFormat":99},{"version":"5118e5ed493b74299ca53eca1e5a422fb8f3207337285fe9206dd5d1f88785ad","impliedFormat":99},{"version":"aacbc0d9b6f47db9784a2193fcc7f4bfb1fc6cc711587a4bbac43e45432332ea","impliedFormat":99},{"version":"5be892a93003f44bc4420408ec0726322928020fe22f9a68264a176dc4eb8b96","impliedFormat":99},{"version":"9cf47cb5d151b9a09d0d2fed8b5858d726cbde497560ccb136557aa203364208","impliedFormat":99},{"version":"981feaf9d706617834eb318674966a8741ea35c93ce33e0ce155498e665d2593","impliedFormat":99},{"version":"8e661b24aed6caeef42e16eba111174c13ed178a660b41fd8f82401dfe129515","impliedFormat":99},{"version":"74606837f50a3a16d02993364c004db527b47cdb828edbd770595d5e4ee8dbde","impliedFormat":99},{"version":"d7e7588481cd78747b1d6a9439feede87c2e497df8448acc74d9803867cfdcc9","impliedFormat":99},{"version":"a46d60895edd2436d8927e02798c82975267d0b6fe3af28d7596177f23da3639","impliedFormat":99},{"version":"f93b561633fc4bf5005f34f0c2f96f48c3e548d1593136cfaa9331d7294ca417","impliedFormat":99},{"version":"d64b9ad5dc93f6dc86e1c13f5e483583597b35fa0ad3170c928e436253b1a252","impliedFormat":99},{"version":"23bbc076a14d01df086f77870c735b053cb1c9dc07c2c8b160f6a04db80c469e","impliedFormat":99},{"version":"7f1f69fdcac775d124fe626182219327b833a962de2c9751073d2643695ce2e0","impliedFormat":99},{"version":"ad774bd48cdebf2909e354cb24ed9ade7763306edda185b7692890a2aa96be4b","impliedFormat":99},{"version":"f53c345523d49bc3e6a11a5f6540ba145b441af3618efaf58d25b58db03d2922","impliedFormat":99},{"version":"164af37e5cde8d2d830b5a5f2aaa6be547b8004e4e98b33fd6977581f8be4d4a","impliedFormat":99},{"version":"2aa08243d9c596b3e993b558033dd391f39ba4d6525ccba992b11bb5be54c04e","impliedFormat":99},{"version":"208371c97acf811ef41ba4b217816aedb802a570129042463b198c7d72d1cca1","impliedFormat":99},{"version":"6311ecffa1680ff0f9587217df76d9556d4c8c623f12464b8beb44461d1d22af","impliedFormat":99},{"version":"16e2700613d061c8a3c21fd26bdff099948396954d5935ce913424d93c97815c","impliedFormat":99},{"version":"0890d6e6870d35b625590a98abc2bd3fa880fa46d0dc3de22dbf01628cfd34b7","impliedFormat":99},{"version":"193814fef68f60058efb9c02cffd20bcbf70eec1d32ea0fce4b5887aef746157","impliedFormat":99},{"version":"c57b441e0c0a9cbdfa7d850dae1f8a387d6f81cbffbc3cd0465d530084c2417d","impliedFormat":99},{"version":"51954e948be6a5b728fcfaf561f12331b4f54f068934c77adfc8f70eea17d285","impliedFormat":1},{"version":"2fbe402f0ee5aa8ab55367f88030f79d46211c0a0f342becaa9f648bf8534e9d","impliedFormat":1},{"version":"b94258ef37e67474ac5522e9c519489a55dcb3d4a8f645e335fc68ea2215fe88","impliedFormat":1},{"version":"a9ff5614fec6e47cd306851cd39e2bb0bd1b939a9776cad032bc06753a24b105","signature":"2641cc270e66b5b412cf0f887ef90e12173ac7773390a8e0008f653358f66841"},{"version":"709504c4a347b021a9984ee3e65359992e9f0f172d22e63030207d0c604296d6","signature":"b0a30a6f3075e34a6a108ff4fb8c54e7714f964c0690db0b6e82bed93ef6568e"},{"version":"c91f4f63d6c2c84d2e0f7368a97dfb126ac74804e775e5aa5bcefd853917e69e","signature":"96d032d99c255b941936f513419610586f7e642f2abb57d1b8d2581f7d442eb8"},{"version":"a313760e9f66c6f819c3426e038acb9aa8f47a59be74062f51321caa88a688ea","signature":"439593d167651f2e1c0c439482dc3d5d5eb248ea221ecd8feb5c62cd0d60cd86"},{"version":"2c82ac3566fa4072c5cc6320a0a786afb9d27c061d41316411483f61353560eb","signature":"e0d9f1fd5544f50032be81792d9409f65c8ea46853ed0450b9934372d4255930"},{"version":"829b5cb87df9dfb327efb8a4e55644d809f3e03de209067122b99ffebf284f00","impliedFormat":1},{"version":"415510d38ea33f28cb571ef11ebd6ee777a377e0d1886b6771dcb15fdde7a02f","signature":"5a4e0d921d1c64c046a46838efd87367a659f8debed6c7f7801b8440576657de"},{"version":"6e9445b11a3d075d64853d8b32efd159b4a45f37b481bbbb7d3bd57f5a5d5f35","signature":"589cdbba6bdaf20ddef1fe78e3bdedfd4e7f6b6e08179a9d8197ded860ebaed0"},{"version":"80d23e36921e787529d3ccae753675b91180dc2326b4c1a3d8f270205b85af79","signature":"845a9728a8fd9284d40c63aaea7b11076866271659517e0ab1a1cbd041bf8588"},{"version":"1aadf3c39d08e4aeea1b9950040079b0fa8baa1d5f9644667cbbb6b9c8c0837a","signature":"730f18e9a86d7032845d6a326f8c5ec9469490304565e2a637f4dbdd8db08977"},{"version":"03a87f22d5567ad70a9761d76f0d16ca6ae32b6201d79c4946e751f2c4cb4e8a","signature":"daaa96af8feb9c538eac60042eb231ecb684bd361d5d7d5fccb0a614a41c365b"},{"version":"07e1a6c1c01468d20ce760a06f39c9e59c56103e63556c7240bbc9e8c2b8d24b","signature":"e6ec95dc819ab75e36c9e4492ba3e6bcf21507403a6afb5bbe8cdea76fd77fc7"},{"version":"e7205096e87497cb983cffe2ea271035dc0f7bae9db702859e5a2d0941d99597","signature":"1a85b0cd6837d60863844ad43f065863cd13b3cb956c369d493761bb603f4b63"},{"version":"47f5078d810ecb6e57eea5f0382dbfb9db641a35460fdb723e920c4898852e0b","signature":"df6ab0ed5a36c6500e0cd4e0928f73f80fa1bc047359a22f5023393f4023cdcd"},{"version":"21e365e7414b00e1dda3cb0e8c1ffe7eaf8f4cee8665857e7a4ab0051c694811","signature":"d36c6cc5adf1dd3c897e4bfe96cfc0506c9352c7413cd83da0d3032f820781b8"},{"version":"800de8bb8ea525980e16dd155bb6e6847e7fdeccaf816e5c2674e1a24c5bfc9a","impliedFormat":1},{"version":"88efe27bebddb62da9655a9f093e0c27719647e96747f16650489dc9671075d6","impliedFormat":1},{"version":"e348f128032c4807ad9359a1fff29fcbc5f551c81be807bfa86db5a45649b7ba","impliedFormat":1},{"version":"8ee6b07974528da39b7835556e12dd3198c0a13e4a9de321217cd2044f3de22e","impliedFormat":1},{"version":"deefd8c43b40f9797c3921d78d3f9243959621a17b817be7f5d95c149f23a9dd","impliedFormat":1},{"version":"5f12132800d430adbe59b49c2c0354d85a71ada7d756e34250a655baa8ad4ae5","impliedFormat":1},{"version":"ec27c0cee1436f58e785f621703d19d588ebbd489eca245e5198b4d6b715790d","impliedFormat":1},{"version":"b16e757e4c35434065120a2b3bf13a518fc9e621dc9c2ed668f91635a9dc4e75","impliedFormat":1},{"version":"efe2821496a760b9128309bb69ad43f1a99feb49d3fd004673c5e406de523da6","impliedFormat":1},{"version":"ea0e3c7d1347a549ac7ec32d3c61a30e473dbbbc901d458064db03f673128145","impliedFormat":1},{"version":"4374cefdde5c6e9bad52b0436e887b8325b8f407c12035194ad02c28f1553a3a","impliedFormat":1},{"version":"5f1ba0898eb0a54a644cb9c95c2240beaa961d87fd080cbb90807a6cc03daeb3","impliedFormat":1},{"version":"8e92ee8710ba85b158c5d91b0bbc9d0d033f5e062b6e70178063f01b20f63a14","impliedFormat":1},{"version":"ee933420aacba1f60aa70fb8ba47c5e69001b005073b71973114587089a13c7f","impliedFormat":1},{"version":"0a0714999d0a5bdfacd15c7b34cffbcc6f263f6cb0ccb42076cdc541c6987797","impliedFormat":1},{"version":"56584bfc655f9df64afc0f22f7d1122c29e5b74b342c203b891e19de9fa37de8","impliedFormat":1},{"version":"40ec58f0fadd0b3981b3d383e1c12fa0680115ae9f018387fc2cfc0bbcf23204","impliedFormat":1},{"version":"59709e26e08d4fd4c6a133552ad8f94c5b31463f295c4bf75fae1907738b8441","impliedFormat":1},{"version":"849b9e7283b7309a4556c9b90bb8e2dfc27751f157798065bbc513dcddb09a8c","impliedFormat":1},{"version":"76bba0c97594248c1be19af32d5799f7eff51cec2926d8e4dd59267d7636a0b4","impliedFormat":1},{"version":"10e109212c7be8a9f66e988e5d6c2a8900c9d14bf6beadf5fa70d32ada3425cf","impliedFormat":1},{"version":"f4558bcdc26690cc593cd59217cd17d8e00af0f5fbd0c4f1c0d71ba75029c42e","impliedFormat":1},{"version":"51d621c4e724720dd1b7ba6374d8a5b988beeda22d620ac84634a13691b631d9","impliedFormat":1},{"version":"f57a588d8f6b3ce5c8b494f2dc759a8885eaee18e80a4952df47de45403fedbe","impliedFormat":1},{"version":"34735727b3fe7a0ed0651a0f88d06449163d1989a2b2de7f047473adc7c1c383","impliedFormat":1},{"version":"a5b13abc88ab3186e713c445e59e2f6eee20c6167943517bc2f56985d89b8c55","impliedFormat":1},{"version":"8b29e3ed0c90b2ebc40b2bce5a518a0e86c0c417f7fe99a5e7658a61166bd9cd","impliedFormat":1},{"version":"7ae65fe95b18205e241e6695cb2c61c0828d660aca7d08f68781b439a800e6b8","impliedFormat":1},{"version":"c2c8c166199d3a7bd093152437d1f6399d05e458a9ca9364456feecba920cda4","impliedFormat":1},{"version":"369b7270eeeb37982203b2cb18c7302947b89bf5818c1d3d2e95a0418f02b74e","impliedFormat":1},{"version":"94f95d223e2783b0aef4d15d7f6990a6a550fe17d099c501395f690337f7105e","impliedFormat":1},{"version":"945be5a9505194381cfd4a8551a5f0ae48090847e454fecf834e054207c5a57b","impliedFormat":1},{"version":"d1e8b78a5ce49cee9ef4cd2565d4645d269c6fd0650e3592f85ba481f13da3a3","impliedFormat":1},{"version":"61be8f1d5345cf5750aed87af2869888ca1b675ffa481f1d4d80554e10084b4a","impliedFormat":1},{"version":"eeb6c806376b9c3464f29b6058aecf113328f9ce290af0375e520f1a844529cf","signature":"61f11ef9f7b473f14c872a139f0a329251738f177edfdcaefb3745adf8967036"},{"version":"216c830de5b7e1ff7336a1bf11dfe9c98ae2de2da56f616e7e4b4405aa14050d","signature":"f9653d5c0a8d7199894c3721eae87d898c8ce6668c3c28461dde2236367c94e6"},{"version":"ebfca49b6f505f572648960feb0bc5e131c9a6bea97f3f5883dfa9374ed4028d","signature":"5d2270355cb77cb6e68e65ae1d5c258abf97b4845ab9653f0ed1626154bbc114"},{"version":"57bd6ff2a23d6d3df5b81817735cb34c18e6c75e13425f9915d146273a27e776","signature":"a1f8f9135feda1e04f2f32d3f5fb9ecbdaf2dfd4fb30be0d1aacc901a55929e4"},{"version":"b16d890b0ea02f67586f064f87af862c601884fd40ec000b3ec8dacdf1c4c7cf","signature":"c4bf08d84391225b229f7d67fe8f7b3ff511782f27e0d6f5f4680aab2cf451af"},{"version":"ed0300836934be9970c950c0d485e8276fac87cf1fec92f07e5f13fb7203604d","signature":"e3d48af43b4af0455edee6944467120f4272a8306e90d504935da490b053cafd"},{"version":"a7e4a0f02427c3e07643a6d5bb9bf0cc09f2ebe28b37a42189f923133c43c186","signature":"01279e64b86fc37995c2df2f8acd601c7126eed6c6245b1e913a0eaa353f4362"},{"version":"5a6ea8dd0a1da3b272eb9ce6c258c6513b5473f7ebcb2395ff7ec7b352b61e93","signature":"0b02029cc6faaf310eaddc6a996db9c2bc36df3075ae2318bca2847f71444afb"},{"version":"172445546b246f00923ce61b907837020174c84335bfa24cddc78b6a5d28d0a3","signature":"b34528c74b3ff693ae3d27488992d045d0da79151d70e3240ea701f4a8910b5e"},{"version":"a207d5278346c5ef6ea5ce0b34dcb377bf4cccbd7153ab83953cee72c59ab34a","signature":"1dd308df0c17f9580459e35f573f15a40609c032465913c8d86a10883edcda1a"},{"version":"f299ec29ad652a02319d39bcb58adf0803a2bb2387a025aec1a0a16f50519176","signature":"9093242bf5a271587e65352246412d050ee6cca17b21bc0990a7fa7f0c5ae5d3"},{"version":"b53aedd97ce9ebdcaf94f60bb120b172149f79045edf37b22858ff10bb61e09e","signature":"ec13261703b24c5ffb56fe30e3d7b64fb29d7ea5fbf548dbb3440646b65e1316"},{"version":"e8731d253cc0a26c73597bb44242c9dce9e7b2c2e3ef9fee709784f301b90ee0","signature":"20e6bc4b20ee7a3a764dafd105809550487d4bec3cefe2eba50c030422d59de2"},{"version":"39a31ce781dfe7a75500c7e3b106b91257ba43c89970570bcf2f74b3b0852145","signature":"1b402b21cb6f47459c223905112ea4d3e37b3be572fd633ba9786b760bd4a49e"},{"version":"d32fa498a8aeb58c092a884aff775cf3bbc2c734cf62a6d3976dbbeef663b4df","signature":"49859dcca80078ff2d8839e53d9a1aa9a9f89ec3f5951123bf04b25bf5b241da"},{"version":"350fce1cf0e1ebb8beaa4205ef82d2d17c0a963bf17829198d7f2bab3508701b","signature":"c50b967b871d37bca57adc25e444664be2536774dcf45576625ddc4cb0711f38"},{"version":"e63c358caa122f2976f93a564b2d27772cd9a87cc8ad3334a4796f89aeb17e85","signature":"2d220697a8464f714de95c5dceacb017948d1ad9814160f51a0ce1a9a98b8e28"},{"version":"6e94f91e94cd57bc68b891cac44e2ca3044f0242761a183c07dc3c5eb523aebf","signature":"b8b8943e02f90219b68ec7d495ecd78c6f8532f3794df792cfa1743da376a71e"},{"version":"1251b05bdf0f9d8118139c3ed7bb0ea60466ee664c2d58e710be9b39032c6f6c","signature":"f2c6a00624f44434d49aef27eac8b74b150c4ad7ea531992cd5ec7b61cff698a"},{"version":"5bcca0c2e2f15929cfa8e0d91bad9f61ab85e7f256377ccc56d6b0f9f8552960","signature":"d77db17aa371d965761001b744dba64792f22b53c0a9ddd4d80d8c8b359c482b"},{"version":"641984c05f82a6e0b8dac973196b8ba146f1644b3706d318427096d844ac4f0d","signature":"eb5c97b219f68b8629c278d916c59c82b514b848ff10eb0db5d4196d69654147"},{"version":"064945c8a414c7a78b237a277403afd2b7ba4bb433d8cdc41fde3cddf09880f4","signature":"20bd6d8b518e6345256f0e7d38f412028f1c31d21376c07a4f41e3b65d0efdf1"},{"version":"5323f2f109370900f8d4f85c82ff47df76a7d63dbef322abf601217e4e677086","signature":"f59baba97905164ae2797a2a2869308ff3435aa1c66fd33034c0237abeababe1"},{"version":"1beb3dd4e06334a36673fbdf6df977bb28d28134285a21da8584cef98b0e7c46","signature":"1f03749fcec8cba452cffab3b318b2bde43ae572704f238a8d8f8ae059d3b86a"},{"version":"f13cc653015347688208b6a2817d6a024cae82cea1b2be422061ae1fb40e86a3","signature":"9d34eaf37fb26f7e3b5d52527e1cb097956ecec3deece2590b99f360ab4428a7"},{"version":"cc319ff8f06a331d4b359aa39f43dc18f8d4402f1f3c446b22a197389c4067a6","signature":"a6d8aa22b2e3abe3192321c687b18ff88b15d42a8c3165a2ceef83a58045e9dd"},{"version":"3c27fb3f66fa5c3798c663843ad30a16957d6cf39d4c4ee8c154dc03b777bd80","signature":"ad4ff92dbea4696533340e64c444a2c6d93c4cc8f12fe2c7017af7d0eb8d2dba"},{"version":"0c50f7da7e287df66e69485e4e5b56c4a0fb9f8730571541873377e7ed45a2c8","signature":"987de9b3dd9352f138928040bd0776e179cdf67c235a18bd54580bc4163a2999"},{"version":"ae5f21d33e9cece1850a7223c30edff9bd2b842b05492b4d1f5c74891683186a","signature":"1bc767c3ecaad8c2a205ad502ee7c4f20cffc11447fadbd4ab3f573481073582"},{"version":"bcbdfb8a08bb7ace1743deca0384743dc28179759d8d856455913401b3e3f2b1","signature":"cf836e95cd5f7ff40aece41f706a0b25b70f4d6ed2d4b82a65f01a41c6e19745"},{"version":"0bea811d597d5b10907dfb36a49c188fc9b1d7ba4785325447e1c78e0eed2d7a","signature":"b5ace5dea1bff2d9f60db27e5b08c348733034474a3abbc97d47cae81d167f9e"},{"version":"721652119aa07fa7df69fec15bb05e7818c69f4798e424b2a444889f482d5118","signature":"c4b089b69cbaadfddd82db7f25dbb73ba7125d4d401edf45e1aaa9262747a529"},{"version":"31f74c987ac1c8dd1bd2a84a270623b054c1fc4ce81a30eb788ce3d579d95e40","signature":"58a5ec371db12fd72d7b69a8c237fc87c5a131763b45d262a3d191c5d1356d6d"},{"version":"7efed9d38ce35662483150baaecb0eb98e400391ada29a436626063a3cd09be5","signature":"56e3f4727284e65c0f755411270bbf10da22e3fe5529baed216b93557b41276a"},{"version":"b3247c06acbd296275f69ae7aaa4572cfc9228e70de48b19ceb4584247fe05c8","signature":"a6a5dd455139bdffd774acbcf9371280adccee55dc5dd44c7eec8e5a5ac9325d"},{"version":"3ac434b15ae79961bd05e34522420d556094a115c238e97824bb72474b367fe9","signature":"02fca86b2afcbe184abefb4b157f0d795eb0062889ea788cf1bd7da67b76eacb"},{"version":"ca88391208aad15040edba757c64498bdd0ae0bcdd591e7c8407621501307108","signature":"6439ab6cac63fd2353c93b1795e267135c30379faaf24c595846710a20c9fa73"},{"version":"27f4208f541705c9e22a604a7ad66a4b03eb6132e1014489689122c33ccfebd7","signature":"bb26db505bc0e0cbfa1d5618a5ca643d928fbfc0b012df4c0e111c96748c6acb"},{"version":"2b3c21ba0eaf7f23e3d91a4d8871886eb43cfc5fe4af69508ea9109119417e2e","signature":"40ec6fbee6bcb9edbe5e5726919d09de143e600a85ed019f4f0647dd7f946032"},{"version":"8b571e8f3e760621f044a9dee0ea495b7cf5da82cb75828992ded169b5cefe9e","signature":"d0948979f76da0bcd6723ad5fe1ceac5c6e7ab4dac9025b278f4ca3119f835d4"},{"version":"10f72058876ff0b07c27916ebff1c9a5e350117fa4632635b2c18e4e287ecde8","signature":"751ba76f055fbbce30097cba1ce5f5d3e6453224fef819a7eaf7f0383ec9310b"},{"version":"3caef034a84be3e33a76222b9eb965906198d8aa25cd874ff933780f55869f18","signature":"3564adb90073a7f8046f6fb26f3a00d86a7681f0bd8433d7fdca8ee81f3a2feb"},{"version":"afa6b33bd7d4bb9694536653237820f279876c907e0fa0436e14f3b5b09e4310","signature":"14ad6a418f276ae00176848d46d92337e4bda91e388663afbe5cadc4e74db63d"},{"version":"2cb5bcfcddafa73663cc7a0b9d07913ff00864af96cdf56ce809d55f80a1753b","signature":"f543efc561c3e8efe2d9061153ffc4a5881bbd1727d13e2eb8f3afd8f61a023c"},{"version":"8605ab3907c8332a03b0fb2bb8ecb8259321c15adf6ec70b4032b85d771cf2f3","signature":"06ae795b9ca99a2466c46639c2ab809198e6c67d400165f05424a012b1bb817f"},{"version":"0b596ac641129a560bec8f495f52adda3c82d92b1a115434a46e9c48080c9157","signature":"19485a0daffc617967e78d145ebf48193c8b2e01162a202afb02bc4cde9547b3"},{"version":"4a96c42ed30bf77b8c127453fda697a212495a055f6dcafda400e42eb233d029","signature":"c4e5d6b5f65bfd77c192b36ab608481de02288d42739f978b0c01a812dc94321"},{"version":"d2f9a9603312036b4c99561d7ad5049bc3e471140cdda96dc8d18f053c81a06b","signature":"48e6c8e53a8e681596e770eb3a65c70c14302492c4cbb406c67581059091a194"},{"version":"63cbd997e6553c877bbbfd11bdf92e99ce911bda174e6106d6597dc931f126bf","signature":"f052f97a9028807638935bdfd4013cc7e5702556e87ebb14d0a5883d33520daa"},{"version":"b5688bd390446b7c8e669650489199cc24b497853a44c358b45e23ef6a4ab84f","signature":"4211d664ea3e6cdf079df95c3d542279138d328db6a19c6f0916b201ab920d89"},{"version":"000d2e7631f4814dca372a4cb7e3fda4a0197d3a22723e3820f2ae3f74821e08","signature":"6594f6c4fb747b7405701aa752898fded1a76f395aa9c77ed615c28d848c4b44"},{"version":"d37220c6451fc2a23f0f15014dcd63a9a75816d5c9b39d8aaf4a69e09429a5b5","signature":"fdcbd6c4e023a4fbc39b4f1161838845e2d7491dd46a26fd2706675a13fa4e94"},{"version":"1d01e6bb5f169a300b055a8746fa89b35c851d3d09a593bbe42dcf69ec526e17","signature":"b38b35552f23ce23723086036788c7121e840421b6dbd402bf865cfd5cbc790c"},{"version":"dcb53ab6d5213ce22d7073c947756ed346f559ec36284f6c33069957cee62c41","signature":"2313b8fb3f40f12cd9c0f285433be8a4e832ca14383d5eb72ceafb7ad4eca47c"},{"version":"093bafe4dec3ab6fcf9e9a5753856775bb1e0fc6d032a4913a8d847828884271","signature":"634bd3ee5a6e4ba1c1671ed182dc9969b7a429116ba5a3cc8bc899a8aae2e8f4"},{"version":"bb23c7b441db38d447145cda42a252dd88d0ac4113dc27e43a3a7db35524bda9","signature":"c4e6581c0c2bf8d017173140969f491108dcd5784f12ddf140da8b0daf20ac83"},{"version":"e7c2f40dc99121500ad108a4f86541d29cac105ed018f994c7c5a2836e77b257","impliedFormat":1},{"version":"90e930283286ab117ab89f00589cf89ab5e9992bc57e79f303b36ee14649bdd9","impliedFormat":1},{"version":"6d48a6c907c668a6d6eda66acec4242e367c983e073100e35c1e234c424ad1a4","impliedFormat":1},{"version":"68a0e898d6c39160f1326ef922508914498c7a2d0b5a0d9222b7928d343214eb","impliedFormat":1},{"version":"69d96a8522b301a9e923ac4e42dd37fc942763740b183dffa3d51aca87f978d5","impliedFormat":1},{"version":"ff2fadad64868f1542a69edeadf5c5519e9c89e33bec267605298f8d172417c7","impliedFormat":1},{"version":"2866ae69517d6605a28d0c8d5dff4f15a0b876eeb8e5a1cbc51631d9c6793d3f","impliedFormat":1},{"version":"f8c4434aa8cbd4ede2a75cbc5532b6a12c9cac67c3095ed907e54f3f89d2e628","impliedFormat":1},{"version":"0b8adc0ae60a47acf65575952eee568b3d497f9975e3162f408052a99e65f488","impliedFormat":1},{"version":"ede9879d22f7ce68a8c99e455acab32fc45091c6eed9625549742b03e1f1ac1a","impliedFormat":1},{"version":"0e8c007c6e404da951c3d98a489ac0a3e9b6567648b997c03445ac69d7938c1c","impliedFormat":1},{"version":"f2a4866bed198a7c804b58ee39efe74c66ecdcf2dfebef0b9895d534a50790c4","impliedFormat":1},{"version":"ad72538d0c5e417ee6621e1b54691c274bcacaa1807c9895c5fa6d40b45fb631","impliedFormat":1},{"version":"4f851c59f3112702f6178e76204f839e3156daa98b5b7d7e3fc407a6c5764118","impliedFormat":1},{"version":"57511f723968d2f41dd2d55b9fbc5d0f3107af4e4227db0fb357c904bd34e690","impliedFormat":1},{"version":"9585df69c074d82dda33eadd6e5dccd164659f59b09bd5a0d25874770cf6042d","impliedFormat":1},{"version":"f6f6ce3e3718c2e7592e09d91c43b44318d47bca8ee353426252c694127f2dcb","impliedFormat":1},{"version":"4f70076586b8e194ef3d1b9679d626a9a61d449ba7e91dfc73cbe3904b538aa0","impliedFormat":1},{"version":"6d5838c172ff503ef37765b86019b80e3abe370105b2e1c4510d6098b0e84414","impliedFormat":1},{"version":"1876dac2baa902e2b7ebed5e03b95f338192dc03a6e4b0731733d675ba4048f3","impliedFormat":1},{"version":"8086407dd2a53ce700125037abf419bddcce43c14b3cf5ea3ac1ebded5cad011","impliedFormat":1},{"version":"c2501eb4c4e05c2d4de551a4bace9c28d06a0d89b228443f69eb3d7f9049fbd6","impliedFormat":1},{"version":"1829f790849d54ea3d736c61fdefd3237bede9c5784f4c15dfdafb7e0a9b8f63","impliedFormat":1},{"version":"5392feeda1bf0a1cc755f7339ea486b7a4d0d019774da8057ddc85347359ed63","impliedFormat":1},{"version":"c998117afca3af8432598c7e8d530d8376d0ca4871a34137db8caa1e94d94818","impliedFormat":1},{"version":"4e465f7e9a161a5a5248a18af79dbfbf06e8e1255bfdc8f63ab15475a2ba48bd","impliedFormat":1},{"version":"e0353c5070349846fe9835d782a8ce338d6d4172c603d14a6b364d6354957a4e","impliedFormat":1},{"version":"323133630008263f857a6d8350e36fb7f6e8d221ec0a425b075c20290570c020","impliedFormat":1},{"version":"c04e691d64b97e264ca4d000c287a53f2a75527556962cdbe3e8e2b301dac906","impliedFormat":1},{"version":"3733dba5107de9152f98da9bcb21bf6c91ac385f3b22f30ed08d0dc5e74c966f","impliedFormat":1},{"version":"d3ec922ddd9677696ee0552f10e95c4e59f85bb8c93fd76cd41b2dd93988ff39","impliedFormat":1},{"version":"0492c0d35e05c0fdd638980e02f3a7cdec18b311959fc730d85ed7e1d4ff38a7","impliedFormat":1},{"version":"c7122ba860d3497fa04a112d424ee88b50c482360042972bcf0917c5b82f4484","impliedFormat":1},{"version":"838f52090a0d39dce3c42e0ccb0db8db250c712c1fa2cd36799910c8f8a7f7bf","impliedFormat":1},{"version":"116ec624095373939de9edb03619916226f5e5b6e93cd761c4bda4efecb104fc","impliedFormat":1},{"version":"8e6b8259bfd8c8c3d6ed79349b7f2f69476d255aede2cd6c0acb0869ad8c6fdd","impliedFormat":1},{"version":"02b6d443cd64d2a7e8dba0f1d59944e55e91a16b21a7d7d4fb5a81724c832dc4","signature":"e66fec1c73ea068e8541b003c79af072b1b18910017d07c47ad151a438c709c1"},{"version":"a1cc7006ac0ac2dd2748f5b4a07092a330d175b16adbcd49c0eed365e9b4575f","signature":"6521410466cd5930d8f9db814cebdf1094def90f68293a3b330296a35ff2c1f6"},{"version":"d3984cd8c4d6cdf73a81ea0891dae87ae6a01c1895fd68df0b6d740006acb9d6","signature":"0ac76f72a94a13f3081c41c43b58679492c219ccada653f40801a89fcd5e9d04"},{"version":"64a1df79fbba93c3a1642f66057608a3d7e2fd24ba015b260f7014ddde542908","signature":"feb053fdd4dce7ad7c1ba7791bb6f65fb66d38bb9c1f0543012dab8f663e88b4"},{"version":"9b47bc8dd5a4d6b7f03a22a9ff4f46883813ae93554718511b93888e39ee58d2","signature":"32937206cdaee2551a23ce603292dd67e9606a27fc71a984eab852fbea3b9ad2"},{"version":"98efe4bddac330a746d7f43a39faa36ca747935ea6a8322a8afaca68c5e70f35","signature":"4dd7e1bfc2c138b564a1ff5bddcae96f4cefd39724115166bdbe071cd00b3cb6"},{"version":"c4528c70ebf1acf226f198422561ad4348ac9e35a8990b1fd15e17ad9268d60b","signature":"71f762a4ed63ccdd8a60c9930b445ab8e81bdf4b9919c5b94761511cd866f447"},{"version":"d6d3f9395cfd6f2ed3c9eaf572f882a03a1fecfc1e13acc4519df67833342bd5","signature":"5c597452991cbc579454bf8e1c5f549816d79f80ddd3514b52fbb26cc1cdeced"},{"version":"7c6ce84284a608e8ca9b7636cb5da89481d8c945d03ba511da6a2fd56bcdf78c","signature":"c169279b909f77b0c7b26ce990b20c6719869fd76be6f95f4eadf4f3befda363"},{"version":"83e1bfa7986a958fd6e069fc5df9dec6aa1e63f3dd81ddae889c19edf3a6c450","signature":"6efc188b6e1596f593cdcb356be53ede31fa87f972e5d2adc9377fa511e2685e"},{"version":"c463facc7d18f4c36823714a285903d1123cc38a9dc91a5d099c64145432f75c","signature":"2150afbdeb24336371088cf931c6081d224326f5c57580ee0b36925d1569ad5c"},{"version":"dc916450a7fe9f02ea4f2b015b836fb7d3e6291e59c93b47f711623ec4c62fe4","signature":"1609615e284b1a86bbaebd997d03c23cbe145012ba3b3d4376aa8a43a701e4e3"},{"version":"02a313eaacd1d0d97e7e1605737ac03e732648ba6d92fbf2c24716c1349c30bb","signature":"e42b8c3731c42dd2bdacdcbd0b7639df957c3f9b5fdc1edabac4a5e63772a4b2"},{"version":"af2d7b90a50168850a399d83b4e9afdc302a1025148194e2e94e1a31060b93c6","signature":"9836be02a489f0fb61392d0e3fe4127c72f079fcde9e9fed4c282bb070832fb6"},{"version":"c029e55dd039a7085f8a0f08edbfd0d563a07c48a8d319c8fa56320dac00a465","signature":"dc37598a12fb0f6f547b4a8a14da785916df9f822edbdb9657ecc589bfa33f7d"},{"version":"086d9066a9edc176d4baeb61d0075de9353ee4695c94ecfae51f293be8fefab9","signature":"90c1986dad477ad10a8330aeb2b86a0695d484a12a6f3d6507147e07791b1476"},{"version":"94fe52c96742b25429d30bc54d7ab2a2324f37025cbea99f819a77ec87bb1772","signature":"d570651c0a2c5e78e74c52a792b94ccc2cc9b2b927bfb3a5419acc0150942695"},{"version":"1897adbce3874a07180bb47daf0e8ebedd6d1793819143c63cbce290ca2ec80e","signature":"aaf435d6dc58d0a18a54421b3a622efedf9a7a996d8f75a06354219d91707650"},{"version":"68fe3c692ad2824bc811643cd5e239d872cba48006000dfe185146ad106066b3","signature":"0342e61cdf2eadde061c53e1c6fc7907ad69390beddb2aa50e656dc2a45a632b"},{"version":"b15c4ee8a756cf303d0efc482e861876a2e90b194c7cc393a8acbe7080fb186f","signature":"4a1ee69e5477f0d725306d7c9281d127f43ec0b40a23689a1a27b9430f030177"},{"version":"f6a08a8d8fa7acf45c3ba85e864da549befc88abeca247da5b6732a82685bf45","signature":"afc9b47eb28f4775396aacf528a98d207e4714ed7600c47bd33d01d4d6d3852b"},{"version":"bb0365d741d36b7f82832dbcf1b2e0025b6638516fda9ba3061d7d41f7f073c2","signature":"1e5e485956159fcc1eee2c73dcba5186c0a66f780ad21ec3760cd35a723930ca"},{"version":"431206d65e5858f0534c8be80eb4081627924a9d1cdd853982a3a4d811999681","signature":"e4b7681fdfe65ce81bcf251c1bcdd71b93740fde81479a2e3531a23fd347d951"},{"version":"7b5bae142db908800ba59fb353d7274e1f1ef0eb46074f1675af3ee77c4d789d","signature":"a4b716d6fb7e2bc0cde7e9e02904a13247381b91faaf9092fd6c60e2b96c2d48"},{"version":"058aa6a9383796a202fdc9eb0c5eac4cce8a19ba60bd8b551091beee197fe25d","signature":"b067ed3257c3d808d867d834eb5c1688ce5984ce6377ce6213f7fbea90bd6b58"},{"version":"c837dc2de1fef03dadb1fbe3ae46565ab80cbeea60c057acd5bf1e1d8df1b509","signature":"b4ffb98ee6415d12844fda388c4ca6ef430f4e4217d9ea7a139c252576a464cc"},{"version":"e6c4a6a15416b28fac47309cf33fe8040115a2849ccd012bd098efa4ec4ce9ef","signature":"f3afc6db2c2172dfa631f271d2bc28e8dfddcb2807285ebd2ff547fb786e49bb"},{"version":"283ec3ae2b171cd28e6778d2bdba3f8b055e818dac238b8a2d27403c551974d2","signature":"b8eb376386840de0303ba01f15f27e04407fd37199eccff44e6f51ff9410bba3"},{"version":"63ff2eb7041e18879c303484288ffe060db245e20fb92be664b39fb9f56c4b35","signature":"f350f485c8889421846f7acbdfe9ade72bdb827ef1905f5cc94576b802ed1a57"},{"version":"e0340f2e710b3caf03d7435335ed6441df684f5f416b3008077280a53bc0d195","signature":"043d0bf84c084c637ced77530bd97faa0aa3a8e01e2915aa8cc2129f79d9cedb"},{"version":"d4e9258acb020b007d2a76f0d9870d5d10f0a2d6bc8d24bcaa0f65931892b736","signature":"345eb0a009f9b07377ff2e8bcbd390da1648e549914b3bd027bd6b4987f92481"},{"version":"ea17df0c800a3917b052e4711913019aa15c567d1112d0f12ae22655f1c8e75b","signature":"c333598ce529f5350d9ef0c9a1bcd8c777b86845fa327aa24eef575669cacc91"},{"version":"f5c622b1d6ee97b57e7ea203c6a7a373f7a17eb412b16246b956bbb548fbf1d4","signature":"f8e7efabfc4eda9c2900703c25975deb5ac7d331e2ede7cfc50dd2da9011d789"},{"version":"c32feab5e5456978529c9eb1c2d8b56a04d9074f2f43e757edf680e132d37d00","signature":"ea673b0a7771824aa72008f0f86c71b712e5355684f05f24f8c387accd03f14b"},{"version":"133f92295e085a33a1827544debdab026bfbddc984a1a6b0865c924c38df13a1","signature":"fdd9cbb46caa8f1ba8359945e433ad1f2b954b1496a933f3eb4d29c8ae3deac9"},{"version":"e111d7709868c64a5ec40c93a0831eff084f5f3747bb50300878504738e28c19","signature":"8ac220f8baeacc2d3ee8abf3398308e10ea42de2e146be47ccb866ceb017a397"},{"version":"3dfa41a4c00d6e79b3618e1dc111188c9288bcbe16547bea8305e6af449329f4","signature":"e367993516c9f05fa87238bc5b53220f06b7f84b72629958930e2a7a37436c24"},{"version":"4d6792c606bdd2a9b2cddc4d24923ccc18f7f438cafa31e0e21285e97c58421f","signature":"2d8f81759b547e64f1b0e290fd4b0ac7316dc9c3e96f5ca93db1a1c790ec6038"},{"version":"b8b666a3d41df3b7cf4066283f67e72bc5e8e04ff4414695eb972ba7561ce133","signature":"171b8eafff7d0d126a6df4cb220dfdf7ae67c7c6687fbdc02bf4b791bca40091"},{"version":"bf66b3a8b8a28e911776d8ead5e2b6553ee37f8fa1bf0ed209aeaac310786fd9","signature":"6302a282d76ef8475759f62e01049001af013cf8f9cbcd07dc9fda62eb87c9bc"},{"version":"18ca2e6d4cb671ed4530429e6b4886aa0792c79c4e5e74078dd290668b540599","signature":"c606b46f5784bed24dcbe2bec2d9ee535c29050ba160ea11fd41b6a173bbf25c"},{"version":"93c88804801702c2ebf4d7e282ff71d90f118253ee206e7f0ba03305cc581546","signature":"0a7f51c3fb4b7c9a30745a92c15a4cb4eb88aa3ea69dec8f6286491fdfb99dab"},{"version":"cc2cc8e0ceffbbb09594103d85e205eb53b848b7ecb27d91d7b5cde80e792236","signature":"7f20f857d4b5aa3fa620d0708434711ff7092dd304c9c38f8854ab9bd334edf5"},{"version":"81aa2ba3522bc10222837dca4556efb07a6628de274c94545a536c1955684ef4","signature":"daf649274b917c1d7d6b8e8488d04d7e47f3bbbb09842c2a9899b4ec507fb243"},{"version":"fa1702a90530bc09b078bbfc9e98010c20333706f9d225c18558a146ac9e2219","signature":"c75b37dd144d43a77944ee5a7b8195d397ae78b74065052bf3a1bc721b1f77b4"},{"version":"d5ebf3405d09e5eb9e3316e8b6a7329bba4fa306433222f109b9af077ec77525","signature":"71108da668d27a617e4f2ef6aad932227d526487149856bcb3705f0a2aa9fe9a"},{"version":"c7aa0820877b8341f78e794fde60d464746eec82b0e2624982000ebb19fb8f8c","signature":"488c8eb8a1054444f74a12eb49f8e21ae583aa2ba59ac9cfe4ffc71754c3b1f7"},{"version":"9c2b60f6bc9aa74d213f7dc5bc0b666e7c18c3572adef9d884521c36412bb814","signature":"ca4a72006161d37a422dde05feffb9667a0e38b99737b94e0b564b5a7409ec21"},{"version":"f38eb0a2421beba20ee66d42353732cf2ce6f343c1b1c322252842e0f22b8308","signature":"cd575032c427cf4eba79247a61418781b801f14952fc1bf8a48ed2747def2bcb"},{"version":"f7409ccc7875d9cebabc5e27d9df8f3aca19ba959f30fa1b486418ae9c3058e9","signature":"de963461fc2f6d1fd065c283aae92de10d72a4cca1f7fc0afef6301e741fd381"},{"version":"f51d0b8cfa092a592caf543d772238f191037278e2004d58e7354447d830863f","signature":"986bf1e9bc3d1b0b157927aafcfbf9e94478b28eda319c209ed8e9e613e14827"},{"version":"3b9d0ef4847a6525e297172e340c0dc383c8ab6c58a27aee0a27b2df991ecef1","signature":"57069ea736148610272f87e767f23439015d900f230c3060afa193d6b9029cf2"},{"version":"b6ce8cc18189aa155b9d4386c03e3547f48121a7e3f37b66ed9ad43190b20dd3","signature":"12dd7bcb0994252cc8b7a0155db5662ea1c3437584c67f010758f962b023797c"},{"version":"2c4beeb10c123c02ff09c390cb92953e811cb0e846e2d040c65a8a0e73746894","signature":"5431108d0a4a15cc5f6d78abd5a358d13bcabb849877e09f3efc265eba21e6e2"},{"version":"40f8a5ff101ec9d2a6a08af84db2d4865c35e2deb3da94075a482d94612ca24e","signature":"57e73f014bbd5a960cd0a3b39a240cddbf1842f7f06a09757be73de97a234a79"},{"version":"812fbe241e51f1fb745bfdb0cf447cff8a9802beeac16df1980f14499990900f","signature":"a4c0f47a1176dc8ca692834c31a2f1c95994955eb191e76cbf3e58dbd16ec08c"},{"version":"5559d4fcad759cc07a71aca5a792755409db3b683788828abad2a84da3dcd7fc","signature":"c367bae6e0535dda7431e73df32e233511c1e9b1181082d551efc822fcbaae83"},{"version":"46ea0bba2f2e36762dae6bd527f342a19fe9f46c653dc4e8061d9c8530ba8dca","signature":"19467dd75b0a6bae42afc2ac103445b6ddc4a3e259599ac20b2ec70e75fff499"},{"version":"5112478f7f7dcb622157981c8ac9a0fb3cad40c5eb87a19ff2e37674c75c0fd5","signature":"8ce6788984fbc5caf642946b8dc8a405629def762f166473ae6389aab4822034"},{"version":"3cdbef5d6bb0c6201d2edacc2f930322c36b9798b90131787398324b67bd2130","signature":"b84e31232011f6ac9ded7e727871f7c1ece606d8179950f791bcc03ac3c03b6e"},{"version":"d6c2a85159f32ddee646895592b5c53e04b18cfb5346de79c2d003b814b601e4","signature":"2bc381b2105d5a05c2724fa4ae393e83f0adefda1e390db743e55a0cb949c099"},{"version":"9fda6fefc6a936e326113a3d3dcb56da7dc7c2a7a064bf451429b51e7b645d8f","signature":"f668ac39f924b2946f0e323d23da14308c0d996f579dce2b5fe5c9f2085c9ad2"},{"version":"182221370b4c51b9fdd08f71c259596d747b565fcebeed2875832d1f2f556c8a","signature":"00ec18666782d50d3be062bceb46231a3e2c4abae3128f6638529e9fdabefab0"},{"version":"e1d7527f3d057bd92e487081450d9037a1dc9dd5e2f8e84e1fb2f6c09903db4c","signature":"0b482267029d52a5a2ed300385e2fa5accbe0f69d22bcc5c5f541536e169ad5e"},{"version":"827a3da73904af54ac6cad259cbde0bf0b811d253a3fc370664cf80cb65ad6ed","signature":"02778fe052be781d64d090064f311da1b30eda7863ab768850a522f3c83dabd7"},{"version":"41d344efc8e2dcfc00c0cd0d7bc8f5dabcc6bb0062766fd17aaf85deb4d60ecf","signature":"83df5dd9f98fa4184cd1227ae312c09558f5a00b35243e263069a3a545e7f6b9"},{"version":"d6452b09863385bd57e48e1fb836f95c3a6f36ebe690e342d834fd2868d6ba74","signature":"b9288778951e14a9a541d06ead6a2b1abf3b7541a1680af62e4769e632ff1263"},{"version":"fc002c1c913427c1e1f45dba404ef61759bb7ee2bae7bb396000e3cc521009fc","signature":"486e05d05d1c3a7899f66c9c98d8ae6b2c3a5e391bd74086d2d795f3c091a159"},{"version":"f697b44f7c3fb5f78ccafcf98d0b863e87e997ab71eda6ea9fb8290313fea562","signature":"95e6dbb8526dbc167bdbe072c5c78a5f281bddc36f430a34f36192566c2ff2fa"},{"version":"d0fe15b48303f79346e34ea1b4d207022219399afef4af6451928eec88606e3c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"447809300abe6967b66fe4226b18bcd8513258b0139a8fd1ac516767bc510372","signature":"6d31cb09b5e87e0588c937c73aff7673a15865e355903fe16ac3bdcb3d2894d6"},{"version":"309ebd217636d68cf8784cbc3272c16fb94fb8e969e18b6fe88c35200340aef1","impliedFormat":1},{"version":"91cf9887208be8641244827c18e620166edf7e1c53114930b54eaeaab588a5be","impliedFormat":1},{"version":"ef9b6279acc69002a779d0172916ef22e8be5de2d2469ff2f4bb019a21e89de2","impliedFormat":1},{"version":"71623b889c23a332292c85f9bf41469c3f2efa47f81f12c73e14edbcffa270d3","affectsGlobalScope":true,"impliedFormat":1},{"version":"88863d76039cc550f8b7688a213dd051ae80d94a883eb99389d6bc4ce21c8688","impliedFormat":1},{"version":"e9ce511dae7201b833936d13618dff01815a9db2e6c2cc28646e21520c452d6c","impliedFormat":1},{"version":"243649afb10d950e7e83ee4d53bd2fbd615bb579a74cf6c1ce10e64402cdf9bb","impliedFormat":1},{"version":"35575179030368798cbcd50da928a275234445c9a0df32d4a2c694b2b3d20439","impliedFormat":1},{"version":"c939cb12cb000b4ec9c3eca3fe7dee1fe373ccb801237631d9252bad10206d61","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"26384fb401f582cae1234213c3dc75fdc80e3d728a0a1c55b405be8a0c6dddbe","impliedFormat":1},{"version":"26384fb401f582cae1234213c3dc75fdc80e3d728a0a1c55b405be8a0c6dddbe","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"26384fb401f582cae1234213c3dc75fdc80e3d728a0a1c55b405be8a0c6dddbe","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"03268b4d02371bdf514f513797ed3c9eb0840b0724ff6778bda0ef74c35273be","impliedFormat":1},{"version":"3511847babb822e10715a18348d1cbb0dae73c4e4c0a1bcf7cbc12771b310d45","impliedFormat":1},{"version":"80e653fbbec818eecfe95d182dc65a1d107b343d970159a71922ac4491caa0af","impliedFormat":1},{"version":"53f00dc83ccceb8fad22eb3aade64e4bcdb082115f230c8ba3d40f79c835c30e","impliedFormat":1},{"version":"35475931e8b55c4d33bfe3abc79f5673924a0bd4224c7c6108a4e08f3521643c","impliedFormat":1},{"version":"9078205849121a5d37a642949d687565498da922508eacb0e5a0c3de427f0ae5","impliedFormat":1},{"version":"e8f8f095f137e96dc64b56e59556c02f3c31db4b354801d6ae3b90dceae60240","impliedFormat":1},{"version":"451abef2a26cebb6f54236e68de3c33691e3b47b548fd4c8fa05fd84ab2238ff","impliedFormat":1},{"version":"6042774c61ece4ba77b3bf375f15942eb054675b7957882a00c22c0e4fe5865c","impliedFormat":1},{"version":"41f185713d78f7af0253a339927dc04b485f46210d6bc0691cf908e3e8ded2a1","impliedFormat":1},{"version":"23ee410c645f68bd99717527de1586e3eb826f166d654b74250ad92b27311fde","impliedFormat":1},{"version":"ffc3e1064146c1cafda1b0686ae9679ba1fb706b2f415e057be01614bf918dba","impliedFormat":1},{"version":"995869b1ddf66bbcfdb417f7446f610198dcce3280a0ae5c8b332ed985c01855","impliedFormat":1},{"version":"58d65a2803c3b6629b0e18c8bf1bc883a686fcf0333230dd0151ab6e85b74307","impliedFormat":1},{"version":"e818471014c77c103330aee11f00a7a00b37b35500b53ea6f337aefacd6174c9","impliedFormat":1},{"version":"dca963a986285211cfa75b9bb57914538de29585d34217d03b538e6473ac4c44","impliedFormat":1},{"version":"d8bc0c5487582c6d887c32c92d8b4ffb23310146fcb1d82adf4b15c77f57c4ac","impliedFormat":1},{"version":"8cb31102790372bebfd78dd56d6752913b0f3e2cefbeb08375acd9f5ba737155","impliedFormat":1},{"version":"bb9b5a18147a0f927e0fffe91515a39610e2477b0d8a0d0b391c283013e0bfac","signature":"d373335450e0c74b3455541e03c0ff8fef26b51201c49ef145a0afb217a9f026"},{"version":"4bc5159b0bb1e303f1b662d485b7f9dcfaf785a29f8cd101ea85817fdb3a518e","signature":"70cdd1bdaa655ea305231ef8f3d9f830459ae85cad5a2395b70b4caa2d81abe0"},{"version":"25bb698c825c728521550bae3d4d8777520fea078d96529db79d3901278e084f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6b71e71966963661cbc81a93e3017c50f97fe724452ffc49059ed4cf0540e464","signature":"eb4779cf04d947168653273aa00f2a6a72a61fe039f17400e6d76179a7d62fd1"},{"version":"6fbedb59be020e7d349de8a1ffe8aaa52d16c78f9aea437249f14782b290aee9","signature":"eba9ab6bd63d7d7bc2a05d255e9d56cb7321477c3ec92364db4cdfb12873e8b7"},{"version":"1318f5ed0496352fb48c4c03ed01567db651e1794260df1b2da1e146a1aefab7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fcbc73a398e35777c583049d7a6315455a1c340d06a7ba06fd65a08a998576a3","signature":"bb33db3843913e4d9bba12a3c10ed9c8bb77a67266905cfd9e0afeb093e715fd"},{"version":"8f9f32fa2ac067466268c5f25be56dec212c0bdb9b62a123fa588e72bd6b0019","signature":"a01968c471a290a4b9657a07cf814af299366e7c83a56d8ae6ea425e9b83c81e"},{"version":"24defda36322571a1cc5b324b17b3c1e721804a2add0166477d24efaa523ffc1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"70ac7fbe8555de02f7cb0fe42f479173ddb89a737908c560014d733348422046","signature":"d9b4f0fd652a60e8727bf295164c2d0a652cb6d79ac90e8b13c48d4230a47039"},{"version":"656ebe6a1e35fb1e45ace5b3d8975099fa82a7a42542c09ee1e1e975b4951722","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c4f3d9c6228f744351b3f3d6ac2593edba9e7cf0a965cc6ccb1805595f44f275","signature":"96dacaf48c43f86fe63578c47b008b14f31ea3b26ba6604869be23386548a0af"},{"version":"37d5de471b9dc564c40740998405448999d504e4199bccb259fc7b0734b1ed88","signature":"cead6dd08c0e7a7b5fe6b1665ac0c10e08d5bb8ed6e044a5716acdf61bbc658c"},{"version":"256ac94c8da7010cbaacfb3e0f55cab2ce49beb7f21309659ab1e5c44b66cba3","signature":"4932a57ec8dc885c99967df2c08c4be4dcde303de1727465afc901bb526c9dce"},{"version":"16774047f3b38fcf2d977046b054d22bb00258771f607fa6e4a06e76cc114cfa","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"45d8c8b72f837a46ca63ef01ea3f4244112587c0abac142367982f443e31ea7d","signature":"eb8463d6df0ca2c38399823f1e38ff66180aaefb12e5b403155c2abe1eda8b5a"},{"version":"964e363030719b2e66f7eb64663b22f039bc64985dd1e75eae362e378608ad32","signature":"52b37759b4c21b0266e113f72e72db24ca11859fca9beaae88ac286fa508c5eb"},{"version":"2aca0bc14bc6a0e2ce70f410e002eb4aec77e7622afd0e40200a2d6c36542db3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d21bdd834776d159085df8f067883d3437dacf6eab3d356f7c3bb2bce9a9c98d","signature":"114811397c9ad0f10abb90a439425e93671eb3698dc832ceeb9147bfc4848dbb"},{"version":"912bade28ea48bda95091b26d3082abbaa1f48637b4b4ac3e289ded44a153a1a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c29a2bfef4c6aaed66d0e46bf5601f98e4ea8fba4e230773bcf8a57d76d7ddcb","signature":"724894208b6a6516915c69b386dc88e8ae38db466a047c122baa0acb05b2466e"},{"version":"338fbeac4bc8d461eed91e454d837c2cae2c403822f1c3f8a0a68f1d1e2488b8","signature":"ded5f2d0a193751dcb1172fb8ae1678a74ec41f30a52c8992268992fc21b9f6a"},{"version":"dd902a78d8188c4f89377024097f0ba90aed6822e4f13c742e498eb1fe9fbe8b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2ade88925e99f1feff0c33e971814e734e05f6f9e32c2fb7c6260247635417ac","signature":"06ca53e7c778e43262f44194db43a44dae84e02e9d9ae674f74a4039f043a39a"},{"version":"a5110f54ba5e9c7c7fdc029cd20e65f35a9ddab6830394949279d03f4baaa112","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e08859a0433654484c23ea7d2447e7da43768e228643cde336290e80359be015","impliedFormat":99},{"version":"427d5d08714a73f909d965ace2642ea9819e49245620483d47acb73b4eb922cf","impliedFormat":99},{"version":"242e53307a5705e9235ca1be47168a4c3155ea674e80f5a13f94d7938c23bb5d","impliedFormat":99},{"version":"44a6c89655f418dd8250d22276530cfa20826b2fd8d51e90fa4d1ada108706f7","signature":"b8ceda97cfbcc009561ba63ca8e39df0dcab8ad77f6bb03d001f12d0f5174f03"},{"version":"8ffa57b994af8cee7411cd7bfec0409118909a2a897b417d5ba378025b9b8eb3","signature":"66d21fb03c05d9e19a9e6328311f0e929ca450163fe3ad5a1f19b3e0563710df"},{"version":"a2a59b41271e830cdf2ce76662b32e2f9d3c2a3c35e9f9fc359e9bf994ca8104","signature":"9b835bb9c177bb27a9d814290adebe192bcd22f633810aa7d4f0a4ec1d6fda4d"},{"version":"c4e23c61fd109ee83f2f5c355698161dd4edccf0882115c1c1450e55d02fba1c","signature":"cfb868d5da01515efc27424a0c579a94c56621768b4e7a4d2275203046da4685"},{"version":"338381ea6d7ef717f6f5c22294b6c1d820d06133f4f5fbc86cce64fc4fb81f5d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"93fae08c020f721198112b7bd06a7a405f3d5ed77c756e20bcaa264d585b906e","signature":"c9d3eacf4c5ea3ad593e74b88cb8b3dda3258df41d236c336936146d3847fdd7"},{"version":"849d186951b6fe08777eb595e7b5423a933404a59b255b15b3ef91eaa9e03e2b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"398322573c9e0ba2826eaea162b97f4987dd00caaa4dc29df93d7dccaff40c2f","signature":"dc1df284b2ecb2adb8124f0411490cbb6adc27d6b3f783cb98e4de022894c67c"},{"version":"0e614492dab5ee5f4418895293386b642203c8f1a3a9d14a8eca94a906c91c04","signature":"1ce004dab6fc4c13fe2a946bf541afc29f77e4b6d197bd7e078c216bf331c288"},{"version":"f22a38020548019be436f7d33fae243aecf68c9e068dd7fb5d88ec31fcfce2c5","signature":"990a86a4c51ffd7c3c146bc5b5e4f2a6eb31f8a08186da00d699ec62be38d14c"},{"version":"07a57ea6e42f784f7664053d917baf68d010a79f0df1fdb8fba87a6af92ddd7b","signature":"e57424bb8ca9fcb02c7b73c295bff56d7889e92610490ef4dbcf83dcf5006809"},{"version":"9462d342a282781c02c97f4c036158faa37e7fc0ab0a14e1df622ccd9c757f5a","signature":"d55fa4fbc6da7d276cc39c6f62402a7a65cbb0924015aeb475013a0d50eb9d5f"},{"version":"50418661dd171c08d9cae6e3b5ff41663e70267a80e0716eef30fdaa381b3607","signature":"438a206e90ab4a99894d015b8659ad993a2e15df506e3e52ef38b5892098917d"},{"version":"2c0d69ddfa8c836d8052a3a7589b0d6c6e8132d50949d6440d5672e0f0948ea7","signature":"8858e8d480c6f9ce98c8f48ea265e292dbe3fc51a9f9c3b0d125ffad18d89e29"},{"version":"ba0789108c9bf8c7ec38cc853e188f9d131c7850a44fba82868eebd7230cbe2e","signature":"e6cceaf655d91958114f0707a4d6c800cfd0d72ea8673f4f0face6b049c90ec3"},{"version":"9f2145479716604449381a636127459790f9e428a5c526cd0795223bb66dd9b3","signature":"d86c8b7a6c6edbeb6a14c73aad61eec9d13cec0040264ef13c78a8c2f7ecbf43"},{"version":"fa32219cc14042734452368d122b82bae2849be88d8941c5b363e3c47c9b651a","signature":"7c56fc0ecedef369430a6cc78f797e1f72ac6aa30cf3861bea222fc9efe93d49"},{"version":"f52646c7394ab792adfca993338d590f7d9030ae3269526d5dede9b131247717","signature":"d1d2efd128275df07279bf887018192c1b38c0cc2aea96243de78a8e92bc30ad"},{"version":"b1ffda5abe3874eaf7ee7e57cefd4c4ed1e85e00932b5da6847cbe0e22c7eedc","signature":"29afa7f4d2f64a222d590227109f01361ddb9c6588355096f6aaa036b9d67d05"},{"version":"0cdab5351929ad0e2d94ecea2b8d60d11fcdb153ff2355ab9c5109e84a697ddb","signature":"91537516c066b5bda3446b1dbd01a6b3ff342925cde014d5acb7b6f8b99ed12c"},{"version":"0172224d148517f7cf90527aa73f03cb436b3fe7f06ac70c039d6886b8f9dfdf","signature":"d01168279f1f9a417a578d3768c58eb5170a5b3445e7b2dedf880dbf0d1fe873"},{"version":"a6d5aa9d1ace7ec2a992729d02341737fc267a9d2d7f1467313bd170e4b26b15","signature":"a65ecfa05330aaeae23d23b899f0bd37c34e42fa5083d180b4a0bff3dc3ae25e"},{"version":"831b967c1911010eb3adbfe96d76340dce858803d80310236352a7b52de799c2","signature":"76d26c617c0a9f48d4e21938e684ae22166d2d3604d00cafab5212b0e15b57fc"},{"version":"d09b5dbb314f199a0d7ae3c2c2a9c7258b611f3f28d1cc83c2685d3c738aec3e","signature":"068d0597a17af822c2ec3af9b1c2a9b9a26c0a4387eb66f655a0f1d26e36ba84"},{"version":"35164c8ef06e7c366f6f45f993da6e0df0f7c2cc93e78198c199bec111da8fa4","signature":"942546eaf5ae2d0c5948c6d25a748fba25b6f4d760911ed595b40f043fbae102"},{"version":"ea484c456f3f9236d0b324d2c6563f6e77571c9414768590248a016b2e248a3a","signature":"65bb767048368601ad35597c54f6b112e3147dc84fc338199931af5d57b8fe95"},{"version":"1ac040d4f47be4c1b44b4a521c7bc1264c97371b3c51bc2170326827fc8a5406","signature":"dd24f7d41609a7eb1c990ec2f7d7cdd63a419e355e6294050a67c029bdad0d78"},{"version":"f3d7aece0ac20c6911c50aa54b50c1ae6768a8793a72412d346aab2b66b4a7f7","signature":"ab04dac1f806941027328926be16232e21366ff829c3be737572abc646b0ff3e"},{"version":"6a282a4b745d9ac9d04d759b34b9e51124a950ba33d83a1408f76742cab5d8a7","signature":"519c584866e4d804355422ce52d8088fda0124969a9668779f10b0d50e114153"},{"version":"3cef134032da5e1bfabba59a03a58d91ed59f302235034279bb25a5a5b65ca62","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c05d0fcee91437571513c404e62396ee798ff37a2d8bef2104accdc79deb9c0","impliedFormat":1},{"version":"373cf226ee7ddf9535231d4ea2c24d47e4262372e1c075aee7b48e0d2d38e759","signature":"3a700951382c62ca71c0a4fb951071e1a2692a3ddfc899ba0145c275ff12a006"},{"version":"29a8061c2db56515c67ce514bcc6f91c853407acf4539f5d08aa723526270a1e","signature":"7cf5ac50b3def9f8df750c1e7ea9a102216484b4bba94f9e0bf68458bc77eacd"},{"version":"2d4b53789aab997f99121021686c05f5f54aae58fbb0525243fdd322c80d612d","signature":"5c975df906b720e560dc80cf99f12cb2763329a0d5c42ffe3039256137dc3a70"},{"version":"e43472b89b27f89f28fcb57260e48230b95baff6fa6c489c5710115cfa6c9506","signature":"2f9e549adb20bf7d44ab18efcdb5e7dab6bdf423d310f3df05e5ac78e3828990"},{"version":"91d7a64938101c27f0f5493074dd0ebc4f82ed6d58c42c8d148235de3f8978ed","signature":"b4698bce6f7a4a17593cff994a72d565662855439386bcfabf5f0335ea8d4be1"},{"version":"d563b38c81c713a23b730e0e385c44442992d3b1dfad2424fd9c635e3eacf593","signature":"97ebafc9d89ce29d62958a732cda28a5cd408a1257cfcac0d253584fcc850e6d"},{"version":"6d4b59d8a599531b5bd5cef904c5f8832f062b79eac5298805b9aade268d66b8","signature":"97ea7a733867ec926cad347ae90178d3c3fb96a4fc076d2d2d41201e9a1bea75"},{"version":"fee8eb73b4397c9d3fc50904fb4d93947f32879251345c687761a5ac20a76314","signature":"4e69e7b65fb23298ec08b6e0cc86692fb6602df5473948f46baf219a07967697"},{"version":"0d922edfe50c6ffb2c16d49dcdd160dc468b0f93f69fcb021d6464db95664a21","signature":"aaa2dfcda87fdc4c24fc251d7d04070f379d25c631d2b130c846becc582e1b77"},{"version":"df3a3dc2616be7db489fe6a853faa1e52a83dfde06d2f3214994ee7ef81f18e4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ae77d81a5541a8abb938a0efedf9ac4bea36fb3a24cc28cfa11c598863aba571","impliedFormat":1},{"version":"3cfb7c0c642b19fb75132154040bb7cd840f0002f9955b14154e69611b9b3f81","impliedFormat":1},{"version":"8387ec1601cf6b8948672537cf8d430431ba0d87b1f9537b4597c1ab8d3ade5b","impliedFormat":1},{"version":"d16f1c460b1ca9158e030fdf3641e1de11135e0c7169d3e8cf17cc4cc35d5e64","impliedFormat":1},{"version":"a934063af84f8117b8ce51851c1af2b76efe960aa4c7b48d0343a1b15c01aedf","impliedFormat":1},{"version":"e3c5ad476eb2fca8505aee5bdfdf9bf11760df5d0f9545db23f12a5c4d72a718","impliedFormat":1},{"version":"462bccdf75fcafc1ae8c30400c9425e1a4681db5d605d1a0edb4f990a54d8094","impliedFormat":1},{"version":"5923d8facbac6ecf7c84739a5c701a57af94a6f6648d6229a6c768cf28f0f8cb","impliedFormat":1},{"version":"d0570ce419fb38287e7b39c910b468becb5b2278cf33b1000a3d3e82a46ecae2","impliedFormat":1},{"version":"3aca7f4260dad9dcc0a0333654cb3cde6664d34a553ec06c953bce11151764d7","impliedFormat":1},{"version":"a0a6f0095f25f08a7129bc4d7cb8438039ec422dc341218d274e1e5131115988","impliedFormat":1},{"version":"b58f396fe4cfe5a0e4d594996bc8c1bfe25496fbc66cf169d41ac3c139418c77","impliedFormat":1},{"version":"45785e608b3d380c79e21957a6d1467e1206ac0281644e43e8ed6498808ace72","impliedFormat":1},{"version":"bece27602416508ba946868ad34d09997911016dbd6893fb884633017f74e2c5","impliedFormat":1},{"version":"2a90177ebaef25de89351de964c2c601ab54d6e3a157cba60d9cd3eaf5a5ee1a","impliedFormat":1},{"version":"82200e963d3c767976a5a9f41ecf8c65eca14a6b33dcbe00214fcbe959698c46","impliedFormat":1},{"version":"b4966c503c08bbd9e834037a8ab60e5f53c5fd1092e8873c4a1c344806acdab2","impliedFormat":1},{"version":"3d3208d0f061e4836dd5f144425781c172987c430f7eaee483fadaa3c5780f9f","impliedFormat":1},{"version":"34a8a5b4c21e7a6d07d3b6bce72371da300ec1aed58961067e13f1f4dc849712","impliedFormat":1},{"version":"6a534c594838029f9096b88db91c054e612ff951a57ed9d9efd92f19643a2753","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6223f56cb79eac77e1211e76830da993ddcd9baea0dfe2d10a61a131d39f427a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"20d8184cc9bf496dfd9415be762d5233809d005d149417d3c30a16084b0c3842","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7f72a954c349bccf89e393e243763fb141257a54d6647e369c79beda371378f2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c6b47f0ea0108d741abf731f728b357a8370c238ffe73fcee007619484c24356","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f0d1598df1729ec0e33ea6dfd7ca8e063941308fc28b90fe59660158167b9627","signature":"f592c7e333a33b4e5dba58516b31a9ba2c3f5639c989fce88351556ba49606fc"},{"version":"99338b1762fec1fed1659b7aad2bcdd744d239177d0c147c540b34ef93ecc986","signature":"408859ac0de230e5b42b2bb888ba6da273ead46b0a8123a5d21282ddf6335afb"},{"version":"20410f27b941544a2d84068655c0f03e95d6bfc15348fd081814eedb7dd1a023","signature":"571d3448b7e5dbb700ec919745d70b84c0859909793935026a8331b7666d91ed"},{"version":"ce2867edd9fc13f9654dfd5cc76cf42d05110585108187658b8cdd4a797bb144","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"aa4db26266d6f651c711350ddf671278179e6f59b28d3c390ae50a9b20a3aae4","signature":"921c81a312317ce376b3db64ec158a40d264b56c798653f7985b9361289d951a"},{"version":"541204b45188f36c133a8dbdb67627cacea1fcccc8f56e34ab3a6df5a8842e23","signature":"03da542307af2869e7b2c1de8d1237d05b584cf0acf36c9d40f8e994f21adb2e"},"5f1ab4340b3a3f3d2c88167d0b98d1d8ae6c6d4b1ed845f25c3069d7d1b902d1",{"version":"62fdb9ea1d1284dc72bae3338d2a20c737814b30d30c9d0ce40aec4fcbd51746","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cab9185002bd5ebd154b6106ff93ae480bb26be2bc14bbf19180ae690449af28","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b980df9c1d9398fb15cda202074eeb45eca1b733888708d0fb43c021b5411991","signature":"b0ba848f7538ba06336d964c03d2289007500242648df4d1a2e1f693d4823c38"},{"version":"3d64c2914a71ab3af9fd253eda13ea735a784923284005d07af763636578b46e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"178dc732f2d61a4fd094b6672b6c438d1b1d6cfd5489564206a84e3df486beff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6f815019b8b763503cecf9ac86f9de6bd8593180a0db3a624f98acf88dad162f","signature":"b5e89db47e4299930bf6020c3ac33fe228d590042b7dd4c5a3dc245027bd9a83"},{"version":"8f6aa64ab08524e8ee85ed63f8dffa377a7f4017680001a3669a963162f9ddef","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"03c74f23371366e58d1160b9f84eb63aedecd51d3ca319bfb994b8f6b3ea344d","signature":"7b27496df462d7c5956667f688b1b318c2ab3081852bcd634ba80e1de4e9ffe0"},{"version":"9382ac249f4efbc0256803deafe838b51123955ca8b68c68a4be2b2c4a94027b","signature":"f4956881b9e58a4a626bbd99a98451461e46649ddbdc1560b635cb904b527c19"},{"version":"e9cf4f29b3c48849f12aa161f42fa890e606873410f535dd09def91b19b4924d","signature":"8f1e73205101242a9924a904f571655aa7b1c1c193ff6c4ba0e7e272aeecaa37"},{"version":"e1ae660c622a39b34c87f6dd244d59846a5d110d2c39bcf4316a499b166b6e7a","signature":"6940bec49524de4fcb325d41bbb724aada9cc73b4df1ae6498284a510bf97a59"},{"version":"9071146edc7aae0d6978d242a8f9d5988db42204717a627555a2342181c3ccd0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"695e9a27d875aae3f404cbd6ff901fedf0d77c193cfd9552edba781658ed0e85","signature":"a299ef368d46feb485cacc1257882c710c962c3835c15268544a95d9385c6641"},{"version":"d0608ad5086ad006c7c2a10e4fce58cbdac946d73cda4c270717bbc11751afcb","signature":"9d735a325ce1bbc7e6f8c5188f6eab0f769559979163f6083635913e83cbe5c7"},{"version":"42b18867a7543fec221e4f0321e077538e596cbacfec0595872df4876635dccb","signature":"baa8a117328606a5a80729fa29d3b99e604d1c58274ce6c705b1dd17550d4173"},{"version":"c07f3037b31e0bd7e1384c41a6fd6524ac141e8f56b93a4a12ec63d75a704edf","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"da142229522424470a86d0f568ba8eb149a4792e52f53cb7ce8c6b117ef65a3c","signature":"131aafceaec427354fbdb2f0c518ce34b5d29ed41ba96f5be2e0c6828e34c36d"},{"version":"6e925ea850d5c27ac93c1d0a26203779a347ded9a658643ac71febb557086d09","signature":"faf83f25a2e1c4c2cdd395f86877ce483031a5fde85d0bfa74cd27548f3139ff"},{"version":"25b749d6ada24514fd767c7212f8710ed80c3f54499a13642246913e678553a1","signature":"27bb3ddf3da26f0251f6fa1f7b1d888720e20fcb54f8513e691c7276c730e0c0"},{"version":"f77ebf90d0877e84d5f546d128be5e362554f93395f46ab6fe1fbf060b962765","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d8efd52cc22298b4d6f0540b8c96ad97a563fd1a0effd9c245b9da300b5eef04","impliedFormat":99},{"version":"0d8cb9539485655600b329ddfcdb91d1b4b20f5d1b40a9e40c8017938fb68d5a","impliedFormat":99},{"version":"51aae950c97b61105064619e52f8b4e702ed9d88f6a38d8a6d461389be28dd0b","impliedFormat":99},{"version":"08a8feab6868367d5112474f9015e5b00c101012a639309e88fb105f94ced534","impliedFormat":99},{"version":"a15a870f6ab5a26a7eb91ddd8c47ff4e00bc23ece96ab48ff8aaf42450478a50","impliedFormat":99},{"version":"73390a82cbd5ea87d8bcdf183d66853207a111de00a8512c68ca17b47a11e65d","impliedFormat":99},{"version":"53755d0e8037d36720dc68e2e8c77512698befd0c0d48ac5d20c41986df91bc2","impliedFormat":99},{"version":"e0f44fe626dcd0026670f01dc0af34c40332729e8a1ce2dccc67e2ad5c96e3a5","impliedFormat":99},{"version":"2919501096a871a68fa6bda28480d7c237b232928215d6edec67e75dafc820c3","impliedFormat":99},{"version":"7f7f3dabb63cde6344d767f66379aa90b17c87362d999507724758247445d005","impliedFormat":99},{"version":"1d6f7095a9b7bfd7d035a4b07f23378ba7c44993d881b75593a252f186671e51","impliedFormat":99},{"version":"952f574aeb762b9927559c9d128dccf663352aa92736ebb856d2cec80fabceda","impliedFormat":99},{"version":"c06c1379c3f1bcf21007bd9a92e8c7a8e63611387392411bbb2399c0a4c5ae04","impliedFormat":99},{"version":"0fab16fa249312e20d9a96e0464c7ae63c841b17c02401a59ccd0bcdfa67bfcc","impliedFormat":99},{"version":"027464bfd5f5d3110b7b5303ee3a09d3bd74e630393c5caef2cbfe1bb6ca59d7","impliedFormat":99},{"version":"3e9aad7dd39dc61c41d0c249427d39b3548f7ac02f2fa2e4a813c38a8e1a2e01","impliedFormat":99},{"version":"3f28c2bdd8d3da9487f032bf85ea09bf9f24f6f02ae2336cb65e6988aa92de5b","impliedFormat":99},{"version":"6a437b4b58f8b3b220f3ae8af2230bf3bdd0fa4c17db62a9a2a03fd224a68a70","impliedFormat":99},{"version":"e16749a9377888735e5edcc765da4ac2f5a552de2ef46d930039b2d54f199fb6","impliedFormat":99},{"version":"bf973f547b27688728916b64a98fcfa836772e7382211a9692684947220ad550","impliedFormat":99},{"version":"507b0e93358d09b74a0caef2370175290a47c790dbf71fb63d1b4593b7e070ff","impliedFormat":99},{"version":"eb7e05259b0603e91365983fffb6e6dc1e574f1cbcf09c51bdad4f3717869a82","signature":"5679163e510a4314da81e928dfe7e72c6671b0377ebfa606c80e18db43ad402f"},{"version":"8269474f9aca3f56fe5ff007900aed4be90d6271a628d561d20cc29de0d5576e","signature":"298cce3b54e8d74b37facacfcc1297add32f454323d60ed4b4ee24ad651c76d4"},{"version":"88b5d609cf1c008e5d7926489df81bd606581dd083772e8ca735c1c0bc103093","signature":"3ba28f6b4d58c39bee9b307f9a7267970b31adae4c3163ce2fb889c48f25396f"},{"version":"dd0a4bfc93ee858cf6af173c428400652c01288761e7dc00b513652d005cd91f","signature":"a32a75b40daf9f63898f39c282c8975b28c1f9086ee5957707c075c8cadc8bf1"},{"version":"f35ccdbcb49becc34f1c71a68ad0d843bf02fea572cb852884d0a96ac7169830","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3d1ac90c29f450b8b90705d05264fd29f1034b5ebb6c2d2e9807489969e0a33f","signature":"140c9f01bd8744fc1fbb72ee2a7747039b9637b3976f2284bf1423d1bcbc045c"},{"version":"6cdf25b7999cac1327ad91005d4aba65743aee71a2972eba11f5b1164e39fa4d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0d96088dc89a49f328fc47dd75713536556aca01e187da7dce124fbd2f395f09","signature":"77e82444cebc04e9edeb4d759ea3c8be067ac6bbc3b652d668a3f483b0d5f7fc"},{"version":"ef05bfe2ed3c6fc7575cca3a8ee25f4a4aa28878b83c5d986ee47f4483f73b3f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dcdb66da2fdcdf0e80f084c8347e2114f573d97619a5e37f680a9ad5656f614b","signature":"f090f8e7e1db58d734c2c7434bcd43e2ea1c30e049be3443fa3a83a063e59324"},{"version":"d322344da57346fad32f22627e0028bdfe12501d7de9eb4a103d47c9a075a85b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2d5bb04855694db19d13174de99509d4d4799d6bf3468318bb74b54ffe994129","signature":"9a6473983696c0401765d2ba2558ef9b0670592e8d74239b4d5623c42d686600"},{"version":"dbbdee1f403eb2a952f5e8ea724cb1a20a2b4dd63d8232e375a750d4929baa88","signature":"7f1336dec949b3008a181a8873c5aebe07ea42b6730e6a5c6efaeff90abd09dc"},{"version":"28c7612edce38076988a57695e970654fd09467b806a8e37b38a26946afafabc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2a40fc7affa68ec88b30b2d2d19639d8fa8dadee567da499d460257372e04ab3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4e50f3ba9e8934a6d1fe00ae73264a3ab2202db1939a6fe875e3375022d62b4a","signature":"29022c2d2e6ec02be440b4da758f983ded6daf9a771dfb932ad7f3b93a04142d"},{"version":"7f13d1469822b3ce57bd440274f4a8d9c2d9925fa644d284fdeeb520f0ab43fa","signature":"248bf8fb49df3283090f3d2a137e3d74dbe93490ca12bcca8686a3e1004e5c40"},{"version":"38b62c6f9b44cd3b4e899e63d64a5dda62a2abfd9a14807995944543680ae4e9","signature":"6c7d3152f35e7feb3abd1e338a59f61ff28eb27f96fbc42a01b988084cb1999b"},{"version":"0ffafc0be8faa4fdb62e0a657016a6ddf07e40096b6f5aea41809208e4465534","signature":"7f9e8c2aef5ef9f51990ac594879a01c44bb8459d6c0d49fe5c311cf2e08af8b"},{"version":"0ed05108ce0f4538b5351bcf9d523912d200abe5b804951cf18b2e12feff6b70","signature":"f453c01ca04957da00f261867fea88fe674b34dde9b1da183dc55f2bed19f364"},{"version":"fe55ccd61eb15dd0480443215f5556cd56d9a68075a79a1294e2d9cae200d70d","signature":"9992a93411c1d80cef73f32ab5ac10acddd25700903cf8b5b47925eae8be2a60"},{"version":"bc37c33a1ff23e40250be0eb50b16aa3ebc6d24f58a55e5c41d968c4a23439ff","signature":"dbeea29fcca6bdfffbac8a6c482bb4199d8fb77cc64503880fb3dddee81212b6"},{"version":"7fd1557a212fb2a671abe96de2e1b6a3e5110a07c49076705cdaf9c3e0f81f7c","signature":"dc57421ea686f59b81eeb7885916d7a5cfcf6aac9113b8908c5edbe4d4a7a296"},{"version":"b9eb4fbe039e06b65ba30bb786e50fa9b48e25d7eb26c4cc1cceba3a6c81615a","signature":"70faab149c7f9a9cfde8ede12a99419d9ebbc61d822a7c16757902918cee94aa"},{"version":"7ad085c66c15e665b43f1055b09ddc1d9c11a3d8f21174c9d3d9205d7dbc03c8","signature":"5b361261b9d93e4a2c0d2e02bf9f0dfc60fd8c761ef6fccdabf563bd3aebb419"},{"version":"68dbb99a0ef2ffb046b1385fca8116795a7b4bf5700193b7bf2fe9cfb62e72e6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ded33ba85bd4f5491a76b8972dfb3104e8b7b4e1b256c44313d7ea9d21647d2c","signature":"43d84b56d871c4b5bcfbaae3b58381ff0a77d0bca1733ded9b89350275269033"},{"version":"1f260100362d7309e0cbae29fc09c4c36be2e4512013a3f6cd4706ada09c6675","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f911a52096fa219660557bc3c9ccb5745889d8a357674e8ae67585678891e106","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bb2d10f9aa6efc083456e706d085f717688c6c0a4030e0c0a1afe45068b41d1c","signature":"88525a755e322d9d9eac12bc44f4e51464db195ab9b4a5e0534d7a453fcf586a"},{"version":"b1a83e0013403df0cac8f2fbc11e735c29efdfc41757d6c49702a330510605d0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3a2cadf5506cf6c268f65051dd63ddd5cdc82f5effcf658778af2b12e497726b","signature":"3d3c808c01d46ac6f212b5bf9a3780af30c9ac93fabf3f754e01eece8478207d"},{"version":"2e1f498ba2e37492111a97c6048e07af342ff8df126eeaac6cb99f94372f8ca7","signature":"4a997dea3de3d148c650f5ad6c57d75d5adb6655108e0af42e57f9661d5a9297"},{"version":"759a24229c02dafd2cd73ad6e3325c043d16ad85081d2e76296664bd96226ae2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"315789c8527a430a3cd968051ca6d731a0f13a47ff395dcf180e0d02dbee4dc4","signature":"0b8a37fabf134f4cfdaefe57ab21468b1c97d26b90c4ff558d695fb8e4c11be6"},{"version":"9b7287bd51e848b323551afe464c4a91ef2b74bf1ed703dc7c7c5e35cd9073f4","signature":"bf47aee07d830c691e0bb1caecf0a38aba368d98da54866d98258c4057feaaee"},{"version":"45eddefc55733af83490debe404d6f06892219189f1b3cf4470e304415600773","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"89f0ba8c9a02ad55e23f296ff35f0d1d6361a190811401c3f0d767a1aeeefec1","signature":"11a904b87a71e58a2283da4755e0042d78cd7f56395ab9a1b6ecb09290f3672b"},{"version":"5aa6936f80aaf206b952e46cb830f50e49e37862c6cc4fdca99000c797995a54","signature":"2bb79d1f86f6d11a1a240d2a4a538d676a6ff8231126766ef84667cc2e945903"},{"version":"1f607599e3d2f94f8bc20f8f46a594132cd1b1b1004f0a4619dcfe84f792c774","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ca3da68186289d0f9bd93dc9a7e5c3eb30dae2526ca55be4b106af239b63d0fd","signature":"d539ac9920f9a947cd986cb61772e65f48bc0442d1d94e2ce8d6e25f394cedac"},{"version":"fe17ac85f4e0b305bf44f3a4d81cb75397d9d7776aa2649ead1caf2291f0d416","signature":"8326d42a2c5e8fd8339f23ecbcb4a89f839e171090d9d2daf0e758e12a154c89"},{"version":"22c8ef3ac26c1ded1d40c6e1c48b6382fdf0d56a5a3e77f8bcb3bb198a1f769c","signature":"d394730410e0700f09bd96049137fcf096ceed2f5e7bce00a2937aebc9bf4240"},{"version":"c7f6c31638211891b8f6ea106d4d9ca0cfe249adba1528b2d1ebd0f267fa324e","signature":"b70605e01f0bebcb73e3de21b8c1dfa27372859c9a142d66d8d69f1f91e99adc"},{"version":"5d513a6a908bdec9f0c3a72c4fce232063a7365983847c1ade848a9970e97aa9","signature":"302704f3830a828ac25e5b3d330b257810004892d2acf61a6a656b05978d7a2c"},{"version":"bcf829509dc2bdb8f3851efb4451010e917abcdd8a52e2ae302886045c0e38f1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"10ebed90f6cf7c3e637d5595d4dedc3377575aad097fd0b28e15312572c09622","signature":"7d26b77586708051f6f1735b57756edf0be83ca4670c4af58a8e28b965a33a08"},{"version":"0ba02535f72f0cc1712b1a073897c522f3460eb86620046f1f3b250ea79fa567","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2b4b00a99c93370ce060e954393ebac6d299d87a0400b9a69307c97e8021abcc","signature":"1af687725c0895163ae338b1c94acf5819a042e98cfac2dd6e83b993c57d5623"},{"version":"1f1d8292770ec1d17820eda3e3ca60550d3c356d4f4832e211e9ab2ae5d5c9f7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"07e108a85e9c41db3bfca18812565985fe9c4185c09e44cbfd365364daf67e80","signature":"7231ddbad84b7265695458d181b33e24e857a11dcf40f694a4dd42b3e265293d"},{"version":"a82585ad71d31401551de3ffa5389e8b078cc4211320f84ac2bf055e522c2765","signature":"60ff22f8744ceb8cfa14cb7ba540a56a25d7f4902114a3ff67a6e54586a3d3b7"},{"version":"d98bc291f61d6be8db9ae5fab941757fb72f211684af075289bd48cccb780aa5","signature":"4b0eec9971cc44cf2d3c76d33c5d4a7e7465908fb225e9b612064b90d5fb394c"},{"version":"92b944d4674ccbb19cf8373ffa9e6f50fd707709093aee9fc445163550002a40","signature":"8f7b9781bbbed468f49e1f74933b7c0e5d2c6ddf8afbd6a5d81183d053d43a28"},{"version":"5be39f3f33dcda0458cca755ee8c910507c3d2683e10541dc7e5a8393fc82601","signature":"94b0e10227654e51b43772a7332db7c9bebe31377ab1cd1cef4797d40a3a0c6b"},{"version":"7236d482e2ca1a6307e2182466f18dc8ee373209e5588f156b019f949cd9ece0","signature":"c92738c8f42ef530edebc3a1912a4ba2ec85ad86494839d23b6084782f9f2e91"},{"version":"da35015d12dac52832201a4900b07e4b0f4fc08f282eee6e7131d895db1930c5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7d0aee097d3d04fc9227b58b76f92516a8b29f419ac2b93d10b58ea75f213258","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"24ac88c5cd809281eca45ae09b8b16dec4318221ea50d1b47888254798a046f9","signature":"c97c4207c753de5cccfb48d3488e193f8846f302690bd3ff73f4de951675b01a"},{"version":"63bb37a1d958427795e2e6ca7fb9451aff711e665b45bd4878ba693da9a140cc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d8f785b87f0a4f596648fb2e5fb351d34d4bffa5288980a8eb52ee82ed27180b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b581577a6affe98a790ea707971843ad284ae4cbd4bd1dc17642196e3c0de158","signature":"840d3e2bdea7d5a418436aafedac9749b1d9de78bda0f825a48869cfbb3e7f83"},{"version":"3b8a02a9dda0124bf30a030727c96e3d34272b55369bc55cf55102cc90ff4a41","signature":"09d3eb6502b5bfea1281c54cfb4111b4a05d9716f4643a5029f964c230b5b551"},{"version":"0eee1242c13bce68990b788037aaecdb865d63943bc7b5681b8688cbc6d64e60","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a3be9843036cf2c658821504c4931c7e1055db71db2a607b9bc5d8aa7be679a2","signature":"3a526554ad06e5c46700e8b1ac5e6f817fdca923787a3c9344acba81e8d17ff1"},{"version":"ae3566ec3d167f05176112f609460a77863016fec3216a87a15c36bef01d370d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c914982336f64f67076a86d5569fd1b878a42f415962a07d1d151a4d65d187d2","signature":"33831d2be2fefd1ecc0b722e8270094b857a42109f3eb3bdb5c5e666233c588c"},{"version":"4d5caf53ba49b41ff378943f6b1dad1ea8e042426fa713b57c9524219bbbf573","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bd703e7c27cc5dd4780eba552a51a698b459c9f12dbddfeafa9e0d216a4e66b2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c09c42c93bcd3ed631ea6902d069b5798db25862e47fdf4ba5f47ff0d36b2a51","signature":"fa9886eda8d6ff931fdf8e61b9af2aa42491e152278324020c487e489e778f70"},{"version":"bf82447aeb19b4df2e40900f920c15695a8557392588397ce359c51b133c00df","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1e4dd6a5a91d2798fa4dd70d7e6f682475942bedb36b9f870cbb15e5c1f1a54b","signature":"a8a191cc7d792c8cc2d87c992ffee823187689960dc717e122e158f24b77a242"},{"version":"0a5fe133b4ef41f0f2443b3cc82c4b99be11738a93d613fd12014e9d632c2fcd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cbf3758a6cc16ff397b8a2a27221d1f6d5f053265e353af0f37b356f0384b85b","signature":"3d07ef5fca347d934f76c6eb3558e0a81da33951129d307c04716a0812321893"},{"version":"e9dc912bf8a7678feb40d7d996c9263bad3b7fdab9ad9ac95d4e4ed023403d29","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4b3f5e2463bca0844de6802fe4ee8e1e29a3c11d14fdd3fa7c96ae69fba8d74e","signature":"053cec7f0a8bd24eeddfee887cbc9883f56cab39ebed9e143de9f0a6cf34d202"},{"version":"d878f9fb504fbde395cd7c61e48f2fc7bbc7d0cf14828004f95e5f9fe64f238c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"12564f17d21c31e77240293e7434c0c933945a3a3a142130eaa166d0042f5529","signature":"ed4aed28c29ff0fefa86143fc6824969cb43f6bde467d4f9254c84372fa63cfc"},{"version":"3e8a4a82fa1baa0281a0ccf309c22c954488a949c13a7ab6fd7c171b1bafb361","signature":"13428a7125f291070d1c531f233c9e8719c80160dd3587d51adf86a3e41bb62f"},{"version":"b51fb5ae439f311990f5a5c2fc4914fda89e5ebd585b33d22d0251afe382d391","signature":"f49a7f528e4e42999b277a5ea73799e735e349e562a0ac6c97b99644a13a3ed0"},{"version":"deb873b1dff75e59633350db7fdd9e3c125d248ac7bf2c193a81e9665bbad9a1","signature":"0502f677499fe5b2d8cbb7f8e703465005e5c77788839d14377ee4b3da22fe5a"},{"version":"d9628bca2f50c1a70ef77c452fe293c91380dccd76c285dd3aa988c0f93fed8b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"59c0ddf46c0d1d17e34ceb4c253a3f3bc7654c450002d8f8080476a3baaf5755","signature":"f7d9758437ce102b893d78f8a901109a32ffc713b3c2ab288e8e15860dd3a835"},{"version":"7adf0dcdc081964a00a2235aa42fd757563b15038955013b98097c5731705a2f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d9df7df9ddd168648cb6e27223b325763738bccb1b57e965ba0d8443cd166fe4","signature":"d786daad1509af6e601e8de4259a2c6abae27fd33287b4936fd079fbfd1f0ce0"},{"version":"99f67ae9774e4bb88839948649b26a55dfbe8b99ec80ecab4c548102d2ddcaf3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"aae8eb9b4f313c457b2f82fde10a63f117333645b818b48d2ed26fb2333ca42c","signature":"b76cb4bbf6287754fb7246ca57b8b0cfc52c84d5696a3363f193d2a3fa0b1e16"},{"version":"53ed7ccce63fb30e129e73dd0abff74d68929d92fbe3b20f8ade965780b2353c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3abcb2afd6c10eadcd9e5cfcffe3f93172b891ac80b079ad1421105d1574b983","signature":"2cdedb09674dadec42708ff08cf53e8ebfb3dc9402a0aa42464a061d228c7ef2"},{"version":"ade0a7d50f110afaed90ffd5f24f1617bf9b8a0d2f118530892d139ad67f29fe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4bc4b9b2e5a66597bed3af39f456ab78cc11601400c5adb4ad46a173bd03da41","signature":"2dc1bb408cf19157f86ca0f3984f3837afca66209bd009a34b84daf16f8c7543"},{"version":"a3311c2d4225d8eacfa5a9e662811aed57fdb4de78b824f1a702311b3f99b09a","signature":"651d947dcd8fb9009c702ecf43eea7c50c9ebe342b6e0619cbcda9d8a8b64e10"},{"version":"d7e17a1b90344a6d9c26f1462f77d6350a6882706064a36e5d640f5726ce49a6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ea2d922ef7dd2904b092b91fbaa35be0af427504b9fc7e14ab5fbb6cd7c40846","signature":"cc4b917492e221996d1271af2f86e5e864c2d8053a299038dcae940e332e312b"},{"version":"5456720ba13d5a5037b07c10816207ca9a81cd79a370af608115c578d61146fb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"695b2c91c87bd844d4b97002869db4dae9f2c96de4cc397b6841670d184a3a8c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6a073d10bc03e721d0b1ce2620253ae9b69daa32fbebb2f7a8c67e7e1579c6f2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a80fa136e8559ddb40afdfe957e7c625614d02ad4a72476a1dd758fc31ed2e54","signature":"a1acf9b04a9f848692e1a5cb1bafa0e53bcefdbcd40c5bf311062ba23188a339"},{"version":"e79d387b4470cc2ef4df34e09b4113eb85200a7c8c6508e4a2f418c63e29ae5a","signature":"ca37703109f463d6107118f4b3d1fa0eca1bab385f6e35583a2fd13ef66b3112"},{"version":"56396e7c37789adc6f28a7c461ae904c01688c2836ac6278a9f9ee864078c7b6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4025a1efab8877af2ed8d8edda349736055a800404134c6b24ee95cdfb0c0ba4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0a46d4afc023db18a3c9d7816e3f90d960122319e2765c5d686f76da661864bb","signature":"b5a97abd79ec3360bbef597b4f34eab1b9f0d3545d0c3f46e3b3e2ec6e91771b"},{"version":"4f649bd1b169333de666f079f7989d184c27109ce26544354957f5be00abd232","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f84acbbf9c1536d22e69d354fae1dc2d43430ca0e524721df713722f9e26890f","signature":"bcea8c0d3b0636e8255a7b6f3c42b075dff08702bc473fa7d6ad74adaef773b1"},{"version":"b5da1cdeaf5fc3b53aab62bbdd5da7d9385fdb2839a18fad0e3b2c31c5d888da","signature":"a43861be0f45c9bb0763d1c8aaa880b6c5d0b2a37a07bc65bfde949ce648ad79"},{"version":"0e30e16f547666851c4fe51505a9feeff6508b2d720b7b8c60691e5158db10d6","signature":"cc094a8b2d9686d5ff268266e02eedf9d66e2389a04c46fc49cc819d23134a39"},{"version":"1c03c99ee1b5f5bcf0704dac9398e922805929cd23a7ad246ad0d67cfc3826db","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a8adc0df2bc9038f9423e9947d03f490375a9f615cc8119055af8a695bb830a5","signature":"bb01d18cd374f84cff1cc159163df3a7f602a298d8e925ac993012fbcd7e2bfc"},{"version":"6096174ef99bb11f2656cd3f15a2fb649e504782c6ee27090448b681e33c2b40","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"987a3bc405a132b704d415e99a6708c6ea54d0a70766ecf1ae59bd13034d848d","signature":"2d44dcdbe1d297af2ef6785176a9165f4feb886490712c82ab8578ca96ee0d10"},{"version":"187610881a6b1f7370788848d0a2af5a17e94b9b437727556ef3d2fe018a98f4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f0709632ce350e4970bc7fdb88e656ecea4b7579875a6f20ad20cdaaad26f369","signature":"f9a530c655221c9f5a24fc3421f341b21bd38da824f7612da7c87804306eca36"},{"version":"2b3793a5342b5d7ef5498271aa50c1fd31ce56b70f70d0dc5f9da4174eb1e5cc","signature":"9ae9233a7cd435509757e52ca1503b31fc923e1bf163d9bfba847b1a7dd89e51"},{"version":"1391eb93befc7b56fcc8fc9d4c37affcb37252ce6e91400da018023fac32c807","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"17b39df68c9417376ab9d3f845ad45905499eec1ee9798fcdb980cbcacbf2f44","signature":"f8c081ad7f58588db5940385eaf280c202e6ce42c0117ad62f774ffc421712ad"},{"version":"5ed076225f05deb1d6965f90227ac87a5b06e66b9a30481c699bc0fec1ed6c9b","signature":"883833dec7bf0238bfbbb33db50c709cc7ca3a1f6714d17992c0d7e82a964d00"},{"version":"e0de0a265fa6fa0c0460995f60fe2255ab2ee907915b7a2fd8f509053af31ab1","signature":"3d655def48973efb420a82a2e05119da3a2c45672bdfc7a695f6e569edaa417c"},{"version":"ef25eb1291626f363fdd9b26186aadc8a5821fbdcba58c9464050b1269e27063","signature":"f793afe8c2bd867c272d5562b829000a5df12669a8922a4977e3ea420299480c"},{"version":"6e1b2b7611a6083e43d49888a2437c99c989141e611966568fe0527037358608","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3f072b168376dc71baf99d36fea4aba49a269f5852826888a3c6c95e5c9cb202","signature":"7fb20dbe5a83b73a18118cafefc659b66c75e285f8f6100023eed6218035191e"},{"version":"98ba07f2f211272213e4201fa31bbe0de1f95049cb411f0c7dec9e9de1fc8232","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8d9a8784de549deea41f12c4241eb87f77fe7b7f8222ddd7a6ea05085980d5c9","signature":"88af6abc2bcc060a798687a7bc8f8bc23f47f5bb2ea736e89666093f4e682a0c"},{"version":"365e7300df17dc4f13c79443fecf0e1adf057cf17556c2c6fc20f1021f72ab22","signature":"e18de9a7b62fac87db7bdfab03946f00b49de5cfc11b37f39d95c1f6d05b7dc4"},{"version":"731bdeaef898c61ad06b71022c0669d246ac039c1368fbb38c2605d34d867da3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"da0f84fcd93700b4a5fbf9c6f166a6cc19fc798231bff56dd1e3875bfc6966eb","impliedFormat":1},{"version":"634ff08e0143bec98401c737de7bfc6883bfec09200bd3806d2a4cfc79c62aaa","impliedFormat":1},{"version":"90a86863e3a57143c50fec5129d844ec12cef8fe44d120e56650ed51a6ce9867","impliedFormat":1},{"version":"472c0a98c5de98b8f5206132c941b052f5cc1ae78860cb8712ac4f1ebf4550ca","impliedFormat":1},{"version":"538c4903ef9f8df7d84c6cf2e065d589a2532d152fa44105c7093a606393b814","impliedFormat":1},{"version":"cfcb6acbb793a78b20899e6537c010bfbbf939c77471abcdc2a41faf9682ca1a","impliedFormat":1},{"version":"a7798e86de8e76844f774f8e0e338149893789cdc08970381f0ae78c86e8667f","impliedFormat":1},{"version":"eebc21bb922816f92302a1f9dcefc938e74d4af8c0a111b2a52519d7e25d4868","impliedFormat":1},{"version":"6b359d3c3138a9f4d3a9c9a8fda24be6fd15bd789e692252b53e68ce99db8edc","impliedFormat":1},{"version":"9488b648a6a4146b26c0fd4e85984f617056293092a89861f5259a69be16ca5c","impliedFormat":1},{"version":"e156513655462b5811a8f980e32ccd204c19042f8c9756430fe4e8d6f7c1326e","impliedFormat":1},{"version":"5679b694d138b8c4b3d56c9b1210f903c6b0ca2b5e7f1682a2dd41a6c955f094","impliedFormat":1},{"version":"ca8da035b76fb0136d2c1390dda650b7979202dbe0f5dc7eaefcde1c76dee4f4","impliedFormat":1},{"version":"4b1022a607444684abeee6537e4cace97263d1ef047c31b012c41fdc15838a79","impliedFormat":1},{"version":"dd0271250f1e4314e52d7e0da9f3b25a708827f8a43ceff847a2a5e3fd3283e8","affectsGlobalScope":true,"impliedFormat":1},{"version":"47971d8a8639a2a2dd684091c6e7660ec5909fed540c4479ca24e22ac237194e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e1075312b07671ef1cbf46409a0fa2eb2b90bb59c6215c94f0e530113013eeda","impliedFormat":1},{"version":"1bfd63c3f3749c5dc925bb0c05f229f9a376b8d3f8173d0e01901c08202caf6f","impliedFormat":1},{"version":"da850b4fdbabdd528f8b9c2784c5ba3b3bedc4e2e1e34dcd08b6407f9ec61a25","impliedFormat":1},{"version":"e61c918bb5f4a39b795a06e22bc4d44befcefd22f6a5c8a732c9ed0b565a6128","impliedFormat":1},{"version":"ee56351989b0e6f31fd35c9048e222146ced0aac68c64ce2e034f7c881327d6d","impliedFormat":1},{"version":"f58b2f1c8f4bcf519377d39f9555631b6507977ad2f4d8b73ac04622716dc925","impliedFormat":1},{"version":"4c805d3d1228c73877e7550afd8b881d89d9bc0c6b73c88940cffcdd2931b1f6","impliedFormat":1},{"version":"4aa74b4bc57c535815ae004550c59a953c8f8c3c61418ac47a7dcfefba76d1ba","impliedFormat":1},{"version":"78b17ceb133d95df989a1e073891259b54c968f71f416cd76185308af4f9a185","impliedFormat":1},{"version":"d76e5d04d111581b97e0aa35de3063022d20d572f22f388d3846a73f6ce0b788","impliedFormat":1},{"version":"0a53bb48eba6e9f5a56e3b85529fbbe786d96e84871579d10593d4f3ae0f9dba","impliedFormat":1},{"version":"d34fb8b0a66f0a406c7ce63a36f16dda7ff4500b11b0bd30a491aa0d59336d1f","impliedFormat":1},{"version":"282b31893b18a06114e5173f775dd085597ca220d183b8bd474d21846c048334","impliedFormat":1},{"version":"ed27d5ce258f069acf0036471d1fbb56b4cb3c16d7401b52a51297eca651db62","impliedFormat":1},{"version":"ec203a515afd88589bf1d384535024f5b90ebe6b5c416fb3dcca0abd428a8ba4","impliedFormat":1},{"version":"32a2a1374b57f0744d284ca93b477bd97825922513a24dfe262cbf3497377d96","impliedFormat":1},{"version":"a8b60d24dc1eb26c0e987f9461c893744339a7f48e4496f8077f258a644cffab","impliedFormat":1},{"version":"3f9df27a77a23d69088e369b42af5f95bcb3e605e6b5c2395f0bfcd82045e051","affectsGlobalScope":true,"impliedFormat":1},{"version":"9fd080a9458c6d6f3eb6d4e2b12a3ec498d7d219863e9dca0646bdee9acce875","impliedFormat":1},{"version":"e5d31928bee2ba0e72aeb858881891f8948326e4f91823028d0aea5c6f9e7564","affectsGlobalScope":true,"impliedFormat":1},{"version":"9a9ba9f6fd097bb2f57d68da8a39403bbe4dc818b8ccd155a780e4e23fa556f2","impliedFormat":1},{"version":"e50c4cd1f5cbce3e74c19a5bbf503c460e6ae86597e6d648a98c7f6c90b596dd","impliedFormat":1},{"version":"fa140f881e20591ce163039a7968b54c5e51c11228708b4f9147473d06471cf5","affectsGlobalScope":true,"impliedFormat":1},{"version":"295eca0c47be1191690fd2fe588195fff9d4dc43852aceb8b4cab2aa634579f0","impliedFormat":1},{"version":"59ee7346e19b0050508a592702871dc943083c6dcb69a47d52e888115d840781","impliedFormat":1},{"version":"067712491fb2094c212c733dd8e2d56e74c309a9ce9dac9e919286b7245a1eb4","impliedFormat":1},{"version":"a5eae58ac55bd30c42359e4b01fb2be5eddac336869d3f04ffb4daa54b58f009","impliedFormat":1},{"version":"d12d691ef8933e8db39f2ca81d6973940ff5e37bb421752f5b6e7bc15dea3abf","impliedFormat":1},{"version":"4c5f8bd9b3a1aae4e4fddfee41667e495a045f73ed603993038fa6a8ba92fa14","impliedFormat":1},{"version":"dfb274ab0f319cf18ce7152067c25f984c7fd1924fc72b3f66734588444c934a","impliedFormat":1},{"version":"108c8c05cbc3fbbbd4ff4fc0779c9bef55655c28528eb0f77829795dc9f0b484","impliedFormat":1},{"version":"a7e5444d24cdec45f113f4fb8a687e1c83a5d30c55d2da19a04be71108ad77bd","impliedFormat":1},{"version":"41ec17e218b7358fcff25c719bc419fec8ec98f13e561b9a33b07392d4fec24c","impliedFormat":1},{"version":"23c204326746e981e02d7f0a15ab6f8015f9035998cb3766c9ddbf8ea247aea2","impliedFormat":1},{"version":"25f994b5d76ce6a3186a3319555bbba79706dac2174019915c39ac6080e98c7e","impliedFormat":1},{"version":"dfa4e2c6a612d43851ccbc499598cb006a3a78bc8c7f972c52078f862fa84e47","impliedFormat":1},{"version":"02c1705fa902f172be6e9020d74bcd92ce5db8d2ef3e1b03aabc2ac8eb46c3db","impliedFormat":1},{"version":"99d2d8a0c7bb3dd77459552269a7b5865fa912cedab69db686d40d2586b551f7","impliedFormat":1},{"version":"b47abe58626d76d258472b1d5f76752dd29efe681545f32698db84e7f83517df","impliedFormat":1},{"version":"3a99bbbbbf42e45c3d203e7c74f1319b79f9821c5e5f3cdd03249184d3e003ce","impliedFormat":1},{"version":"aaacc0e12ab4de27bdf131f666e315d8e60abec26c7f87501e0a7806fc824ae6","impliedFormat":1},{"version":"3b4195afd41a9215afc7be0820f8083f6bd2e85e5e0b45bb0061fb041944711e","impliedFormat":1},{"version":"108df8095f5e25d7189dd0d1433ac2df75ec40c779d8faf7d2670f1485beb643","impliedFormat":1},{"version":"ddd3c1d3c9ff67140191a3cf49b09875e20f28f2fc5535ae5ea16e14293a989b","impliedFormat":1},{"version":"7b496e53d5f7e1737adcb5610516476ee055bf547918797348f245c68e7418fe","impliedFormat":1},{"version":"577f44389d7faedd7fc9c0330caf73140e5d0d5f6c968210bff78be569f398a7","impliedFormat":1},{"version":"3046c57724587a59bceefadd30040d418e9df81b9f3cfd680618a3511302ed7a","impliedFormat":1},{"version":"15ccc911ed15397e838471bfe6d476c28deffe976c05cb057e6b1ea7491242c2","impliedFormat":1},{"version":"64b5a5ebdaead77a9a564aa938f4fb7a45e27cda7441d3bee8c9de8a4df5a04f","impliedFormat":1},{"version":"a48037f7af5f80df8973db5e562e17566407541de284b8dadf1879ea3aed8a2f","impliedFormat":1},{"version":"dab97d96ce986857150db03f0d435b44c060d126b4a387c7807f4e9f6c92e531","impliedFormat":1},{"version":"85f39366ea7bc5e34b596fc97de18a7e377856755e789d8e931054f2191d9b8b","impliedFormat":1},{"version":"daf3ea3d49f6e8a2fa70b7ca1f21bd97f1b65021b31fbfccb73dd55f86abb792","impliedFormat":1},{"version":"b15bd260805f9dd06cd4b2b741057209994823942c5696fd835e8a04fb4aab6b","impliedFormat":1},{"version":"6635a824edf99ed52dbd3502d5bce35990c3ed5e2ec5cef88229df8ac0c52b06","impliedFormat":1},{"version":"d6577effa37aae713c34363b7cc4c84851cbabe399882c60e2b70bcbb02bfa01","impliedFormat":1},{"version":"8eaf80ad438890fe5880c39a7bbf2c998ce7d29d4c14dd56d82db63bd871eefb","impliedFormat":1},{"version":"9b3e7f776f312c76ac67e1060e5398d7ac2c69d6a3a928a9daaae2eb05b15f56","impliedFormat":1},{"version":"202042eccb4789b7dee51ba9ecab0b854834ea5c1d6a3946504bfc733d4468c3","impliedFormat":1},{"version":"2b2ef76a9f36094b07ee6f76a5ac6903f2f65c0a20283201814a8d1e752cb592","impliedFormat":1},{"version":"8882e4e087d0bc8cc713cb3d8090c45d33e373e6f5c83e0f8d00fe6a950ef875","impliedFormat":1},{"version":"baab78e9401b9a82e8fb3634de0d3750cbf4d3d6afb59ba28472ab15afd3a749","signature":"3d01773dca02fddc18139d243b8adbbe1c6c6447b8235bdc3cbbd4b493e7ffbd"},{"version":"1cce0ed0784dfa68a0572c20ceb1a173664dbb3ac59eead22d55be246fdf17d9","signature":"5de2fd3f978ef1724ed1d72271f8d9bd911d19d80a709137225e173127e3c615"},{"version":"57e3b4916970da260c692cda82bc670552fa93563710da8485ef3f1a40fc0cd8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"24772aec0e0f59dd17a2a1c4a924fa4fc228f24c64bee4fee8c5c08965f05925","impliedFormat":99},{"version":"e636cb7d61143bd3901daa91d1c2c3d8a53b677f6bfe51fafbd2d14a51efdfd0","affectsGlobalScope":true,"impliedFormat":99},{"version":"d33b19c5e9b2f8b26b1875c7aa12229cdd1e3ff0e809c89189805a05c626dc3f","impliedFormat":99},{"version":"bdd14f07b4eca0b4b5203b85b8dbc4d084c749fa590bee5ea613e1641dcd3b29","impliedFormat":99},{"version":"077cd7acbb4a3b50b4a01690d6a7d2583ebb39335f612763442a4d33dde01c36","impliedFormat":99},{"version":"31b29d52cedf2cb6be5fe1a8ada9aac55fd4933351ad18d71d77466659c95bed","signature":"6444595fd97d8356ac957d200cf99d4653612f84715bfd4905ec02a1162856b4"},{"version":"2a01af100d878ce1be4ad967937beb31bddb09778227c52f425fd81cee137c8f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cf0f4d9e5edafe3f777e151b9719afb37ca6abaa904fb8289367fe99913f0ad1","signature":"307d71207bdccbbf886a1b1044f39eafddb7b2457a81eb1a1843a81db10e37eb"},{"version":"a30abd322bbe6fce3aae6ef89bfdad79fe165b3dc284d001d8dd05392cf22033","signature":"13573020c64550f2efba1d5a0380288280a85b4da7c0693f1f25c759a8114211"},{"version":"cfe8a898ca6cd1e4e6aba4518ff485036d62faaafbe219f8192067d32016306b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"03493681f3175378f73cc1994441b55eb2f178585c613377853cdf5dfb39ecc2","signature":"4540e50e72fce7b0cbb91e773d9c3ace94268cad237d1a032f294b9786a348b1"},{"version":"0ff60462f4b1f78e599d6266ad526e552d85c49043b0d5f48f992585da46ae81","signature":"5c539fd09316939fa4dabeae933f1289e99585d6fc398630670d5d16bb41137a"},{"version":"a0cd91fdf58c51fd2e581171ba5d3bb4a8f374abd39509a597314c7d3ce2c0c3","signature":"bb97d8ce68b8061ea36f60acf43e82f3560c4e53522b05949721c6b7d014d89a"},{"version":"533063ff41e323fba7e5cbdcfee392348005ed117c5badb1151f6b4803a7a3d8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8d096c0b73874d64e56e5fe99fb94f14b5a6fc0824a4a1cc0251147a823c25a3","signature":"06980b548bb6ff2b15c92032296a46d7f80d3e8ad9af172f5c2ccdefa86b3fb9"},{"version":"d330961532fa59192f0330dd430076475d3a3f5cbbb60c2ba196351c069243ae","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"34ef539c1384da9fe858fb9c16368ddf1624a4914dc3f0fabc7a39811bcd2668","signature":"ed6cfc1cf330cbbc602b7a0305aebcef220219fcd5cd3e4493e5afe79058fcee"},{"version":"f7409e1093e57b3f7be327a71c71087e1f7a767333fc10cff7c2504af7222f88","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b203b12c30dae8bb84ff0f770a857445942d8c9ec8d6ee472c3ca6b0b5b261d0","signature":"9fc50c5741ded49943ed4b81fc428d0aaa18cefc596400fdb71fdd11a21d8d8c"},{"version":"24803211766a60a0fca7ca541d6a158d376c65f7ae4827a8661063c2e70c06e5","signature":"6e2681371e356de1c4fa982616bd1d26b7c525b5ca9f75e1f688b83332e8a81f"},{"version":"1453393d564bcb47dd35ada6b469c661ef5d6c98f9dcbd7bc0f9eee3470ac944","signature":"22462cc125563699336669ccb959793d6c462626957a1da4ec4a639d4341fb3c"},{"version":"7c56faad4a628f9671b73a1227c941f930b55649699ac62931e360389775edff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"579a472a70260bf6b40d68aeffa65bf00fa5c59a44bf214f2385dce399f5898c","signature":"fb4582d6a3a9b2152a49c918d6c98ace7ce35978ebe850af889e7aaa526551eb"},{"version":"3fa0c656d89d31fa67f25c8b3eb9974b1aab6fd8fe5ae0fafd521e7d6808f71c","signature":"cf64d4b205595fbd260e6fced4216298b35c82faba7dce73a9e205add66ef85d"},{"version":"ad5ab4bc064063efa662328e47c8eb39c80888a25467fefd231a32e874162d6f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f0e29314c7c3239de9961d460d5347081cc6e49bf065dd0a6cca6e7132a99ee9","signature":"ccb2b3ebb1fa7bd3fa3e02c7a23ecfb2ebad06df9c3c8a9e685113d81026d0bb"},{"version":"614e5cdc3c5f89f035510a0c61652fdeba39a62c9904acde7de79fce2d60bfe3","signature":"bfe40cd3dd4d0d35754643dbf07ecb96362953f4fdb490803e122593e679db64"},{"version":"36e5bb11081348bd0869d683fadc9a4115fb28720594bdf185a13ff19faac88d","signature":"d356e9c1bad769f9e8d358a35c420cc37a0ad01ea4f865d968f3b7fe10c9c3de"},{"version":"4d46dcb6027f62db92924103d77c199455ed38d1dc1c6c29bb65a707c25f847e","signature":"14084429a03e8974f38aaa1890d6a80ea62d5c8f0b627e0f824afb0ac621a65c"},{"version":"1d5a2531ab660f7a4f8b4572b7a19c23fc5a431299ecb8c1846cf8e279b97851","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2d6a819db1ca73bc3e3b29597b18ca5c36e2dafa02b171e8bb92208b2d50b353","signature":"1571b1b7546d0267d42d0c0b3e1e4593b2ef990541b260a9652427dd82758bb7"},{"version":"a62a7e45347ca1dc80dd4df8d2094790870c03b1c890960cce9628934f695295","signature":"af0b9205baeb5b5d2e4470da33f26673b5c363db0ccfe761276f8af34cfe3e9b"},{"version":"4217680981bab6d62ee8fe0cbac591599bf18f30cf2be39170d344eca5f7885f","signature":"5d3ccf27d7ce9e5f390fa882da69e253103b64bcc4e1af716d4e385d1f7dea5f"},{"version":"0571fa29dd502778997d9453169040a83607ae311f6ab6a7ce90fdaa83f86a72","signature":"6abb8469a763dfe1299c79302eb5559ecc978df41c92c0444a30c1b55710860b"},{"version":"afaea598c95b1ad2bd0ed30bfb6ad867d78bf021b73a048b8fdaeb90cff22d88","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c1bddf61aee12dc67fa70b5e40a9124f1f71f960b6bfe006fe4078273a7f77e5","signature":"77307295274cc402aca163afe863f0ae8a1d2e94588f2acd35ec24d77af97b75"},{"version":"d579bdba0db63d615c541195af19b783527c13bbcc870e83ea61d198e3be56c6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"10412b70545a4b21b51229be0a35ddb2bdff35e164c35e214f5f56baf863f12a","signature":"8aa83e11d68ccfd7360e5de9cb82c18ce6dd67f9c7dd89c81dc67e55dab60864"},{"version":"ae6af43f3746a31699410d070400739e19ef2281ccaba80b5e4b1c4dd4ce644b","signature":"1fd318722bb7bd17560dd18e11b824febc7904ec355636c7e042efcf18859c12"},{"version":"acce55a78691675d126c86174822bc67282435510ece51f11f0fe3e3e3439a2d","signature":"69db250a4e6aaf0f4ed86907855c93327ae8e663b3b1fdb429fa0405ee768215"},{"version":"7aa764a1146707f1b9e18292969e24f394ef3c347d4f396cc6f90d39c3f3b6da","signature":"7c52c6c55104753b3519528829004136bfbe6e76535ec0a19430668fabd41269"},{"version":"688c5e58ff9137a2c5d6eb1a79475ec4c9d61c34bb10080e21d09babaa30ae1c","signature":"7a49a822cb790c72be6db966c7f0d69c641479732f211b020cbb08bc4f30a3d1"},{"version":"c51f1961b6b22a86183d4e8e166a4b08df4cf3537533f9249a79d3b460efe6ab","signature":"60f3fa0096effac152c038612c40d100ab675f4d07eaade19ab26291654c5322"},{"version":"13cc0e63b3212f43a760f9618ac9a5a26a3123954baa408ada44dd9744d060f4","signature":"6d2de774f7f1930f5a1a0061d45b779777be9dc7e6125661388a11a43f386636"},{"version":"693212d0a67ee305c09bfdd670455ad335e448c9fd52fb8c69ecbcda23eb2b93","signature":"225d95d4c8f9caffa003ab70fa3ac2d8b66e4bca291dc775b2d1ad4b676b660d"},{"version":"41a4b706f190423fb86f0fe568b685dc59c4a76760b506b02c10a3eb70ff880d","impliedFormat":1},{"version":"e26d25ac80157bf4dfaf1b15917520d7d5aa515389c7c0464911ab0129e58835","impliedFormat":1},{"version":"e305ba550c25a1807b5c432b31563f897c82111c8bce166e560029c94df204c3","impliedFormat":1},{"version":"a3e0936d6b795d2fc46850dc9c590152942ee297f3271dbd3d8588c3983592c6","impliedFormat":1},{"version":"2013af14579369e7822fb5f20f01268f3338141d4a382d5969144fe9c6a25ccb","impliedFormat":1},{"version":"6b7fcb23322518af97092aafb777aefa9196a1db215071fe8f96ae6f6101b498","impliedFormat":1},{"version":"a98d81cd9207ec9f1bd54d454809a60b267043dd56b165bdb6912bf9d63d4759","impliedFormat":1},{"version":"cd1aff7bdfa3af19aae3a21c958b932af6941ed89af069bd789f9ba5ad43377c","impliedFormat":1},{"version":"33e9caf10988726e5f7be53d2a4ebaad8db16e51b5f81e5ed4dbb0068f3a88fb","impliedFormat":1},{"version":"cc0d535d54cfec869fe4d28b9acda7e3427b25d8d84fdfe495021ad4cdee301e","impliedFormat":1},{"version":"f3e743e40c9a3b22de28624c8428e1df4cedc2fa3b3d99c546ebb95592ba1978","impliedFormat":1},{"version":"f8dab311b48db5e3b4361d76a629201098efdaa1b785120353f0ad221b263682","impliedFormat":1},{"version":"261b2daf69470a9e9d1ef26aa5e0c23f1611af380eb5e5f031c776ba57367741","impliedFormat":1},{"version":"e181f3ee5845726fa8077b39742a875d4fbaeb35f036765f95a77afcf982f989","impliedFormat":1},{"version":"9227c7d1182bc93c52d39d65472fef3f850a0d0145e1fac7388758d995878ccb","impliedFormat":1},{"version":"d90fecb86a618f308931b2efa45d7beeb73185db71ff184c1066b4e5f5900771","impliedFormat":1},{"version":"69af21e2252e417f1f26d2e6b1ded1f20dba11f066ce0690c53946f4369e00cf","impliedFormat":1},{"version":"fd4ae6100e700ea9e36ab622968f12ec03792f98f7da286cc44649395022453f","impliedFormat":1},{"version":"1adede2d971ad167525195ed811b8e0af2d17c728350689c470b6d5c5a3aa60c","impliedFormat":1},{"version":"4486b61dcb644ea4527e90b5bfbe7600b9ed50371d334a0c01e8fe661a99d952","impliedFormat":1},{"version":"dae0c9cc1c106f7b91a726813cfafe489dec4fb1e3f4f7c684d14e5f99f96b95","impliedFormat":1},{"version":"bf5fe5a9da7069bca0bb83fdc31ba9f624ac778c3ce25a06a3099d83a92a4858","impliedFormat":1},{"version":"7a0958a789740f9ddba6b7d80fbb1ae4d97da910970c1d59965ebc80304ad22c","impliedFormat":1},{"version":"54c9f10d962306a39448b924e73e631bba8221b5797ca4b2dcfb00dca78827b8","impliedFormat":1},{"version":"7125d9240eb556853fdaf7c892b60c630f6d01bd11580a32e9f299b0b91ca4f5","impliedFormat":1},{"version":"b339079a6ce4d7e86dc68ea8af379ee8700dfb205da5546ffdd3cdfe4be43df4","impliedFormat":1},{"version":"a97af3b565a5649c8cd983df40878da9a4bc080cf1a918199574f82a25fa727f","impliedFormat":1},{"version":"6e5996003f6555309d988ceae5b9952f3de47fc6e5110e5d64c612d4f37cb490","impliedFormat":1},{"version":"f07a1939b55120050d3fb95633ac6f8ebdb594e63eaf8f14c123480cc4bec03a","impliedFormat":1},{"version":"325e48e1f58a88830852e00f4fe7c515afef925a1254975a3ba9c54c9156f3fb","impliedFormat":1},{"version":"79caaa6f2db6cd3a1362f50b48ebae937a234b421c66516b8c98282b7d713d12","impliedFormat":1},{"version":"e7f0aa1f6c72a395ef5ac8749c03a31b00ea527abca2db5aea63804fef2dbb72","impliedFormat":1},{"version":"edca8fd9f3960acb4223f2a596d1f944320193afb076cb8b96afc19fb83144ec","impliedFormat":1},{"version":"511a250228acb3131a8c9eabaf45a53c3015c8a5abffc1c2e8b37942b779ef31","impliedFormat":1},{"version":"496927b9d1a6a7bea58453b9749b878ee1040ddc22de6db6e689db11ec111ebb","impliedFormat":1},{"version":"d17c142d0bbf9f20c696ed0224d1740fbbda0b0211225f2e48732809e15f2505","impliedFormat":1},{"version":"67e2a7d5facca25f1df14bc4fa4167337405fdc98b47df49b98aa2dcdaff5696","impliedFormat":1},{"version":"e16f1bf72fcbea703f1b9dba81e6faaf65e29d24128f9e09504c9a862f41c9d1","impliedFormat":1},{"version":"2e47b85f55b2c6606b137b4be24ea386c58bd9621370d2373e95b530c955102e","impliedFormat":1},{"version":"1f9fcdb7e24f1f9b391bb9200e32f6353781a1c604159a6257e9d8f1df7b9bee","impliedFormat":1},{"version":"3c788378c8c8d9525c168ce0578d536607896146b88bb5f6670ab19834be30b3","impliedFormat":1},{"version":"d16aba532f5391ccb1a61f90cf8e904ecabf3105641c398df0f4a79cac27d472","impliedFormat":1},{"version":"8d3a30c921fa2ea91d1bffc7fd9b8c42dc74bc819c503208122bd2e84cbffb5f","impliedFormat":1},{"version":"e4fb93fd2ec72ebaaa1e657435d607ed155da0005e6e30c440257d9fa877d834","impliedFormat":1},{"version":"ad103c0c76b809a8830ca4f9a8c8cb43b53d578bc63dc8c63b1342f1de90f8d8","impliedFormat":1},{"version":"7e15e2f23da806eabcf4bd0f1e48d7a5baeface210f05837171b50eed7d2b894","impliedFormat":1},{"version":"fb5e2afe7c4f988b615085166421f8d88464f6234abcc740ed861eccc0e17ba4","impliedFormat":1},{"version":"652af46b250ee5144ecb0eef7bcfa1647c88fd170cca974bdff9bb315f349f52","impliedFormat":1},{"version":"790daabde36636c46a264b1628f488ae49b2b378810383a1059ee722e82ceab8","impliedFormat":1},{"version":"89a58d0ab59eec78a6b6532e5d748f9942568891619633c890638a2912224ad5","impliedFormat":1},{"version":"9af24ffe92056dea7acff1dee779be364ad35e5f9861ca417d17bfb447a0a230","impliedFormat":1},{"version":"8fab0b106acb9de629cc3f7bf784187cd59d506d734917c4f140d02f0dcd167d","impliedFormat":1},{"version":"d0ba3b6aaef0c96be907938b6fb2a3a04a5db59de34a40f7e426bc7f10bb46d6","impliedFormat":1},{"version":"d91c919538e393ed3c649270a73f239ea7cd9f312dcee7dad037869a6eb0eee0","impliedFormat":1},{"version":"fcf5f4ff294643e6ea5100d09f40668a3a8744b73b8f1c397fac4b17ccecc72f","impliedFormat":1},{"version":"af3bebc2d30fe79abc9a505bc890d16af72f8ea21ec59009e9d57c2d8f6e0b01","impliedFormat":1},{"version":"784c657f85bebb1a7d94ef05e10f1cad4abdf32798203ef8631f7c3aca2390dd","impliedFormat":1},{"version":"e488fdf1efc9112b9ae08aaf2be027c3cc5603b916582c45d73bb3885728543f","impliedFormat":1},{"version":"a43b695758408470608b548841c97ad3827e453fe81ad835e29b9871129785f7","impliedFormat":1},{"version":"96dd9a7f52627f94b64da26c1ce05d2350941487861c8c27a0014c67273c8a40","impliedFormat":1},{"version":"0e754d4ed9a6cd6c131515ba94f3f1095fb10ac3cb0c20c2cbeef9e895f924c9","impliedFormat":1},{"version":"72cac4a4359c6a5e2e5c0ece767455797e46871350324dfe42ab14238f675729","impliedFormat":1},{"version":"cd091878f6b6994d9307156bda8a4419c7c41c524228d9e830f5fa618d70672e","impliedFormat":1},{"version":"bd3bbe444bc7cc28757c7669fb186a9ba326d4b65dbf99e18b2b5b9ca66edeb9","impliedFormat":1},{"version":"b10c73b3d7703d2d870d35631428cdec737d4bbc06706b6fcc6f6e058b8e1594","impliedFormat":1},{"version":"2543b46883befbefe10c2de9f3a0e7809de7baa09e192edda748443fd15d38d3","impliedFormat":1},{"version":"01cdf83ab596024078a6ce08ff990770326ceaf16f9081a8e369b9bc5110cadd","impliedFormat":1},{"version":"bbb9a17f2654caa1d34f49428c0e48ca0fd0d9550f5f82da6f544c924afa17b3","impliedFormat":1},{"version":"a9d8ba2c15cd99f51aa291034a1afff2f67f1f88259d2162eddaa25e3644032a","impliedFormat":1},{"version":"affa88c9484982a9aa35b30480059dadfe98c3bbd92f76513ae2d1d7e68096d2","impliedFormat":1},{"version":"3436c4b40e71c333e253578f6f3176870d4963d5f4ec14862ba5e40794bef8bc","impliedFormat":1},{"version":"a02f786598d002e2e42854d9bb4fc5a4ac03589538055a0eca03c9ec7ad35457","impliedFormat":1},{"version":"348863ce75f819f43557d134e5e7ff11a8ae582ac879349cdf9156bb696012f7","impliedFormat":1},{"version":"235ede03bdeeb87ca21b68fb1398ebc4749a924e2a7219977b71bfc9c51574a7","impliedFormat":1},{"version":"ec83a163716e8a8c2324e3f6b64c907ba7e5247b43df47f52edef954232c0211","impliedFormat":1},{"version":"15a76d1390ae38fe474023a51778887a6e39cc4204f65519a448ed7d1931275f","impliedFormat":1},{"version":"d7a9a81362adcd395f9db48531a89df84461595d189a234796e85ef983399042","impliedFormat":1},{"version":"7ac3584d37571a5ba61326f50860d843072ea95673e53d23b0b684635db9db00","impliedFormat":1},{"version":"96354248b7b7fe792c9545b7b153ef20763677692e9baa9aa6d1afbb17376ba7","impliedFormat":1},{"version":"5dce9f1eea7d40ad9f10295dff47b7de6fbb24b33858c0ff91aa75043e9899d3","impliedFormat":1},{"version":"d324bb068a3a98f3d7ff92eed388935a5ed4bd46b7678c5bf057b5a2ee9416d3","impliedFormat":1},{"version":"fe6e76ab5933ca777c6ce422c7023d44799d4832a8c5ce35e3592c8430867329","impliedFormat":1},{"version":"cc5beca247a7da7286a82c0f3b84686a922d0a402ead5d11b9ddd0dcdef5c762","impliedFormat":1},{"version":"24661a3d44d16268a8ac8260f35651526c54496ed5f29e559c066b7b7e6776c9","impliedFormat":1},{"version":"7c7ddd5cfbbab70612c216ab1d1f982468118fead1d57948ed31b41cd692c2fb","impliedFormat":1},{"version":"461ffe558e162cb3e451654eed59d0090a267818fde655088616be907007d654","impliedFormat":1},{"version":"829df07ac748fd372b8bcace5b46e6ce0420d2fd65c23f64b86e8a099b69a21b","impliedFormat":1},{"version":"162ff76724612621b2623b71139fae21981b276595c5e93a909b464c6eaf6310","impliedFormat":1},{"version":"4ec0abadee52b5287cba7929ce1c34e81a67a046c0299908158497ee85ff20b7","impliedFormat":1},{"version":"3b60b6d30d8be5b573e75d148abd155fb74cbce0795055965ad505afa4b181f9","impliedFormat":1},{"version":"5ea0f9746d216da9d45885462fdad43ed26dc4473ac6d289a94661c9a1e7bbbf","impliedFormat":1},{"version":"96198fa503333a1856039319ad4bc45c6e32afe2ede6a050e23fee2127139a40","impliedFormat":1},{"version":"e9dbaa3c845b96ebd98a7a3c59296fad4f5cbe4c2e471d0c54a248dab6d575f0","impliedFormat":1},{"version":"63c5fe7a04273a69125008737aa3c18212a1276b3a2f3892080c346cb589a716","impliedFormat":1},{"version":"adca096d8a06e8fe1f6f8a1d95dd176e0ec2216f5dce683c9c3656a9bb1e1f10","impliedFormat":1},{"version":"fe5cfccf2b757c44e7251d6ac822f4892d63f0dbbab920da4f24b893e655e836","impliedFormat":1},{"version":"e2a5e2a231048f1b0a8c6c123d524adeb3eda1464ac2413fd039cf5afa57bc54","impliedFormat":1},{"version":"8701130ab14da66b4e908e13c3ece584b420399cc543bafca971c414059ee5e8","impliedFormat":1},{"version":"811e9e98ddaacadbbcc92015fafd5f5ce0dbebc14f3536cbe225094b1d61f885","impliedFormat":1},{"version":"f7c8e5f19d7159bde8f8e9c6561c6e517953457faa86018a7f963b72863380fe","impliedFormat":1},{"version":"c0bc4b28c78bccbb158fb2e8b3e37a86fee5f26b6098a857befd864790da7cd8","impliedFormat":1},{"version":"e7b604762369c8fa5ffafb6e238a2c7af296e5a25bfa25cb33191b525f064cd0","impliedFormat":1},{"version":"dcabfb44bd25183c919819f87428fc589b20c3b9586825ec456f94cdb67bd316","impliedFormat":1},{"version":"41c39405eb8d94777d8b30d2bb295c258391ac4a45deef8d2f569b29bb82938c","impliedFormat":1},{"version":"f5786f9b0a39c790d245b33436e75576990e41e995e1fff1b0919833a57f4357","impliedFormat":1},{"version":"970025f12906d26ea3c1c381199eb6702b9c8cb0bec44edc02e86e004cf95eb1","impliedFormat":1},{"version":"8dafc03ad3ee7acabdb9254c702b80755bdafa7d7548cc6ffc21814e83055abd","impliedFormat":1},{"version":"e0dba6e973edfd7a5d8a7307ad1e6ec014b51fb7dac507ae132d1f9429016252","impliedFormat":1},{"version":"cc2f61e4781ad29f2aa93d4850de1b1d8313f242631f10ca17cc99411eb63022","impliedFormat":1},{"version":"6679676c1dc90d9c371f3de8430bf070ed36d2677d9ce3d2a336b54c5c40c2d7","impliedFormat":1},{"version":"cf2b168364792895c95f8f98f8fb662f07787e518e6d25b0f7c4aca9927a1bb5","impliedFormat":1},{"version":"db115e097d9ccc281414e7cedebfb0435d5bb14022f147331fb1bdad09404885","impliedFormat":1},{"version":"aa78c2b93bce87b73fccd6726cac3cac4f62927460ff3495f2a05553b4c04d3e","impliedFormat":1},{"version":"298104d50f65103c256ad79ff5128141000e4544a6afaa998d3099bee3975b84","impliedFormat":1},{"version":"a99f5ced5c95d7603c94c66a4619dfd0e737351bf20757de516b7f8ca193cce9","impliedFormat":1},{"version":"341b20f291eecbfefa6760a69f7f3f18b2094edcc794f4e78e903a5f0dd86fa6","impliedFormat":1},{"version":"238dd354909fc4a682e0cc4bd0d1eee8ba03197a4efa3cb284e502355eaec8de","impliedFormat":1},{"version":"8a3156a33e38b19f00d543a9f7a96054a0a4b051533449fb04267b4e533f55ef","impliedFormat":1},{"version":"bfd14082a4db87c2847135aab3d617ad7b488b3e65ac82f1620742548ed630f3","impliedFormat":1},{"version":"e2aa5b5cbc067b485de95616efb852886f4a1a43685ed7ea0ee8e08fec961cb2","impliedFormat":1},{"version":"3d6d27c275808a7e8540b1778a5d3808542518acda03f5c1ea4c9c5831058ea0","impliedFormat":1},{"version":"f534c1a02cd756679611ca2b36431b51715a0c59a070d413e292dfa23b9b5c6d","impliedFormat":1},{"version":"e26cccd0ef5654714877908c1674bee29a8e53d60c8c2d82bdffc12cac6b0fb5","impliedFormat":1},{"version":"ccf581fb8928f37fbd6509a7d8fa0d32156fc4eb414f434bf81cf7bd6849f7b8","impliedFormat":1},{"version":"69b227120a5245cddb0805eea82a0bea405872bcf595d2fce9fc03dd16133291","impliedFormat":1},{"version":"d6f495bfd0020102c67a6f80e411b00b913a001c468001b7cbe8c592c748f301","impliedFormat":1},{"version":"eb4463ba66be74eb04aeba3bded1f485a6ee90bed8c28a2c2573f0c983834790","impliedFormat":1},{"version":"a76187885d25a8aed20f71760c116bfb89ed1612d125bc190ab25a1a7a87ed91","impliedFormat":1},{"version":"76d36099aa1a0c7ea690ee1675ac4dd86cc62e2643cd40c097899268f8d2f7a8","impliedFormat":1},{"version":"815d7ee4cb5383f94b88688b8f2d70ce3e5df4de147d3669d225f8bfcffad673","impliedFormat":1},{"version":"afc6a3b94e405b3ae5d5038fe66f3b2412300c93ba1250805ee1a7ad19964ff6","impliedFormat":1},{"version":"b168bf198df3af94f54863a77ca14dcdb67af689d4cd876328f7c70bbb7b985f","impliedFormat":1},{"version":"54a40fe6e389146cb444299ef2d7d6e4ec83b05a9df2e7611fd1d1d862b2743a","impliedFormat":1},{"version":"65ec140c7cde7edee0f611bc84ce505cfa71916571e76f5cc197ba1dcde32f2f","impliedFormat":1},{"version":"969a96cb343d30bd8a28bad66c77049aeb0fabd9f608ad82caee751082685eeb","impliedFormat":1},{"version":"b30e161d3bbbe3f8d15b0bc5d9b47d1935ffffc7ced1099fcd84536c906af711","impliedFormat":1},{"version":"3eaea558e36977f924d85b3207406594568053d7a52950081b7603d112edefce","impliedFormat":1},{"version":"4918bfaa32bc0f63380d84b19bf5adff3188797b17b055417bbfdb09bd531d1d","impliedFormat":1},{"version":"5750305060905aff115cc0a6f295357099856fe72d76a1193204c23b4b9417f9","impliedFormat":1},{"version":"98195f663b1c09572bf794bf2fd7351b05d5895ed471589c0c79ae2e7c7d6b97","impliedFormat":1},{"version":"355e8226f1a83a02c2c5dc22781defbcf171df314f6f3205318851fd4550132f","impliedFormat":1},{"version":"8abeb772b7a7763686fd699980b74d9895863a758f2a6b824edb3d5e5c235078","impliedFormat":1},{"version":"dc1e2e53c9935f62739ca37d9acd484e83fd25bc051e5a330c9be0acbe774253","impliedFormat":1},{"version":"71a542cb540a3c0d4d954d311937a4df56157b0797489ea5cb8a9e57f449aefe","impliedFormat":1},{"version":"49fbd0ae0b53936499ca6293450230273cf299e017ac4a1cc8de936d50d8e696","impliedFormat":1},{"version":"d1a6aec1239a47bbcbbc55324d6ed293f79d4554f6bb0e411e206a9e22c50aa6","impliedFormat":1},{"version":"4e81f6b7048f1022bd8dd7dc18e43b4aca8967c4ffbfc8fe80bd4277936e5be3","impliedFormat":1},{"version":"5ff6328b404fb34d2828b501bd16f75bf17590d2b03a66a469a0359da07a06fa","impliedFormat":1},{"version":"149fbdfda86091cc37779a6eb3f01ac5c73d6e6d34a71e76bb3702a1ebdd8bf6","impliedFormat":1},{"version":"3c1e92d9a7a0c81d02f016c47de63a39706bb0e36231f2e6727a08cbbef6cfa2","impliedFormat":1},{"version":"ebec459a7d4933732cb453254997b9b9e7d17319dd40a29946f985719e297927","impliedFormat":1},{"version":"394dec85f81a33891c71f4e6a1b9a40afdbe93210d5dec749a2791deb57df5e4","impliedFormat":1},{"version":"19d2786de07b0dd973e5515d84182d2734f1c1ecf602929f75b30081fb20fcce","impliedFormat":1},{"version":"728d3db3ffdbd649c96fd64cb5766993cc8cb10b4ef207403fb98304eba04f57","impliedFormat":1},{"version":"8e4ae5371abcf89ca3059e621b666f1a340db0575f0c8635431417528f2d6367","impliedFormat":1},{"version":"de4f5917a7bf2c62cd3ab4c171620fd6e88a4a92902f02c0808ae67793e6baad","impliedFormat":1},{"version":"8f09f0abdd346cdbb1e545a44c85dcefeb047d07080628562a328d3e88160beb","impliedFormat":1},{"version":"4557c1259460570a893f9adb1547b5bdd19948497740c53fbaf654b20ded5855","impliedFormat":1},{"version":"ddf8a6692f74ae9ebdece687ec1cd9ff63811c5af27381b40c7996053a1b0504","impliedFormat":1},{"version":"de735626154dab7ceb24b208728b7461aaa5ad8848152ab0b39192e7bc0aa4be","impliedFormat":1},{"version":"c6296acd69aca14033c815aebc1b4c7fe72b92e44145dfe6432fe855c8c7b463","impliedFormat":1},{"version":"fe7df3fef3d25c0455e7cd4f36fdff7ad7b4d2163e7285a74d6a98a6cb48a282","impliedFormat":1},{"version":"7e9bf7c76b60c4402bd48996bfb0d1fa552a576991f9f73dbb856d15b0346793","impliedFormat":1},{"version":"29199bf01375b374d516e5c8d5d8ce1dac3c07cc52b00eebc0a7ba7b05baeb1c","impliedFormat":1},{"version":"7696d51d4ce2f2f21e9f73214396bfd823bee6c57f65d39e6a2264c40dd021f2","impliedFormat":1},{"version":"4b74f4072dacae8ba4f21abf5d042e5da499d027b0aac8e2d8f42f5f591453a8","impliedFormat":1},{"version":"d3b884fb07719c9457497fa9d5146f7977fed29df303347ff4175f630b903fcf","impliedFormat":1},{"version":"665320db7cce83346ce47ab3baa657b3115d796d28d8f778a98e7f23962b1247","impliedFormat":1},{"version":"3f471b5d1f378519b8276a869cb746d5cfc9b2bf4d7e5cbd0bdec3f9b5f81fe7","impliedFormat":1},{"version":"4afef388856e350489141ad7d90ab0767a02954d53d953f558237e04c89b5d0c","impliedFormat":1},{"version":"7b16f7f12771f8e25117321d1cb607e06be688ed8db002fe2cb13b716d0662b7","impliedFormat":1},{"version":"9412339ec64000a6dce5898fd848f025d31ec3b446872070cc041ade876c0c1f","impliedFormat":1},{"version":"726bc5d2505bf6051e80ede05a576e21d65118c077a8407ef4f9eca8d5445464","impliedFormat":1},{"version":"05f2af853ef135671b9482077d19a2935e3d924debbcf2f4803110bde114f113","impliedFormat":1},{"version":"134078b75b0105535f6164680bd73f88f9ddaa84b7e0126aeddd2af7ea27bcb0","impliedFormat":1},{"version":"264f0b10beaa4141a6bf228d2e22b19ff7baa76b39388da319bffcd1ced741e5","impliedFormat":1},{"version":"98682512d496bb618315de173d7a25ec363676da2457939f42b75a23c5acab87","impliedFormat":1},{"version":"1471ca4439a9dc9787976d1c9ca2b913908f4029465c87372d2efe2069c375f0","impliedFormat":1},{"version":"54b8abfc4713160ce97f91758ee1e8a547ba67c7328177d5e4c40613a3e87f79","impliedFormat":1},{"version":"60894b9993d7e1a7be37933c9bfe96b228ebc206cada93a44bca9d30e12d8d8f","impliedFormat":1},{"version":"236ec9d640fd6438a08dd2be3fb739352f147de529b4aa1953e4d4f74d6638b8","impliedFormat":1},{"version":"e75ea841bf22a156a7ae8f95eb7b1d4479da8c291019148d0a643027356b76c4","impliedFormat":1},{"version":"4111a7444998538da1f6f76378412547281e30cc5a7249b32e7402f66f83a492","impliedFormat":1},{"version":"4b9a4b3456012104409fde7f7631be98068daaafe1c49b627b8d92f033960b67","impliedFormat":1},{"version":"7f7840713032b2ad3bbc379ee2d401489b4e563294f7c87dbaf2424a6682beb2","impliedFormat":1},{"version":"b52b80732805d494ffee704b80f689772e1db9440c1728b907f7b25b3328d5ea","impliedFormat":1},{"version":"a805a58a4c72c7d513295fa7102284dd9cf76c1470e1845a6d7e9afa4bafa609","impliedFormat":1},{"version":"724e0ca25a06f553306e33a45951c368346cf1fc4b26b8bf4bf88b1479131659","impliedFormat":1},{"version":"b0880e598b7256855af6b9ea2aebdf47372444114264b56da9d25ea5f95d064e","impliedFormat":1},{"version":"3ff6607fc3c3a85814ec3d6e05e358d99773ee7c7b5e9deed5e086e39f5372a6","impliedFormat":1},{"version":"62247290540b91ed85258d7e8c67c7786e38bc1111429da9fa42b1b34e4ffdd2","impliedFormat":1},{"version":"656ee3f8184b5dfb87200f73350e57827dba056130d15064406487c0993f0e6b","impliedFormat":1},{"version":"b14c19907984b69ce25e011e6391ff6a150bb31f344d643f2a3d5af9aeb4ba73","impliedFormat":1},{"version":"2b77a0e88653109c708203a50fa23bb50406a9c8c7f61883c92e009f778387d6","impliedFormat":1},{"version":"2aa50966f709107108e7ad733c129c81f9e42731492948a9c23d4f8a0de5ae1e","impliedFormat":1},{"version":"080afc7aa193ecf03c78afe2e3d81cc3b18fa482f1bd955b4dca67bcbf220eb1","impliedFormat":1},{"version":"7282df0d72afd1c283644f5827159ffe0c899850fe121811e6e7e3eb77416868","impliedFormat":1},{"version":"07c70d4602003ffd9f23f8f6fc2693b7cf024a323a6d6146b11659105ae588fa","impliedFormat":1},{"version":"79eb7464a0215c82cf6fdc55b2378dc0a3aed416f03dc647fb6956975d446ec1","impliedFormat":1},{"version":"8cd341d72d1ce25d33dbe1681a9a5f27fecfcf65d426a0d0bb80ce97a1e37d50","impliedFormat":1},{"version":"4f750b488d0d1019bf8b6651e68689debd6106312ca7f4fca22627fc2d0acc04","impliedFormat":1},{"version":"4307994ad4d3a8d842a7d7da76f45f84e5eeaf1580e9b6071dd5fa6b8b21de19","impliedFormat":1},{"version":"87b54711b1c9791dd95b4ff88f814b489089f5b128f29e8a5fb7f6b8123f739e","impliedFormat":1},{"version":"4763437e8a65ec15310aa20a4ec288eae3de1b94b9426336ac423fddd482d70f","impliedFormat":1},{"version":"e2398ace0c73a5da036dbd6cab98008a251c709c56f1b665b6202e99ae3450dd","impliedFormat":1},{"version":"1a59a1ed95ac47cb6d1798a4dee9088b847f41491e57545e788ede35ed1204e5","impliedFormat":1},{"version":"c89ec75e2ebb2f5c2dce2d5f85ab59951cdd217748a49a6e0bd102fe69f5eb75","impliedFormat":1},{"version":"e653e5173f22f243892807fcd85800dfa4efe84be40e0ed1cccd15d62e1c9da4","impliedFormat":1},{"version":"973c290e94835130e51e934d078ab80e975a7bdf5b9563f5fa8de080a06c588c","impliedFormat":1},{"version":"5abc40134600b35d15fcc7305d5bc9e29942c64dbec6413137b55e77b689da1d","impliedFormat":1},{"version":"85c3303460c6339a77ec37ed9b7e06974f6d8e1351181b3f6f0c5180e0e7a76f","impliedFormat":1},{"version":"88d761b9b53ee5aa4ffe94d18e1c05c31ba8778beddcb8418f303c184f4f61f4","impliedFormat":1},{"version":"8ad1059fc2cf09ab5208fdc50a974a1a0c0f3737d81c2aa0718c583716b49f7b","impliedFormat":1},{"version":"ded7cab1c0c297958efedb1c4569674117693e0ebb6e8a1366cf7a629ba490c6","impliedFormat":1},{"version":"997c088bb8c6e1d869a689a3db0157d3efea26fa3fe49ece5f01044321949769","impliedFormat":1},{"version":"aa4d9968e228a15f4f93ba782c05f19de5aab2598ef8acd819ce72d6f4c9953e","impliedFormat":1},{"version":"1c4420d12393decf20734fff3f09f44daea5433869633ed11e9fed9b316523ec","impliedFormat":1},{"version":"25e74c23ca9d123ad4726803694ca249b958270fa3e98eb3de7f339f15241a45","impliedFormat":1},{"version":"87537c5c41597d3355cd28531f715bcfa77b73f0622d49b28b6064b3c0ba0ab3","impliedFormat":1},{"version":"56acbddbfb96d3e9502c73d87f216fd16b9954ce7fc345adaa51a054bbd548cd","impliedFormat":1},{"version":"ea5f8a7e470eec67d9b3a57a311393d9b8146d59e7d3965fc2a07745aabb50d1","impliedFormat":1},{"version":"d75a15490d5dc9b8bdd209200429a4cd31c139b1503e22e1ef743e6d4fd160f2","impliedFormat":1},{"version":"fb238a904625a2a0942f8f0aad2c96d5ba7b684b59890599a10d73c1bfd3f771","impliedFormat":1},{"version":"35c91fef4484772dedb9c253a07ad91912a8e349838bef1e7c85b93b6acd39c8","impliedFormat":1},{"version":"b92d1f42b729031910871b073bbf05b8d68994a91dc43a7fc64f88abff16db54","impliedFormat":1},{"version":"beaeb7cae58c1b074e1e5c4c0cc4e205b1763f0e9f413d7062951e1cf539b450","impliedFormat":1},{"version":"2059b7deab3beb764a2368200ead025358b48fe470bd6850500785d94c8fb5cf","impliedFormat":1},{"version":"da08b48bf74446b6f988e95f501693844d59d445487d89d2364b8bf431c8a21e","impliedFormat":1},{"version":"17c4db14970964450f5bdcd1349e6f3035419db7f7f9f88ee966b3b34cbaa8b3","impliedFormat":1},{"version":"a9e6f34151c8629364892059c1689e2f99775b3642a24854d0330112d6892cff","impliedFormat":1},{"version":"bf5fb8cea51021d395c45a092c1e97534d498c91812b99fdb07c658cf5990585","impliedFormat":1},{"version":"6bc9832d675edd15ca0c8e096cc4008e2791d822cddbe218e7fe65d33de8fa2e","signature":"78f739f5b91e1135aadb4752b0fbd6b6bad0fa86b3f0e889900982b12176fc2e"},{"version":"baad5518e27c0ff3bc6192606a3c70d64e52338ecf1a1492a3582c9e8827a7bf","signature":"9d01797abc1ce5d2b2ca095bee592fa4887661c3cb1603e9f126b767d68bb57a"},{"version":"a418d3e5729d2bc1f21789a3926a6e5db364e9f80410207f4eb28b55a5c70cff","signature":"afdce15dde5537aa0c81dab15a2367924eac28ab8f25ba3e403f7338da845b92"},{"version":"71dde8ef5faa2b2f5f4a8f56944429ff768600489ba021017b68473c93660eab","signature":"b3a61d1bb2c4eff882c25e5284189e1934aeb4af535fdb36694fc461cf4b7068"},{"version":"0b791c213954a91e7d80daceb4b7d7b53600a731e2227d3541d88a09fcea1621","signature":"b6e882b417c55fc40bb0b42ad061d8f97bd0b2fdbd2aec5aa2aa257420c7c2ec"},{"version":"c041ad3802a420609f6fbb3200a946b897838cb21b76e176e78b0cafa83698bf","signature":"63b2f936da9faea8c52723d6b78ca09ee0bb769833160bbcb000be8b7cb456ac"},{"version":"9d0c46e2b8776a71972db76904d933f54d190601cbd57b82438254c275808ccf","signature":"95bee50322d4d787b4a886030c691a2317aca49f557c115e52f95938343f65cc"},{"version":"df804257d254a2e00d640a55eefb2ae628da95dba0085ca824a04ea3ff69ac99","signature":"2302a6d37e153539b259b1f3bda1c10d344984b15efa30ea39ff5c83b5825977"},{"version":"74226e280a2991fdeba3808665dcce17f87736137ca79404c5d8d7c668eec8df","signature":"b4e98fe21b2b7cca7ccabb5169df346479a0e3bb6bf46c25946174977917d316"},{"version":"bb520dd5abb511ac234e88f420dfbfba03a6ef74a9c783850bddd833b8235b23","signature":"80923ea73f37baf4c06eb870a02060c68149fc8a8e47daddf6b55af1047d2b0c"},{"version":"372db055c8310930dcb90ffa00df06b44ac8e725c75e0c172786676ea6a11794","signature":"6179a86622a28cdafe5d99fb99e1ab06b1a06011bb9a8ae9d65d4697e61d5316"},{"version":"0d7213f8b71376061118e4f91a6faac51b38b372ad171b4df50bf3559ac2b3c0","signature":"37a718acb4d240ce0d45b7082a821b9bc8d9c523df47980aed0547887beafcb0"},{"version":"b858c7849e256828563264a2345354ac829be7d7afc77e2c04f7683b81ccc79d","signature":"e724140889de1a68b6fac45652942a37dbe94d0951da3249196817e9004181c8"},{"version":"a4c991c3fa2bc9437a6d84cd1b2557b904adf57f6e7098943d65a01b8c57acc7","signature":"4fa89a082213215027fc85892fd3a42bf898e652eded33469c2c31a75cc7db12"},{"version":"9da149c4fd78a4ccab4e68a54c0cec7c3bbe48163c7e4cb550569bbca603ddd4","signature":"f50138c9b21bb7d4b52c5bcb99ff08cab9112b3fa3eb67ba583c1c033f5658fc"},{"version":"baa1b838cd0e200f302fa49ee523d8f74fdf7a16c6d14a121621aec564cc92a1","signature":"fe0fcdbbfc40a17d638651589c6fdae7c4d56ed10a0bf9e04dc47fa42b94ead4"},{"version":"ea99af3c9a22cee8ea6b5754cb9d29c7076588361543519e0a95675747e2c17b","signature":"c77e23e46e99ad3082a3636b3fc4955831a6c50cc6be267983ed20d86c42279c"},{"version":"9bd02ddf990e7c7a97c5b70d4357ed3d8ca8f7bc5061615ea7b6d36f2e469ab4","signature":"59955746d15769af662a431f4bea5f2887de185eaadcf0f8e0df9766d138cff8"},{"version":"4dd94a38316ef3061df0358c3174ae3b95728649913ae705fff8343908b394f9","signature":"cf8dbf5f0b2752bab0df676b52d9848117b17fb7306ea6e45145ab60205b4e25"},{"version":"276463327ef1cf4bacf602f9a03566fa793923397e6bd8b85c4ffb2da5554a02","signature":"88658d83f1351b1364c8b286846e79d8ab064ec332f8c693f8d133347cd2fb52"},{"version":"eb3f998132c1ee368d9196be6771f374f6b809b6693f1f6a75be7118cca56145","signature":"f6a21896af14802ff331fa38713f7c2649cc5e19bbe7c90707dadcc592236ad1"},{"version":"5a11dbf49dfc0bacac057085c4c818507b8613630a6080846a330caa09f40a1e","signature":"887a929e952df6c08de135d3c73360dd80e833b99706ce3aef0c8b64b26ce68b"},{"version":"a128283ceced70086ed7a99436e55575c7d385f95ec1937b86e2d7c725c6e532","signature":"bb7d350c5b0c764dc29222248163f61f8540997db099b636c993ce1ec6981018"},{"version":"954eafdd8e119ae7fd13c652d092ac62f95a3c450127f9bf2c4235b9a5550f9a","signature":"7515a48dc017014e10b59b93449b24053cfe2f6cbec7424292ff04fb29f14569"},{"version":"6d9e1b7a1fa967fb8505a5fa33073efb38aec5e7b75f2dc6383c9f84f3b5c0ba","impliedFormat":1},{"version":"0462956c97fcc2f9a0f7a498600008751aae2b004f8ab4da34af41eb2fb5317d","signature":"8c23d09073975011bf5b8adde26ee58c4c5c27b5c4cc656a32313963f3388846"},{"version":"763e7dd605f87e9ab676c1dbd4ae54ae2bf6f75de50bfe5625b951ecea65dd58","signature":"d546106877ee81adbcf30a6867700a253c87d52a5cacf673d034666e2837a8d0"},{"version":"40c9ed5a63b54bd64ea351b5d853e67c373d730a8243e2fad4757eab3ec5ab8f","signature":"26ab3593d88f84d8250fde332b61ef8e6c9331bf4da6e89698ed83e95c57f7ee"},{"version":"42858b5e9f40b8a0b2f860a6304d779419ecf0c8773f6cf498c882bcd9aae1fb","signature":"2f55fd6804783792ef44c4afb78fd8a5d6a2810a4c02007e53ded6f01e24b521"},{"version":"819a9152da954b548e16204dfbcd75208938e5e1a21464998d2d155c14f08f64","signature":"b6ea2388d7e17effc8c7a702bd5e736213f77468d04bda4d8871a07ff6b191a0"},{"version":"7d57f62963f7f76d3e4604f86fa9e7fd005e3e11bc81490b32193dd9b3f019e4","signature":"f12acaa6f04cc3698628891d95a523a4bf0c03d03fb103edc7e4929709f1baf9"},{"version":"d6f4b9a418add129da3898008b6036a68dab54ec3997b04dd0bee2534d59a2fc","signature":"2bde30c5f8e10d5820a401c68e63e2b81a23077352c01977a10cc10a15f266f9"},{"version":"e918c0d2318411d705b553170e3a914970fddab501547d7e3db4fa9886565c9e","signature":"383dfa02a89c8b915ec43f711d0c60864b9a9f9921468dc25c5e67891c451c6c"},{"version":"e2af71fd224c850cd29f8cf8165e9eaa251df3dcb5fdb19cb10e1a20119a6728","signature":"e7a405682b3c45cda0ebe90315e22f754732efeacd7010bc0f6d8fac3582eadb"},{"version":"1632954ce7df258a88f3bcf040fd8a47839bc8b3fa71b64ad14ecc4819388202","signature":"b99c3b4cb099944054ae1cb1917bd5fcce00dc0837b241497749f379cdc0e9d2"},{"version":"a67823a8d4a16991b3653dda2eb722a15efb2762dec299662a23322bf2394e43","signature":"419996c73365008124de6ed63224ae81323de45079174bcac6b0dbdfc0108b44"},{"version":"2076d2cf1cdaeaeb896e27ec77082c91b5e485d297935597e76c8fec1c08e39b","signature":"dc1097eff258d192d1b76e71f09eb7a5a8c9b4776c6e8ce8af855e41ce73a274"},{"version":"a84795af5152dc3fc5782eedd4031079b9753301847f158ca3c979e551a4ad34","signature":"363524bcf11b6a009efd4becfed098d9fb297e3ce41c225410cc1ac2534b2025"},{"version":"92dc4e8b3d0e8dea1f5abbe30adfd3910a7be441c12ed6fadc738adb59f9bb2c","signature":"322baceb1c9f45aedb9e5100ebbbedd07a164fc03ab9e98c462e32b41cbdc90a"},{"version":"384f588ecd80fa9da15753c86111d3563d56538d719f8023902703847dc198e2","signature":"fe0cea76d1c6a718447bcd594059ac0a4c7f60b9452227e9bd6af7d564519f45"},{"version":"673b4a9dc7a23138c3dcb75f1a77cccf8d2a11167df9587286d6140b2c60499c","signature":"2a19b3b4d3185bf1f4436a1b8e98727005b207da50322e24f3cde25162e2ad2b"},{"version":"4ccc411eab7ab26ee65e6796dc137a43eb6d3145e6c616cc3fba32bd3901c240","signature":"6ae54612df3cf99e70e10a844f31d4aa1629ae828bfe1e915701d5dc1311278b"},{"version":"c37155416601c041802206333c2537b309d8031770da2717d9ebbc0fbc0f1527","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6d870a6ebc4af392ccd2875f99d1dd4c90db167636e4b267de2e1d3ff14a6104","signature":"d02c70b928a2b77ba435d387595ed3b27e4466e3115d7b71a79c36c592238798"},{"version":"2f79a63626adba271a461cbaa34c976c26be8e474095b1bc8a80ce6e4fc68536","signature":"019c10e3a4d1413779d87a055ffea70d5dfd127b4b65a51cb2e20fca9e8f1f66"},{"version":"ef68d70baf9635137535142e9df63e515a9b8bdcf9906d6edaaf0e93313ac3aa","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d1d17705658519c7d57ce6c5cf95f0c894c2dd754ad1fcaeb1e2c4207d54e6e5","signature":"0bdd8b0d06cc0910bf6a36f2bdf87794634ab7014e178c7a7eb928437c6e5b76"},{"version":"688842a137a6cf51df9153af0452d1099fa559ac47504178827954a0957f4d1b","signature":"b4617ba82469c6492e4a6309e2cfad3c4a4b8b9feeeabe26293e7b3559362ba3"},{"version":"a8419536a4ca141f1b73aaa6a223d024f4d380c94cf9a014f96592a17c6690e8","signature":"5ff75228fff32475fdc9bd5c7d166c2cdf98f66300937ea4fb7e4ff39f9d0c8e"},{"version":"70f9e74061da378a88537a5e6745381f00c470abd9d6e42ce93c6a7ae63bcfc5","signature":"ab7896a0010701f3ac6de01e3802ee76c98f7e3f658445a3d854394d1d9b8590"},{"version":"c8a733a097280e5ea476cacf56518883a0fd509d36b0f04e0bcc5e5a81b68c0e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"697256913297b09bb0b83b40de56ad875baa198a0d1c03b8bd9a8f8f77c2e500","signature":"bce98e573080ea97bd3c360011d50db0affee86bc74443866897c0061708072b"},{"version":"3b455aef3a8f1084aec20cf655ea99e1f68620df4c3f6071e8eed404a1c379f7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cf8a587e0092b7b11c83927802ac7efb7c40acf07276fe9f1175c5cc3ffacff1","signature":"2c542a6fdafe6885c0be90ce444b56c9f6871bf121ac656b5662e8c79fc96561"},{"version":"cfd8b3c5fe8e27f93868763708b0a25221595b18dae6f5a28c515bce83cd9715","signature":"76964f1fd067c7ecd79d2dc18affd81fb2f0148dce268546b64bb6cb0cad859b"},{"version":"2c17c6e842123c5c921ba98cee5bd3886f3eeffd42eb3011819cf99cb5b02ebb","signature":"fad4e252103942053bbd84c183603d06e19da7332de7d295294f368a69af0752"},{"version":"47762a84ce21afc46f46c000e87fbf6b3035b5944da16ca8ac62de576d877fdb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fa8dbed00530fb4114906cd93f7fb55512c8eb9551d2f2e9796c69a4da4b594f","impliedFormat":1},{"version":"5a3158e3ef43807bcc86f1b8d387e35156859e577cf3ef44f71aa2c3822e5b6d","signature":"1449aeeaf41373c6ebb083751de3477f2dc63177873dd65e1af39288e0f8108e"},{"version":"bd6e83a9e6d18e2fa74d9dcd45f986329d347eff178ad9923a2ebbf3fcbfd97b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"937a3521bff8fc032a57777777feb260c9ab218d266ac3d7723f7de32a48a430","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a3858cf95d68efd835700eb41b1fdf881906eaebd35b07596bbd5b7c1c6fec6c","signature":"1671fc31a114078bc9cb71989c1919c504f1af4e0690995b055181a1932bc74a"},{"version":"339fbca5cf5752f3fa77eeef5ec37c42010f1549655b3796eff5f4747e419488","signature":"62fe02bacba35050e65ee17fa4bab71e61914182c3dc9339cb6d40ae242efb41"},{"version":"357afcfd45b1bbdf4029dc5107fbf70fbfb519eb1f7cce5c9d9e5dfceed98efb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c9d2066069488cecf420d111d0201193958022a2905ac6c66689f50ccecda6b3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5452448d44362f60de4ad50c0c5eff76066ef5b1b9f2b4921e83fb50a0c568d3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1f8647ce956bc178fa042840bd76b650e34d22b37292621c146c8fbcc4fdd5dd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a5a7b42900c17657e4fecb9034c6bbd87a02fc402ee49415ae9cafdbe6f9d1dc","signature":"12ee1ad5a651c4484cbbdc6ea7d594fe1f8adc9684006988275bd671f510f581"},{"version":"e1f90140453b0fd52c46cc6ebfec2ebe754483c05e50dc74caae321d41e7a9ac","signature":"da215cd8311e3d53ac952d9a12e0fcebedf7d76b9f5692525046d7bb0ecb1cc5"},{"version":"5563052849dd3f93ba63a20568ac06acf6b8e15c0b57aee1bd452e7e4b1d5d95","signature":"91db33413af7e79f8f5639385fa3fa68c2595993f3f5bd6d7efaa4fe19dc94bd"},{"version":"bb2cef14d223750bf32f070eea09b2b2e176e10811d5c34f7a824628bad9dbc6","signature":"cc4068562a009b8285b75a2c53ea7b7323cc91785c59635e98b38256e80a2514"},{"version":"c5e286949fb1b24d3395196df616ec5f9090c2569534e48d1aa86e14308f6f2f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"908b7b3f2c71140accb3333fa49485ce1ad10ac4e14faab10ed51c0c28c68782","signature":"d19e4f9294c2efb124088d0e5dc71b2faceaed9cb614ef974ab0c3b925374891"},{"version":"637c70ab565be71168064142fdc7fde5a58ab95425066d3c8a6c3c592ca7167d","signature":"5baac7ee5e50c4c52bf4905d5cf4f735c939053555b99a56d1b743630788f665"},{"version":"2178789bd22566bcaa973006fa541e2c70d5698b5c099831828c9a1ec141802d","signature":"b905f364397e04bc6a90718495a5af33bf9720262dfdf619a090e7164d4f5408"},{"version":"6fa0ea6916329d3aa5c6056e13512e1757edecbce26dae1e8e5a3334e81fbf93","signature":"98d26fed15c1969891bc73c9dedc7278cdbd15f3afe3e34efe01c27dde514ba5"},{"version":"dae66bf6a17992ce4aaa4a16b8d8c590e84c396a341cb70ddf61ac2fe710e089","signature":"173c629dcaca1da42db9c0a508d079657fcc0cc56db24103f8a8171294902ff1"},{"version":"e7a672c4cf7f2314673b2fded201b122b6b4eda779709e2cb235531e8fac004f","signature":"db1016666977bab29ab1854fb90c9ed76f0632bdf412c73c6fa81412a02bc5b6"},{"version":"8bb437a4f36353bd4f61c7d432a87e38fc13f7b17e2d56b670d8d9b52bb25c29","signature":"8f04114d05b5db969453536c2b5f0b92cb28745a7a03fd47f425146e4b9ad8c9"},{"version":"ad5ea69c890012a5b61d4cad41a2d1c2bf581a023eb58290c5ea86554184bae3","signature":"d73e7d9f551a968dcbd471ca03440ca263efb053defa3a163b94c429ac47729c"},{"version":"7b94368c5875f22f86e56257f96884f67d2b68b2f967b06f52e5b735fd7e5727","signature":"2367a890be9d6752275d2ec6b9afd812c0b856d943b32e51ec662d6aaf6968be"},"6958e241f880588015372a690454d0f7c0727b78e0a9882f493e2ac0fada857e",{"version":"fceca4d896e6fd11de25ba760ff482c087c3a2150da1d841b8092bf8e1dd812c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e685858913eebc48e218f3546127d9e35f9e61eaf110891876c589096b31418a","signature":"423a9884352dad4696190f73f7dcc7316a8b1360ab70811a6ba9642929940718"},{"version":"7deb2948f4295f5751846f0e22361a43fa92480461be9c0b118cb388172308ae","signature":"6011e7e17c36babf1dd074634babde201615d25815d9140579d346b903bc9dda"},{"version":"7885c9bbd5a3cb3082f0a05df1f9c448d1c39e6c1302e935f9f986bc479ccb27","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c2d4dfa9bb5bbafa31b4423a78c2df02ccb51ad3f4abe7dcbbfaeb8dcf2cb82f","signature":"90b39c231c33d05240cbaabcfc21d94f68e05b5d9d2e972b644363be2133bebe"},{"version":"94adbb305113a8e6572989713200d1eba425e7a01443f1d02f4bd9a66f7f4fa3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3f8860ee39a7c1ca7824f29d948eb3f2160caa4cf4b1df9380a9b9b0c1ea184f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"01637488bd4591490ea3957f7e0d5a0744299e13944168b0b29015e57beab4f7","signature":"9d9ba57804b618dfd2e432502395172f7ccde0e2543ba0633ae9405c50451d7a"},{"version":"10aa66ae348d5ce62f6447d5af00fe40adc1f961e8fd6fe4f331ee6156c547fd","signature":"22039e2144d09a04262d19b5f0e4237ee40bec8322b5c9574403a9103af52044"},{"version":"cbcd7e3639eae69860983466b671c160570ff8bef3295eb9d9cf3a918f7c3924","signature":"22039e2144d09a04262d19b5f0e4237ee40bec8322b5c9574403a9103af52044"},{"version":"2b7bd4c530f8df99a7c513289d15cc3d919182a3e47a509f7dd66f7c0c618c64","signature":"72347dfb5a68565183de9758ca357bb879acff2d8dd025002d023281dbc9b755"},{"version":"700a699bc316498b27b98820c837965a737debebb4fee5d0a027e95d3c4a1925","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6a569d1174a24e8305c08d91a99e9f7f03610b40a3639da982b923f0fa380cc4","signature":"a693156ef4795aa6c9cdf5e6e88fd5a8aa3db1e770643c679f5f0bc652b6e384"},{"version":"8292f04350336900ed9f70ce80be9ff51b36602e17de450e43185054c9c9bc32","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b61da04f747568084ac75ba893c009197a7a0bb511ce6e8ea11ec3727b1e0bff","signature":"659c6cddd4e661edcbf460b40c7b690f346714057fd0faf27d1400d95cb6a398"},{"version":"5e92985539c56d5b665b392fd3883c103e0a83b63a79955d940547f494a87f27","signature":"d273813f1a71341c5f482788561acb719f12c65fcafb4f36423a6d409856d472"},{"version":"1e36aa6fd246d7240a3598e917647e1d2ca0380a1b7bb3b8e3945cb26941b031","signature":"9e2bb88f173d3209e25d8856088cd88006b416949bf633f766578ae5b18f8488"},{"version":"205f7ac530c6e5712a640fe3b0dd9f29296ace25043f7179ec1adb56882a1c37","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5e9142cb5f88305d5b5db601df9fe78244144630b03316f24b123c2bdaec5cd8","signature":"72a4b4bcd25bb33acac0c8d83f0d4d198714a06e03c49046690f380b9736998f"},{"version":"735334eab2d97fbdb987695b8ce10bb987dc318c3dd3f76f65a2f955bc4dce45","signature":"eea48c3b3a4a380ccfec9ac95ea1f6535cc57b17efa2d13009895942af52555c"},{"version":"b6fa86562861ef430157dbc9d6913461f6bb416c58b7052efa28a7fd503a2e59","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"34817c134a9a64cb3564c424f056a13554d6c40d31af05be7ea6b28cd9d0ac53","signature":"cb7b15b1e17883bae1ff4a7a2edc4e33d311a2addd22d3799520aca9c35809f8"},{"version":"a23c66cb088ab278c1b732ff0c39c3f072940848f593eb78807712292c9d4082","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"90453ef456c9284945d132c4d1c29753bcc06b0b7b1df7910768f748d4630160","signature":"d4d3b854dee0def611af8377422b5caef70f3b8c2c2d10ee3bb9ffb97d51cd45"},{"version":"404d9825e0fe3cc10db20060d068fd4f33c85c245ec9d2f99a20d05b02291b20","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c3450fcaa834dde1d97160d430e46beddb77253f999ca2a9730a518cf8ff9b41","signature":"6e557cc6a5e01a72e3f8159ca0c3c0b55d5d85613540b6fab72f9c70458b6c4b"},{"version":"e04940a8b56da3de47e0cc13fc8343b6d61620cc5d888100e2bdf51826f1a2ff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d740794e45fbbeb9fcf59f3255e9e6bbee5abf88c3e7736089e534fcb2c55689","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"354afe485d131f817329e133ea768376707f9e4041d68975ba6f8b6eb2deca05","signature":"6fa430bbcceaa6953e336c4592420298d31fe66327f7ca06e6763ec70c20240e"},{"version":"0d0fb8169becb3c35ffb1069d105e59d36e1152bfac10d47d122129c8b6ac89a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7a97cc4473384c86ecdd6b6c2d092db47c5420ade0eac940468dbfee5c21cf9e","signature":"f84c23548103c81ed53b1364e06c7c00104b7dee0573f4f7e2e74e8788222aa3"},{"version":"a08516f71cc818d1a07dc95126ecc36862a01bcb5c8ba86bf347a159c22029f8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b5d29855d82e8158a99ed976da509012e99a5fe1bc877009aff91627250cecee","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dc4ffeecc189d198af9ce492abed824b47bff7e7e6f8ec739a0eccc849836e4b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"598e1063a09bc7bbf1bc527cd19769aadf213b151d89921fafd9eb6c74121fc7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9704b377c8700004a19f328ee8a4f7ec61fa305e24545b5621e562410a63e2ae","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9b05dbef22d051726098dcbc6490886790bf7bdb93aa9f8a46403fabd59128cd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a3db9bee2b4afd4032f6040039fed1022b127166da7f0143b4a8b69a8a85d264","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b290d4bb3bf8f9c797f36ed8543b50571df566dbfee6f4234c3d0e63d837011d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2d0006e2c2a094ea0fabc4465b2cab0d7e8f5e785b3dda2961c2242257908b6e","signature":"a82c92852eb3872216a45757430fb88588440285e6f17c1bb864abe9f209fcd9"},{"version":"2f1a45e754761c4c17af1ecbd707a35ef9421ddb2daf244d1237aa929f919ba1","signature":"a76cde90a90b5582bffaa8aecdbdef0ee7d82667c57cad2c076404a3bcb741b8"},{"version":"5ebf1bcfa735477bf05c2a72f05efa171db37d28e39a690cc57d28447e09b070","signature":"ff96e4d1e720fdea29de66b9f495391d4c8c6b20fa4db88964df688d5a8538d4"},{"version":"73a0ee6395819b063df4b148211985f2e1442945c1a057204cf4cf6281760dc3","affectsGlobalScope":true,"impliedFormat":1},{"version":"d05d8c67116dceafc62e691c47ac89f8f10cf7313cd1b2fb4fe801c2bf1bb1a7","impliedFormat":1},{"version":"3c5bb5207df7095882400323d692957e90ec17323ccff5fd5f29a1ecf3b165d0","impliedFormat":1},{"version":"ed79d26639d1d98ab19d6f419180e5abe2f7fc6c194877d809282813888c98b5","signature":"46713144a8e07e24962b43c73b40a4f4b16e696eb52b8b519876fcd1f5e6eaf3"},{"version":"02c59c05069a1174a93fc2886fcd4ecfc2686711c1432511940f93d42f5a8dcb","signature":"3e2364dba15210b59a74593c721b4946e89b6cabf1c4852738003ee79509f4a7"},{"version":"26a7fc2c9efa90591c196a780ebb5940a3cfd7a74245698b2c0e648986755e76","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ef20d9124a12e824c20d04362452ecbbdc6c9f3de2c3c4b231222870b9586e27","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"02ae185acd25001f4af91e9f275661d7d284ca994374867cc564ddf22f8a6082","signature":"ffdc10811704d4d944dd41a35e45ce568ee973c9ac0d5c0ec71fe098829aab6e"},{"version":"535fb697e71bce5739129ef269f852ba83a2eea358ce8ca090f4b1cc905af9bb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"27cefa9a8df763b7c4e3abc76cb9867d1ddad908ac8c8d1e2bb32c3838616d4a","signature":"ea71b9399fcc1d3c46ec554ae15f397c40bf146ca2d9a58374cfb7116c343ab1"},{"version":"d1520fdce7489a3ad57359fab13c79ddc0a2a6d743940358a4dd3ad8d959fb38","signature":"45b074b67e77cbd4509dbcb1d78e40925cca1a0e67ff79fed1021bc48c262eda"},{"version":"c65bec5967ebb52be456a4fb70ac4cd92ffd671aaae4661cde2062fe3117fb7f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d3cfde44f8089768ebb08098c96d01ca260b88bccf238d55eee93f1c620ff5a5","impliedFormat":1},{"version":"293eadad9dead44c6fd1db6de552663c33f215c55a1bfa2802a1bceed88ff0ec","impliedFormat":1},{"version":"08b2fae7b0f553ad9f79faec864b179fc58bc172e295a70943e8585dd85f600c","impliedFormat":1},{"version":"f12edf1672a94c578eca32216839604f1e1c16b40a1896198deabf99c882b340","impliedFormat":1},{"version":"e3498cf5e428e6c6b9e97bd88736f26d6cf147dedbfa5a8ad3ed8e05e059af8a","impliedFormat":1},{"version":"dba3f34531fd9b1b6e072928b6f885aa4d28dd6789cbd0e93563d43f4b62da53","impliedFormat":1},{"version":"f672c876c1a04a223cf2023b3d91e8a52bb1544c576b81bf64a8fec82be9969c","impliedFormat":1},{"version":"e4b03ddcf8563b1c0aee782a185286ed85a255ce8a30df8453aade2188bbc904","impliedFormat":1},{"version":"2329d90062487e1eaca87b5e06abcbbeeecf80a82f65f949fd332cfcf824b87b","impliedFormat":1},{"version":"25b3f581e12ede11e5739f57a86e8668fbc0124f6649506def306cad2c59d262","impliedFormat":1},{"version":"4fdb529707247a1a917a4626bfb6a293d52cd8ee57ccf03830ec91d39d606d6d","impliedFormat":1},{"version":"a9ebb67d6bbead6044b43714b50dcb77b8f7541ffe803046fdec1714c1eba206","impliedFormat":1},{"version":"833e92c058d033cde3f29a6c7603f517001d1ddd8020bc94d2067a3bc69b2a8e","impliedFormat":1},{"version":"8e6427dd1a4321b0857499739c641b98657ea6dc7cc9a02c9b2c25a845c3c8e6","impliedFormat":1},{"version":"58da08d1fe876c79c47dcf88be37c5c3fab55d97b34c8c09a666599a2191208d","impliedFormat":1},{"version":"e770447d49d5c7ee25f80ccfff0f95003e08bf1147d039f0e8320d95d882c76b","signature":"399eb8b682bd93241cc96cb483306f8634ba94bc17ddb123e9106088240e9c7c"},{"version":"15ba1669f8cb8433a7a7b40422f81fed4f7e037e3cd4ca65b7b4af0434a43560","signature":"4f83f97fe204009c8bbad58d06e956970062930bd694b7ecd88d13a6f85f7e3a"},{"version":"a18970969188e47a48af09738dde83579f9c85bfd731675b671c1f32c5bdc134","signature":"f6c3f2c52494a1c44f58bc28dc1f8f89c7e3b0d005a5c3bb8789f82131996dd5"},{"version":"68ec8a37a3f7ce830a6be8e0ed448f8907f638e02a22a12a0f76a900d9f7b258","signature":"37f7acbfb476967d35f33541ea813afd46bee4432026b3d709b41a0fa1b0168c"},{"version":"e3f54d1b036952cd9973fd6a6e68b5de9ae9620cbdfb9a421c5362175891b7cc","signature":"d5368ef391f2681ad969a5bb0d8d858701641ac38a7de983786492d2e5698ba9"},{"version":"d3f39e4c09a88118ba35eefddfa580674f5b89df79c1477298d4c4de0bc8ec35","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a2ffbcac73b1af75bb70a1fc540f059a227257d9746279d67af5698e9d9c0a77","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e70993be79de2ffc2132f91126903db8573e68b0f5318ec48eec97a5e09c5f8c","signature":"c4c000f5db2334ea4e2bc0b9bc437d27c292ba078ea53202378b878846840865"},"9eed204f26aed45ba513a001aaa78dffd4bf0194ed42fb59fa4a5b48dc382767",{"version":"bbed8132ccf8ed24e09b7a0c103afe746ec74c3f6d497676ce9a2b09a8e0e4ad","signature":"e9805c8a045ade45cf5dda8406be734ed77bce51fe25e6a431345e403964f502"},{"version":"1b046683cc56fca31919c8cfc9a7b47796d986b2df18c1e55615f7f67a464c0a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"954e64b65c8632c8e6c602f86ddb7a855b541f719153c52586da47df81740592","signature":"5747318e625d94d50968119db96e4e9b57f386c0fce3b015e26a5e06819ded72"},{"version":"8e67d08427faa2cd614ffde8279aca632928a75610fab7f0e80eea0481c3ffa0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4dd230e5ffb901e4d715a7507fb671f3510cbcb3781701177e09efce8cf30c6f","signature":"7a3b7f911a6906b2fd8d38f7347bc751ff290914c35f2998438f2985dcea418b"},{"version":"d79917970e2012fea644dd1c3d00e7499579d4adfdd3628bc4d4153c2fa38d2e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"eaf4a58ef586c168ad73bf0bf2e4fc7b50b0207046cb03e70f064e23cc7410a2","signature":"dab2cd6f392f32d0e94793582857c06a2d1fc79ebafc631e2b80e4b0c40778c9"},{"version":"08e69d30c063db86ea2221adf2282b1c118723cec16131cbd40024f3f43e5a90","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"80ccde47e35b2546400135ffc69d55feb65a4a473964a40cdeda39bcfa10aef9","signature":"2cc35ee4dd1c4f9d97475451cc25f443a692f68f9bc47fb0044f009e356da599"},{"version":"a2d8e5740b1d7e274651ad4e68fd99942d7b33d67adce2d3ff8b976d12327840","signature":"d14e729f535d0e6d801090b439ff6f73f8ae7d713de7468a36d5989f0f10f19a"},{"version":"1d57813ffd927563821c58c16a5a7c35d350415b2b8de5978b370c78b8a750ff","signature":"d5d64072f36683f1af5cdbc66e7ac58d839b6b2d99cee1b0e96df9f4413640a2"},{"version":"70fe6d07a4bad7a73b493a4bfbe2c5b501167449f0e95e3a896261e08d647b67","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2a312ff9bf104a2617f538482d33f951d2de095a79bfe2f641f52ddf558a3c5f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7d2792a15bf4bcd948e330c3cb747a075d137db57ac53adc6900f69009dd8978","signature":"fb6fdfe7ee4e1c16d6bc8b3c8da0d22ebd365981b6c4dfe881b391328d68f220"},{"version":"75e64a7fcef4db0c9ff13acc31c53cce109194012351733ce9833347e0a8e518","signature":"a97e6b4712135857efbdd73004c551d3a71d65d6b8a9d8f661f608a47b607cf3"},{"version":"a24154a3954030448c58433c23ca4f6d78e763a3af035de3d9633cc9158d7038","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"72fc4be3f73ed954fa04a52ebc5975c56b5f13c4265392191c95028ec27daab5","signature":"13a8b4fdf45f95814460c5001fc04194f85ca7055d460a9f852eed3fbd5c2293"},{"version":"d3a3a8fa4cac4860d3fabd83dbbe072bd0db08b6dfc5447fbc3f65a480bbb896","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"773df341514640879d77b0b24b636e6a8ccae2e88bbb09cee7383274046eab2e","signature":"3f3e3ab94baceade05836e0805fd32550fc1cad12d3d31a2fcae6d56882ac2f8"},{"version":"2d96663076cc7fea06c11a0165be63c11b533672c6d02ef361bd86f8394ecdb2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"95f053ad6f9e8f22fb9a0309e14a768302ff5f8072b9bc24b8decdcbdaaad0ff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bc4cc5cacfa347d15035093ecc8a2c650968fd9208de260c8c141749d1797d23","signature":"8efda6ec7129eb4762df1d2b2a593fe59c69f1a2d5696d6d7bddeff50c24b17d"},{"version":"de7f6eb89010bc7d22b76bfd8d01ebdf803df6bdf7e7b7528d2705f74c401e58","signature":"524d6c27b0e7b81e021da931ddfc29e60f33e2573ff117ed95e8cbeb32f5c8ad"},{"version":"745615f591324c1ce4fd8a905b5af838474e781548807dff21154e64b51e945d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a1a2e508046cbc9709255c938bf9935cbffa6cfe006cb5bb7f36b9f4c5a3a2db","signature":"ecaff6497b5a358a301ee7363dfd9c78325e9cb23d95bcc873322faedca7d3a7"},{"version":"d09eaa9c4d651a351d0ed84a88a22b35bd41f307ff7aa0fc356a2b7ac41ccf25","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"26418c7b2f5a9860191966b63f2761e14f52fcf8b0cd040bb94b9623f348b9f1","signature":"01762a4e0d1830ca6bddf2667adc23596b265be66f3b25b96193103aabe080ab"},{"version":"c2882b00edded3f8e7bdc5f2169866bd6a1c0402b77684c4eba188ae9801e6a5","signature":"745546f965215f793614c1212548989fa75b322859fe83033f26979cd44e5722"},{"version":"8d3496abc1d78cdf10e4e6066233bdef27a1a00c40796f8cf8a5a34ead5e3fa5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d187fa89b22ca44e8ba47b4eedbb5fbfb1efeae4d14f6c8756b751067e4164ea","signature":"ce13f793f9bda7ea5885e373ee25d9140369d5eab45bdea4eb8526ea020257ff"},{"version":"268e798c4155b57314f6fb0ac3ff224e049c5fd8dad1619771328f77cf66e958","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"998e905a8579d49c9ff4cd1dfad86ed6608ac548b240d2ddbee896c013e6dd34","signature":"3e4b13cf490d9a92245cbc3e5477dc486352b38942d390fbafe48c4d9d226d1d"},{"version":"cb96c5f2b7a4c06fdfeafd2b2bb52e5026215b5f9c81264a502b58372d393c8e","signature":"36eb3a3f2bb64aba7182a39a6360214c084660224ab8c23e3a9e293d79065d37"},{"version":"f247ebbdf2915957f47462152c65fef96da9273631b739c263009db4efc13b4d","signature":"7c4575057d5b4dc088c1e7f338e1075351c0cff2877360a00f5102baae76dd6d"},{"version":"ca140e99f367b2a1a666b6f61ebb703d059f3b81d59a710058b6606dec01d72f","signature":"31c64d4bfa7de3f3b9ae69741ad29192023a49595897e764d62fce0eb931023f"},{"version":"265c9ae2b7a62781e57de439be00ccb1b8693156cfb98a0618ba6c5c54596e42","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"34330c5f52442c69dd7c50d7a95912d87b85cc01be135026a5d7ac060b184464","signature":"720e771373458011bd56c0c6bbeea34302eea42ccf08c8a6b5840a338e7e93b9"},{"version":"e7f0547a22cdcb3e5d9b0fd91191cc2dba8f75a2694eeb4d45a9ddf2a8352960","signature":"c1f5f74ae95ba44d64781ed79486fe7192478040d82d787876f44bc7e77418b2"},{"version":"56208c500dcb5f42be7e18e8cb578f257a1a89b94b3280c506818fed06391805","impliedFormat":1},{"version":"0c94c2e497e1b9bcfda66aea239d5d36cd980d12a6d9d59e66f4be1fa3da5d5a","impliedFormat":1},{"version":"eb9271b3c585ea9dc7b19b906a921bf93f30f22330408ffec6df6a22057f3296","impliedFormat":1},{"version":"aa4a927d0c7239dff845a64e676c71aeed2bbda89a7fb486baab22eb7688ba1d","impliedFormat":1},{"version":"340a990742a00862049b378aaa482b5bb8323d443c799dded51ce711f4f8eb51","impliedFormat":1},{"version":"89eeeebbc612a079c6e7ebe0bde08e06fbc46cfeaebf6157ea3051ed55967b10","impliedFormat":1},{"version":"4c72f66622e266b542fb097f4d1fe88eb858b88b98414a13ef3dd901109e03a1","impliedFormat":1},{"version":"15d8dcd70d6cc6c75476a75ea83c53df1115bdd551c73ef2168a9b4a4bd55a51","impliedFormat":1},{"version":"2acad3ae616a9fb5a8c3d4d7bb5edb11d1d0102372ee939e7fc64359fec4046e","impliedFormat":1},{"version":"c812eabb7d2e13c8e72e216208448f92341a4094dd107cbb0bdb2cb23d1a83e7","impliedFormat":1},{"version":"f734b58ea162765ff4d4a36f671ee06da898921e985a2064510f4925ec1ed062","affectsGlobalScope":true,"impliedFormat":1},{"version":"9619b4a3db123eee6912ce9cbeae535739a1b1736dbbc224a697a2a98fee560c","affectsGlobalScope":true,"impliedFormat":1},{"version":"9c5c84c449a3d74e417343410ba9f1bd8bfeb32abd16945a1b3d0592ded31bc8","impliedFormat":1},{"version":"a7f09d2aaf994dbfd872eda4f2411d619217b04dbe0916202304e7a3d4b0f5f8","impliedFormat":1},{"version":"86ac756569f83cf0571646c916b546634652e92a775e964304912ecafa81dc42","impliedFormat":99},{"version":"a7f23fecdccf1504dae27c359db676d0a1fbaaeb400b55959078924e4c3a4992","impliedFormat":1},{"version":"bee66a62aa1da254412bb2c3c8c1a0dd12efea0722d35cc6ea7b5fdaa6778fd1","impliedFormat":1},{"version":"05d80364872e31465f8a1eaf2697e4fc418f78aa336f4cea68620a23f1379f6f","impliedFormat":1},{"version":"7345ba3b9eb2182d8cdc4c961b62847c3c9918985179ddefd5ca58a80d8b9e6a","impliedFormat":1},{"version":"81c4a0e6de3d5674ec3a721e04b3eb3244180bda86a22c4185ecac0e3f051cd8","impliedFormat":1},{"version":"39975a01d837394bcac2559639e88ecdc4cfd22433327b46ea6f78eb2c584813","impliedFormat":1},{"version":"7261cabedede09ebfd50e135af40be34f76fb9dbc617e129eaec21b00161ae86","impliedFormat":1},{"version":"ea554794a0d4136c5c6ea8f59ae894c3c0848b17848468a63ed5d3a307e148ae","impliedFormat":1},{"version":"2c378d9368abcd2eba8c29b294d40909845f68557bc0b38117e4f04fc56e5f9c","impliedFormat":1},{"version":"9b048390bcffe88c023a4cd742a720b41d4cd7df83bc9270e6f2339bf38de278","affectsGlobalScope":true,"impliedFormat":1},{"version":"c60b14c297cc569c648ddaea70bc1540903b7f4da416edd46687e88a543515a1","impliedFormat":1},{"version":"efcdea26e9115d5c05b3f4c5827fe3b32b4fef1b59dbd67f529c6cb685c7d9c4","impliedFormat":1},{"version":"bb0c361fd2b4bdabbf1307f1a61fd14c953f2692fa642391f93276f2df41de50","impliedFormat":1},{"version":"90588fb5ef85f4a8a4234e8062eb97bd3c8114dfb86a0c67f62685969222da8b","impliedFormat":1},{"version":"6ce50ada4bc9d2ad69927dce35cead36da337a618de0a2daaaeeafe38c692597","impliedFormat":1},{"version":"5fbc333346d28f290d42ac81cf16e454fd3947c6e524384dfd3ce59d4ac3af04","impliedFormat":1},{"version":"072163fdea42ece03bd323b907f5d6acf575a34a9dac4620e517e4378d773d0d","impliedFormat":1},{"version":"df6251bd4b5fad52759bfe96e8ab8f2ce625d0b6739b825209b263729a9c321e","impliedFormat":1},{"version":"846068dbe466864be6e2cae9993a4e3ac492a5cb05a36d5ce36e98690fde41f4","impliedFormat":1},{"version":"94c8c60f751015c8f38923e0d1ae32dd4780b572660123fa087b0cf9884a68a8","impliedFormat":1},{"version":"db8747c785df161ef65237bac36a7716168e5ebf18976ab16fd2fff69cf9c6ce","impliedFormat":1},{"version":"3085abdf921a6d225ad037c89eb2ba26a4c3b2c262f842dd3061949d1969b784","impliedFormat":1},{"version":"8e8f7b36675be31c4e9538529c30a552538c42ff866ba59fe70f23ba18479c5a","impliedFormat":1},{"version":"1fe8b45c1564eca8b1bd27d427d193ea8c1a5d64f7144a5a64665d5d0f27a9e4","impliedFormat":1},{"version":"a03c6f93651e458531f223d52eac1a12f2aee8adc2cbc4b4154a3fe515984e5c","impliedFormat":1},{"version":"8d05dbd747569cb1b0cc2ec1018a3378c47d803de0e7d34f7e12909ff48bb437","impliedFormat":1},{"version":"1afb31819f4b7d04f4089d575acd30854a4cc614baea960066f7cc5755e9efcd","impliedFormat":1},{"version":"35cc30df63b9fa7c9d3637ef315eb5f21f5b0dc0f982c736cad20d39e29b579c","impliedFormat":1},{"version":"c5b47653a15ec7c0bde956e77e5ca103ddc180d40eb4b311e4a024ef7c668fb0","impliedFormat":1},{"version":"0528a80462b04f2f2ad8bee604fe9db235db6a359d1208f370a236e23fc0b1e0","impliedFormat":1},{"version":"94153ca0b430f575f45a5e07d66771dc5ab331af7791691855ba3499958c4e49","impliedFormat":1},{"version":"dd361fe00d3033451e4a43c9eaeafcd1b9b6777adfbc8b8f91d63ea56818c31c","impliedFormat":1},{"version":"b86720947f763bbb869c2b183f8e58bca9fa089ed8f9c5a1574b2bea18cfbc02","impliedFormat":1},{"version":"fb7e20b94d23d989fa7c7d20fccebef31c1ef2d3d9ca179cadba6516e4e918ad","impliedFormat":1},{"version":"8326f735a1f0d2b4ad20539cda4e0d2e7c5fc0b534e3c0d503d5ed20a5711009","impliedFormat":1},{"version":"8d720cd4ee809af1d81f4ce88f02168568d5fded574d89875afd8fe7afd9549e","impliedFormat":1},{"version":"df87c2628c5567fd71dc0b765c845b0cbfef61e7c2e56961ac527bfb615ea639","impliedFormat":1},{"version":"659a83f1dd901de4198c9c2aa70e4a46a9bd0c41ce8a42ee26f2dbff5e86b1f3","impliedFormat":1},{"version":"1db5c2491eebd894eb9be03408601cddfe1b08357d021aeb86c3fb6c329a7843","impliedFormat":1},{"version":"224f85b48786de61fb0b018fbea89620ebec6289179daa78ed33c0f83014fc75","impliedFormat":1},{"version":"05fbfcb5c5c247a8b8a1d97dd8557c78ead2fff524f0b6380b4ac9d3e35249fb","impliedFormat":1},{"version":"322f70408b4e1f550ecc411869707764d8b28da3608e4422587630b366daf9de","impliedFormat":1},{"version":"c4ef9e9e0fcb14b52c97ce847fb26a446b7d668d9db98a7de915a22c46f44c37","impliedFormat":1},{"version":"0e447b14e81b5b3e5d83cbea58b734850f78fb883f810e46d3dedba1a5124658","impliedFormat":1},{"version":"b16a6680ef4108fb2982b1d47e7ce36a8b2c382cf76b3e1b500de70f0a62fdff","impliedFormat":1},{"version":"929939785efdef0b6781b7d3a7098238ea3af41be010f18d6627fd061b6c9edf","impliedFormat":1},{"version":"fca68ac3b92725dbf3dac3f9fbc80775b66d2a9c642e75595a4a11a2095b3c9a","impliedFormat":1},{"version":"245d13141d7f9ec6edd36b14844b247e0680950c1c3289774d431cbbd47e714e","impliedFormat":1},{"version":"4326dc453ff5bf36ad778e93b7021cdd9abcfc4efe75a5c04032324f404af558","impliedFormat":1},{"version":"27b47fbd2f2d0d3cd44b8c7231c800f8528949cc56f421093e2b829d6976f173","impliedFormat":1},{"version":"d5426c0e36296daf07cf2f38227907469c33a53473d9c2721d21dc515c5724df","impliedFormat":1},{"version":"cc03a3e284393b02fdb646931e8576f6dbe839a249d172eb3397adec80559450","impliedFormat":1},{"version":"3d94a259051acf8acd2108cee57ad58fee7f7b278de76a7a5746f0656eecbff6","impliedFormat":1},{"version":"fc745bebefc96e2a518a2d559af6850626cada22a75f794fd40a17aae11e2d54","impliedFormat":1},{"version":"199d42a358b3b73312d499dd04d9f855bf8ad492452765de4ef80b8cb6871cd3","impliedFormat":1},{"version":"eafa048ffaf72cdb64fa1d0dae49aa91280a7bb94e0b034883ae48cec27a04d7","impliedFormat":1},{"version":"593bcf66433eff881c9abb75d2e55a7403c57905aa61d818a616bb3c7f076b49","impliedFormat":1},{"version":"3f3526aea8d29f0c53f8fb99201c770c87c357b5e87349aca8494bfd0c145c26","impliedFormat":1},{"version":"6ee92d844e5a1c0eb562d110676a3a17f00d2cd2ea2aaaff0a98d7881b9a4041","impliedFormat":1},{"version":"b9dc36d1f7c5c2350feafb55c090127104e59b7d2a20729b286dab00d70e283d","impliedFormat":1},{"version":"45d3f1d53fa99783a5e3c29debb065d6060d0db650a6a1055308a8619bd6b263","impliedFormat":1},{"version":"a14febaf38fd75a88620a0808732cf9841afc403da2dc3de7a6fc9a49d36bdbc","impliedFormat":1},{"version":"6052522a593f094cfee0e99c76312a229cf2d49ac2e75095af83813ec9f4b109","impliedFormat":1},{"version":"a0ceb6ce93981581494bae078b971b17e36b67502a36a056966940377517091d","impliedFormat":1},{"version":"a63ce903dd08c662702e33700a3d28ca66ed21ac0591e1dbf4a0b309ae80e690","impliedFormat":1},{"version":"2b63d2725550866e0f2b56b2394ce001ebf1145cb4b04dc9daa29d73867b878c","impliedFormat":1},{"version":"e885933b92f26fa3204403999eddc61651cd3109faf8bffa4f6b6e558b0ab2fa","impliedFormat":1},{"version":"22338cc18afb909a95a6c55417f1a67db99badecbac1710a963f0bf63c952124","impliedFormat":1},{"version":"e61b31fd5fd627c73da6041d201c0bbd721170288381f09055cad4fcb2ad327b","impliedFormat":1},{"version":"6e2d2b63c278fd1c8dd54da2328622c964f50afa62978ed1a73ccd85e99a4fc7","impliedFormat":1},{"version":"e151e41c82004cf09b7ea863f591348c9035e0f7a69d4189cbac89cc9611b89d","impliedFormat":1},{"version":"dedf4655c327e9c5294a63d75764946308700825e8d8c1d4318a10602581cd6c","impliedFormat":1},{"version":"b83ffe71adbac91c5596133251e5ec0c9e6664017ee5b776841effe93de8f466","impliedFormat":1},{"version":"61ecf051972c69e7c992bab9cf74c511ecba51b273c4e1590574d97a542bd4ea","impliedFormat":1},{"version":"068f5afbae92a20a5fcd9cfce76f7b90de2c59a952396b5da225b61f95a1d60a","impliedFormat":1},{"version":"18d97e6b17d1196d88d4deb0e37d8edb7fbdd102ae8a5681f03e15030b6b2fd4","impliedFormat":1},{"version":"d7ded5d2060ac6a4404e6001a46d5a704e3f325f95e2cb0dc055ea05404c9cf6","impliedFormat":1},{"version":"99c88ea4f93e883d10c04961dbf37c403c4f3c8444948b86effec0bf52176d0e","impliedFormat":1},{"version":"e88f3729fcc3d38d2a1b3cdcbd773d13d72ea3bdf4d0c0c784818e3bfbe7998d","impliedFormat":1},{"version":"f25b1264b694a647593b0a9a044a267098aaf249d646981a7f0503b8bb185352","impliedFormat":1},{"version":"964d0862660f8e46675c83793f42ab2af336f3d6106dee966a4053d5dc433063","impliedFormat":1},{"version":"292ad4203c181f33beb9eb8fe7c6aaae29f62163793278a7ffc2fcc0d0dbed19","impliedFormat":1},{"version":"aa8e5ac3f73eede931d5da74ef1797c174b00854ac701ead5c4a7d6ce4a49029","impliedFormat":1},{"version":"f1a4ca3688d951daa2d7740da5a0827fa34d4a7709eed7b8225215986ee87108","impliedFormat":1},{"version":"08e159b5ef9d14bdd329457c5cbe181e84f13c4ff2546a24b9eb9129b0c71c46","impliedFormat":1},{"version":"f8453a3fe0fe49ab718357120bec2b8205e15eb91ff62eada60a4780458fa91e","impliedFormat":1},{"version":"06f186bb9a6408ef8563dbf17d53cbe23e68422518b49b96afac732844ddbaa1","impliedFormat":1},{"version":"525f9c06245b5b43b1237cfd757396fd7fd8090e5d6a4ded758c7ce17a04bf42","impliedFormat":1},{"version":"e46b752c48b3aec77516d23b5cbc0b85df78c740c058a822b43a32c958e468f0","impliedFormat":1},{"version":"f693b1fce39951823f128590c6c837b70f844b6d3746ef778b7fae7f1340338a","impliedFormat":1},{"version":"bc264419318f0b174b5dabdd465e1eddb82f872e899b6c696c67217b346e958c","impliedFormat":1},{"version":"6046bffaa17bbb55ffd62926a966a7badce21b27d6239ba0b569b8266bedaf19","impliedFormat":1},{"version":"9376cce4d849f1d6ad2cb0048807c77cfeb78cee6e29b61dcfe74c7ab2980e18","impliedFormat":1},{"version":"2e0dc55ea1ade444d285576a4ed7915834d4a87f71b147c38afdb877ebb0ad2d","impliedFormat":1},{"version":"4edfc4848068bf58016856dfeb27341c15679884575e1a501e2389a1fea5c579","impliedFormat":1},{"version":"0c3d7a094ef401b3c36c8e3d88382a7e7a8b1e4f702769eba861d03db559876b","impliedFormat":1},{"version":"1a3b915d24b2a26df000ef55ed356028dec11ff54f7e93a5c095c313d7016e1c","impliedFormat":1},{"version":"7e3a4800683a39375bc99f0d53b21328b0a0377ab7cbb732c564ca7ca04d9b37","impliedFormat":1},{"version":"b1b5e35575486918e155ef02d995598be2b5d8e729f857f8309ba0b76e14d833","impliedFormat":1},{"version":"8d87de8b839a017541ec1baec68292ddbcdfad0f2f3b5f2ae8abd61f06105cbd","impliedFormat":1},{"version":"7cb0d946957daea11f78a31b85de435e00bcd8964eba66d3e8056ba9d14b9c55","impliedFormat":1},{"version":"b3e441cdb9d9e55e6e120052fe8bf2a8b5e5a46287f21d5bc39561594574e1a9","impliedFormat":1},{"version":"0870e8eb0527c044e844a1d83127f020aa7f79048218a62b2875e818355f8cb2","impliedFormat":1},{"version":"38400b70ac70600c632ad498df2d956ed8ca6c6774dfb0ef69a2d35a9450df7a","impliedFormat":1},{"version":"abab86c01d001d0cc410c7aee59168eb09bdb7d6d9d39d3c0081b36235e2824f","impliedFormat":1},{"version":"7ae39872b4f4d38b9df079cce4223e999754eb3b3f90e4e46b978b29e72c419e","impliedFormat":1},{"version":"dc0f3099379383bf14f2263c7987584e81b6d9b60259c9e31390455ca0619dba","impliedFormat":1},{"version":"6dd704b0ba0131eb9e707aeedc39be6a224b4669544e518217a75eb7f5dd65c2","impliedFormat":1},{"version":"6effa89f483e5c83c0e0063df5f1d8b006d9d0f1de7eed2233886642424dc8fb","impliedFormat":1},{"version":"5c6dc17513298b4daac99bf8e88ad4e4a504310cf69a0cf3cffefa5912b85234","impliedFormat":1},{"version":"d43130c35762a80da2299f8b59a4321b6e64acfb0b11a36183379b4c7b83314b","impliedFormat":1},{"version":"6bf44b890824799af8e20c0387ffa987e890fac5c5954a3a7352351eefe55d5d","impliedFormat":1},{"version":"e61999c06ae79ec587c2e7db514a024d85732b32ee2c997bf4a1ceb2b561c611","impliedFormat":1},{"version":"aecd29a5bc49b1de6b933344e9c96384cd098162c46873673ffa1408e6195c52","impliedFormat":1},{"version":"f83afa274e0f11860c6609198ecca220f5df60690923b990ca06cae21771016e","impliedFormat":1},{"version":"af31f37264ea5d5349eec50786ceca75c572ed3be91bdd7cb428fdd8cd14b17c","impliedFormat":1},{"version":"86d01647c3c215e53729aa2cb15d7bcc2b049088bc76bdb5e04a0bf25f97c386","impliedFormat":1},{"version":"9d3173cf740b742d1048d8ab20469060a2b5e2d426f8ff7df36042e6829c4aa8","impliedFormat":1},{"version":"f1063f0e6ca22a9fae0c0338768b03911c954b8e6ad4fff5381cc6a964b34324","impliedFormat":1},{"version":"4f85d12a28937e950b123e5385448a3bce0f04dccbca7ceb8aef351ffeccb228","impliedFormat":1},{"version":"85e4673ec8507aef18afd4a9acfae0294bdfaac29458ede0b8b56f5a63738486","impliedFormat":1},{"version":"40683566071340b03c74d0a4ffa84d49fedb181a691ce04c97e11b231a7deee4","impliedFormat":1},{"version":"81c8ab81daa2286241ad27468d6fc7ad3ecc62da04b18b77ce9b9b437f6b0863","impliedFormat":1},{"version":"268755fe3b7dd5ca84fc043de1502c56c5cf8ef70271c017964a9dff8af94a7f","impliedFormat":1},{"version":"8e56db8febfe127a9142435940c9a5a1ad17ddb2b2a6d8e9e8984785a76db1fd","impliedFormat":1},{"version":"f1efa458a3f630de51e30c823a8e1109eadf8562b8b90c764ef1fd989329bcaf","impliedFormat":1},{"version":"1ea64554b23db011171f7e0dd59d006b239fd4ec2e7e8b31ecf995528de79423","impliedFormat":1},{"version":"46788ee6b4670d904a54d56fe9e3bb308ab4c4ba01a435d39d8beaebf85f1a54","impliedFormat":1},{"version":"f4f6e61f620861b576f466e8af34e6064a997aa93ad62593c0c3f51489784e5c","impliedFormat":1},{"version":"f92fe945f94fee5c2811d6ee81b1751a1f1970b29063907d48067f1c2389bc3b","signature":"a33394f16beeee3312796eddb1186492dc73074f2cb480027e2de871ddb32a1d"},{"version":"3974befffa7647e5d975081c15016cda5f16062c8957af8d5b93b85bd6b57b21","signature":"8364a4867aade4b7b8e12b3116edc4c0cc374833476df15a0cbbe7b147bb1387"},{"version":"da094bbe2ba0c875d680fa8957a0b4056d806ed8093c4eb84f1d1319bc148924","signature":"2ecfec679572556d5739697241ee12faf6d1c088a64eb646f358d6b908201893"},{"version":"884b3c4b6de733bea0363994edfdbc08f23168c3819ee92eacf9ee2ff38b9e31","signature":"8b18201daa2caa4d6dad664291f923d8607cf8211ebd0dec3986e400f02376b4"},{"version":"d61b3b8b5d54ffbc1159015019c05472841f9b12287ad1eb0febb9d50b3fcf2b","signature":"7bd1aae3ca5e15b45dc603fad958b8d228f09e8c43ad9a4efdc70c7b3f96fc35"},{"version":"4b9b77c14bfa8102fcb57b14ffe92dbff3b513a8c4ba62893ab009fbd4c73647","signature":"9d2c9cbb279702e44a3ea7fe24bfe19cf27352d4cbe4882bbe5d521d27c9741e"},{"version":"aee88de82317641d6391f0686ca4acceedfaae5ade43d00dfdbb2e32e83870b1","signature":"979a61915ecd6734d45f9ab06a423a5b75cac28c23c512c838c10e333ff88a02"},{"version":"9a889402f27da6ba13bcaf7e0731fa06758e971c0d4ed730d6b46f08d9a05f34","signature":"f9d6f6e5c3e8a1dbf9499c426fb4d97386c7aa5b205662a4777f9289ef9152ab"},{"version":"f6534bed93400a60ab02368c4a698062e31ce5ad4eefd0f4994c2385ae83c54b","signature":"9c2f866be60bdff85a59bf2cd9b85041d63bfc369560cf59b88d7a95c6072f28"},{"version":"b6ced0b0b07feec87098d3eb446bdf772cc268ee3ac4230a4069e61dbf75cfe4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bb040b86775b25fc0848d43e3cc03e1b5cf4f00c40cbd350dbce010c06404df1","signature":"6d46cfeb3c048590784e9c099f0dfb8de954141c9fbf25f6d9ec87981ebc6fe7"},{"version":"d20eaead87726a364899203678c3e81614bfc368630e7a38ed0008acf7f7c1f4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5a2958fdf63b7d83f8d734d08ab6975b2a66defa7d7ee4988c0abfec0881b3a3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7859b822b52969cf5421d8d07df464fabd68c67579733fd51cc994c6f3b9452e","signature":"91d869ffb8dc40ecfa1ed7675197ed893ea5501d5eab07a48f22dbe192cbd9b0"},{"version":"05a0701aab09b3b50154c4469670a6af40d716c1bd84258ab88c4486efccc2de","signature":"cd7eee6f9641bca037731468d9b1012d11858efb65ccb7a23e35377d824b2a4b"},{"version":"cef9a872724202d022975121422e03878a38b6c4a78977b7e277733a2ed5151f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2776443e3c5ce498f62ac5661d0e35884afea55b0d3f6f9306f8ffb97b35e9fc","signature":"c049b08ee071ee35f8623f69360d9b11a4e78f6f903a9601e9f76346ff07ffc4"},{"version":"18981392c502332d353be793e0eee6b4b71b92c4cc159879c76c0e412b50166f","signature":"85a5f8ec84196d475ea68d0239a7ee678d96c40274f16d0e940937166e2f9fa7"},{"version":"ef39108e7cfc70e36f0d3a795f5ee50fe39e5a9694626b9b88b3b24d505303e5","signature":"49497b292dab6540b4e9ffc2e066aba503cda20ff5407de0bd946016f7acfd01"},{"version":"ede33324139612cc144cb9ab0658d31f633fbbf6e5654b4867ad17964e494463","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"90bdcbca0e0bf6c2b0bbb00673a340a12647ee9e9c0b7ffa08c1cbee51683f6a","signature":"081dc84b2cfc91d910771f621446c86f99ef16cba361a195bc5b6415671e3940"},{"version":"c3a6e46122a15e372681357101c151aefc21040e65611ab4edb7366b6694b2ea","signature":"84a5f8d870d0e3a83ea81b7fdd41940ea8af6ad244f7b5a41347a696ce8ee863"},{"version":"5c6fad27889b29555ec4270fa34f6d318520fc36e3c368efef6988c2240f943d","signature":"e569b4fa591b1a8f2e8bc65df628ea7b8314ea9e9fc4de877bae1db7073f82d3"},{"version":"75d850624f64a90b0709ea1dc2742d4b189c106098f94125af7cdfcbc9db0852","signature":"2bb5816f801dc67b86549dcb0c662be56b248c5dd1662c1042691c7329148f98"},{"version":"4e127bfcc9d5d93ab906153cc602cfcc13188847639ded607d4e83fd57838e12","signature":"f91e8b73b979d76dc70cf739261a03907357f4b464c2c2b57aa1da3ef60840c4"},{"version":"43fbe80cee30066d6ade0e64b13f0987cd6b23946ec6265728fa2adb27146000","signature":"017bbf6636858e6e607294afca49a452e39c06854a04aa20ad3850defd0025b2"},{"version":"77d37faa796f4c1b4eb99dd6645176245701ac08aa52886f8f7e3f340f3619d4","signature":"a615ebfd3685ac1590d379b501100df211539f37bdf6c544e83e35b14f69a057"},{"version":"753dc412c871f3fdc65bfea46ee79b435fabb41509238f866f6249d44f7c1dcd","signature":"c286b503f750f73cbf22d1031c189fb27e7d8a93ef017dc18d17bbe37fd5dd9b"},{"version":"4051f6311deb0ce6052329eeb1cd4b1b104378fe52f882f483130bea75f92197","impliedFormat":1},{"version":"23cfe48b32326ffa0bc8a8ebcad5414bcb7c3f6b7a525b3cd30c2652370f237b","signature":"eddd8bad575c2203156baa8df91727157606bda7ae0d100246ae7deae963bc2a"},{"version":"cb715246977191dfd17c33a8b96ae65a8c62199f06a09d36501951fee268a28d","signature":"e922958ca59e7c52c6e0dcfe9748dab953af2b18d58d716884620d408935efe2"},{"version":"d6ad5279f8d296ebb50264aabcbd50a5296edfef7f23ae4c159579028935d119","signature":"48cf5a32fd77ba27774c29824c8d1bf27cfe16081169b1a013e0874292c3709b"},{"version":"6f2d007923eb835494e65dcc1034da47cf8e60aef0554d323273a59b8b8c2f86","signature":"bcb9686b97930d312e851d879aa0ceb39656e4e49b07b8aef72ec0eae03cb376"},{"version":"406af28178f025030a57332cb2a36516048ecab7acf102b84f1c1a84f09d77fa","signature":"aaeb521b6f9317f1358efeed044f7b8c9da2de643c9c444c86efa4b5974707ac"},{"version":"00e2552694e9ca66c48d911ae3a46b5ec592ceaf1aa11fc892a11ea68e8f61b4","signature":"7edc93fe90f8fabb25092054cd2ba3454b5665dc1470229cd49300d2787f9256"},{"version":"72a2cdb9a957dd93ac6c7a5ec584a41a2eac59df7693ee69a8cfa93503b7d8d3","signature":"385f9da32da6c7c156c4c273f1e08ab2fbe264bb424469850b65c9e6ffa9c9ed"},{"version":"fa8896708f7c899af3f718f77f46489b8d3efd15204184f74b878992dd516270","signature":"e89614e458edec1676ac424f0a893a6e87bf5bf38d34a8758b3e4823f0d2b48f"},{"version":"85b1d0061b1268cbaa7efeba177d96bac002d38d7acdffd7a023decbaab2ef7f","signature":"0fafd7d6cde3e37c7c4045f298b00355745dcb2147c0e8d0f1240fba867274de"},{"version":"bb5660a80ad6edc1e4a7831bdc38cb4f70adbf718846aa3bb936a27b62d742d6","signature":"e017494fbef6d93da248b22d25416f95d8412cb4a4e8cb12c7e64495f16de3ec"},{"version":"91d4ebbf20c7ce05ce56b901d34ac84f18c5de49cdcc8b4e2e79416bf5863a52","signature":"00aed0049902c591b92c49af96b5a8d1b3e202604017f34241bf72cf89f80756"},{"version":"f7cde16e51986d5a1361c4d7e36cb8f8089acd60e7b43b7c0cb7ec9d3c58bbb8","signature":"fbfd3cb405fce3aab2cc8b6c68371f03f340b5bedfb22d1a0b46408ca184aa4b"},{"version":"0f55704e7fce1025a74958ce04d7d099a3605ab1ba105c63b7fde02139a17eef","signature":"69652f240dac09436bdaa4cedabd63700a279aaa035b43ade48742fbe5b37d08"},{"version":"4d8d7e049c7a369a07b41963903b7041bd8c88560b55af2b4b6c4fd7be645cd5","impliedFormat":1},{"version":"83f6b233e11c9f2855f7f318f608570e9a45db007ae924278e7a581d7ef99b35","impliedFormat":1},{"version":"c450dc59cb99ddbba3191c8964bd40dc732d13be5d9436d3557cfa38417939cc","signature":"1fc7a196b7cb9628c96283d1c55177082524e81f5e607404a5ca9a1ff53e45e4"},{"version":"231a843f95abff5b70bf76ded015c4d7c0ff006544d27c9747471a495743c2ed","signature":"1507e471793e1215912dd1ab92c0797ae9259ebf7fd0f3146e2bcee42b776bc8"},{"version":"deb4df42f640706245617d22c38500d0d24e34689f405224004477c47b30a287","signature":"84225d531b0d673c7dee0a7abb7592e937c207fb0393e85d8e9808505e415642"},{"version":"05196723f295c088f92bd93f9edf2f9d72a0e6fb7a468f55aa270214519857a5","signature":"1c7c3f06b140f7f31f69c3f2a6f87659c1a487c552bc733f9de6ed963779a17a"},{"version":"6326a3828a9427e88b8f11ae4fbe842af33faaf0fda37cf710d8f7a8f76fb912","signature":"a0b2ed7ed78ffb63bdb8c45c49596bf2792676cc3c527c599be027c2d772c840"},{"version":"6f4aaa2857e98b57b5f3239f2b05d4c0010dac665952f9b57c2b11f4b93ab550","signature":"9813abe08f8dc60f627701e1576bfbbe8498fa01840b3500ef120ffbe3ece69b"},{"version":"babf8f17c539cd8e5309393275eb17fa2a6790a848f9b6736e3e75b69ca12ae6","signature":"ee79b4e030d4b005413044e47295b78001ccb4849995c4dc59e42e65c509f21a"},{"version":"41ab97f834dc579a841339dd2d5ce54bd7458891414c624cadd12c2ed7985cfd","signature":"d703ffb3cf86f2e1cf7460554b6fc0a3a0eada0040fc48aafeacca14bffb7ebc"},{"version":"cb262ae73b7b864a9cc5e62142dc12600f5afddafa458e6c26218259d5ff67d7","signature":"433e57f0df48dbb4612309330aee7b075651c0ba5d29c483b17bd92e81cad910"},{"version":"f060e1946eb32ff62b101bbac21a6cd02835440c0892554566d0dde5d4838cec","signature":"036240f98ae8d5e07ad6f648996ff0630d6112eaee5b53fc3b309a1acd7c0721"},{"version":"a8f8ddbbd5a595a3a45b89108072fd7c11afcc5df839f3b2d234ce93bf5ba511","signature":"621d7479105eaf0b7002459dd4a7746134df8f621a6d9e62ac5c69f4b73902af"},{"version":"25a9445362108d35961825c730d1385aa52655c253523603fe3a514699a08308","signature":"0a82088daf1f69f93a6f03b7ba430d6605a8b48febb578e7ecd2c3564b8d235a"},{"version":"8ac5cd94bcb63ff4ace80712b0b2da1d8e3005c15c6fc2e96e3964e2cbad7221","signature":"6e0962e848047bf57be651109d9ee3d4e499277d114392e6efdbf60a3863fec6"},{"version":"b92e316d7caef01a7d96aae2fc81bac3411d81aa08c08369fdd79b79052d0804","signature":"a15f6b3477115f885bb24033267a6e06e889bbc393c5ae977513f0ef2c29efdc"},{"version":"883e6a16350e6a237822deb193859ba6f80f68b5bc63d37932eb5a222afabcfb","signature":"4e4f390cf28f71013350ead1ba25290872b936b31244feb495c7da040c655c54"},{"version":"20eeac8a87d7e85f13f2ce118073cec7275054be646bd47823f1e9cc8951ed4d","signature":"9f2d02e65e22f5bc32f727fb091f17315fe58a8792d8280ec59ab072272e3376"},{"version":"88f2985b43e7af3d4dbcba54e609861fcd28cef3ee74ca4d54e82917a9165b30","signature":"2e54daabbe58c730286e014d2bfe4a80b6d533a2bc9c5ab6fb1e3e654d3a4872"},{"version":"417c3d98d4efb99cd7f3c683c2caf02ae28758f18fed72ae0389aecfdab29878","signature":"2a39da52aed89ee43bf5dcadf72fc7ab5d16b8dee17ff890bf0ad3b72a0320c0"},{"version":"15e9ece6b9f5f2ce89f2ec8a96bc9303b35f07374b94005eb2443efaa0c6a49a","signature":"46676fa7ca6a5b6552a61d40d41f41eebc81cf838c14933cddd35203d298b874"},{"version":"0aaaaf9e39d6225f0fcce6949faf7254a473de642dd96f1b6cf5501b87347546","signature":"1f8e872ea16e6ef3029e47f25725a22c286734fcb4a88ea2e13c437e905f0c21"},{"version":"735af8f14d6e3866b5863742bec289903bdde848ba8754fb628afb54af74d5bf","signature":"6058d5942879388147f8aa5e9c2713af05d0f1680d7ba91d1999b97dc6b5ca01"},{"version":"69e1aa237956759cc510e91cf22b8b81ed371fd80d864593b86acb355e43795a","signature":"29df4852e710dfc4ccf300cbbe4f3ed1e109cc09bc55d540eb40bf2ef0906d0e"},{"version":"e49fff7bc7242932d50b6f1d894784704a65bec211535046a7b7983891e30972","signature":"aa5b770dd1b4e7ce9fea3c83330240ac673f9913a0535d02994bc0511eb85cf9"},"98ebfeb0805807ae08415404af1b664e76353e70e5e71e6f086c7ba264b76fba",{"version":"fd79331caf4d1c981f82d179a8f8ee1f5f9db5485b5960d2ac5252ff91ba195f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ec213e4001fcd5e8446fea02e4a123873df592e8205987405c0e6664886104fd","signature":"f5ff573f4a6451fc2e8c7e1e6ceb8537cd0f25338065baae8a5735c24f22d431"},{"version":"c38e3b9be1619f15bec612db3c9867c9e3435bcc9798dd0d8f6342ab0d8ae946","signature":"e8f50d40694344b6b22ef6d4c3d5fe9347601c7a435fb7e60f820bf37d881d0a"},{"version":"1d25e7af5af3830d0cbc77493b047838dc152a775920710be72c3c8472b980a6","signature":"3560dc1bdf53f078348f0e499ab1e3339be7659310d92861fba7a9277024ffa7"},{"version":"cb83a4611b876ddcf649c18d80609654bb2c80fc160da1e7539cd3be15811a60","signature":"4220ee655e09296874f3ae3d3145efdd76398677bfa27fbef8e133c1b09cf50d"},{"version":"51a8c66060d8e08fa1607e33758afebaa47c542b40cf7091a791218255e7f769","signature":"475b03ccd54597a3115e13c07c1fe8ac417f6f6857d485114a00039d5d1ea179"},{"version":"817f663192fcc81412090c3e7167fa81c63eab9ea977e6a6ae8eeafb8742001e","signature":"77ce1243fed91f1a9f7be9c18192af18f4ea1603611a3dac2ba4f726c298de4c"},{"version":"4c31fd07fd4e530b6da7febec131d259907d5e179ef3bdac0a4a43cfa56b6935","signature":"b1eb5e11871638368fc4cab710e40fe4b23e0f7e9d4a6e22052edfa50d185fa9"},{"version":"9c5d15efba9e25be56ae6fe11057225c74316fb0c90e7d34b81ec8f633a9810e","signature":"a68280c12af5525ec8003356652c9ce50a24a6b3b6fc83bf793fafe60909fdbb"},{"version":"b02c8a9ebc617e95159a1d928fce2fbc345f3e9ccc9f7f6684195d8f8d9bab5e","signature":"45e169847975d5baedaaa5fbe3da4bc92db0b90a305f2536491b7a4a2d262341"},{"version":"963c32aa76aee7050bf4b5749bec7c775046f03bcd791d54992b3345f7b80e2e","signature":"66be142f9806a1a6a875064f7a0416e1ccedcdcf6a9a209b1c633e475b975dd2"},{"version":"9e473cec8b5dbb77baf8db593da0a943701f1edca3b3b1ac81af9ce178dac9cd","signature":"219dfcb98664c09e2a901a0bebd0a1990dece13622fa81b99a4fd16e6352c936"},{"version":"10ea972b401fc77b7e35429345f02bb02dde34fc9d7d1fc3232a187f5b52facf","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b85f8b41e677c1e4e486b8856355198d5266556bef1fda2f1b0cdd23aea308f5","signature":"f25e6f98b3f2f28fe6ef1e8a2d53a260e20c60f99d311e57251ab5dad00a1e3c"},{"version":"eba4d3bd1b2ba75f33dec7019769e75d9cb5ab19609cb3181e08ee56e1c8fd45","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b1ab7dfe2e40a14457c44447646438563ffbf187e60a175f258af4189bb414e9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3616c07b4b641b8456821754d23d7db606498fbf52d04c97fb8cc285ccdd3347","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0d29ff571f05d9f1c9ecabd53c10cb9bfcaa313b3b64612593bec64745c4d224","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cd5944f91eaf3e04d8c66d1c7c44508f932ae86fc033403193a81a0e3a95e53b","signature":"2630a9fc3e9a2205f1df08e9d39ac89290da5a35ab782d1504364baa70c67104"},{"version":"3ab2b6455439badb3d984aef6d2519029dd8595f19f614654072798269598876","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a8f3b78fbcee37a708acd2a86f1c22645cf34b444cfd7459be341415228f4b63","signature":"14fe776ea9f72086fe119d5df096c39513d6bdd3ba1615b8d9f5cbce35933f54"},{"version":"f14799e6e43275054eb876159fdcb6c55b4e76808911ffdc9f81a2e3e5baa564","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"aeabfd5da8290656189b20d20600d0df6381dc3881c381b815807e9fb745f5d7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3dc95ec98d2db484ccbaa31a47c2633bd619a4d86fd655739ed248f081f49f07","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"647f3c90fdbbd31bc9f337d7674f74f40f32c3adb60bde42ad85adb0de922b48","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9ac8c2249f0a97698a155031023e87eaa74c871229e36b51c3c83fd1a0bc92d8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"35d7cacd428e674f89a928ed99f34ddc7c36958b395627a9196a8ba22618a29a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c51ed8506a74e96ec467047bfb51a97c31209bbe090f330fb29306a19cdba765","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"69a81ee2b65949687067f23e7d5ca0fec2110620ff93ca8b92bc56740340b760","signature":"bc160a1badf1d1e0e9b92666b4848dfdc428b553d00ac5d2304b7ad80ac4cbea"},{"version":"5f44765c75000e8fea925fba6c2ba696386103cab9d813e72070cdcf45e1f804","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b11dcc6b3a1e92851fa7626c01c543833b96a9f37a29d80de6f11b320b626c9a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"63e34e9210807a4d1af057003031a6689dd3295f8f2524ae7597ab27f326335c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f825e992a9f7f8576702cb198f6eb82c0fa45188b57991f5d23ee5f0bd16e85a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ad7e4e17c67808e01a84f46591f2d4b763173ecc84a4863a727b7058b5786945","signature":"b7902bfba5bc8b901152f1ed5f8d9c2cbf2ba2790d351e5e5d61e00bdebbc624"},{"version":"3e5b494cda4d0ac2bce024928f0eacc77f8d9e4ac3d51eadde967ce542efc27e","signature":"b94b0bc72e5a26387c9314dc4f72106bddc0e5056469dcf4a43a351c1523c49c"},{"version":"1c230948c8120cd778cb258a0b70eec899b458db58229de16a2951a7ebdc57b8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0fef5a24cad5c4948eb776b00fa114e2560ce3dded3abc27db2e2b2b66832331","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2cef84bf00cbdb452fdc5d8ecfe7b8c0aa3fa788bdc4ad8961e2e636530dbb60","impliedFormat":99},{"version":"9e2f5dc3da9d83bf4a0a9e5d39d8c9918482d586e0c403a44021e4ae7662697e","impliedFormat":99},{"version":"799003c0ab928582fca04977f47b8d85b43a8de610f4eef0ad2d069fbb9f9399","impliedFormat":99},{"version":"a62e448d3f09fee63ec1230acb23fb54f8f6ccf8d6f0001c7b94fd51594b7c9b","impliedFormat":99},{"version":"5366549884acc57185eeeb64561c2060af008230a8ea645f048f747cfac6549c","impliedFormat":99},{"version":"cd3229a2e4ca10207178e22f215c8e196c837254dd34ee440612a2a14993ffc2","impliedFormat":99},{"version":"73b7e3d5300ad64f9231f5bb145fca4892574d85e2d1a015ce095628f16915ca","impliedFormat":99},{"version":"42944c2dd3115e25cb0aa77aa05fe9e3d0f8a3b4ac251896cc680b7be41ad60c","impliedFormat":99},{"version":"7ed8ed496092801dd5f25f39af223ebeddc97bd64a7d9a5621f790dbc836eebe","impliedFormat":99},{"version":"abc549dfea982be25e0379cdcb6ef2aa6716b0013c11d8d6b14c814cc955d7c8","impliedFormat":99},{"version":"0e6ba4003cfaa90748b69ed0dcc9f99299d1af70f4bd835a872e52705b0c850c","impliedFormat":99},{"version":"3decd4c8e355126e76c9a43cc7ae08017fbcf1d766b204d696ccdfa5128de1ac","impliedFormat":99},{"version":"40410f51558d0b3d635584333fbba6b58b4b7f74037f59a08c0577828539637e","impliedFormat":99},{"version":"096350f9446ef08832b935d4a97c66f74a9133faebd90a40a12abd5f8bc7eab2","impliedFormat":99},{"version":"26baad6aa356ef75b2e1ee150ef6325988be9700bba14249f9e8d0f66bb36087","impliedFormat":99},{"version":"237dd4f246265a3efb18c3d40f54f98336ba2c329a9f9e30b4bb0f1a27baf324","impliedFormat":99},{"version":"3fa62b954262157916a65b3dd57faf6cfec7544579e673204da30eba00852543","impliedFormat":99},{"version":"8ab0b13972c8018bd18d49236b8c08448a38c823e28f5620b3ef0b43ff521589","impliedFormat":99},{"version":"065dff95b2b9cd6f5f7404222ecdbd371f15c22b844b731eb32286540f499d2a","impliedFormat":99},{"version":"d1c03f0339b8514b7d5420e075684e6b1dfb9d6c27a7fc6fbb09bc3f25fc7764","impliedFormat":99},{"version":"f8900ddf4a4944cad4a81de965c4761094758ee39bfe24198668c397caf5db3a","impliedFormat":99},{"version":"b2b8376bb1ac24155cde89574c32edfdefdb926845d9426ab52815421b3d19a1","impliedFormat":99},{"version":"4e805f78a8acff48feea70836df232a6db887b2e376492666f6b70985fb706fd","impliedFormat":99},{"version":"cfc5a66408fb9a7dd136ec2afd50a3eced54baa3321c473ee4d29046e761a3a2","impliedFormat":99},{"version":"594201c616c318b7f3149a912abd8d6bdf338d765b7bcbde86bca2e66b144606","impliedFormat":99},{"version":"35c190fc184fc2fdca132bb8aad00ac84819f135428b0e906c3e599c74125d24","impliedFormat":99},{"version":"8f567d63ab28f074ab2be3ddc2da27107de8022488f4a3bd91609752045bb612","impliedFormat":99},{"version":"f6c7ae690e2d224a310c8f967cdb415d8c7c55791a30c00da30e0c19ef4def49","impliedFormat":99},{"version":"f0ee7287284a844f4d04b80ae7a955b12cb50f85fb0021a78cc7f20a90459823","impliedFormat":99},{"version":"956e7dae5b888d02ec65dfe4113b541042cd2c70f96f6b9de0a5465bdf9565fe","impliedFormat":99},{"version":"75722639ade81b4d9a9a7f67f9cee2abbb68c52367322fe4fcd51949dbf60706","impliedFormat":99},{"version":"f90d3104f554535c4bfcf9d429e41318563c40b3b7e0827c0975624722546514","impliedFormat":99},{"version":"c61b9b3f161eb34fe5ed7fd3bb84f0774d74445928a06e9089ddc0a152f2a016","impliedFormat":99},{"version":"17268b7c5aed233ecafd22ac3e751c3aafc101b7ff982de8617bf19fafb7058e","impliedFormat":99},{"version":"bf6060c0585e76d2670629cc4c592e1dd938ac356e974916ced7f46587ba8181","impliedFormat":99},{"version":"dfe68566e870382e203fbf082e3e094b3d3d6712a3b6bf56fe66f69271d27cce","impliedFormat":99},{"version":"f230e4b9b3a7c27975a8af6131b08f6b17505e829073a3faa6ebff4a163090aa","impliedFormat":99},{"version":"e89ae5ee53771a98d89105723fc4dc73205bd96bfd2a784597b5ec6c2ed35abb","impliedFormat":99},{"version":"7f79b823d4b2a1fdee3a799a6a46792a21e4400ed0c2f45f1e1a9bb8de21d18c","impliedFormat":99},{"version":"42b828f21d7b672495a1f538ac49e93ea12da980d07d28999c7eb8dc55f297e5","impliedFormat":99},{"version":"35e7486045f8a29b25ec8adad02823bb82e0876fcce76228bc683e0da0726e98","impliedFormat":99},{"version":"a43f4964d97d0feeb6b33944f750707dbdb539e1c9c3a0496c40789a90d7e0d9","impliedFormat":99},{"version":"8dc6b9b1f772053689d3b298f089ffedf29ee93be2eead0d9c07d77e68aad9e4","impliedFormat":99},{"version":"b44e0ca6cba9c3f98a1b277e93dedcc31990c57c08f0fb37c29eb929afae3a49","impliedFormat":99},{"version":"e236485fde7c092508a177ccfef03ba15ec72ac697b50e241802d68dd99c5f73","impliedFormat":99},{"version":"b7ea66bd111288844e2d0cfb12abc02242af0786a83ddde14abc156a7f80d500","impliedFormat":99},{"version":"83fd9ba9e82b881f410b69b30d4fa9e41b1b6e445e4d7c7eaec836d4cc5a5712","impliedFormat":99},{"version":"ba2733b454a9756b8207e110896e4889859d4a23581e54fdd659f09267a63ecd","impliedFormat":99},{"version":"d94b9c4da700bf7e011fbd442c54b5c88a52db58bc71bb69db67f46a1c525320","impliedFormat":99},{"version":"bb19a13fddc505d633b9d08340c851a16638a3a2c6ba4971d538908b0cce8671","impliedFormat":99},{"version":"da588a0328ea4fa648563415d1ee4cad0587e3d1e1d29cf54d761fbe83ed9670","impliedFormat":99},{"version":"673f71885a78cdf431dd29b801ef2f811a2c793b415c44e64a55489ad010f6e2","impliedFormat":99},{"version":"4c0c16f5d60671e0654e560a94cc549a858a5fb9397a072d3f9935b3068be740","impliedFormat":99},{"version":"2cb58371baa22dbaa02e2abfc40b5640f00e7ed203e70e97fa20226a776b2e16","impliedFormat":99},{"version":"29db777661a60ea3a85cd21ce29b0bd877bb44f52cd583f9f3f7580ee08d4fd1","impliedFormat":99},{"version":"cdf79d50d5ca102a6ccd1ead392b0f5ebcb9b6c8b230e4f4931f0fab8b6ff3c4","impliedFormat":99},{"version":"6e21729eb1f94c93f99d1c13492b6e835e5c2d2ba552693c1c699f0e34d1fa1d","impliedFormat":99},{"version":"8267fbe09febe68384466808d3feaf055ebb7b15903728d23e7fb4c01949148b","impliedFormat":99},{"version":"54e45f5f4f7684c5c49d3e6367ba73c55c69f82973ecf7aca793a86bea5a99af","impliedFormat":99},{"version":"55a9664e49c8e8db27d8eb413749957eb222485b91b1148840a73e065ef6c028","impliedFormat":99},{"version":"af7945629e88f161817436aeab27906b947cea60102066575eb31071b4f84168","impliedFormat":99},{"version":"847b7eec4ffc81b7eaa1bcb473fd5da4aa73ab7e56944df3caf7d284317e95f3","impliedFormat":99},{"version":"5dd262cbb746c2a4d0a26f09369b3ede4a1a36e15c272adfd0289c47cef81ad7","impliedFormat":99},{"version":"677e4d55a1353f1b83ad68faffbdd91ffa7dbc34d67b1e91e88d3ac71b88be0b","impliedFormat":99},{"version":"21ba9b6a4c6dfc6dc403884d34dec961eeb965a4e0c99521ba2b3f9929e26b75","impliedFormat":99},{"version":"452a373c93cae3a20fb8f8309ac48b40cb2a33f05c3d54b090582ce3b8ae96c1","impliedFormat":99},{"version":"112f147e1f4b44b4a4f186cefcae4e58c49d6a0a61faacf7a12f55694b9f2232","impliedFormat":99},{"version":"c293793b601177e19a4230a9ecdaa167e6a44c93147da549941eb8e154510f4f","impliedFormat":99},{"version":"82ece43251947dd304e6f5dbfaf8b97588e5676ddf0bc0fc1a6a861aaa3eaf7c","impliedFormat":99},{"version":"f67c58823afbf2590f2c239d09a46aba9d3456327eee05b593c48ee248758ce0","impliedFormat":99},{"version":"e2647503f56e5c6d41b256af0b17ad3b98455cd8b852ba7336221af5fe99d805","impliedFormat":99},{"version":"c2d12e71e905f9ae80895201ae4b52b0082716d3177d794799f0140c3bbdb65c","impliedFormat":99},{"version":"668eaa98e8d54dc5a22d7a66d659a47f0b152e7b109f798cb295a3c3dd817dbb","impliedFormat":99},{"version":"81d447a1f248a2345a89673774ca673e79da5df8e25c6fd6bffb495d3b704362","impliedFormat":99},{"version":"900f1f5341752c6c2824ea871ae941d60be1359793a0284e56abcf277955a511","impliedFormat":99},{"version":"00c8b548f04329a012af189dfd8e3f3ddd8d4fb187f4fd22fdeba5e1eb740d92","impliedFormat":99},{"version":"919ea552c5b52ac5c8303a96dd7357986a2597de5760416468550b659113bad6","impliedFormat":99},{"version":"8255114fec0d6189524bf52d90580a2fce40bdac621215e562aa5f5b058fea33","impliedFormat":99},{"version":"6020d3e324725ee474aa4637005d2449eb8bce66e8aaf85163d683df86384dd0","impliedFormat":99},{"version":"d95e4069a535a118c22ac66a8b018818f9f74ca7000c8eac977dceaf752d0f95","impliedFormat":99},{"version":"5d8097f4e2588d7912d82772ac6f05ee6def5b738f5e4605f2e9bb24d26b4e86","impliedFormat":99},{"version":"6703ee0cb2405fc9e98a8835e4266ed4131fd25c31bcc0c302e66e9b05271eee","impliedFormat":99},{"version":"4b83d4ffdcb29aa6562749ca797b76a3b914d80f54819c6a08f1014fb6841623","impliedFormat":99},{"version":"41ecfbc96066dc0d03f1a8139e28b4b3297bc231257d27a7c5796d017962a438","impliedFormat":99},{"version":"46e060979c9bb359578744342c37b843529c284e20ebc219bd71d5fbc04b3704","impliedFormat":99},{"version":"720b258293ffe0939688db7b4729d24f64809718157b14ae50fb9e2397c69fbc","impliedFormat":99},{"version":"1273795a90591a538b11c91a7840b1facbb5b6d500146cc055324a16f58c0346","impliedFormat":99},{"version":"a06814aa3f18bf501a7bbd1cf3ad9b1fb090cb89b19375debf6ac3b906ad9090","impliedFormat":99},{"version":"785afd3f604c75ef24a65c0f2ce4b3ce2137f941773c201842abaa7385b12e3b","impliedFormat":99},{"version":"ee3bfff84df83f9e3cf0ec85aff97df52fc57e740e41fd4780de1cb3f9e73780","impliedFormat":99},{"version":"2025d7779d9356a37ed4142da93898d39f811d9c5937f8c107f44ab2344e87b7","impliedFormat":99},{"version":"471b3d02d1af08c6b58a9a2ff5c85da205910f782a7783d7a1f59dcb681ee8ea","impliedFormat":99},{"version":"e1902decb3f07a58e9be70b5136e3d715997025e0f0f20cf7e2610363f38ee04","impliedFormat":99},{"version":"323156c80e3ac6175f4b75952ed871ead30b58b9ec463131e368e572d89777be","impliedFormat":99},{"version":"7c54717447fdfa134e43c6f1a71f8ae4e955538f9e59a8bbd60eb65f5bb965e6","impliedFormat":99},{"version":"0d153b01d0b1e33ad2b8c778765c3f3539a3ffaa595dc3e9d53d91cfe5615f11","impliedFormat":99},{"version":"0ef8dbf7f717c2d8912df768687073cda1d7ec73ce2861fa8ee30ea8c15455e7","impliedFormat":99},{"version":"355ae3751ad1378804c850b212bbfed1bb68af9e4cde0cde857b86c6cbbe2140","impliedFormat":99},{"version":"06b02b230ad18789680a5d286d55d566451973456fa33b63ddff6c9b2c2ab41c","impliedFormat":99},{"version":"971f0be2884711cdbd2dc522224ba68db24abea620e9089b5432a9ed73dd406c","impliedFormat":99},{"version":"1e45c92c3241e189027db53310d5b3b8d713fad08ca6ec5f8e0734b275b6dd76","impliedFormat":99},{"version":"a374180a9dc60b15b4fea69423ae9d8e3cdfdf604e8cb314325db23a2a8e3cf9","impliedFormat":99},{"version":"acc82e49137ccc0be7e523164613032cd0a35a08b38721138a228926539a33f8","impliedFormat":99},{"version":"990951a94433c2efe6e42266ebd096f63154115a37c1f4e5bd37bee57bbd3563","impliedFormat":99},{"version":"b65b675fe2b1ad0d621ce5ad94e9fbdbd16b17e8afebe2863361e0d028dc73fc","impliedFormat":99},{"version":"1bc87b80ef30a78d0cec6f6c56ad41b68a8f03d30a7052d1a0f1e946f5eb5150","impliedFormat":99},{"version":"79ace3491ac2d2585e2e3748827466f99d0fe06acfb8cfd7bb5ac6e272d9b742","impliedFormat":99},{"version":"f51bf6581de40babf85946efb37bf4bab0a5357b46b4a0cf904278f3b8234350","impliedFormat":99},{"version":"c728002a759d8ec6bccb10eed56184e86aeff0a762c1555b62b5d0fa9d1f7d64","impliedFormat":99},{"version":"586f94e07a295f3d02f847f9e0e47dbf14c16e04ccc172b011b3f4774a28aaea","impliedFormat":99},{"version":"cfe1a0f4ed2df36a2c65ea6bc235dbb8cf6e6c25feb6629989f1fa51210b32e7","impliedFormat":99},{"version":"d94d06e50f58be0a417ebc0336be0c51e5aeb06cbb59ae7d5d4cba95e4948418","impliedFormat":99},{"version":"02246d22f0fc51c76534d953f606aab7c012d1acdb182f822c8ac8a37926a72c","impliedFormat":99},{"version":"0166e0f095473027f6f8744378f5ac5cb6557e788540fdad76e0abca9eef2567","impliedFormat":99},{"version":"f950a4cec73ccf53ee3c56f117e5c585872bd13328c487cdf7a614246feb075e","impliedFormat":99},{"version":"f325583644b63525d1c4d22825633c220e478411d813f134d5930207cdf8aab3","impliedFormat":99},{"version":"e25a05c0fd866cf73c00a281ea11bb51fa8d2a9955f2edf8a7b8f3081b37c165","impliedFormat":99},{"version":"2c86f279d7db3c024de0f21cd9c8c2c972972f842357016bfbbd86955723b223","impliedFormat":99},{"version":"df6ccc0d7f7324035b05a6294404b310a23b2f07fbbebe1cd298f88647ab8b6d","impliedFormat":99},{"version":"8cfc293b33082003cacbf7856b8b5e2d6dd3bde46abbd575b0c935dc83af4844","impliedFormat":99},{"version":"62391e62217e8a22a4d5f3ff123912bb4d182598e051f31b287096d187cbaea9","impliedFormat":99},{"version":"81895ab68da9cb1656eca90934f01924181d57439e980aa3df8c788488363272","impliedFormat":99},{"version":"cb1ee5692cfe21d8865ab74cc64aeb2f3319f2c2ea2f63cf2662b6319160beee","impliedFormat":99},{"version":"ef6ed27ceed062efa353f3c108dd31d3e4e83e222ed9e18566fa85ed4600e366","impliedFormat":99},{"version":"faa076baad26c7c20856aa86a22c8afd9113f0bc47feeacc680a9e6d4493ea5e","impliedFormat":99},{"version":"782d76ae47ae31c1169c04d93f11e6e13b50c704833517ffeb933516abc4dc12","impliedFormat":99},{"version":"9036dd2d0b09989692fb0eb69b5142647a709aae1f2bfea464701df33758345f","impliedFormat":99},{"version":"14eeb7d5737bc074d1020b7648358ad0488dfb576aa82937e8447586c1b02bd8","impliedFormat":99},{"version":"73b252fae9083ac46b9f2fa376c3a4f5d2c98f5fc0d31922e6d74e7a416f1034","impliedFormat":99},{"version":"0c35ff99747044453c64a4fe3e0e813adc45e67c16d6c47a39cda7d5b2c45764","impliedFormat":99},{"version":"a127363b7f50b5ce89ee98b2faa52a3e7247af32785f937e827e9ee32578d803","impliedFormat":99},{"version":"ae9e8befa5a81361fda14b5c44953b69a6b32abed1e9c62c533230796ba2b39f","impliedFormat":99},{"version":"f3eeac608cb47badfaf2218914776864558c08a392fa626d3a0ad678b0fbfe38","impliedFormat":99},{"version":"2c05477216d0da559ce805e5b5cb8f3c72e1897110886a0fe22808ac37a2f5f6","impliedFormat":99},{"version":"6307d21b4a02a9de0ec25ad7c8a36bfb3a25d38adb1dbe877f7e73595a4a924c","impliedFormat":99},{"version":"cc78721e9ec12b7b62352b8bfa1e37abe055c17965d5fc956d4edccb1bf4f673","impliedFormat":99},{"version":"53565e07ff42ff137d862dd402cd1799785904a50cbd75fbf8402d7ae76fb6b8","impliedFormat":99},{"version":"c8c4e8de61ce90831b7342b6e4800a3e70f4c06eadb17dd743e652ece3562ebd","impliedFormat":99},{"version":"37ed869a9de36bb1ddf343286b5cd0e0afaddd892ba28e82fd652b7ee2c46dec","impliedFormat":99},{"version":"5dccac21bdd7a3a3f399f2a0110bb1bb22a7bb002e3c4a3403f781299faf3f53","impliedFormat":99},{"version":"976ff2cb836f3b64382f2090462966b6b82a059b8d90c4eba54ffa2021e5c150","impliedFormat":99},{"version":"9432e9ba2ed3ef0169d133a2fdb113002be901691ec78ec9d2329c12c16d5065","impliedFormat":99},{"version":"f7e369493bd11921421f51025608f6450675e5d5fba73a1f5617c96072449ab9","impliedFormat":99},{"version":"0919c74e404e0f876c1687425547263ceffe5cc184404492ed2f8deb8a13cbcd","impliedFormat":99},{"version":"7df13a374704470d39a931dd1fa3602a3bd1cadf064115784e4acc3b25e6c24f","impliedFormat":99},{"version":"36944fe70fea641703d40efab3585844c0ed20ce7e783fdcde90bad50bf77f5d","impliedFormat":99},{"version":"cde65d40e64bf0aedba644d8841fba8fecc6f4793d7e4a4364be954bf273ec0c","impliedFormat":99},{"version":"ab9a48af27d31f50da02f40b83b2e8695c4ac28bd446f37d34d5ded0443aed3e","impliedFormat":99},{"version":"0b1a50c36805a5f3be773ea73339750c3619a7ac53c0f441f5e9f1cdfbddc695","impliedFormat":99},{"version":"b85424e3eeb4843556cc1838289e1d3aafc8907b44fad864f228e2abf1af55d4","impliedFormat":99},{"version":"0bdeb9f8d6472b196355591ea4a4313cef5434d24bc79c6e5e733132380b87ea","impliedFormat":99},{"version":"91fe1b91f77a6080c156f0f6af3f6b12524f04604b6e0925432c48f7ef58cfd9","impliedFormat":99},{"version":"9866369eb72b6e77be2a92589c9df9be1232a1a66e96736170819e8a1297b61f","impliedFormat":99},{"version":"e84281e45703810be96251405f8051317362e453f39f26e078cde8967fd2945f","impliedFormat":99},{"version":"0bcb04a160a2a2a934480e3b899b1d2255970b25ffc7408a5d07aaa07baf2878","impliedFormat":99},{"version":"8e3a9c17439b657424fc7e311943dcf9444fbcac73f3b9b72aec2f449a11e203","impliedFormat":99},{"version":"a6c3df80c7c5e8a15e302df97c8a35b1deec48f6a8639110663d6c85ea562fff","impliedFormat":99},{"version":"4c69a93a4645185c445f0050939645592d49f2b8dbc999ff63176c607f3dc319","impliedFormat":99},{"version":"0e2d2919246a4491005fba1612d101a68dad27a5592a77baab1523b2de335cc2","impliedFormat":99},{"version":"c32be5821ff157b2845dacfb257531e932a1161b933e6cd1cd0a4de9e057bdea","impliedFormat":99},{"version":"eb14bc57e220517c752f74ab7c810b72a80632c26eccbd7af690ed9ea7b5ee03","impliedFormat":99},{"version":"ee0de1f85e4fcafe9019c89085cedbde41a22d4492bab87623eed5afb91065ec","impliedFormat":99},{"version":"588b99d933490c59f0ac74e43491ec1b71348b049b1a391f24318b84bdc17b97","impliedFormat":99},{"version":"d78f57a7b922e855a90900275fc93805e07f8cfc7689039840118eb6bf6f0057","impliedFormat":99},{"version":"3c20a3bb0c50c819419f44aa55acc58476dad4754a16884cef06012d02b0722f","impliedFormat":99},{"version":"82c69793fa09d8b58a3589f08c7d16163c566cca5657dcc45deaf5160f2c0e95","impliedFormat":99},{"version":"b89268c927a997e32030d8d8daeb0ee65a7c7db40b167a39296459e114ba7511","impliedFormat":99},{"version":"fb8bc4e79a3b9442dd3e8b1bea89b3e0ad93dd154f94fcb7ca81f511c7c06b65","impliedFormat":99},{"version":"b6c9a2deefb6a57ff68d2a38d33c34407b9939487fc9ee9f32ba3ecf2987a88a","impliedFormat":99},{"version":"f6b371377bab3018dac2bca63e27502ecbd5d06f708ad7e312658d3b5315d948","impliedFormat":99},{"version":"31947dd8f1c8eeb7841e1f139a493a73bd520f90e59a6415375d0d8e6a031f01","impliedFormat":99},{"version":"3a4b1b3e62543a3955e1ad5cddfcc59b25074f722d5dbf7aee1971a43de8acd2","impliedFormat":99},{"version":"19287d6b76288c2814f1633bdd68d2b76748757ffd355e73e41151644e4773d6","impliedFormat":99},{"version":"fc4e6ec7dade5f9d422b153c5d8f6ad074bd9cc4e280415b7dc58fb5c52b5df1","impliedFormat":99},{"version":"3aea973106e1184db82d8880f0ca134388b6cbc420f7309d1c8947b842886349","impliedFormat":99},{"version":"765e278c464923da94dda7c2b281ece92f58981642421ae097862effe2bd30fa","impliedFormat":99},{"version":"de260bed7f7d25593f59e859bd7c7f8c6e6bb87e8686a0fcafa3774cb5ca02d8","impliedFormat":99},{"version":"f9d8848e3c6d82c1e348a9e5cc531e433be58c4ba233a6683a4e9bf6d923a462","impliedFormat":99},{"version":"48a3ae8b6325c87135a210f6d6a7ce15d58417870a2ad78d70858313c47eee99","impliedFormat":99},{"version":"9822da8046d00ef9b8a230345cc163599e58629112081ba55cf4f8d88ba5bd93","impliedFormat":99},{"version":"260a0f4a8a6dc69a2dec8ea672d702629ff7624d5684b29be55cca02a3e42e7e","impliedFormat":99},{"version":"9789d7263d261044cf33f0bb5fd31a2f4ae3a4cc2a010aa45db6f7d01fb019fa","impliedFormat":99},{"version":"d81f0485800e8813d917c2edf184ca3a7fdeada1472cad6dc41e43c37e240800","impliedFormat":99},{"version":"f59c2a64fc652509e0cc56fffb59d7b81f4c7950c7dcfc2da44b681637604797","impliedFormat":99},{"version":"ac0d6f9d09ee9ec076ec3045d20f0d6f5b32300d5a2fa05b5c5a9b6492c0de1f","impliedFormat":99},{"version":"1d8a6497f663251332519c392c6053d5b5e93e5a2189e2669620851b93fbab65","impliedFormat":99},{"version":"52f2d4cea9e3b8e4821b6ca71077ec5f41316d1b3c7d599ef10fd7c8c839ee09","impliedFormat":99},{"version":"013d7f1c5798ac843bcf24e6f3d97efa42c79f038da9cae4fc95ec686b3087ce","impliedFormat":99},{"version":"83b28136beeebb45a635f0179b828e0d0ec9c59330db43060c5958d796e35ddd","impliedFormat":99},{"version":"81c1ea7f9b00460828ef1c92fbbcfa9ff0a7bfcfb2dbfe2510bf7916c914fa75","impliedFormat":99},{"version":"6cf0bf08cc2ffa6d25c7a9852e58f7de9b26122a42380a89105c201e8bde13c8","impliedFormat":99},{"version":"07350c1be768f0446138cf700b47a8aae8e2f6d828310e519bc500200d519a92","impliedFormat":99},{"version":"4253e0bc9530f4c0eec62d1c566350dffef04ab26d0f72befd2ddc08ccb61925","impliedFormat":99},{"version":"9237ce9c67ba997f8cdbc795be7628c1eafefc3317260c38c1e2df4ebd63a62b","impliedFormat":99},{"version":"1ae2b7f6a1352e73754401f16a7894c1335f3fd199acf4c473274243f89c3230","impliedFormat":99},{"version":"ecfe3af749f3c44ab0fa260d7027b067332f0841bcdca1c8db75eb9b1890bbb5","impliedFormat":99},{"version":"94899ca690be8b491a49004460b79426162b218ef26948625fc025cb40a092e9","impliedFormat":99},{"version":"7fd2e48e2ebd92a381e745c7cfe58003969296f7d0cb0109808e6e867bef6a4d","impliedFormat":99},{"version":"98d7fcdd7c0c682528a70f6781f7a00cc0f314b720d1b15996f223c74dc0cf69","impliedFormat":99},{"version":"155e18326afb2fb26a380b480e0c892cc85cc9449537b3346fcf5aaceeb953a8","impliedFormat":99},{"version":"523d1775135260f53f672264937ee0f3dc42a92a39de8bee6c48c7ea60b50b5a","impliedFormat":99},{"version":"e441b9eebbc1284e5d995d99b53ed520b76a87cab512286651c4612d86cd408e","impliedFormat":99},{"version":"f67db9e9b24275680e88888b618e0d6514a40cef9aec2b6ea8eb1de899f97933","impliedFormat":99},{"version":"0968374af7bf8bf67301b89a4fd4bc8594dcb90b16b4be06ee57d26a708bb776","impliedFormat":99},{"version":"f57e6707d035ab89a03797d34faef37deefd3dd90aa17d90de2f33dce46a2c56","impliedFormat":99},{"version":"cc8b559b2cf9380ca72922c64576a43f000275c72042b2af2415ce0fb88d7077","impliedFormat":99},{"version":"1a337ca294c428ba8f2eb01e887b28d080ee4a4307ae87e02e468b1d26af4a74","impliedFormat":99},{"version":"98a667d4585d5b040af90fb5062e31da7c215abcb47521ff57e33f62755fdc17","impliedFormat":99},{"version":"c29e02568f8b68e62b83db2243e4bbacb5ced2d6c8d120e322b56a018d1070b8","impliedFormat":99},{"version":"53c8e58bcea418aa22f5ee013774c08dfe15f0df9625868b7ce5a7201de29785","impliedFormat":99},{"version":"d56ca5a8aa5dd4937a82df98dd930ef154f340d259bcb2980d36c28a47ff2901","impliedFormat":99},{"version":"3d421ded6ae2260cfd45b230eabe38b6c8498a1a35db809384c85ff2bc3ba822","impliedFormat":99},{"version":"ead83f43dcb956f13b924b9e43e7a64380f830efc67439a9e9e479bc985df8f8","impliedFormat":99},{"version":"d04ba54e15442a067fd28679bf18d11ca2162d64f3b0695b9ddfba2b8e1c3b59","impliedFormat":99},{"version":"6b6cced9b26444d621bb62a1b8cb65911c22505fd559411b5a57e699c7aa519e","impliedFormat":99},{"version":"dafc53212e800bc9bbfed7f3a7732ba8f401516b2bc0c71b2f601ae2583a007f","impliedFormat":99},{"version":"940e51654c3c1967f34160a9674e3bf1dc436a5d36c5d7833718aea235e52fda","impliedFormat":99},{"version":"16f2023402fd0a4eeac99edb5d75d3dd8cb4b2f25f46e9bcdaf0c0bd9670e77b","impliedFormat":99},{"version":"9d7765384806b08a522ff85c20184667eec635fdb809736184da23e89533dabd","impliedFormat":99},{"version":"d53593a008e289638eac5b0a0dfbd4296233e395205831367992a87e81eda13b","impliedFormat":99},{"version":"aa0981acabb92a87323aa1579664c293a968138d9377310fde29429e92febbc6","impliedFormat":99},{"version":"796d8fd55590f854e79d3f4181b54f28108e90118314c858726163bb9961e7ae","impliedFormat":99},{"version":"b37e4e4f8f34745d839c334991d9cf227c34c2ed7fb3b297011ddfddf3ac7d68","impliedFormat":99},{"version":"ef55aaa329259ffcb1694dc5d0d688f05e5a37eec2ae34510ef751e9d608b90d","impliedFormat":99},{"version":"264b53b60d27b252258cca58f80b81e143b6299a866402819d5524fd20febd0c","impliedFormat":99},{"version":"714456dfe665ce8b398af312b56b68a927a8a182f8e78dd7c1ef5cfb596ade25","impliedFormat":99},{"version":"27c9ce7c539db9b37ec0d7476b4e9d9ba7439dc41549e466aeadde43746e8390","impliedFormat":99},{"version":"903e813fb2d906d278ab54626f4ade4f43f96f4e636dc66f5aced69d1afb871b","impliedFormat":99},{"version":"c80bc9ee4fa024302308d14084c0f6c3026301db9abbf6789e6b1caf686ce35c","impliedFormat":99},{"version":"8cd470e7936934cb17c70c18a2e03282980d8d047ec08467925a31bf99ec1bf1","impliedFormat":99},{"version":"c09f5d7d8cdee279972790105f90d6adbfb18efb905cf04815ac59d033f7bb7f","impliedFormat":99},{"version":"e92673d9d3c39fff66b14270f144fd32d2ec6fe92e8b2c51e65bd7b4a0e5f355","impliedFormat":99},{"version":"d465455e9f29288b7c879ecd390256571ba306f8b947698f03b1429d6300ff67","impliedFormat":99},{"version":"108b9e022f7dddd5e5ed8165170d65b752fa7b21ced5dd1005ffad3c36242c57","impliedFormat":99},{"version":"1890b77d7c36efdd18174e345b295ece38e66179dae192fad21e8c3642b993a1","impliedFormat":99},{"version":"636f9c9b34b3f33b2258704da1187e271fbf36081a8e22da97be5b53488a9863","impliedFormat":99},{"version":"fa6693c8ad74ce099f2a93ca8d1b0a643dd7f6026f41ba4b244d440d8dd07f03","impliedFormat":99},{"version":"fd76be177303d35dbd29c11de5f935f5d21ad605d34aa4aad9e309ec494b51a2","impliedFormat":99},{"version":"f31af014cf064d7cea0392f02595f09d8cd4b9d06c7794397cf3ddce13111d81","impliedFormat":99},{"version":"d15de8944d6dfb1c8fab88ed1d56947c4ae438b9fcbd9be18f7840b78c9c3bbd","impliedFormat":99},{"version":"040fd90833b34b59436ca6545a00a3b5988f5a95e6cce0a378ddd66bd2cf44f2","impliedFormat":99},{"version":"f332d07979b46f12410417a97153271e1bf5ea11677423718c59010df71a3f2d","impliedFormat":99},{"version":"06911ddbb7160760c75015d2d6fa0f1c0f94d9f0d61265b2d211238b571a3ff2","impliedFormat":99},{"version":"af0612a0e9b7efc543168628fe60a8d3f4d7ae8d97fe257788cb60bdac2459c3","impliedFormat":99},{"version":"9dd05d844e6b99e0a3c8ab8e37bac8f6297d531a844af0738f9b1eaf4aead087","impliedFormat":99},{"version":"5f7be41a9ceed0632c19b7cdb5ad9e07ac19093cbe23a738fe0f1c8c2f27b036","impliedFormat":99},{"version":"b33e84f2148cc81a9afa6d4177a27a1d246fabea3c0cf391aebd3e62eec04f4f","impliedFormat":99},{"version":"5dd273430ddfd576316532f118feafc41f18d5128d7d84e674d98f4a57107384","impliedFormat":99},{"version":"a9c40d74fab8e810c62cfea99a21d09f529fe6a0e60c39353510974c33df980d","impliedFormat":99},{"version":"2665ad2e88b3633b417e176af058b1c20bf5645327a8c4fd4f08e35636b72f9d","impliedFormat":99},{"version":"2321ad799e7ff9c6c6a886dea5ab208d08072a8d33da312f1b9a10ebc888765d","impliedFormat":99},{"version":"8e2f56264cfd71093034fadc1c788d6f46d58036a57e7189e8eda9a7f87eb9d9","impliedFormat":99},{"version":"06deb0a45f5a6dd23244cae8f1ebfa2400ec7de804980f044316d2d9d35a6ce5","impliedFormat":99},{"version":"b27242dd3af2a5548d0c7231db7da63d6373636d6c4e72d9b616adaa2acef7e1","impliedFormat":99},{"version":"e0ee7ba0571b83c53a3d6ec761cf391e7128d8f8f590f8832c28661b73c21b68","impliedFormat":99},{"version":"072bfd97fc61c894ef260723f43a416d49ebd8b703696f647c8322671c598873","impliedFormat":99},{"version":"e70875232f5d5528f1650dd6f5c94a5bed344ecf04bdbb998f7f78a3c1317d02","impliedFormat":99},{"version":"09b103d94e6bf3723cc3642b164dcae50bea1d1f0ab1f5cccc38dfed3fb2beda","impliedFormat":99},{"version":"e3c2cc25f470bea78afb3af5ca6f30759cfd44e4b1212e84657fb36e45a3a1d3","signature":"6e0fa1db91df6484a033340dfafa435f2e98844b66e876a01f963ff84a878646"},{"version":"16bed64f9c23abdf4e18425ab522343d5c8f180214832e5b6d40135d838f7841","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5e6e6f01cb776da72cb3ca11adf3fa20c16d0f68841c2f550e485376491fe584","signature":"981a1e9bb280cbad4485d10bfb76e890079fcc75cdf11f502bd995e1065d2616"},{"version":"2e0902468e1a220489a3f33dc82d5eff8f70521cc4ad8eb35abee7a792a14a6f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ca55e9c482d5da0295fc69d21ed6822af32439b9fc3b1fc55ab593deb4a83880","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a0395b4c83044d52eb3954c29d53ccba5aab9acf9765dbe663f8f95783629609","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"abb13a376d731db2984464da46235b3dd198602a97e200bf687c9a9a2bb43593","signature":"909a9f6b4a08c0af15d0c0e3cb1f290ccda985ee205dadc0c735d3bd1467d5bf"},{"version":"1652f9e9c84699da6c0050e0818ffbd6093fb9dc67e082bfb5d3df1fc92012b2","signature":"e7ed13b72a29fe7f0c628bb06ed80ba591e327268a8acf0cb66fa1725ce5e930"},{"version":"ccb92614f79729906fc8a9f5c9c18f163c1352d2e549cc49b42c4302fa591f30","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ec90498fea3cfaefc1dc5badcfa5d2c8f05a73f96abb856d63707c0cd25351eb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"eedbc489481913977cc00e055bd0f493c8ed3115928ca006929d1b353411e722","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d1635afb57a6aa4a470f3b256348826c36e42594db09683647dcd00391b2521b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"73a98325658b7d7509ec766ae0f0b34b9a4d9819b388d4e5b3c74ef8af5af185","signature":"211206a1cc64d3623a54c919fe8e7e8cb70032936ce7dd2625ba2f185580334a"},{"version":"8072581b3b7e9ce43d9553465431ebc422579042d0a644394d018c6803c45918","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"938165e53c76725415bd8152fc8363d628ac4a95e04d4c2884f75349e3d337a9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c868f50837eedd81fa9f61bd42de6665f74e7eb7a459135c6a14ac33ddc86798","impliedFormat":1},{"version":"42cf6b642a67b27545981d06932f7e5ef948a68dadf5779cdfa9e052e3a13d76","signature":"41302973852bac2a0d545eb886ea0b819803722d9d6344a011477d235854894a"},{"version":"cb61a5aafcdee23a7ccf20343670924ee6cf6ec6f631b65a3ab249e27d9db542","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d2e1e7f9ea69da6734503f8b7077edde2e9fc91596141725e2beffba76ea2ec3","signature":"0f87709207a3c70d4c4dd8ca7a866e5114b412c6629abcd9f4bac4a7b91495e1"},{"version":"1e3bd35220cea102b5a84d579f9bb1adf4dc20dea714829473bb3aa87499a64d","signature":"230d47db97c6f501ec507c267dbecfcf25a3a8c8c13854734008f4294a0da41e"},{"version":"ad3e839b384c5231de4906ea0d62e778d95f1e46937d9c005487b0897ffc48f2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ebe9b7b5b1909551f7fe8a5aedab9f4c713b928f5ffeb7b83c9ac876861a74fc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dae983fc2e940a628dd197d10e67ca9cdaa071d87d7018ceb8fa5c8a690eccf0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"eeb5551958a1e9c5493e02cc7a0eaa112e946b7590a018f1bec0e29de91a64de","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d6fef48c89f7fc4804cd6056fa0e5495a24ff66773aecbce8fc3445564796888","signature":"a10492e4eb6f7d58638216495d74d70dfe714fc6977cfa5510ffb772d56a0c79"},{"version":"eaea9c64fced041f983824a099b8212dca0270f05c328053ab935b9a7546f0ce","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"29e02239f94241d9f26f19f570a5eb688c86873d1e77e43868fd69f6a38e771d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"681ab80103a45e835b91035d733228aa210d75cb0cd45355dbe6e72fcbe1806a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cfc4d1e3e0f3fa0a4a3de8483598fe4ca1f9677de760b092b4919748ca383fff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"aeda61f5b4aa7621f64d34f54d63b5d05051ad117ff705e19069b5ede8e50a16","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","impliedFormat":1},{"version":"ac450542cbfd50a4d7bf0f3ec8aeedb9e95791ecc6f2b2b19367696bd303e8c6","impliedFormat":1},{"version":"8a190298d0ff502ad1c7294ba6b0abb3a290fc905b3a00603016a97c363a4c7a","impliedFormat":1},{"version":"5ba4a4a1f9fae0550de86889fb06cd997c8406795d85647cbcd992245625680c","impliedFormat":1},{"version":"57c98d540df72bdc219a9d4e23e7c1f844459414e4e62eda3439067fadf743ba","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ef4a6c603a685a8786368a221e0fbabdc221135d6b59eb1ab442a1e0d260da42","signature":"b5744d0126cbd200c1bef21bac2f14fa3223bafc24178f26d27d834c9b4ce7af"},{"version":"723d1d05be7e263d358580c9bba607944fdf6e5093e7bf62a2f578754b779390","impliedFormat":99},{"version":"7a59476a46fd4b3e1522e9c6ec6cf436b6d5ab8ac97a17ae867aeb9cdf0371ff","signature":"57f1ad6cd433ecc0e78e4616e780d4db68642604162b65747c70a6142d28e49b"},{"version":"ff3e228e751934dd42a9f05cfd75bccfedfb529eda504ee0c4f0d184da345050","signature":"4a1201a691800bf407a2703017b769c5ce1a53418279b7682e4cde1afc7dc6d9","impliedFormat":99},{"version":"ee70ae40394baf9312c35363c42fa429ba3e037ab10cf767a184ec38d24b5427","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b5b7c87e72f384980ca1d92c4f54d6c30b2f099556e3843588073cfe0a0a893f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f329dfad7970297cbf07ddc8fce2ad4a24e2a3855917c661922ef86eb24dd1f1","impliedFormat":1},{"version":"841784cfa9046a2b3e453d638ea5c3e53680eb8225a45db1c13813f6ea4095e5","affectsGlobalScope":true,"impliedFormat":1},{"version":"646ef1cff0ec3cf8e96adb1848357788f244b217345944c2be2942a62764b771","impliedFormat":1},{"version":"c864801e02e8547ed49024b3a469d6fbf600ee240be6bf413bd6149f26241348","signature":"90ec9100c29e008c3d9194acd818e2cfa6dc6e177154bc8e10c5959aa35619ed"},{"version":"6c8c958cc35f90494284a36edeedf503f3a56a93960016a618a1e587d19d86c4","signature":"2b02e2635e94d92d8a4c1fb05177aa1f9bee04c362dc8600559080aafe963e14","impliedFormat":99},{"version":"9c947051913ac9feed2de4ec57656a9f38ef4bccd22518b765f5877c69894082","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0260838d2473bd7872f0fcef24bdfebc247cdf5c95217670ef50931bf93f2e91","signature":"2affb08b140b8e89210e4b39ed75b00cd5e5ccc3553a80bb3e83514fd2461e7b","impliedFormat":99},{"version":"e9e4ac4ee6a2c612f408e17bfd9bd5398bab08053196d8e8c6cf64d8a7335a51","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"15128feed70d09b1e4f994cee399f093af7c7c42e224db77f3ace502a457a2f2","signature":"c7108b0b3c30b5aa5fe1fb0c2399dbe7da3e7730cfdd42e7403a0402394bf466","impliedFormat":99},{"version":"3bbf19210a7e08f50ce1518710ba0ffa8e13a0d55d78fdf3cb62cbad44d30e1b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ca273ec7d7789662ab7ae9e00a4556a0e42416d6f8a13702a5746b5ea6862061","signature":"dc89f83d1e61d147d010a811cad4539c273b3ed227aabfa8a9a130b4180d2cd0","impliedFormat":99},{"version":"2e71945b350a81ff50fd4e21a3660e7e6055a5cd5691d6d8d867d0e6f10cf313","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5d0e499d96f070dc2607f23d55fe54ac074d1a840b6505e1978f70f57232cf7b","signature":"9e4d212471d83031de81b7c76834be81b4d32b5eb573cda6c61023d1cd5f326f","impliedFormat":99},{"version":"f20e59aa1f8ad6e7dbfb10f7c7147773dab8b5d8e4d59eeeca34944b51e4dd14","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"84daf1ace1a44a36500dd4dbedc8a92e50c4a1c1e935ef732dd18b7e2fb0aaf7","signature":"5cbfab9a555788720d027df70fa580bd727ad40aa2d325eb0b04ec4642f9faf8","impliedFormat":99},{"version":"1784d27f3095418bde9b61739c7ca7bd30b1bf05c95bde803514bfe48ce23f57","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e62fecd6655ce82858142ac7225caded25ac9b7da81632bec4c7c054983bfc68","signature":"2bcc2d03633b291af104b7774e1f7da0ba4dd09809fceb39db956b6a7e127ae7"},{"version":"fe93c474ab38ac02e30e3af073412b4f92b740152cf3a751fdaee8cbea982341","impliedFormat":1},{"version":"3255b97f3f24af29c79cc1aa88004efb13b6285ebdde0a567bf32e19bb65250d","impliedFormat":1},{"version":"1e00b8bf9e3766c958218cd6144ffe08418286f89ff44ba5a2cc830c03dd22c7","impliedFormat":1},{"version":"8589487932fd916840218fdedbb143741d22216ddf630c70d401ee674c448e1e","impliedFormat":99},{"version":"c0fc68a185e7479c68bb3304bf208d87e9d8bbe9a684302d06c40245670cabf1","signature":"42288bb7189ed22d6ecbacd5477ddce0e5ae1fbc1dfe48b1038c58af794199dc"},{"version":"31caf28f1dc08edfe3cb8e7c4011ced09f4bf3f5f725c50e5dc0b5ae573b498a","signature":"04a996928d0f8d5efd87a2990c4f4ce70e00fd0c975971fcbc570df7961daee5"},{"version":"502c011687aee1a48fa221d356f8f2d8eeb035c0706e8f8e9ef0104660cfc51d","signature":"1a734856e43cee0599e8a537f131cbaa1e9290b47f2b496fb504f95e252b8495"},{"version":"1a7163e59864fbaa14672752a70c8b38086117e5a14afa00893611dfc2fa803c","signature":"01a977ade994fe0de990222140f158a0dc3b03529994c449aa39333d0facac02"},{"version":"348b8169a6c19556863ffe85bf1fe1ddb0006affc951bee6eeb7dcb3a2d6eb30","signature":"f12359b22cbaca86f938ddee38c0c33924e768a93042ad939fc2288f2471e5e9"},{"version":"43ee1831235987ca593e76b22b4116009f1ff6fb0e7a3fa6bf1e5df1420fd6dc","signature":"a05af3719b211bbf59b553f0760633dc3095778bb0171502d7bb7342a54d3b15"},{"version":"b2e86ee920713d5b287b9b175ebad35f82e979a456a9b276fa16bd4df708bf20","signature":"138cf0cf601045c4741faaf79f2d7024eca30e59e3b533a4d60e01a99209ad68"},{"version":"ddd3e5c54eaf7ed7710f0b95669f32c7b1693bb3289a4d2d559d03de0c62ea39","signature":"a53ef0141acd28c738153d5b7b9c637142cab2fe0db1dd0d6a55b06aac99925e"},{"version":"45311c218ffe1c8393be29ebab04527a9167c2e48a5fdb15adc0f22cd541614f","signature":"dfb3bb27e47ca92752033b3171dbe6a1f8e9404b34577d1b16eac221e1745a2a"},{"version":"093616375ac2af574eac9fdfcd18193c3f9394e1b1d4d8c79d2e6068790ac100","signature":"335f66607c072668fef3e0563ad5619a7632e75eef9f85740e84e35523d1ee82"},{"version":"383fc1e3823bc2d2cccdbf51be644b7f2297d6d04190008c1ef7ccf82eed9b76","signature":"77658513755ac8d8ad639e6f969539b6d98cdc9ea85a2eabeb33fc94a839f395"},{"version":"eb2ca2e825628da3e61a98cbf8338f9af1da351244346ee4955b09972d60e7c3","signature":"0a759888cb435532132e0066d5bac2f2786bc7160a24f07e7b60ee958d45b88d"},{"version":"2aff6fc2089561043f6978a98e05783fd6fea921b6068a4b3dd36e824221cf0b","signature":"0f2ab2d398a5484d267cfbae7f4512671debe1dfc0056d474a6d6add63a148b6"},{"version":"a3e0707d4c2d3cdc9afc01b85b6503f0a9d52d5910ac5766dca6236f401cd6e5","signature":"257d9dcd4c3e61e552ab3f34ead65de21367da6f92f9d62979a26fd748982849"},{"version":"6b43dfa5e9c9d89bcaca0ffe7da88f34e20d760ca158398a3276cef61f738c4c","signature":"410aae1dab008177682aafcfbeeb27bb71cf90e2644309d0473a9f4840d460b3"},{"version":"4bbaca1506dfa9d1c4075c7c10b707c2349f2017f751e32e6bbbca1f7e11e2c2","signature":"511d2ed9fee12f9ca9d9cb4bcbe6b0ed455f5da5473733413da9b4d10d916a79"},{"version":"63d9dc36da9bc05dfdb5ccf23b5738648c073c545320dbb619c6b0b27ce304b3","signature":"859b36849fa1a6871f9dc68605252132a625792e315c5e58d893b28aff84c7c5"},{"version":"7f35d5d9a42febaab1e7079785cfae8c9f977d777dfe871730f0a360c254a356","signature":"0cc24adad526e7c075f3223582ca642a555751bddc0088d47a1fe62ac19ebe31"},{"version":"bc7bc237e289f8d435d34601a22322d303d64d497e25d80d555f06f7acc34e4b","signature":"7da246bb1c2b2ce4879114715c5bd7714bef80824031c70e814efa143acfdd51"},{"version":"38a9567883eed6a8174f841b784734b949c25fa1f913d1b96eada6bc083c160b","signature":"f4ad04f8b4b9e4ef78fa12961f2c36f0032035ccbac259ca95cc05fecb70d3ad"},{"version":"9a7b469bc32fae75951dc069e760b7945d91829873247f00a5ede47eddfc5d2d","signature":"acaee283946e562a6a4f999558a47c3d5110e5e2ae0581f90b5d2d4e35dd74cf"},{"version":"5b8eb6e16859a5d0b869e2607f6510cbdb93ff3b24942edfb5098f2e6b07e773","signature":"03b23eb17ac097b361cad4f90128b223cfc584893f86a350ad9337aff15890bb"},{"version":"983793b81b9d3f63b32a2b4aed4cbecdd215d0c00487729c5ee788f9d8a77c13","signature":"afd02efecb9f6288c3098659c94182a2d6fcde4620ebda7c2aa229cc5d2c54b1"},{"version":"89121c1bf2990f5219bfd802a3e7fc557de447c62058d6af68d6b6348d64499a","impliedFormat":1},{"version":"79b4369233a12c6fa4a07301ecb7085802c98f3a77cf9ab97eee27e1656f82e6","impliedFormat":1},{"version":"2b37ba54ec067598bf912d56fcb81f6d8ad86a045c757e79440bdef97b52fe1b","impliedFormat":99},{"version":"1bc9dd465634109668661f998485a32da369755d9f32b5a55ed64a525566c94b","impliedFormat":99},{"version":"5702b3c2f5d248290ed99419d77ca1cc3e6c29db5847172377659c50e6303768","impliedFormat":99},{"version":"9764b2eb5b4fc0b8951468fb3dbd6cd922d7752343ef5fbf1a7cd3dfcd54a75e","impliedFormat":99},{"version":"1fc2d3fe8f31c52c802c4dee6c0157c5a1d1f6be44ece83c49174e316cf931ad","impliedFormat":99},{"version":"dc4aae103a0c812121d9db1f7a5ea98231801ed405bf577d1c9c46a893177e36","impliedFormat":99},{"version":"106d3f40907ba68d2ad8ce143a68358bad476e1cc4a5c710c11c7dbaac878308","impliedFormat":99},{"version":"42ad582d92b058b88570d5be95393cf0a6c09a29ba9aa44609465b41d39d2534","impliedFormat":99},{"version":"36e051a1e0d2f2a808dbb164d846be09b5d98e8b782b37922a3b75f57ee66698","impliedFormat":99},{"version":"d4a22007b481fe2a2e6bfd3a42c00cd62d41edb36d30fc4697df2692e9891fc8","impliedFormat":1},{"version":"9d62e577adb05f5aafed137e747b3a1b26f8dce7b20f350d22f6fb3255a3c0ed","impliedFormat":99},{"version":"7ed92bcef308af6e3925b3b61c83ad6157a03ff15c7412cf325f24042fe5d363","impliedFormat":99},{"version":"3da9062d0c762c002b7ab88187d72e1978c0224db61832221edc8f4eb0b54414","impliedFormat":99},{"version":"84dbf6af43b0b5ad42c01e332fddf4c690038248140d7c4ccb74a424e9226d4d","impliedFormat":99},{"version":"00884fc0ea3731a9ffecffcde8b32e181b20e1039977a8ae93ae5bce3ab3d245","impliedFormat":99},{"version":"0bd8b6493d9bf244afe133ccb52d32d293de8d08d15437cca2089beed5f5a6b5","impliedFormat":99},{"version":"7fc3099c95752c6e7b0ea215915464c7203e835fcd6878210f2ce4f0dcbbfe67","impliedFormat":99},{"version":"83b5499dbc74ee1add93aef162f7d44b769dcef3a74afb5f80c70f9a5ce77cc0","impliedFormat":99},{"version":"8bf8b772b38fc4da471248320f49a2219c363a9669938c720e0e0a5a2531eabf","impliedFormat":99},{"version":"7da6e8c98eacf084c961e039255f7ebb9d97a43377e7eee2695cb77fec640c66","impliedFormat":99},{"version":"0b5b064c5145a48cd3e2a5d9528c63f49bac55aa4bc5f5b4e68a160066401375","impliedFormat":99},{"version":"702ff40d28906c05d9d60b23e646c2577ad1cc7cd177d5c0791255a2eab13c07","impliedFormat":99},{"version":"49ff0f30d6e757d865ae0b422103f42737234e624815eee2b7f523240aa0c8f8","impliedFormat":99},{"version":"0389aacf0ffd49a877a46814a21a4770f33fc33e99951a1584de866c8e971993","impliedFormat":99},{"version":"5cb7a51cf151c1056b61f078cf80b811e19787d1f29a33a2a6e4bf00334bbc10","impliedFormat":99},{"version":"215aa8915d707f97ad511b7abbf7eda51d3a7048e9a656955cf0dda767ae7db0","impliedFormat":99},{"version":"0d689a717fbef83da07ab4de33f83db5cbcec9bc4e3b04edb106c538a50a0210","impliedFormat":99},{"version":"d00bc73e8d1f4137f2f6238bb3aa2bbdad8573658cc95920e2cdfa7ad491a8d8","impliedFormat":99},{"version":"e3667aa9f5245d1a99fb4a2a1ac48daf1429040c29cc0d262e3843f9ae3b9d65","impliedFormat":99},{"version":"08c0f3222b50ec2b534be1a59392660102549129246425d33ec43f35aa051dc6","impliedFormat":99},{"version":"612fb780f312e6bb3c40f3cb2b827ea7455b922198f651c799d844fdd44cf2e9","impliedFormat":99},{"version":"bcd98e8f44bc76e4fcb41e4b1a8bab648161a942653a3d1f261775a891d258de","impliedFormat":99},{"version":"5abaa19aa91bb4f63ea58154ada5d021e33b1f39aa026ca56eb95f13b12c497a","impliedFormat":99},{"version":"356a18b0c50f297fee148f4a2c64b0affd352cbd6f21c7b6bfa569d30622c693","impliedFormat":99},{"version":"5876027679fd5257b92eb55d62efee634358012b9f25c5711ad02b918e52c837","impliedFormat":99},{"version":"f5622423ee5642dcf2b92d71b37967b458e8df3cf90b468675ff9fddaa532a0f","impliedFormat":99},{"version":"70265bc75baf24ec0d61f12517b91ea711732b9c349fceef71a446c4ff4a247a","impliedFormat":99},{"version":"41a4b2454b2d3a13b4fc4ec57d6a0a639127369f87da8f28037943019705d619","impliedFormat":99},{"version":"e9b82ac7186490d18dffaafda695f5d975dfee549096c0bf883387a8b6c3ab5a","impliedFormat":99},{"version":"eed9b5f5a6998abe0b408db4b8847a46eb401c9924ddc5b24b1cede3ebf4ee8c","impliedFormat":99},{"version":"dc61004e63576b5e75a20c5511be2cdbddfdbcdff51412a4e7ffe03f04d17319","impliedFormat":99},{"version":"323b34e5a8d37116883230d26bc7bc09d42417038fc35244660d3b008292577b","impliedFormat":99},{"version":"a5dbd4c9941b614526619bad31047ddd5f504ec4cdad88d6117b549faef34dd3","impliedFormat":99},{"version":"e87873f06fa094e76ac439c7756b264f3c76a41deb8bc7d39c1d30e0f03ef547","impliedFormat":99},{"version":"488861dc4f870c77c2f2f72c1f27a63fa2e81106f308e3fc345581938928f925","impliedFormat":99},{"version":"eff73acfacda1d3e62bb3cb5bc7200bb0257ea0c8857ce45b3fee5bfec38ad12","impliedFormat":99},{"version":"aff4ac6e11917a051b91edbb9a18735fe56bcfd8b1802ea9dbfb394ad8f6ce8e","impliedFormat":99},{"version":"1f68aed2648740ac69c6634c112fcaae4252fbae11379d6eabee09c0fbf00286","impliedFormat":99},{"version":"5e7c2eff249b4a86fb31e6b15e4353c3ddd5c8aefc253f4c3e4d9caeb4a739d4","impliedFormat":99},{"version":"14c8d1819e24a0ccb0aa64f85c61a6436c403eaf44c0e733cdaf1780fed5ec9f","impliedFormat":99},{"version":"d36518bd617ff673c7d9f372706f241932a43f27673187f2a8472e93c40041c6","impliedFormat":99},{"version":"f8eb2909590ec619643841ead2fc4b4b183fbd859848ef051295d35fef9d8469","impliedFormat":99},{"version":"fe784567dd721417e2c4c7c1d7306f4b8611a4f232f5b7ce734382cf34b417d2","impliedFormat":99},{"version":"45d1e8fb4fd3e265b15f5a77866a8e21870eae4c69c473c33289a4b971e93704","impliedFormat":99},{"version":"cd40919f70c875ca07ecc5431cc740e366c008bcbe08ba14b8c78353fb4680df","impliedFormat":99},{"version":"ddfd9196f1f83997873bbe958ce99123f11b062f8309fc09d9c9667b2c284391","impliedFormat":99},{"version":"2999ba314a310f6a333199848166d008d088c6e36d090cbdcc69db67d8ae3154","impliedFormat":99},{"version":"62c1e573cd595d3204dfc02b96eba623020b181d2aa3ce6a33e030bc83bebb41","impliedFormat":99},{"version":"ca1616999d6ded0160fea978088a57df492b6c3f8c457a5879837a7e68d69033","impliedFormat":99},{"version":"835e3d95251bbc48918bb874768c13b8986b87ea60471ad8eceb6e38ddd8845e","impliedFormat":99},{"version":"de54e18f04dbcc892a4b4241b9e4c233cfce9be02ac5f43a631bbc25f479cd84","impliedFormat":99},{"version":"453fb9934e71eb8b52347e581b36c01d7751121a75a5cd1a96e3237e3fd9fc7e","impliedFormat":99},{"version":"bc1a1d0eba489e3eb5c2a4aa8cd986c700692b07a76a60b73a3c31e52c7ef983","impliedFormat":99},{"version":"4098e612efd242b5e203c5c0b9afbf7473209905ab2830598be5c7b3942643d0","impliedFormat":99},{"version":"28410cfb9a798bd7d0327fbf0afd4c4038799b1d6a3f86116dc972e31156b6d2","impliedFormat":99},{"version":"514ae9be6724e2164eb38f2a903ef56cf1d0e6ddb62d0d40f155f32d1317c116","impliedFormat":99},{"version":"970e5e94a9071fd5b5c41e2710c0ef7d73e7f7732911681592669e3f7bd06308","impliedFormat":99},{"version":"491fb8b0e0aef777cec1339cb8f5a1a599ed4973ee22a2f02812dd0f48bd78c1","impliedFormat":99},{"version":"6acf0b3018881977d2cfe4382ac3e3db7e103904c4b634be908f1ade06eb302d","impliedFormat":99},{"version":"2dbb2e03b4b7f6524ad5683e7b5aa2e6aef9c83cab1678afd8467fde6d5a3a92","impliedFormat":99},{"version":"135b12824cd5e495ea0a8f7e29aba52e1adb4581bb1e279fb179304ba60c0a44","impliedFormat":99},{"version":"e4c784392051f4bbb80304d3a909da18c98bc58b093456a09b3e3a1b7b10937f","impliedFormat":99},{"version":"2e87c3480512f057f2e7f44f6498b7e3677196e84e0884618fc9e8b6d6228bed","impliedFormat":99},{"version":"66984309d771b6b085e3369227077da237b40e798570f0a2ddbfea383db39812","impliedFormat":99},{"version":"e41be8943835ad083a4f8a558bd2a89b7fe39619ed99f1880187c75e231d033e","impliedFormat":99},{"version":"260558fff7344e4985cfc78472ae58cbc2487e406d23c1ddaf4d484618ce4cfd","impliedFormat":99},{"version":"413d50bc66826f899c842524e5f50f42d45c8cb3b26fd478a62f26ac8da3d90e","impliedFormat":99},{"version":"d9083e10a491b6f8291c7265555ba0e9d599d1f76282812c399ab7639019f365","impliedFormat":99},{"version":"09de774ebab62974edad71cb3c7c6fa786a3fda2644e6473392bd4b600a9c79c","impliedFormat":99},{"version":"e8bcc823792be321f581fcdd8d0f2639d417894e67604d884c38b699284a1a2a","impliedFormat":99},{"version":"7c99839c518dcf5ab8a741a97c190f0703c0a71e30c6d44f0b7921b0deec9f67","impliedFormat":99},{"version":"44c14e4da99cd71f9fe4e415756585cec74b9e7dc47478a837d5bedfb7db1e04","impliedFormat":99},{"version":"1f46ee2b76d9ae1159deb43d14279d04bcebcb9b75de4012b14b1f7486e36f82","impliedFormat":99},{"version":"2838028b54b421306639f4419606306b940a5c5fcc5bc485954cbb0ab84d90f4","impliedFormat":99},{"version":"7116e0399952e03afe9749a77ceaca29b0e1950989375066a9ddc9cb0b7dd252","impliedFormat":99},{"version":"3654ba818fbf4ac2c49aa3dbb050b912277acc71b6d5e4f434720c27a1a68f3d","signature":"02d33dd7ec31c9ac3c91582f2d0a3f665d587d5f98aa667ad74d4b543e626610"},{"version":"6f3c420a59fe4ff53cc42fb1aa2b2702fb048145fcb3c26515d3db35e3854e2d","signature":"1f71e9c9d089eec515e086adb2e10e09414ae876ef4744115edfcd57c6684f7f"},{"version":"2a7a18a2cc9b4656d9eb1d5f4fd0e3f3466f600c32ea8148643dd8c909bb3476","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d9cb0facf05859f0f35707063253d8b55d8fbb565afb642c0edbd72ce77817e1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c8924b198de81de4222b2f0b171e9262f80bdf62beaabdf8ee7aa13b27245871","signature":"2f5adff38c8a75301b364bad4bd26f79cd3a86bbdd3cbba4541673d903d47b4f"},{"version":"0702499aa6384244a89e13df162163ed41949de76518a8580c8044e783876dea","signature":"401f1208590180b74cc9007c8a894d499d48b469bb110c769cb004aff4819b3c"},{"version":"534cced4db5dcc639cd555583be09c6891c0633dc395308c87f60b47dd54a6b2","signature":"33ecf206edccc488e96cfb5177f19809e8bbb549ed0e94ff66d1cd1ff1a1fcb3"},{"version":"9f4f376e778fd1560de1f3afa4b8ba1971bb8bc5f272324ea61e65e15b685f1c","signature":"6b211c08718dabbbcb8d48382a8416b20d9c90e3a7a3a9f8dbb192baa29018dc"},{"version":"b16a9573271f151e37a10543a8faffe67811ac8570d87054108ba01799b73ba9","signature":"c274af3f97f26f9143c42701bf431c06ff0af56cd5b14e86c661a294f335d8db"},{"version":"48f96604f28e1d321ea8c94e7e5cc889f4ab3720d92ed9f412ac7dbc2931a1d9","signature":"0d095606a67e17da85041e7a56c4d15c377ff643b56ca69eef8b42d670748bb2"},{"version":"1d899a3b3c762069c87a2363e38fc467d3fc0c17f6d22f98de3a98e1a691540d","signature":"57bf2ffbbff6d58bb1422d725989e22ba10b14159c98d2e3185361f5d609d9f7"},{"version":"3f56d8959b17508732d17ec607714398e73009d9eaec652c8fb9d5891d1c7c7e","signature":"b4f0b3be4ce1aab443b18ffd19432c63b332180881e573f620cdf4d257b5426c"},{"version":"0429300653bc8825a3a00ba906eaa03d5b322bb198eba44726cc305936446bbc","signature":"9da79310a8073ee98ed1de785aa8283fcbee7e7481b71408c5a99cf0e601dfc9"},{"version":"11ad149a0b977ca7a0518fbe321b005ed1aee824ebe4ae74800837a4700dff2e","signature":"bfc2c8f0a629b259b1e101742e40731bf7af0e33a25c1d4de46cf85d129c212e"},{"version":"6463c4d2ab1246111f0c7be3929d40188e121fa90b62bef6468e455ecad7cb1a","signature":"69b5aa3a3485b7780595d1c9474a3103be32c05414d7409b89972d21535c60e8"},{"version":"978ac9ab1977c957fe99662e28e319fd04ebeaf373bc16fa6adfbed404f61b75","signature":"be1719b0c3e5f1de72217c7107e2106c1e3762ce3ca52d3d26db16dc36c52150"},{"version":"33c2eaf9f2216da640acf1814500bdbc10e08241fd17d8e19eaa203c50152815","signature":"155a0ce1ae5dea70b7c62b88bad1d252f860edbe6973cfb381851ae84fbc57b0"},{"version":"7c3c3e194f59da1a744d6d5c1090d144769d025c068b2f84524dbae0fd481d97","signature":"5fc8fcce3719297a5a3c0d9b41aea6db99a3fc963c76fac983b32894e6694193"},{"version":"b38b60efd27019b6a729129142cfe2e6853164174eb3513547c9c13837407e30","signature":"b4f67dedcee400ef1fcf589dc8a3f74a60f77640595d82519762104c3162223b"},{"version":"eca7b0894466635ec8350b68f82f12ad8f5e538a14a2c6a20e26be53f2775a42","signature":"036a6610a766102c1728437ec9454c572a29f908e5cbb80bcd71040c1a48cfa7"},{"version":"75b6d54e60e07ccdc53fd68958e5fb4dffa6e3cef5e13d478ac8901bbdbd814e","signature":"990169bd34d817d6b9bf57e56e3173cdde174fdcce0cbb5b5648b0aa8fc83f76"},{"version":"e1c0d6996b82a7c4eec322c0b62c75711d6f902f75a5a755b5e0cb7ead8bd72f","signature":"db1738fd62be473a0e572c91da37c7966dcca15d00dc7f9694bf78b170275ea1"},{"version":"dfb5c15f9f6428e3f92028355a9b2fff5ba76aabe338fc058f2cda201b866d8c","signature":"a507a22f915815dffd2fdb731fc8fc2d3a0b2fdee404c673c8340fda0317022b"},{"version":"aa40d71dd57a81028c76d4080716d6dde78ff51e92ad1460e5f973adbfaa193b","signature":"b8df9c14d085533e16aaa58dfa061788e5dea6b8d7e3a17ace1562e6d904cd85"},{"version":"955ae27fdc755f32aabee0f82c2db6b3d8505f99551cc8376df389eb90e7c84b","signature":"4675797b0de56fe3c5a6e468df193709c7f066a244e2da0d02690f193eed5345"},{"version":"4eb900416055b66a7063f285dc36561ccd1d276de8a637165beef04a3b3aa162","signature":"ac2b3808b01524a4b3ecc52121b04eb3b74c6d267ad7db0c4082c2934c8da0cf"},{"version":"7abfcb37b73f3b4fcca65caa3cfe40a12b8a89fafcfa783f93605acdddb0cc25","signature":"1a574fe33afec63182b358ca9e29944cbdd13c69413c53fcbb8e924018e33b8c"},{"version":"4bbc8169c9196d2768927f96e35712502c0445e653a5d427f670aac13452f77b","signature":"0112553dbd79407a27c58713ea3d744d72a100f4d89bc8c817d0a4d027bd8d34"},{"version":"6e525359ee1b437f87a241ee6e5197cbecb25d0a2a575840886e4f0ce4d27017","signature":"ca5e8662d40a41d49018ea294491f822a60db9fccbaabe8281ecc138a9e955d7"},{"version":"2344ae91463701eaa9402c073e769bd7a70557449fc13c0bc4d52ea2d95f6d6f","signature":"a87a0cc69a8844e8d64858edeb2f5b9defe9db4b03f6081c3cd27e2979067a80"},{"version":"964e2cf748eb23916c541b47ca7aa8621f6f99a9da2ee2b53cf62fafa784243a","signature":"7e126f3b1bbbca7fc0092253c0cf8cda8ecaebf818d91bb35bedc35e19f75fc8"},{"version":"e99bce1c616138462e9ad01d669d9667759a66a549795aad43cbd8d3829eabd2","signature":"bccd3d911c3cb5fb8442848a10723e7ac2fba4a94c37c8fb2700ee31645b28e4"},{"version":"88cdbc3bcb4689a70130597de7c941b2450bb2760674a02ae816e0667a1958f6","signature":"6dbaf13dab6dc2db0cb7312fba7996ca7f548c7929bb627315cc89b43bf93ada"},{"version":"93fdaea06f53eda94b236d54909091dbd7046bc96315b59d224962d4a95299da","signature":"71e0ae0ab79469ca19dcf4d5566240e5019b5a59f0aa86a6d1b5afee5edac2b7"},{"version":"5437c086fa05daccd0b205f10e71c34f7a5c65a60b70c449a77d71c547777399","signature":"6a891a6cb7835fc4aec6da5acfe7e699a7657630100a6eb7670e371e5a4d2ea4"},{"version":"9636142d048623bb863eed744dba8c1aad681a8b29574d438931ce5c3d465c2a","signature":"ba520a1e00fa71ff0e6e101beb05e138cbb2dd908976441ed96beda042500f77"},{"version":"55a9358103d4d3fc812e7226cbf54ec885ab5d274c3f0fd7c7b89138761420fe","signature":"09ac449d6431aaeea979c72f664e0179ac698d66b9dd695199bcc985f21b10b7"},{"version":"a64240132dbb4eaab110bb3733845bbfe14bff96b09fb6d46a64232c8dda2b1e","signature":"476b4071f1aac8d5027274bfece00a4fb738c3caf9cb2a033600c06edd10a0f8"},{"version":"9078a75f8fe253aef64d238341117aa3019d80988b1a6c58b49495a42b392505","signature":"4c3c517995254a3515b6df45737e5ce8e1d8debfe98dc634ba28ec324e591163"},{"version":"cf6b889353036a253a6fe8ff0db6438a559bf559d9dea64dce99840eb83b191d","signature":"56e9849331ad4578371de0e5c8b3203f930d0d06a0d62f5209e9c17e41693bbb"},{"version":"f2ca6f145f319cbda91e6d4b0a05184127040e7471ae54e22c4c13c457940bd4","signature":"c44b0bd9da5f7907a8f132f289ad93a7e7b57a9943b661612900ff609dc8ebcb"},{"version":"99a509f3f5decfd71596dc886af5c93bed0ab51a1e69855374290ecf66636dcd","signature":"4edde3cd15f3e6efd0e5d77a9d8b78997e2faf00a87f5741161b140472c267b0"},{"version":"f72683c914bd21a52511b4dfc3f25f622bd7ca5dd0c1c0dfea0c747f7466a7b2","signature":"82795623788e3260d9c6ee7f093c27b61c7257c31135e0bc833258ebfbf21f25"},{"version":"e86e3055a12f91b39baec8443daee70ec414ba65a82282c62163ddd6e968e45e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2564e83977f854fbd3ce140f2d86f6992c1332945634a2f803306596ee0bf69c","signature":"2e3b8ee6e5682bd9e7cad45bc2a5ed071302f74f8bda226965fa0693fa761f16"},{"version":"b66cadd5b2da034134f0112a8d584d76eec9e8025f23eb6b556dea5aa74fe3a3","signature":"10284337c30baf75130ecfa1c52aa566eafbbcf0391f1bbf7e21cad62835a0c9"},{"version":"3402b3070b7f9a2c6ea7f3082c2ed7f2f0d8c589badb6f3ec62044c3b7f0184c","signature":"6f3a722497ec70b05e83ac5087cc5bee72d7b19fc760554762b40072bf77fde8"},{"version":"dec924e208b541616842e85b39e55ef79d7a97062654794b62d56622a67bb974","signature":"ac6b01a79d5ff4dcd12aba55bb4ae5b0886bf9246467659a3bda4620813147bf"},{"version":"913dd719a5b5a91dfc16f29bbb6af8d1f8aa0f2b141eb4320cb2ff4f973bec35","signature":"18e3bf7eab3bfb15ddcbd0e06c36856ace9c7bb9b7a179505fd0f7a9f15b5c38"},{"version":"a4150749c6aa9db1224cefcb07931a35d19f1f8f00f4b79674f6d25c5423f181","signature":"6364708272ae524befeb1cf48d39cc0539e266b6062b8d26e89d41f02afca5fc"},{"version":"5eb87fa9a117af0d5672f63271c070d9294dc2592a8a71c7f67dda52635bb8d6","signature":"71f5409f85ed8b4c3910cdc686ac98abe30d2807197945e3844dc7bf5b9d6479"},{"version":"132f976bdb7a85c0fc4a180cb4673d199394e4b38feaddeae1d0939c90df34b1","signature":"11794a33220970f2e2b523767a9724247e154a37985d933321a00b9b31d6223e"},{"version":"2fa9260ba8c9c073651025b09d81b2da143ed8a4d7334d20a0f7f7eaca3c3ec3","signature":"5012e56859c8f84faba4532e014d0fb50542726d165cb40e5f1f5d2207e1d465"},{"version":"63d2523c2cf8126e1a3c3a11bb79e28fa66c6f816359bd57b87688afd9b3d350","signature":"5307c1d3d1cd0c5977d376721cded10dc36d16d06d1e83cace7e70cd20e49b08"},{"version":"33f4a53a232f13211090183cb571ff2fd60748eba70de4252d62cc6d7a17f1f6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5f2bb4ac13713c6726c29cdb23be59f025c6d9b2d9a22729187be2502a069978","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"311ae6055f6a3f6c98a79cf2f957dec6858d5ca3c4167f403ef2249775f0531e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c742723bb689a361dc0e32cdacf7f4160145254716deb013292a2f45e6f5e1ab","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7b6e8d32728e05107c573c5dc2b6fe9cb14332dd7c82fb530093a840d6b59dc7","signature":"22146890ab30bea45bc289ccc48192249fe1cead53510eda7d9af2b09e065189"},{"version":"7a81b4127658262d3b44f32ca2fb5589bdd370f3c971b7023ec2bf0fa80208f3","signature":"0774366c811ec1c799b0c0922d3a58dd6e81ab902ff9847ef804b1cda0b16cd4"},{"version":"0471574e07ea402de091b23741e7759f0293fc476645407c75e8807fce4d508d","signature":"d621400249ba8e7421459928f59ca558d53c61417f4d07833f994947592fba99"},{"version":"54a60dfbef03a8f34a21a1b21e6f8c6b991390b6bdca741071f0e9aa378b4610","signature":"2e695038b1f0a6040ae88a1a869e11cc466e03a7b13b526efa90b3ebfcb0068c"},{"version":"62b5adce04fcafc2bed80017c0b0edce1ec7219e0d019a8e9c6c8476906dfd80","signature":"b86d5d8bd5104f1ab29d23cd5be61bc514b8146a091257366678f5d99000a957"},{"version":"503b83a8c33ffdf3a4fae4b560df55b7e98c0722c4ea69e32b7e71427888f440","signature":"4d981d6aa5d8dc5af9b343ecb2c5f4c5a9e1e9890c31158e049df1c31bbf7a72"},{"version":"ea6437c6eda871607d5a01adf7cc5afdcd66f674509289cf2c226cc8b9734773","signature":"6ff5a08113fb520f023cd78f8c7151bcccc3824aa41cc276419d8f031e790082"},{"version":"61a5d2a3dd261b3c2b751c713d088f6548be6199705c2c9c5775d12bba1b8fcc","signature":"df57aab767d70420721669a994c9995859df1ca3188599bd5d693061c9a20367"},{"version":"24863e2f4b2b1bb3a3450294a76b5e0eea7b3a2e295f2225745da9c8592ee216","signature":"861096a3a6ca8f6ad72022664dd68b02ce3c37ff2d8f05354e1cd3fb3342b366"},{"version":"ca5c0df4cf20a1e1a7b2961248f35767785a03058ed250be5afa76c2713b202e","signature":"5f733c3a82d525121c2b95a101038ad31ca21b64d9ea2d5841bf71a2b5a931a5"},{"version":"9859454fa6df442ae16cb0ac31d0c02a0a85bac28c82b9783e8f370adb33b245","signature":"f6b87832d9447b2e9d26a9676efe78dc75cff9279ee64a499f3e4360d22f2730"},{"version":"583137ad8d520191737844c217f6e5d839105c7ec976abbccd46060ed8cf928b","signature":"213e8f64d2aee549df8047a587e27018fee7674c72407c2a191a634d8e05ae4f"},{"version":"92f78731c5130df45847dfa1a46a00a27686891e38ba51f116c586e520498ee7","signature":"5782b5f14e5b6835f9effd28f0e567b0b5e5c6901453140d43d5f88f07c9928d"},{"version":"25cc87856525e88d4007f5f84251a00b6c47b90fb435ad8459037f18ba6b8a11","signature":"55dd73018b5b47f33dddfbd384f86789d5d3a081b0bdc1fd2fd0f81e1e4287b4"},{"version":"ce0d61b977618ef61cee89091bf0bc0ac139c64da5b41080486c84f0002e755a","signature":"ca04aadfa23178ab9d04e4e66d60d149595721be7e7b6bfc49ca32bacb93ec40"},{"version":"be2f617d92b80f8cc4e567b59cae553cecfa618a81b93ffd974ee7f2a94ecdfe","signature":"bcbd39c8414cf019ff5752da5e81763bdc747423be425b5f6c7b1a6233076f92"},{"version":"727daecc99427aba51aacdcaf9649bbb8104da32adfc2f0eb01ed3347a173078","signature":"78335135acb2abe0a697de3e5fec33bc89954af4ff036f25bb26752170b1a755"},{"version":"4cd146d14339dd6298c8a257f30e55f207f32c3709deecf4234f32309c2cd23c","signature":"9168d159f9f1ad6b869e06877eb05d2b3a544fb1fdc76b4003ff52f5ede6992c"},{"version":"f016ec19b667fa817360e6f772723b590c5a507b5cbc0de0a53a9635f1846a54","signature":"1b866cda605b122b9310e604b6a6408a903636b8cdc7a0613aa9f9ebb6c6e2db"},{"version":"f264d234b8645ae1bdd723fdeb71a0314d77e06d8e7f6aabeef77c6607acd56a","signature":"411c558ddfdef650e590b3eee15829ff8efc820b6456dde9bacdaf8a19b9385e"},{"version":"a47d50bd2f57719021eb5184bc1314ce3f5837f2f78c4d25858e15c721e07ad8","signature":"516fbe6606f98a2736d92faf0b928b6f1084ed15368ba3cc8f055ebec38fb818"},{"version":"b26e8bf9c6f7701c5fb76c46235e05380573408867c4d57f68000bb3f543937a","signature":"7f80d74fc54976e64175d4796a5077a43b3cd982619d82adc5dac2a996e6a3d0"},{"version":"8b726542035580da854bccfbea23223e0fdac7df070292db0856bc04cc3989bd","signature":"c86f51169bdd99d2a52f43ff7126410a099a75cac62538f1c1f78e4fdaea824c"},{"version":"96164479311e65dfb12975f7cb97fa997328e7f94a0174377f4d6b8884e9ff83","signature":"a107421e44626e27aee78ecbdcc5e93e37b9addb0f6761d5cf2041e5c249f5b9"},{"version":"7ac8e07828dcc1a5e01fee4cc13c788dcbdce430795ff0eab6d39e7b3c095254","signature":"533c37afc84f4a66e5d320a3d8bd4d8fa4d7756da0712e42abc776962f08ce84"},{"version":"0925aa935fdf941309523ecfdc70cfd5650fddf52edee72d25f53aa303fe0f43","signature":"14b994430a17c83325fae751d73b4b91dc638fb2a13b138935416812efc5b08f"},{"version":"f71d2d69ba2857a6ba490861f2ee808e7c362499c19e5f2fd350d2e48b990d93","signature":"fbf6a03985783bd32574b3166c2a0e9fefaef80c4616e8c1a709ff554cab7be0"},{"version":"85a2c2bee88d22f7366d94f7d7b2c78f36d1d67945e7b88ea8348175cabf3a65","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c09242a97ddc30a0ed86ad6e481998869972d43d60027ea2dea569dfc4ff79d6","signature":"0fe3236fcc755ecae3aea84e78a420d59c851fc19f1623254decd6408be9747e"},{"version":"15f6b22a1a9dcb5d6ae6b4cb465b0c628f5d065489e0250ce46921de4c343df6","signature":"80366674fad0d2eb8bac45ad76aacdf3112cabf2e032fee7755a61ee0fd9914c"},{"version":"ed078b6e6e7eea82b93d3e16aecf4e5264db34569ccecf89ea244e130a0fcaed","signature":"92c9c93878f36fe51e3431455c359340aeacd788cd1f7dc1ba24faeb4fa87d3d"},{"version":"1d6bf45b076d03144b3058c0df777f1efec117c18e32e691f41bd9787514eea5","signature":"ec384f17e55f9991111747d49fc1dec792ed0ef8f3780416b3bfd79f4f2178d2"},{"version":"0192d556d58c264cc8f34e445f124160bf192380717c784cd88c27349fcce061","signature":"dfaf8ce103eb00ebc169bd1cd3e26987962b4da62bd249268a3193f0a7b9f688"},{"version":"7c4257d9e5829e9da31d3a15b30507ad7abaa83062fb7b54bf4a406217ff8dc2","signature":"3ca35b3c39d9a46ce3eba317f661fbe4fdf88afe33cb8615f00ea04adc902055"},{"version":"7c7e71e5e39435b48e0271eec28ab242ed6f1a65e740a29932cb83b9e617c83e","signature":"321ff8aac5ff81a75d851738cd323ae2ba1c54955901b7ca936485d93377bf92"},{"version":"cc60fd980e5701b006200ca499fcfc09b7ac317785fe53307bc9a50fc4bec464","signature":"7caf7749ce99278db7ce5e5cb505f29d838da91038eab7447336688cb42001b4"},{"version":"f989d2403a82ca13a448a70d7fbbf3e31542848ce822368c0a470dc148d231bf","signature":"0fc0c1e35e42c295ddd822600742ad7b6d8469daa236585225e2cb4416abc996"},{"version":"d0b7e2a5548f56597acc899917e354c348407549ded42ee13332c83b5c045bfa","signature":"f0cb4703a6fe127422dea8d27cdf77e8bd0f58b380945bade496723a537d8832"},{"version":"26828d7ec809e32ad1bfac8f039b8d2bd214a648a679cfd522f550e1a77a8b6a","signature":"3743762554f6bcdb60b48a23d63898d7c2906b9b64917b05cdec068049b72343"},{"version":"29228a2fd8fa9e03243e2af185473f8abfeb407cdbe4f72ed329bdadbdc484b8","signature":"d2315a4871f3b1af40dc6e9ecaca5a7271273bbcd91f00496b0038c3be25b671"},{"version":"84ee6c19db9aebc0f267dad9f38b59769a344e20ae762030ba2d8db629f925ce","signature":"6a8734b879bc7d5a8fbd40ea622c74e40431165154b8d043d0d54e59081c26fb"},{"version":"1314a35a2551c127f4844fb29fd49321ffaf3701afc6ed7131c90833121593aa","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ec29995066c87e2f5fc0d6779e5be2d40ea3cae986f780cd1d007dc76767d6fd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ab2f5059de5eb22a286a8bcca17d8803b1e866c115f2a9170abcc882864eff10","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dc058abfc4773e645f0baf1ef95422e63b6c1d9962bb9d4c6540c12a650be499","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ac37a6d8ed49983b7045356b04ad84f58799843ea2afdc53a08f2614c11b662e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d1fa13f317d3637fabb663edd46b39ccdc420e0c5a3913b7fa4e906d99497cb2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0a3ea64a69a40bbd8806c9e1a477bdf18f0ee91624c8d9c9a658617fca971a89","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ddac272aa9a5f0f716b38e2fed52714c5cbc10a6a4b60a2b5586d6a75dbd4a1b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"62fe41879cae66d14c865c973556b0e24a904d9c6445557f5414007a236ea56b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0e226d432a81a3d1447e3eef012f251f196e7002e654d35fae4342128122e36a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cc39c13ff8d9df5c8ef59f0e717e3b5b4e96f7dc05859a531f1f703718d4192f","signature":"e6ec51d846f163b420d420782dd42e40aee266aeff314d141b11a0307a86fe09"},{"version":"10acfb644142d4c7da056485bd721efacd6ee61c0543c5762862a88f4ec9be94","signature":"1857ecaad23982cebb7ec28e547ecdb341d40713e95988b7f7d9da4c20f9646b"},{"version":"d337b2b575efa0ae09ab5b8bb94ca907728beb48ad4f9a43c653c247ebdf871b","signature":"be1b859329d61b0f74fe1393fd56c1542d1807ddf5a035b27a7153c471f53e32"},{"version":"9af90dcfb3df248fa3f8abf701c073fa30d6ee7b5758ba4de460594c56e4af8f","signature":"5b09eaef203954c253a646fea5d827882c557488a4ec3fd8cc50493e9ac5ef4b"},{"version":"c44bd1c97aec9b3731f94e4ca33797b718f355040fd1a3531cd1cdf72a092f98","signature":"a646dd3345b4cc02b5dae88b89ddb10adbd4b4158ad8c2a6f72bb83d0b38ab05"},{"version":"bd82c97d17c1bf3ae541b1dd8f1de5230455e9dd0128afb3694867baecea1407","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9d116a47a60bd0dfe34a66d2a4857a9a73bf2406915bd5b19bab3d1f42b8115f","signature":"6d6e3b1d30af0c368c85a34df9c95d2f1318f7080ef2e5749aa4bdaf637f073f"},{"version":"a0460c3775eae1effe1641d510f8cbd74a3b430951edbddf3b8ca9cddf732bce","signature":"ff633c25e6b6144a8904e3f82d41783e674fe44816ac76c8cc92dfdd8a9c8367"},{"version":"6aba6fd003ec6b75e94e40335a2315213295714f33e38b4164aaf7bdb2a3aae0","signature":"d17ae8ee1e9f7c65ef6f4c78ce2b6a7dd5fd1524565c12e6044ba3db661b8ed9"},{"version":"b72a531a79d4cb645c43c6782dcccedaa609b2c7efd71547a56ee74fad0c3dd0","signature":"7350f43a093be766aba20830ce8da6d5e1196d3bc17184977283e038cd281fbd"},{"version":"53cc94938d41698f1994b5de600edb7e89aa936944ce1d2955720f69be6d460b","signature":"311e004a849383cdcdf5bc484d374e5c55b8494a7a0b86f08ae78a9aa7cd0871"},{"version":"259cc7fcae5804316e63f5d00416e69fe28d9a3bae59dd20c767d714626dcd5d","signature":"6f96022250225ecbb218212131161d0ddf026fd636d134eca2e2d4a16637e9ca"},{"version":"f5c2a1cb2d8619642ba9bd687227fe3ed43787235c8e980c34aa844645728465","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6632cc5aa98bf6e8dbf04703a67620807ad7ac96c006255feaecd6d98d37eb14","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0f4d43d34056d61a57ff787c29fbe5b2ef301a333ba157449ba3df4f0a45649b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3bb5a48b30edfb6665a89bf7df4c42dda63a75cfa27c8018f859f2281ea05d4e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f4edf0a9027ff9279ede897f9c304c9f7e42c93170d2b2f66570698048e887ec","signature":"abe72455f516e18ed06bbb7e01ea1450572ff48cd86d4153c5853474dae5e8c1"},{"version":"d45ebed0a7af7351812afbdfe2cbfc7f88163d72bd79807532bce53cea6e9cb4","signature":"4ae59f31cbb1d8f65520a2852714b43fa800e651ddf50dc1a3e68c56d6537f9c"},{"version":"13bf5a8573fc1891a43ebea36a1ef5517d59f06c22f6e3bcacd8c4fbfdc0be76","signature":"654865d2998e7e7aa50e64fba9f1dcd717a7f378ee65b7e40311035c011e91ae"},{"version":"71722573d8814adfc67a2fcbcd61d3c4250e6c370dcbe927a6bbcd8d08471560","signature":"0e6ee02a5692f58fae9680a1c9b1dc94d3af9a97456ec14bea39bf4a9e5931ad"},{"version":"2f33f28160bfb02bedb63ddf4f6a8241cb2ff6967041643a0d7ee0909f75c3e6","signature":"e7d315801dfb219e04a94c847f0ae759b7d2b451783d38974a72e7b695436803"},{"version":"04fd50ba4fdfc24324446f14648d1c95fd08fb7c3f91b6de6a17ef503f052e36","signature":"b79d4edff2b414e35a3bd893e38505d7b8fee3cc678f7c8c06d3ded65ec13913"},{"version":"fa160d0c5713d8259b2648497fd70ba7c7b7a6602a840c574eb1c0a6f46e0454","signature":"4951a5459b063778e07d022547e89168c941ebe6bf458f07ea66f68b5f2e8de2"},{"version":"e74268ffc9270115d1d343bcbba879e819fb149e693a0e0524e1f321bd55362e","signature":"72dcdb99ca1e3ca76a476fa8bc73a89768a7404721c1ff2266d2c649bfb9e11a"},{"version":"ee82aa0ef404999ad87bb7a2baa1d75b0fd94aa2a0ff93bd673b39f7901fc37d","signature":"080b3addbb0d6625d7af627d88f46c15af2dcb962ca35a4715510d924cd470db"},{"version":"9267691f6b1c001d1ad417d316eb19e3448db243cd5eccd9e7fe1933dc80303d","signature":"a9674a62883f5e91daf466b8c3688f5bd9b54750ea57cc07a0318e56edbb9ae6"},{"version":"c256e102702b489676e3738666b34d985b2bed2835c1c6a7da638a2442ac8d88","signature":"70ab92cd22bc23f6f464975b988f1abd8fa3c78cdb14f620730d735585761f93"},{"version":"1fbf86d5c06434863bf58d1e0b464481274e989244d9553ff867d4f742ab0832","signature":"868c432b61889f1028f7b0d5ac70541268dd34a158b7def3d464c0ebc5a0306b"},{"version":"5bb0181380d7d4f24d5b59efd31845ed2835be7a0e6ee2fa113735e2d14f7be7","signature":"98b45d50fc16be69aedeac7631b365ad44e5c1c85f8c535df06f90199d43e64a"},{"version":"42f84fb7fb1bdea79ffd6b67b36c9906b21f0457783277abd39c047f053b3e42","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"65c5c1e3cfa7e96ddf00b29103d558810220aeec2c5e15bb281ff6bfb7e61148","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"279e1abd50429cfe84b8dd7cb57e9684d8ca7864af5c3fcf853efcacf680830c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"09687d78bd8457aede9a7577ef5f833caf9718f2cad6020261caf32b7f237845","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dde235d0a151d2c8ea509d77e32c185b6e3b996563e29d5817af11ae5c09b9bd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9ee3a3696c5ab964b6ba7d41121d5b4d91ed7d70d2ba7cf0dbdcfaa617d19735","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4473be7e8a5b994db4e58c3d4ad7f50b91cd00c3be1c8c3a1867c0fdc08a6e54","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c22f3ec19a761c9989950f01e38fc127ef63f2c0a3300cdd0b3b54cc28dc75c1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a31abfd6a1707f3d3fa8fcd6380a7cabf458285d7190030215d8a92b0c360827","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bc5ea5422be0834017b7ea3550c58d61ed1f7f976feaa321634d7fe60a0f26e3","signature":"117ec0eed14f00ef3524ba8069fbda8cbb45fde70d22b16ed255473b2108f1ce"},{"version":"8de2610573a2f40a3405defab8d036042b069cbdaecd61a2b813fd75f3a2c3ae","signature":"939b6572bef8a2c9bf87136e11498758aa328ba7dbfa32b7387ec8905cb0744a"},{"version":"22d768ed04ecd7cea3fc40851466b04fad6078e979dc2ea835646413b2a05acf","signature":"91da61e42b3cb07db395436e29d0d6569f0ee7755098753b533c9f2b20023e98"},{"version":"0ef3b705c81fb51f3b20c828fc50e9d2902644ce8343281c7a5c057da23c5f86","signature":"3c4e06cfccaf61e890399a0f86638295927ab217e0faaac5e8e7c2a830604f9d"},{"version":"b1535397a73ca6046ca08957788a4c9a745730c7b2b887e9b9bc784214f3abac","impliedFormat":1},{"version":"1dab12d45a7ab2b167b489150cc7d10043d97eadc4255bfee8d9e07697073c61","impliedFormat":1},{"version":"611c4448eee5289fb486356d96a8049ce8e10e58885608b1d218ab6000c489b3","impliedFormat":1},{"version":"5de017dece7444a2041f5f729fe5035c3e8a94065910fbd235949a25c0c5b035","impliedFormat":1},{"version":"d47961927fe421b16a444286485165f10f18c2ef7b2b32a599c6f22106cd223b","impliedFormat":1},{"version":"341672ca9475e1625c105a6a99f46e8b4f14dff977e53a828deef7b5e932638f","impliedFormat":1},{"version":"d3b5d359e0523d0b9f85016266c9a50ce9cda399aeac1b9eeecb63ba577e4d27","impliedFormat":1},{"version":"5b9f65234e953177fcc9088e69d363706ccd0696a15d254ac5787b28bdfb7cb0","impliedFormat":1},{"version":"510a5373df4110d355b3fb5c72dfd3906782aeacbb44de71ceee0f0dece36352","impliedFormat":1},{"version":"eb76f85d8a8893360da026a53b39152237aaa7f033a267009b8e590139afd7de","impliedFormat":1},{"version":"1c19f268e0f1ed1a6485ca80e0cfd4e21bdc71cb974e2ac7b04b5fce0a91482b","impliedFormat":1},{"version":"84a28d684e49bae482c89c996e8aeaabf44c0355237a3a1303749da2161a90c1","impliedFormat":1},{"version":"89c36d61bae1591a26b3c08db2af6fdd43ffaab0f96646dead5af39ff0cf44d3","impliedFormat":1},{"version":"fcd615891bdf6421c708b42a6006ed8b0cf50ca0ac2b37d66a5777d8222893ce","impliedFormat":1},{"version":"1c87dfe5efcac5c2cd5fc454fe5df66116d7dc284b6e7b70bd30c07375176b36","impliedFormat":1},{"version":"6362fcd24c5b52eb88e9cf33876abd9b066d520fc9d4c24173e58dcddcfe12d5","impliedFormat":1},{"version":"aa064f60b7e64c04a759f5806a0d82a954452300ee27566232b0cf5dad5b6ba6","impliedFormat":1},{"version":"7ffb4e58ca1b9ed5f26bed3dc0287c4abd7a2ba301ca55e2546d01a7f7f73de7","impliedFormat":1},{"version":"65a6307cc74644b8813e553b468ea7cc7a1e5c4b241db255098b35f308bfc4b5","impliedFormat":1},{"version":"bd8e8f02d1b0ebfa518f7d8b5f0db06ae260c192e211a1ef86397f4b49ee198f","impliedFormat":1},{"version":"71b32ccf8c508c2f7445b1b2c144dd7eef9434f7bfa6a92a9ebd0253a75cb54a","impliedFormat":1},{"version":"4fd8e7e446c8379cfb1f165961b1d2f984b40d73f5ad343d93e33962292ec2e0","impliedFormat":1},{"version":"45079ac211d6cfda93dd7d0e7fc1cf2e510dad5610048ef71e47328b765515be","impliedFormat":1},{"version":"7ae8f8b4f56ba486dc9561d873aae5b3ad263ffb9683c8f9ffc18d25a7fd09a4","impliedFormat":1},{"version":"e0ab56e00ef473df66b345c9d64e42823c03e84d9a679020746d23710c2f9fce","impliedFormat":1},{"version":"d99deead63d250c60b647620d1ddaf497779aef1084f85d3d0a353cbc4ea8a60","impliedFormat":1},{"version":"ba64b14db9d08613474dc7c06d8ffbcb22a00a4f9d2641b2dcf97bc91da14275","impliedFormat":1},{"version":"530197974beb0a02c5a9eb7223f03e27651422345c8c35e1a13ddc67e6365af5","impliedFormat":1},{"version":"512c43b21074254148f89bd80ae00f7126db68b4d0bd1583b77b9c8af91cc0d3","impliedFormat":1},{"version":"0bfacd36c923f059779049c6c74c00823c56386397a541fefc8d8672d26e0c42","impliedFormat":1},{"version":"19d04b82ed0dc5ba742521b6da97f22362fe40d6efa5ca5650f08381e5c939b2","impliedFormat":1},{"version":"f02ac71075b54b5c0a384dddbd773c9852dba14b4bf61ca9f1c8ba6b09101d3e","impliedFormat":1},{"version":"bbf0ae18efd0b886897a23141532d9695435c279921c24bcb86090f2466d0727","impliedFormat":1},{"version":"067670de65606b4aa07964b0269b788a7fe48026864326cd3ab5db9fc5e93120","impliedFormat":1},{"version":"7a094146e95764e687120cdb840d7e92fe9960c2168d697639ad51af7230ef5e","impliedFormat":1},{"version":"21290aaea56895f836a0f1da5e1ef89285f8c0e85dc85fd59e2b887255484a6f","impliedFormat":1},{"version":"a07254fded28555a750750f3016aa44ec8b41fbf3664b380829ed8948124bafe","impliedFormat":1},{"version":"f14fbd9ec19692009e5f2727a662f841bbe65ac098e3371eb9a4d9e6ac05bca7","impliedFormat":1},{"version":"46f640a5efe8e5d464ced887797e7855c60581c27575971493998f253931b9a3","impliedFormat":1},{"version":"cdf62cebf884c6fde74f733d7993b7e255e513d6bc1d0e76c5c745ac8df98453","impliedFormat":1},{"version":"e6dd8526d318cce4cb3e83bef3cb4bf3aa08186ddc984c4663cf7dee221d430e","impliedFormat":1},{"version":"bc79e5e54981d32d02e32014b0279f1577055b2ebee12f4d2dc6451efd823a19","impliedFormat":1},{"version":"ce9f76eceb4f35c5ecd9bf7a1a22774c8b4962c2c52e5d56a8d3581a07b392f9","impliedFormat":1},{"version":"7d390f34038ca66aef27575cffb5a25a1034df470a8f7789a9079397a359bf8b","impliedFormat":1},{"version":"18084f07f6e85e59ce11b7118163dff2e452694fffb167d9973617699405fbd1","impliedFormat":1},{"version":"6af607dd78a033679e46c1c69c126313a1485069bdec46036f0fbfe64e393979","impliedFormat":1},{"version":"44c556b0d0ede234f633da4fb95df7d6e9780007003e108e88b4969541373db1","impliedFormat":1},{"version":"ef1491fb98f7a8837af94bfff14351b28485d8b8f490987820695cedac76dc99","impliedFormat":1},{"version":"0d4ba4ad7632e46bab669c1261452a1b35b58c3b1f6a64fb456440488f9008cf","impliedFormat":1},{"version":"74a0fa488591d372a544454d6cd93bbadd09c26474595ea8afed7125692e0859","impliedFormat":1},{"version":"0a9ae72be840cc5be5b0af985997029c74e3f5bcd4237b0055096bb01241d723","impliedFormat":1},{"version":"920004608418d82d0aad39134e275a427255aaf1dafe44dca10cc432ef5ca72a","impliedFormat":1},{"version":"3ac2bd86af2bab352d126ccdde1381cd4db82e3d09a887391c5c1254790727a1","impliedFormat":1},{"version":"2efc9ad74a84d3af0e00c12769a1032b2c349430d49aadebdf710f57857c9647","impliedFormat":1},{"version":"f18cc4e4728203a0282b94fc542523dfd78967a8f160fabc920faa120688151f","impliedFormat":1},{"version":"cc609a30a3dd07d6074290dadfb49b9f0f2c09d0ae7f2fa6b41e2dae2432417b","impliedFormat":1},{"version":"c473f6bd005279b9f3a08c38986f1f0eaf1b0f9d094fec6bc66309e7504b6460","impliedFormat":1},{"version":"0043ff78e9f07cbbbb934dd80d0f5fe190437715446ec9550d1f97b74ec951ac","impliedFormat":1},{"version":"bdc013746db3189a2525e87e2da9a6681f78352ef25ae513aa5f9a75f541e0ae","impliedFormat":1},{"version":"4f567b8360c2be77e609f98efc15de3ffcdbe2a806f34a3eba1ee607c04abab6","impliedFormat":1},{"version":"615bf0ac5606a0e79312d70d4b978ac4a39b3add886b555b1b1a35472327034e","impliedFormat":1},{"version":"818e96d8e24d98dfd8fd6d9d1bbabcac082bcf5fbbe64ca2a32d006209a8ee54","impliedFormat":1},{"version":"18b0b9a38fe92aa95a40431676b2102139c5257e5635fe6a48b197e9dcb660f1","impliedFormat":1},{"version":"86b382f98cb678ff23a74fe1d940cbbf67bcd3162259e8924590ecf8ee24701e","impliedFormat":1},{"version":"aeea2c497f27ce34df29448cbe66adb0f07d3a5d210c24943d38b8026ffa6d3c","impliedFormat":1},{"version":"0fbe1a754e3da007cc2726f61bc8f89b34b466fe205b20c1e316eb240bebe9e8","impliedFormat":1},{"version":"aa2f3c289c7a3403633e411985025b79af473c0bf0fdd980b9712bd6a1705d59","impliedFormat":1},{"version":"e140d9fa025dadc4b098c54278271a032d170d09f85f16f372e4879765277af8","impliedFormat":1},{"version":"70d9e5189fd4dabc81b82cf7691d80e0abf55df5030cc7f12d57df62c72b5076","impliedFormat":1},{"version":"a96be3ed573c2a6d4c7d4e7540f1738a6e90c92f05f684f5ee2533929dd8c6b2","impliedFormat":1},{"version":"2a545aa0bc738bd0080a931ccf8d1d9486c75cbc93e154597d93f46d2f3be3b4","impliedFormat":1},{"version":"137272a656222e83280287c3b6b6d949d38e6c125b48aff9e987cf584ff8eb42","impliedFormat":1},{"version":"5277b2beeb856b348af1c23ffdaccde1ec447abede6f017a0ab0362613309587","impliedFormat":1},{"version":"d4b6804b4c4cb3d65efd5dc8a672825cea7b39db98363d2d9c2608078adce5f8","impliedFormat":1},{"version":"929f67e0e7f3b3a3bcd4e17074e2e60c94b1e27a8135472a7d002a36cd640629","impliedFormat":1},{"version":"0c73536b65135298d43d1ef51dd81a6eba3b69ef0ce005db3de11365fda30a55","impliedFormat":1},{"version":"2a545aa0bc738bd0080a931ccf8d1d9486c75cbc93e154597d93f46d2f3be3b4","impliedFormat":99},{"version":"4e49c2a5cd5b413d6f345797cf2db9b1de533863cc4ab32c4de16d4866480867","signature":"4ea82f415bb35563eae553ebdd9cfc541d18967c536d1bd821f38ddf50836ec5"},{"version":"b49285ffdee55942615f0dbefbad0034203e214cb288d2cee09d3e7b011c92ac","signature":"1ce3453cdf163e11309e394025bb62220b69cd2db35e2d0fa33e14cf38efe226"},{"version":"d46d48d5ccca19b55042e2d48a773fb97d0bb9769d9f457112c8273851b84d0c","signature":"7b3fe3dc7a57dab64ad89df76681f912b6782a94c9bfd6f8db407b657c6433dc"},{"version":"565e7c8592a98903a22c5caa7be9df48b5defeb0f9dd5c95cff6cc02db46add9","signature":"19f13e301afd7de9e6c815b06b16029cb6ba524d50bebd2b381b4b5009521f72"},{"version":"6e3cc8174feee7c91df7b15357a2a608ed4389ba83455b70278f0ca5630cdfe7","signature":"1b4f6432935df03e81a8939fb7c4a6db593c5c4bb564504599aadfab1addb27d"},{"version":"a4de4b6980e14aac3f0c46ad86db25711cbcc735d7adb7720af6995fbe045eea","signature":"76bfe2b4ee9eca5bb254288b19e87b463765fd1a10b33269c4d134ad898ad9b5"},{"version":"d15c20e1a122549fe870b5fce2c85714ec80a3d3b74f2536bb8a3fcc20dc4eea","signature":"8eb5d3569824aba69bac738c173d655d8fb61b8585dc4417a04c591d8e1b26bb"},{"version":"954ba07c66ad24d7d4bb222993578083a4423c0f92a9bac4fb9e736a3d4eb813","signature":"0b4872603cdab838437f754c0ab373796accb15efe9d82e3d45782ce193369a8"},{"version":"bae8f47ccab731836cb7117c0a8609e8c02d0addd4fd4c7009e8cecd476e818e","signature":"723cbc31e62b22b09eecfc383ee07ad39e535c9f332b022fd88ee66532c124cb"},{"version":"14a5129ed9a94b8e4a84095dfbc088e5a713ecdb391ee5bd7b0a733e64d69301","signature":"d9bac9f20a21ebebbd29475f51b36635cba15ef0ac64757307d85a4bc3eecf79"},{"version":"8166c477f254219baa01afacf9e1c7f90a4afc2efde83183553b666f582fd1cc","signature":"f4c94ca77daf02588f850cb2f4b5a1ed661d547356c7b49ddb688df1d19aa9a1"},{"version":"af0cf510af3d03a0b9fe72d343822474a7fb9d983a5055e6ff3230b7b5be14af","signature":"fcc4eb2a4b4b3c403097e96ee78482251afad86a6ff172e8104717c80c1475d7"},{"version":"014f38ff04103744e6afef3477513156f3764c2b716e29dc06654ab68ee9b20e","signature":"db3297cab37c1e9cacfe2a3592be82a2a209d1dc46b80256578f8f2caa76a385"},{"version":"b46c791b78356c3de6414d22095ab43fd2963edba051f4286b09859fbade6b8b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"affe446b13aec93b99f351f9ee1e431308f5cdeb18f63302cf24cbca89182e1a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b57647b19d10fc0f2b076f761caebe20f272aee93647f1bd8c9978056211a07d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a8d2915aea530942db273b35206c1352a68a651976b374cebb38c01c27dd3160","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d8f8601853f7e61892c40633650d5ab6fe8a564f1f9d6172095c6ac1d544e5c4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d0fa6fb03d1d5584fcb167aad7269de2625bba64cc45c92d023558309bfe6552","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8ca0ab1ccfa1229324946caa1b9f3cde6e5bc13c68fa5423d5424920c4e71fc0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"041ab714ffa87e79e6efd79998e2d537af753b1450011b048f673d2f550ecc50","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5fe27a379f724d75a3db7c7b0745f6a14140ab74934c6b0331f08c73171f9f98","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"715026154864fc6f3fcc36520f09209416001d92d9f0a244ce52e8e3c9fbf0ca","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6dfc25ef25b3a57faca3f159634d5233d8fc0bf9d77e7e71b1eb3e0a3594699a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5b228259001804ec6228a9a0ebd02b8b549529319be68558801a8d93cd50a5ea","signature":"ad332b9a10d0249b8cbce5d8d9c10f0ff8f585d909d8c4b4987437c15fdb6569"},{"version":"662f4f9aaef37a862d00552a59d1aa314f681e424eebf9576b16df78419903bb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b24944dbb9cae7dcc4282a42546e31fb53bd8a2f2cc7f8ae6c272d5924a2ba55","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3b1cb47f4d87126cf3f2973da87105edb404a1c98c0aef3a2a289b98fc879029","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1856c2b5c7e6167bd7869d46273e730aedb23f80c1fc013f9f018cde1ac508c9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0387f0bfbd708bda5035a03775563836aa22508d2459e017f75b415c5f6b3452","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dde98beb8bef53cee95b020cbfddc90d0012e9d98fa19595035191cd7d2cc1ed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d06d3fee2b986f19cca9483a4420497ff3909f6487e229467e75e62e283161d4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f78fab13e0f5ee19bf3e2ef18b5ab38a47dc60899def7a82dc05860915155308","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c3eba367c1921c8f9e7f231a941cac022824cb666e652cbe754ac1e50804cb11","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2e0c7c56fd6742b25af440e2a83916cff12be55ca6c91f899f1b4fea9827a69a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f0257b9ac5edeb935209106b79f9b4565fc6bdef9f2b4c5be6bed787a60ffdf1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"982ce1b9e222732ba63b4312ad1702dc81cdf4e277bea0040898e35fde086058","signature":"1af3e359f2c3a3b25e6cf0532c9b12b27c8ade0e7eb582007452ce06db289d9b"},{"version":"86e3ae5677e1c3559ed9078231e1e54111f3bff63b1adea3d14ff48d76e2ef0d","signature":"bf3ca96bc59503b3214f0618af79741f5a28de7d7ac663c13578af8a12fdc385"},{"version":"169351f1ba2dc3837650208074687baa7b7abdb18e43d3547565ac0ca05918d1","signature":"dbd0c498b5edc07924a5f7ebf0ae90efb9436dc2229792eaf175e16c03248f98"},{"version":"87bebd1c58e39741b573ee8d32b69b74d318782eace1a9a6d9f80586324b2d3a","signature":"800a7b9dc46ee24c46db2afa893e0bcf0b8c8bb1bd9b8db5361e34fa3c2aad18"},{"version":"06350267a4b707023301d6af8e0011c9403dcfc5eff31ef7cedb13d8f97c22e2","signature":"b7b254f81d9a367bcf98769a957c2f8dfef2267573a862b52ee2748647af39e1"},{"version":"f1844ef5a7fe33fe520fb2ea00ee03f96e3ed528392ca36de5f42964490a70cb","signature":"d949e121e0b71673df6140eda41341316408ac47555ba72b02b4abd3f0b6bc3e"},{"version":"eab55adf55f35c0e404ef2ed03340e5bfbfcc9f8e631c1ccb99d28686b79c60a","signature":"dd569f5b0cf0ca74aa2b1b5f2559d99655fdb41881b534f6d27e226903a24880"},{"version":"013936b36694b71563cf2eeeb8a159cfe7832432802c33a2d991b0932c81a36a","signature":"7bb19ff78f5dad94999cc2e0debb65a5ef8812b4507a7652b0b3fc455a9e8ddb"},{"version":"80ea52c65ce80ac3d8d81821de8e8675a7497210ee37b23efa79f21bd57fc86a","signature":"d96bc2df413362d899c2a26a8be1fbf15d38eb758a9d65112d7eee95611f0bb4"},{"version":"a384e31103a21be8505d837fc43ff1a3653f70ae795b4f46d1484bd9e2623301","signature":"214644d2fea678926fe214494d5b88720514df481a09f665137efc5ae653499f"},{"version":"bc2c8875db5a1430437c82f060faae49d0eab2295f7ff81c5b82279fafa8394d","signature":"5cd36275e5e2e7c71e522a445740890253664d68f28df4d62a4a13c21e3bf45b"},{"version":"ef40ec2b5dd17a51783f18514fd97931d6b673207bc23dbab5914003fb90947b","signature":"ea58ac73dbc859ec9bd2c6e497b70d04e0b87d52aff26ec74c0b2fcf0e4d548a"},{"version":"414844c14d31371280f1024fdc10ff268455384385eea30dc5ba252f3e4fbeb3","signature":"da8aa5942188ad3147f0afacf4c3f11b24942ed40114ac1a2fb9444119d69e17"},{"version":"ac002c49c6dc6a9a524d074a6f4c324cbd4c320e222eda80415d53150d3b10a3","signature":"ac68bf7e24525499431c6bf39d62b264a7708d2393d1aca05a3b8d153657b2c3"},{"version":"7516f8012b8b4fceff405a25b09facf3eea5aa640fd6bbd91c169ef0ba7119cf","signature":"49dbff2eb0425c00c48128b4ff64bc5c8ec07f8aa6fda343bfb9302a2398392a"},{"version":"61df7340e77582676d6a10c309862970af30ffbb6cb10e86b49005764fea89db","signature":"9a1fa87e956dd9e945a728ecbf0bcdf59b5bc35bd4ab98618c8f51fe319ae756"},{"version":"547ea3ada84754869bc28f5822c247c0525383c3d8805f342a512ac2ed139f0f","signature":"d3fcd7e5c042241fda26edb5e44b7987838092f2c30b9fcfd5fde8cc5af1b958"},{"version":"8758d5e30c12540491d40282af28875a47bca5b8bd5e7f3136ebffb4d57a86c7","signature":"30c7af840c72864017bd24ec10cf1173f1c643359a9feffa51f6fa141d08850c"},{"version":"2645d9404e6f5fe66144a16e69d73d297de82df97280772374e42d514a6a075d","signature":"799433a95f4bbcb14479e6fa908d6ccf8c23fc369fbd7c6b5143026e698e1156"},{"version":"43ff1f43dfaad43d87026e3a953b74f70368ec1ba49f67eb8df40164a4ba3056","signature":"4a26aafff5702c778bf7349914063554cabacbf4b74ba530aea9fd2b5c060e1b"},{"version":"c954faf290c5251991902e64a51f18bf0a99836430e50c38126a7ec753629bec","signature":"e5d7539a72d07ef9c3d686776f885111a063fc63c60c975d027b6b643970a358"},{"version":"be035cd5d01eb15b85322a205f090f64d333dc047ca1082de84837dc31c31d97","signature":"af8541ad25caf543ae81e642a69d481f7bb2d0b642df88c46a1fe8910626a935"},{"version":"4e9d357be94551ce5e2d42ed4b0ed6546e32243ef0504cb771919b1f4bbb586d","signature":"562105feb1d69fc9516ca37ffc5e5af73d1339f62eaac2693c4856b3a06f1a21"},{"version":"5dbd0527243a6d622ede33b461f27551614d1d4071c9dc1b246a7cc9db850cab","signature":"5136f18880c11778e02967105e9fae9a0482deaad8f1583230676bdb59fb7ab7"},{"version":"3ad1bdb57b05fd29dc468a42e71c4ec8f12781a647edf5029bb60f5a8afee701","signature":"5ac419d5eeb2a884c1d260bf31248fb2a853d3628aa0d7c3a99757ef99fd6c2c"},{"version":"678dd9537cd28a491bd13f7f3177c851120fdf39f27e9a93b349979bb21641af","signature":"e425fc0486e0242cc540bab0d335759f9f3f7ddd1d8ed233eeccfecbfc5aee61"},{"version":"0d010c0b5a9166166771c8c48bf48e48d9d037de37903d2b2aba860d1108a2a8","signature":"137de1e22724e42c5f197f61b17ec1264467852dd37b27811c311c97d705c138"},{"version":"5d70f802801ac149a5830d8877cb4167e9f9913f7e788079e587b5d63a624d81","signature":"dba53de0cd1e77ba275a61f6203783f10a2b35ff248306fbd6d9689303d50f03"},{"version":"aba095f915652dc697979c0b9ca5a3111b7160144f9a1e18efc81fd485ec9c3f","signature":"f19a0c7e1142fc0502d9e0014961ee6a6fe8b9fc26c4602a72aea8c904f15349"},{"version":"fd1383591235471dc0499e38fa8f0be6bf354c4ca3aef2bd052f93e34c821f38","signature":"2ea178cccad298208dd3300fecfc1e882484d9fdfca4a8c473cc345f0a34eed6"},{"version":"2e496f56e091df0cbbd36aab6e55e16c37efc31091904e36e71ccc3c34cd4825","signature":"72d589144cb568b0b803e59532ac2df4c04998cc89d175d259815ce6a1acd5ec"},{"version":"464597875def0d40d6a0ea4bc5be50373eeb35af3023b4901fc539c19fb088c4","signature":"b8ddd1cdd822f53a7a29b4fa58240afd0688de547a5c640753bfaf99a37c93a7"},{"version":"560ec4980f9fdf84e4df149a31c676fbc624df75f33af2159b30aaad1d624506","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5a31e889fd61c82203d9d046edf845eb54c12d63b13d2028e5c1f16c26c5a535","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e0875a51dcc08220ab37ee44e0d604789fb0c24c6435826eff6522f9af79faf6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4b8f60248250f207cc5481619bdbee3e9ec7645ea74d2b257bcbfdc27037a68e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2f9d111343117be248f5860e96c68b5c55e402894408fbbaa4b031ab12572474","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e8252a1e88c45e4b76044e3ced48484fa04faf5873eeb2a15e88813fcae79808","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bae7d0911c58609a404bcd7255d5c80cdda6d568c3b95fb189620ed7bad20843","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"23d90e3d7b8e5a17f760fff35617a57ecd7b7f042602b3f9dbe314e938c77330","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"00ae1a801699b73d425782db51a2eba53741776741421dc8446480d09091377a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f89a50a4a14e6ef1a1c81b263997c2d728ce5b56bd1d93dcb907d57114ccf955","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"830b21dc28f068d3d362d407c17d010f37a9a29cc412527c274b8254c448dbde","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4492c3ccf40d889bf6eb454af8a7fba4199af810c53d10ef8d0bcc16156e72ff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"60efb61013763a932a1ebeb1ebbdfba609e6f2483bd2def49702f82decb94fe5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6fb49bb8359a76bbd80e39616d5cc6de09d3a9ff938cf58be22c155e8ff42916","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3cc35b9a2be3c9593d41df15a4fadee9a4cc7c145fd9d8dc485ab95eb1015898","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"effed161b9f183f637fba8f96864ffa67bbad3a3339b18d9d368438fbfc00bd7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e2f2f7c6411243d900d1f4199d20c94122f03d61de95cd5435856a125b91d134","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1afaf4cea2ba6a2e1fc5c696e0420842fd52a5a024f77fd63106e46f430d42b8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6d872c4f980d7e6288d80742c84f1dc087a0ec7531e18cdadfb47448a669c2f2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"179703b328f92994e719755b197ff2310945583fded682cb02b88aaaec0b3d33","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d2e752aeb02ae1be73703cc7834f9bf1de14b84d32121fef58982b29bb138019","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"954fa93b29dc7267fecdb55b80d28bc943cf370e0165963ca051c0cc6899e114","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bc8201479e29d49966186df4e5c359d507dbbcd4f772499b365e6836e500bde1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ce4cf241091329ede4bf94c365874f20cb8309b02ec32980d9bb47f6527e86c3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"522ac15e66211cad975f897a7eb70e77ba20b34ba8f9c4babb8f75f37e19c24d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9bbbbd6f4a35a22eefdd4d13b639ad27d2b1316a6e833e262a126d4310d904ce","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"80c14a78262fb095d375cbeffe6a6b53a300098928410181ee1140a3a8869a47","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"66cce2ff73442b6f95408d5847e2c8748bb4e47e44334546e94e52be58c0d163","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ad628be53a44b47262b560ab15866282ad4d257f2f214369e5f8579c84d503d2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"98021cc3dddc6c8ce5498e301424f2314a9f17deb130c609269c900716a6c129","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d4011e0ecac2f22d7f639baa671cb23d19be79b0dc64c1cedcfd026469e5dd46","signature":"444074570bf4108baba10fcc87aa17bbd8f6661575c2c6784199b147faff4e80"},{"version":"ae9060e96d1b1a768ceabbc133424cdaaab0b34210ade597f476ab2106964506","signature":"ab27ea6c0a82fdffe3cce64f5731ebfa2b266fd04fcd0b85e27bfe26699ea15f"},{"version":"622e6f18814b4016fe1103587063f23f819c36bd42158ba3011eb2e12095941a","signature":"f2edf0a07c79c131987b90c4fd6e571506e2de2217b1f01edd5440c94143c463"},{"version":"560ce56fa31cec56226446c1422aa0861d70ef0b1f92fa90d501eebc22fc9f3f","signature":"01c8666120a2b10fd3ee7bab4731eaa395ddace6a8133ea44f410db0846b0f54"},{"version":"72845acc3528236682c69a7b735ec85ed2beea38d8fd4c0b1e7779521b5d4ef4","signature":"112f2f56109f2deccf3073df9c73bd87c6605a307265aef3e247e6e2bd9fd4ca"},{"version":"563fa16b249fb0bf5ed14f72e40b6ead283ccb254dc1ecf0304c0165ffd4dc6c","signature":"506df86169965c18acf5c22cb324fcd3460cfe230046f06de7ad63860e014c1b"},{"version":"42d5990e1bb01e3e1c48a522cba12b25bf4a3b87fa7490bf69d02855c5019390","signature":"900375f92b808a9c742d612bc93108ab61fa9adb4b4cabee9b45a7ba8d30dfd6"},{"version":"92d32911e086e087141b1aac3b7876089e26ada9ca8758a91280a05b4efd3a7c","signature":"6d53e68963aec64794baff110983e875c60a42a3e3d1bf17ea385752c914c1ec"},{"version":"e1c9ad51c0bb65697455db21f2d0a89adfb65c96288dff8254d8d8a88638f671","signature":"b45cf13a19ce92456461eb346ffb6bc8bb229d8f04521fa539a761470de6ab30"},{"version":"7d116f501462b8c241b1c1d042feeacd041b9c48de3cca53984e539d2eeeb3b6","signature":"b61620ca847f6b7d40ef82faaeb0dfff55ef897fd2ab60024001a674f4d91e08"},{"version":"fd3e19108e40b4bd6502bcd08768a75693473f1ac31649f1f4ef6ffd7c88d36f","signature":"0b2eefc3650c7cb2c277d27ea3a3290f5835e2ad871b17041ff92843b06bf99a"},{"version":"b14453b02122266e37e186d1935cd337dde89929a1417cb87c6b962b39af0d36","signature":"d390eaec15d04e3957d9c597121ff483b28f79e54db4c916fd164dfd70372e82"},{"version":"7247f127bd65a32b0c8d5f84fced373fae4e4c541630ae7b05f1eeffde33b9ed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"20a066d0baec26f8ee4902ff7cc7afdec57496053b60b5d3bc5c85732a14597b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c216d4cd926b1cd512c039ee12dfdca10a292a08b76ab11198dc2293eec74ed5","signature":"64718cf0d577ae9ed2926faff603162ccee149cabf0f7d6c3d2eff8bab3f54fd"},{"version":"c8fe61044fac5d42706c4c8854e03e5eb073792202ec4e7180f7397155e34f9f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"970fc7d6d0e9d5716f75362209aa4747f46dbdf1c601727027c3a8b22a9ebde2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ff33e494e30ea1a42746069c83751d1a458d4d8b2336df2c24089872132a2be1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cedadc70983ad1d699a7efc9abd8008710dbd21449893d7273795f68649a4547","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"89af2ec4c45ffa09d872b1aaf9a98fc0c6ab8cce06c5bd6347d80ad8faa69f3f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"65db26f870db2f36af509737119f27bf6fbcfe7aa413b169dab0f6215758243f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"52977bca0c3c391efb84678029b1816a997a5069d91f8de7277079ad39b00c53","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d15dbc0f96e7b8141a77749e01a4e920a5381ca32a2aa58132bc5f7223f291d2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"544ad2754ea5eb052e793f75425624b7522f638801fbfe50cc252e8bda11e0ba","signature":"47aeb932730902d4d8c41ec941269a416311f709495b5d20f2ddeb6f8b483073"},{"version":"376cc50be40b8b36c55a073dd73a382908b575f74972789cdc3ccf786bc91d30","signature":"ac35cd214f851f08cfe6b29eaa9fe5780b0f124b1b30c0fe772a1d7f8b518d1c"},{"version":"f7483014a4b848ad25b27073b061599f520dcc6d57e6068e7ac647aca74eee12","signature":"1416dec78fa5f6be6be2e406d0cc50d6f98ce0c77dad79080d5e5a80be18084e"},{"version":"dce5de27f8737a33d7ac4aa50f6874b6cd16bc7420b3a9b97dd3abfdc263ae68","signature":"e28422e9a6af42ba47f7aef0833e002816fa287c438cdfb33754716547da6bfb"},{"version":"b7277bf592b4832b905af6bdf6120ea14f1b9a9210efb7a4ae3803a87287150f","signature":"3922dbc2ce29d177e9d0c1abe636860f8a1559bc545abd790c0060803fe2e1ea"},{"version":"9a106f225ee7bf695ed69744df6cb6982083b3f9c2fec9610d5ea74e2e49f6d6","signature":"9dab80bdc4cbca67c3eddb3cd102b87f111b1e4d1ba1b3a0e27a38258e31e426"},{"version":"d1df8b7eb29b69426f6328b503a12b4408d4ba4a3a305ada40af859fd0d1542f","signature":"c7a00bbb89a2cb3e0d5521251755518d81666c561cfe0378d03268570d5bdc23"},{"version":"71590a10d662a3f420f700c10793764811c558938e36311c61eefb13033a21ba","signature":"4645d41794484aad552e40382c358fa91dab4452296914190ee23be7af3970ba"},{"version":"ef07c47a9f22bffbb585da6ab96f379d97e6b72fbb658e78c03b11702cd1dc6a","signature":"3896cd910e22538e767007c0f988c5967112fbdd06cb050421f37d80c3736229"},{"version":"e561876a844b5d66796e60c5374a55e3666d17b8026012a3de1e78dc03e045a5","signature":"14001c191ef845d0b28e603f563f4c8d73166db39417fbeb04d227ab4918296a"},{"version":"2bba20822fb6a665abb0944bd00e587f93272c5aa1b2a513eeb9f2fed00a7e7b","signature":"785da1f883cb1f23d0ea0ff209153ea69a2c92d6fe7cd29f8c60fb9776e679fe"},{"version":"fc9258c7768321dff71ae7ff240ad1b5a6b204acaaaf8c088d37f1c4d644f20e","signature":"cf15966bba8aa58508d7159937e65485e4a40ab41fa2accefb0598833cef3af5"},{"version":"78fcb0f3d8effb684b13289aadc4680cfd9ae3628b1f39775621ec50448f22ee","signature":"677d31b96b2ed39787da58e41524dac24a285d4847b9413d4ca54e165afbc66e"},{"version":"c78318850aa730e4e4d4900d62112d87546cf76bd8c0f8a5389de62a8fab97e9","signature":"99f2cb08736fb3f90c502f329487966ef577077791b334b1d4ed5f0ac57a4e86"},{"version":"e21face6fb69732353a584c8ca54d4c4f32840b9d976bd2ed16e6e04ddb1b689","signature":"ff61de4e1af35108cd592760b2ff1a5f58eef3a4b29f4172412ef408143d3ae5"},{"version":"cb29061301b3205ef763d9159d82d81dced1528068d82d9248aa828b293473b2","signature":"49e666988a2e38af956233e189f9cb4667ee80b4bf738bd8ab61b8cecfee2e45"},{"version":"47049411d18af8a4afd89d6507e87fa1e1f761cfadfc49db94ff0fda85e2db4b","signature":"696d0b315d34950d1c089eec0c54c6ccdb7f2c19eabfed730b115fc4f63ec0a5"},{"version":"ac87cb8a327e1a7e15b3597bf1ea9207128645094941d34c91a4f4294cb50c38","signature":"834bfde39ed7879cb9e282fa632acbe344fe8d7efa6d01d05c6c6ffccfe806ea"},{"version":"897ec00e887fac246f9604b78bc3434fb61b57f6933ff36c8e5167d8fb4be86e","signature":"b68511cd2934d124b9d935ae94f90d131221ff96f95f2eca61e963926cbd69cf"},{"version":"2aed22685bfd84e88aedbc3c1d0943655e851ecf53da220041d42fa63e52eb10","signature":"51b1d705ced6ea26b44f528941620e1b0fe53538c5a245505f55960ebaef5dfc"},{"version":"b552dd1e51bbd18d0d9b4904dee01cad165d7f0d3471b42f00ee36ca78cb81d1","signature":"c1961b1d48bc6a1c7f3d115979d6728a6a8ac59869688a5bde08933c18adefc5"},{"version":"0e233393348d2a4b45ed807756c2d967887d7f689c522c936403b137cfe09e47","signature":"def7b08005b8d1ead7ec1cebff3703d1edd146dce701e3547a25d6f830bf1b6c"},{"version":"b83570a2939d33a6ecfdd2766a3e416ba612d0e8d6f83ad11156a004fa033c77","signature":"2d58b00459deb63b953324cacce6a50bdd0b7d7487eec5e0cb52c21a94e13212"},{"version":"3df325ba47f75517dd4720f0dbf492a07168a52264fee2acec5d81b68551b323","signature":"2141658926fd33244c616646eb68fc34928c2c3e76cb2f0fcc49f66b7a6c2e71"},{"version":"bbc074919308daabfa9d8eeb4636d8d82db13ecf1d497ba88070e64bfed7c0ee","signature":"722928e1b1078348b1fae3539d0944ee9db279ff7731a5c90baeed1a7afdc11e"},{"version":"a6bc0506fd785d58fc01916eed093992884c77047de6ef24a1984e870958f3b9","signature":"6c67fa30e0db9490403ce70c9bd112dc16256f74e793f48501b0af56cf31c2f5"},{"version":"ccee7e5b2e10837265d6396acc046566cc79da4210d0a50e1f1795cf86b47ccd","signature":"37d3ec125450ce03a992226a8a4952bdacacfa88aee5d09f821236d9c63b20a0"},{"version":"419ca4ab657409f45b8db6cee2d5d6888a2b08a2fba70ddda952d968c69a16e2","signature":"072d63362c70c19e5647e1dd12ada4492213157c48c17ccd13a008f9c6b4a12d"},{"version":"8231663779bfba7f580018479f74d02df7c9160b3e8dade1940a569ed9d80ac8","signature":"b19e055eff7a9ba8d3416874c9a679d800a5df0a0c5219cdd5aa5335c6b8b072"},{"version":"7bcbaef9af060a63375362bc183bcfa7633039fef24e75cfcf087591154693c9","signature":"0537610aa50477efe02a1d81e01f834e498a0db8f1bed3289ebdfd4775774c4c"},{"version":"3e41afb4ef179dd27f926c46f913abc3bdb66001716ac56256147179f5f15252","signature":"bf27ec3588059046cf735262879e81aab81ef0d702250e085ce6c07b17bc58b8"},{"version":"a0cbcb3cf77ff16618753dcd282cbac664597f8d6d775370609f8e128c598407","signature":"0e094d3f18ed4a44baa44ef3264239439eadb03b3f8e2ae278d766c852fa0754"},{"version":"ca572f8634347350a00e96f47b44ede6cc62545d87e839f959bff12bee235037","signature":"bc973f44ba5c54e1074bebbecdab061751028be94340dcc5481d06befed1f855"},{"version":"889c6cf5d2f17ebbce07ec946521f4f1a8fc55d9530b4959a7ae954ce7f0300a","signature":"170e54c7b03aa71a92de1afdd4cf56b47c9df01195f98a8d933771b75ecff8f6"},{"version":"bbc45438a3de93d2d44f46fd0cbded993b3f8afc82779a42d0a6819a10898fcc","signature":"f376da706da2e3ce62334b6d086d2d91040531879603f039d3cb7682d8d889aa"},{"version":"c15fd275a051a6770515950834e07dc22b7ebce6a9e8a93bce69d67d92f39e40","signature":"6f166a044fc4fed85f167f611d19bef8a2070e23281ad7aec01e77836c80287f"},{"version":"f2733e4721a9ff2047d46ddf9f771aa053a8f482f93d4820401d0be58dde660b","signature":"e3afb59a25c83c15f7f195f4fe92300ff8710b2a60850e9f869a07ec5a228838"},{"version":"6026573111eb1beffbdd6e3e3609ea711d4c2582469a258b7cdb6e4d36aaf28e","signature":"10e113ec036dd44b69d961f2b6616239ccf7f025823f75eec292640c3b4a793b"},{"version":"7db8deb452f9faf63b51a33fc3a09dea5a305e4dc231b770aced708f902dc7ba","signature":"2fe7ae68eac160827cc1ef3f71109e12ae1ba4c407fcf41d653877c7a3008970"},{"version":"8dab908f81bf0eeb9611fbbccb2508c2b4a8e1d57622968cf98993e878a972fe","signature":"b01970e81b7e682cd2d51def6b76c7bffa451a1b58fc54b528629c35dd89c9f5"},{"version":"9367e99e6028dfce0d37891b19a17bf1a3b04fb2649a89ae7ea832ffc7507b99","signature":"ba000331a8a0915160cf82ffd04d583bed6ea5547a117b8d88ed7d3ff6eece7a"},{"version":"9c9c3a0c8c7de68218d26f06c8a16c2901984f7486f1b2a494454f70ae694cea","signature":"24203ac0990f3fe71e252d5ac9b73e0c6e4ddf584be3cf38d14b1bcdec5176a8"},{"version":"3ad4c8f86a215282206449b63fcc1ddc7ccf6cb8e5b80a04bf64d92d2aaf6fb8","signature":"525d97773ff298b2f2fbdf14a791f0587ea0172827b6ac8821f3eb70b20aaf1e"},{"version":"24892b8255b88ef0102847ef8b231c6bfc0ee618a69b17e40ff1438f9997f2a7","signature":"59203658389170eec22beaac1509a33cbbdb6dff49b69e34593aa96c90c7de1d"},{"version":"d644c33e3d80969acb5b187976c8cf99eb0a259f63bcef80a6ee38da18e83247","signature":"2769f26e263572cb6b16ff1b24f373ded17e74e710e33725c98a22a0b7ae79b6"},{"version":"314650281c03451fe80bb91889aec0b247946fd5b52a318d51c5faf64cdc57ef","signature":"5fbeb568fafddc09e602cdbfda7df5cd0e561ba1dd8443318f1bb3b586066c9a"},{"version":"a45b7bb6b8126f59efdf77d2f8cf8ef3483aadbe34abf5d03038b1876a5cd1c5","signature":"0ce70420a3f859e7ada24e14d433bf75f91954ea538e9f25cf32a9afe7e86539"},{"version":"fea326b4e1f45f43699900930e1a685557728c14a394fd847072bbb227f7bdf7","signature":"e3575536a31286b081d4db3ae027a171f9567fb73765c91c67550cd330650e49"},{"version":"656330b9d0697dbe04cb1d8b8402b3ba3953dcf48e4dea01887c992036bb173c","signature":"24733ebd4c83b4d7b05b39d79f1eaf60c6edfc8f0da5c2f848b01517947697f7"},{"version":"8c4c8c4467f9519b0878333232f88ea38920588b21fad94d09e7d191c1fac691","signature":"5bbc828ff668bd2cad6f88b3f8bc1e85e3ab4a84af3eae83b3931bddb79d5d5f"},{"version":"b7567dec5ce2d27ed70feca5c5a53b033bbda727b3d65c1eac4d5256adf09315","signature":"31f22cee584992be54d06fdaa9dec55d060c358cf67c4d162fb2f5fc0c98283f"},{"version":"7354576cc5cd9410252734f2a40c4fff01428a753a672f354975a958e7c63329","signature":"b4444c70cf4bbb122a1983ec33c297027a05fc1dfc23376d3b140745a58a2ed6"},{"version":"3e8f5153df2b58ffc421a7d8440d3f92fe8ed9bade9a7b18bb0ed161998b40f4","signature":"15c69a20c8c5420b76b7c62d82cb284a1608ad67c2e0d1a71e3e3caf90bc4201"},{"version":"4d87ee3b202e0f2f91804622d86dc5cacdf3596c0fd62e4debf04d02ae25bfed","signature":"fa281b36685faa9c4a9d379f8a1ebb2f13f7a09f19e184becbc8e58848dc2396"},{"version":"6788a1deef524d1bb463645a178f02627169ebb47346eafb1a61faa5cb144333","signature":"ae6b5544fce2c65f20d0e7702aeb8e5bc2faf2c4e813c4ae999a44ca2d6b9929"},{"version":"2ddeb4d8ce27590153aa6ee84b36bf9764700d7260124167167a2d2a32166bee","signature":"68597f354e4595d3f1f99cfed58a445a66dd9b5d4ed5d8133e445ee2a3de6cfb"},{"version":"f7f56d7774204ea550efee0d9e05494e8df297bdf32634dd601fef7fe45f54a6","signature":"1aaeb72b56fbfc7fe35e5c5195bc4b102fdcb25a30fa39a8dcf26e4e03afe4f0"},{"version":"2ac12549f1ae0aa1775782876baa9c06e9d845be26d99ce56a36276a8831a395","signature":"39df2da2a2737d9f0561b052a23093c44d84bab8f276b5bdf2b3e41094666a45"},{"version":"79e36ac740e122de9323550df934df06c908a26820f221c758dcc16beade6618","signature":"24bc52911181a6e9ce7ccd5c8fc3b03b998f5a3ea71cf80e3c93051b68523ac9"},{"version":"bbe55ae5cb40ed5f38ecbfe673ec070dacc7e0d55dd02472263f7903b3c5ffae","signature":"21566e332d1f7e6c8890b6bc364f4d7e12afb504b71651d6fb92fef4d17835d9"},{"version":"671715f351a1eb2abe9ed42846a3c47ef495fbcc7c45891305b75cef89cf7dab","signature":"aaec910c194451376eb035a9e90aa018ce9121d164eefb346aaa3e19fb2aa90f"},{"version":"c10b1247cc334d64f4740702063dc4dc4251b96427e0d846b5eb9a7d0379bb1f","signature":"b204b8dc8a299d4fce994ceac613f46bfffcf4486c9a5b9f457f42f5f518b0fd"},{"version":"672ec17aebc02c37f3bd6a75778652f5cfcc450b0b2f4dbd8d5821ccc7909af4","signature":"0799f99f4e37567f2fe31840ff206efb30c29b21bdb0af72d55aeae15c70760d"},{"version":"6c6bce5fd86564171cf1bfc4122e6b4906a820790b4097c87723c2fb92eca8a1","signature":"1ad6ef3b1c1c48d5cf24ed8ff9b0a5a5592dce6ade6d6827d3fceaa920f6c500"},{"version":"7bd42610639a14bb0c854bde2d3bab07cce272b4f9699027258eee83d5c11a73","signature":"cbe7252f19d4397211500df1c2861e7c4ed9218b8d1614ae2d11ca03679f9551"},{"version":"b7b303f6ccc15e4db96956737e538d893c25b7092a159a39c0aa8ad932d3c636","signature":"c2f55b90471ad64c25a4d547225d37cec7d8f869fc5bb4cffe6c71a8b836f4b0"},{"version":"8b2eedc0f7bacc05c6f0b56dc41f46d1b06ba1b9868fe0fe77e8cb22bef6f2a9","signature":"198353c3f827b800288c5a5cb74460fd080c22ccc9881f49e9f5ccc59b35ee84"},{"version":"0c7459b35e2665327b17a7693b824fa83a3cc5647510a2cdf09a6635b4561c60","signature":"da68b6e91d25229268f69fa9173920364f23c6b50469e9e01e663e0de32fa6ce"},{"version":"674282b47fa4cca2df2e68b4a3674045b7a51ebab24287c15286ac61afa52110","signature":"0d3a59858de0d93e48bf7541964e12ac7d59c6d970c824cfa307bb3ea932f66e"},{"version":"e0c10ae5e38df160cb240dc9e46ac464dd22ca7432f783f75d77b1b0e1aabf46","signature":"a53c9821c1526959393efaf002082f8644ab2054f490e9aa6a0d23862d0ecba5"},{"version":"ae6c80232b4c2c4a00fa2f7dc51552a73683afd6acd88dc6c8a745cd39a823ef","signature":"b5184ac9282a657b51d247adf925cbc239c16ad3cdd8b4dc54dd369673e9a321"},{"version":"2a4f83a64245f53cbd1eecade0cd429c73f5b4e992439b773fbf2e8680ca4572","signature":"26dfe7cb950c6dacabb59453b56e2a71df14e67dc91ba3a35402e37a109393e5"},{"version":"3e0238000be6f6ecf94fff98c1c71072caa73ccf6c318c7b8fb324ff2903103b","signature":"ea3bb88cee2f2752e48e75c00ba80500d1ec9404160859804ccddccef1002ddb"},{"version":"aa28402c225fece07db6a4decdabe92cd905b329b6cbc2673f4c464fa4fa300b","signature":"37756386a07460ca40caec0a192629c709af57ecee057bcfe7c311f5da0be5b6"},{"version":"622cd7a5b9b304ca18aa1723952f21ec5c939f7c70431c1063bc25e59a912dc7","signature":"90c7406dcc6fe0fd8b0fa3e23b8b1440b2506d841d8e629a6b1df0283c8fd1b6"},{"version":"83837a404834ce7ba3f2498e3faf5dc31ae0a5859cec3101d684f824f8cbe3f1","signature":"3eca308a8adead7d78f165d89c01c30c4dbf141cfc5900a563ef47bd2b652a27"},{"version":"64cd8e7ebad2b8827d66171a80c2b516c5a57a91eddfe3f9b317faf8879dad26","signature":"084cd2150bfe1929b5fdad5847010232f8d7ed1acb1a965409d1009ab02b945e"},{"version":"68ee63044e87286b7a2100c05437babf550d647e748e3ee66ea6ad4cb268d52f","signature":"0d27b4098a7f8d9daca5e7f0304750773f03dd567b4d7d49db9a983be5a2e57e"},{"version":"ff60cb0d4b987911a9db25c4e372a81da6211e9248bf9eb336d2070b77771bfb","signature":"ffae22976581bd977560fb6a27d3aef9508d68c714c28c9be036c1ebe38f26da"},{"version":"187b3e36e643d6484ba0286c361eadcf4d3f4174865c8bb2e92ede5cfc0206c7","signature":"742178df5f8a476681d0544713fe87cee7a790042b11b27da9b102e80c318bb7"},{"version":"b5cc0ba3df9e33e2fa3849d474b4c558b77c854ec3addb719919720786f2462b","signature":"7ffccda9f5233cf7f4dd76c403921a51a2db0fba00c6d1c5156f463d95781b86"},{"version":"996c05dee2488fcd52dea0baa6bb03cbcbbd451bf22ca0982ffc1bb412ee5dc3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e83b7266b4bc5653f60004a5a07e2dd1484a92b256fded2dc1fc65e828b4bb57","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2853d5c65a6ad064deedce24ab8dbf06aaa5ce9542a47f078fe02f03ac7cdd03","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3383d105a4eb14ea4ed618769f30b75f90188e7935364332f7082793d1196b9f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d96eb2f5d3802c4a877dde7cf5c19f3e938d792a6c623e806c9cb3d64f134d19","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"df1cb31463ecfb08b80ee1fb021dc44fe79934972679382b951fd13eded5d250","signature":"2af67711c0b92f1ec7bfe590266fb550a2a274b8e60fdf1a37d57af36b0bed07"},{"version":"40b19636fdea5f4ff717e2b8c783e06978d56ddf2e56cadc547203802f3ac0ed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dc0e638cb5f96071486da3fcc349b7f938455220ad96d4e80a1afb444b7fe0f6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b65293780e6f9a13e12fd16c069c51294f40e1e12a28add6a26d8205c74b17fb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"82a1830c87e3d98fdd26f717ed49b8781d7fc773c0de5264e05e62640da8987a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e7b9731ae386cc1518aaa4172cab2116a0b1a791cd8ad34ffe09459f4574a415","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0dda19732a158aac112629965a55ef3a3dbb4696aae000963193b51fb860c59a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ab754ad0ec19423ea27bc7313015d6cf738360f4631148d2d49b9b87c0a46929","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1395fd01fc1397b94c1c12676294a680c57d649db4b3b28e1c260f0ebb541e6b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"80a9cb4a7a8161419dfe4d452a08286676b67d360f2bed4047942d1f76f2bdb5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4156d5f13cb167807cd3b50f1ab673c57ff0051d3c5ba40aa2dcf95f310465f2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1cc522ad210d2c7dffe081392e099776aea1a12b7341387bcceae1546565fdc6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0eeae146f6113ee176a29b1625a3e63bf9e84e3d15c25b672653db26e45d8ba4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"85251bb0af0b000acc3eddaabb09b481db2d5b09be42f25d52b056b966ddc6c8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a63ab84a834b223bc3cd8224f1e39c2ff0f906f3c29375f1dbca5a34ea1b4005","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"093fd001ca343779855f7f386b448d526e734c0d8c707eb3b979eddb84d40161","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9598e8f2c6880331c2f57e6fe39fe65d279d5fcee0879cdfcb10f676f2af9ae0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2deaf139a18640875564d069b8df011081214018c145526504bf2e378c716a3f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8e7a92c6c568e3073f67648be4f0ab0e8d77e36fcfad8aa97bbb268ffce6cae5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"be1f6a316e168ee956b44f0e9587e97a5989614d65651278328e6de12800fe42","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1c26429a84968da0f9f6818874208d5395d5d681789eb62d1e97874afbe55156","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cfaf579c6bd64023a9f079619c3c42aa47cab047556d1299c9789a6bb4a93be6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a5032259dba8af052f8650efd4b8290d61a0213e30c43255cbae505fcf6d1581","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a61e00af63164e828f24a3b5abb6c76837562fb18ad83607d245762b92566c22","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ec80f7ad05865aa145a03395e48710a388452c5573da23b8680f9fc4c40f7023","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0c2cca1042ac885087aefc61dd07c98d4d763e1b93da979e9cffefba1535103e","signature":"956433971a3be8ead015f2ba25dfa9b9a9dad91a092766686625e24987c820d6"},{"version":"90f918fa4bfc8a1ca28e5ee6c726fb4314e3dd5e4e6e5c138d3722a4406dec1c","signature":"243160e9793898a75bb1706e22e14be7dc4f7503439d0bd4385c9002bb73a9f3"},{"version":"c1acbe64c0dffe2769da1455ffa7a7a54c630553e711ed9ce686509d5a7ca22e","signature":"ed6c54273b0447c505914973fedd613d6bba8426779fbcdf58d1a900bf95d3cc"},{"version":"3979212bf8fa950277a8d6177a6ad6c4839a771a7074d9610bae0e35e6ce9040","signature":"f6aa1162ff9538566b39c5592f558b1dc70974ab34c3d1a592bd7b65711e988f"},{"version":"d275631a49859ae715b00bbf0b5f027c44d809861c2436dfcea1797176f9f4ec","signature":"2f0415ffbf291a21f7b8e32657ea9757e9b49bcd4b1dad5524ee463a1927916d"},{"version":"68a712c8150b2351406c2564d71be4e6bcf2ca9d5d5a241e99421ecd917043d1","signature":"f1447d898e5612d1a748da9566c03045a70199ff3535c97280baf53da785bcbe"},{"version":"75b3fb36bd172a0191b3540170778693e0d098328f7f6b783d0155848717a104","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c360f159bf7cc50cdbf9fd68912ac63bf5889b7220045435cc681b4fbe0b8f99","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a93c39a33bbf74c81cd249032ab84d98f1bb5b86a5d557111792af7fa51fd3b3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f62ca695015598c5971af372d459d280dc75e566c024bf83c3c3fed392218927","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f6df81427b32ed9bff8b40394ae1322afd73b4607ccad4b57225a153efd6d6d7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"aaa89e91914fae87683fc9b47537b7561fe705e3a19ebe436209a8553a9504df","signature":"78ed4e422bb1101f6a3186fe4b0b70d24d3503382ce07299b406a68f01141809"},{"version":"6d55514cfe052291428316f8b5ddd2620f161abcf9d180a39ad04f2572852a5d","signature":"8d866b691c98d47c7eecc6462e0581e3c9e62a4b13b87c6a7f830f0c2b918015"},{"version":"e34138d79ebd653b7302f054d2f5b086c40075dd387ea3c02b9cff1cafbf66c2","signature":"488ad3e9fa660fbbf03ee600d13285edae90e156fbb5b1c5f4ab396e5ba87226"},{"version":"6c6f6ae7a61d464a58070d9204181c34f88def3da2364ab213b2769fe1da314b","signature":"0183321e9456c163a3b9630a73441c67d709bfbcc7425c09a97ab1ebb83c1216"},{"version":"17af83b68e8692e64f6e475e6d405dd8dbf0756b44f05b22f61535e88f84acb6","signature":"aa43218ec3abf932ac6aa3fc859581b8f2fd8c9469e3c919408062e57d232bda"},{"version":"31c12db46e3320bb3d198856123b8875d4c18a00a9e8e8e6aa4c87153954a24f","signature":"4910911105559d5ec48fa179743ed357001b66fa8df1227bdb5820ec71c3c5bc"},{"version":"442f6f1df0e9859c783e9c1260324833376cf2ef977b0a8e7c5fe85ac45b2b4d","signature":"2e9fd6dc4a8c33cf0b4b359754e567e8c5c4a714fcda3716a4c1ea413102c04c"},{"version":"727b5a06aa2c2d16692b1ff55cac347033ee492fb0132f3843133567175c5926","signature":"edcc0d9e675c37f8b8345ef683965422335c183997a5abae692e03fae3b476d6"},{"version":"104321bbbae499a49b02b529e4e5176eeb094395ccabb51475b94ee7ec3fac31","signature":"cd789dd692d4dd223dfd8938a1dfe00325b137c3852e6a85bfa9ace8ed00a10b"},{"version":"8d1549cef4bfbd34d863903405a6f4146fca4310628edf97cca7f43eb1b5b70f","signature":"b5e0ea031f83d837aab204deb17bb3dc7bb49f3bc3d5f5ae00de7055b0957bdb"},{"version":"90ddbf56e752ea3aac5906c0bf372a35361bfdd05e37674295b930d1a6145676","signature":"f9dfcc6ba837fe9dfbdb57b71db828463f35d1938c29d7405b78935fd6551ab7"},{"version":"0bbccab1ed21d3bfef65a11d71e402b85c240e811a9a0f5d557927e6ab7a1ef0","signature":"60e6043a56300fa24f867fa24168c0ce827d6154625db4aa79ed2d49432f06af"},{"version":"f8c99e101fa518ab3c47320437e1cab29097021f2e4db90088d14cec9ab15db3","signature":"e605c17826925ef50254a6f1fe1b7615d239bf637b30cdb0df4637d362fe265a"},{"version":"1ea8cf150ecfa2e7100ccb91fb039af6b12d8f5f022266a716f6c6d3d0564280","signature":"307b1f6b818d93140e0f0a31acaf65d20eb606098df57cc97103fb0d83e79529"},{"version":"1d75e713898af44896feaec991b57d2e9a23e8790c7715eab5617b37c81b1304","signature":"c44cc8c4930b76d1fbf935484045f099a078a0e56218439da28da5d829065a89"},{"version":"84dc9514ce5ccc030dbec7dbe9731508854c3e7004783452997054dbda666a6e","signature":"0d2eed2f304bc1f3b1d854f9f66a454ab06477ac5e5c4541e10b6111c9b288de"},{"version":"487eed855632a69ade2c123a6190838937543c0ccb9adff04b2fa4acc6acc55f","signature":"1ac9fb8cd09e61aaf85cced63abfbebfe7620efd14a30559e8c4197a629212b7"},{"version":"73179d9bed7d28d279c73fdf5c7ec4dbc78493af676d383b3f3c5c35d839e7c7","signature":"65c789d98597042f1c69b58091dbb80443537c26f1dee66c37a32ee3c625087a"},{"version":"cfdec451e6198722f6f1a470ae1d702e91aba34c5a82ddc8ca2c46eb2841b25d","signature":"51e03b177ae1693a016731d78123a9375e88191258438907cfb8c28289ccb8bd"},{"version":"aa0e1cf4ba7438c59141ab42db4252344f3c2a78fe48e04a4a74dacef405bee6","signature":"f26c96975df3621c30ad0e860d7cb2679f76721cf94e7f8a733b9f3e73f87925"},{"version":"2e818b0de54379a805ff642430dd2ebf684b6cf4d3ae133f12fe826a47eaebd9","signature":"bac0a2c0df7457f1aa977c6199e5579baad02f84ad8594b715b67a312124bcb6"},{"version":"fe83b119da0f5ad1d6de35dd8a8ff11c6d3b4f430d2ded235430d4cb84bf32e0","signature":"53f10c22876bc751399d19641a5d1df99980c8d1b24ea5bd17e074a4034e56db"},{"version":"993f4c89fd25bd6aa86e3329183f0ebcf30123d243bc6505e945c7d23213fbd1","signature":"076becc81584aedfa7349ab56ec3058a2c48e51aecc5ecdabc2fd4aee654cdb3"},{"version":"5c0d0bc099cf3cde30d02b2d11f7fbbb934c2434cbfb69d8d67595bb2ccc1d95","signature":"43b7a0a2b2def00095492d167729428073dedf1d85c28159254d3b64e77eb0b6"},{"version":"70b90a13137fcb5ceaefaec6c636bdf5ee4fec1b03803f5bf1d93d3443231741","signature":"a7ea8dac8d777c73d4af8f9fab282c874cafe6b2e8398ddfc1e4e1a00f2f44fd"},{"version":"d9aba09758928ec2439f08c4736980c6e52ddf6a0cab476c161b3592a34d44e5","signature":"ffaea7fcaed416769800cd74682a38d1335953e1eb903bb59c22e45cced12b52"},{"version":"dc96972b8f5a164f425ad5dbc5a07ce9e4e802fd71ef03f44c19ed9b1a4a1fd1","signature":"c17952b18383043b4bb2c233acf6c88794227a30f545cbaf0d701e44a1562ccc"},{"version":"5b42e9d056ba435957700d0585861287052c08afde82e5349a21a43ae7113457","signature":"c892b55f40a8f35ede8ef7f1e0cdd1dfa70b22bee55d10674222bffbb703ef02"},{"version":"fe2e6c470b89b10fdc90714c6c734713dd0809189913130fd31fde1c152dd96d","signature":"d5dfca986d325fb72b02cb63065520cc0128b46d77c1c68441ca2241ce17113b"},{"version":"e221838e10f6d2c4b1fe86acc4066491685a9c6e878ce39480638cadd1fa2650","signature":"b98c55bf5fe063f227004fe751cd334ce9690aa25afe7be7570d66c48cf86e56"},{"version":"742bbb2ee54b65f16f77094b8444fff1e3f1c4aea3a2bd44cae5de3fcc369411","signature":"8d2883f78b4357f180fa333405e5d6c5d1d08305042060e331f9b4b21c262dae"},{"version":"dac20589f2919a63805df5e02ca738dc974c5363fae35c526eccf6b7f9dacca1","signature":"a6d58c8a4ac0a18d66afe6789e372e54e3663f5198753e6e94481cff20b7452a"},{"version":"be476947bb48a7e4e2a2bf100c43026c646e115085d076247f276f88111d254d","signature":"c34b363d2b6cac61ffe29e155fdac051d7caaa197bcd33c1dbebb9632c10dcf0"},{"version":"a1c585659ad6a50677fc7ac3252133c90ec1d60d2f44a716b6ed4f945c0c337e","signature":"092f1a685f107b5dcb94b5d54e07eaa58894ea17312c10bcbf11921448776f41"},{"version":"0c0f21b2b2173e3f325fb91099b32f3e19843b80a92f4ff2ff5a3d4235afa72a","signature":"e5861628b3c26867cdd721803b8fd63ac2568b1a32ece964adb39bcc895a1ce2"},{"version":"f8bd37d0e25c4048cb2f19e6039b8ebfa0bac6d24ac8ba58aa0fa4efeaa571cf","signature":"e21a61ef0088c9d954930ad728286dd23c0dc3790192a6ee38dfe04a0f074140"},{"version":"09256332eb93d63b5de0c4c87a64562486589143068b03be359bbaf2038601e7","signature":"b33f409b8ccb6bd57b604bf76bed489fcad328a2706408b6c444333ef7bbbb7d"},{"version":"2dbb440d516e8a8107ae311ca6371d7808832cab07de9c432b60d3e3a7e89d5b","signature":"eda6d5dc9807881492ab8f1b3d2e72637da870377f3a4742979f049f492a8e14"},{"version":"e46586b8eff1754102c56c3132d0e4622535a474a8a6b82f001baadaffe33779","signature":"7d73e178ecb304b871ac7db31ef6508abbacac21c1234cbb54dcb173b53a0a6a"},{"version":"f513672a732d70b885487041083cc99b50430dd50ee20a8e8175cb68dd6c8cc5","signature":"638eac046436ecd6f612425af86067e56d2a699a83a5eed192b905a4e9e97eeb"},{"version":"0712f215785e5dd0cad60b45d6812d835d1b65bd2d9634228daee48becd93c6e","signature":"f20151181ce2ce06fb16e3cce85a185ebd37032c37598144071c3ffa634b10d6"},{"version":"268d9444d7e21783addb24299011460c065a57101057d5ce904524742f7fd5a7","signature":"5ad606a8ca9d6d3baf284b4af21e08329b8bb2b9963eb9c8730a4f4a0251026b"},{"version":"b820c9c3de6cb1040413353cacbee04c9b8bc8dfa653a4aab2c25aa6c7c65120","signature":"35170f7ef283cd4dd0a6848be2cbdb95d0d3a1e3472a12bcf21fa59d6f81c778"},{"version":"5ec9b5391ad2f1fc9329b4d7f8684642d34f6c7fd339fbf5074ea3115ce9b5dd","signature":"9eaae9cb3456005143c5fc9a8938ff63f654a96cc6e1b0df490e68d0815a6390"},{"version":"40d627eda1855b36de7021d43907d566cf190cc41bb19c45098750d39f6fd454","signature":"97670088c72dc3f74a553fbd7594d6647c0425baddb95661037559a2d34c6030"},{"version":"e03417e94b5cc0dcf823345bb66d315e527526d7c114a3d8d5334279c7cfda97","signature":"ce511872d83ff623f8ef004de954c7688b48dc397e4d06da86d2b3ee8be28cb7"},{"version":"f33f31e669affb7cc29fda99eb3aaa0a0f3780667e31ed20375abeaaaa30138f","signature":"e4fe18f29c0d82feba07a9633acda1463e3bde4813bcc719fb4d0e5e26b468bb"},{"version":"f7956442417275691905a10a694cf23e778b1d4650fc39f23e4ea91435e92cfc","signature":"7f145dc473fcbbd9152b5f0eec88bcfefe5e415ca70d3edb84aa0038037f61e2"},{"version":"a090199ef0404e5cf68230c4180a409125dc70299541cf6894355022f641fe0f","signature":"e1b2e5515776399fcd972989e7208004f188f075498aa4f77bd888046110ae61"},{"version":"9cf58515da90a71a9353678a765a8b6c94b63b625fc05bf5f9d691992d0c5ef1","signature":"0646461331d1a1e9f1dd6b22fb002a043259f5210cd693c5959bb6d1737415b6"},{"version":"b667b78d8db3056c14aba18e2138fd530838de4e44437153b8c338d97eb7944f","signature":"67cd5d46643ba488aeb104da791a837e1c564361ce47f8bf02f18a49b1ff1eff"},{"version":"6b440e18084a343fc1590e831adced1b079f7a344aade681cc70cb688769c674","signature":"0e651dc17c75471cfc564c0c2fd648826e431307eec33d167fad6f8943b0b9cf"},{"version":"a1f703b3b7e3200dc2d735de4ae5942ced19ce3cfd5423da9467fc280fdc0329","signature":"bb851ecf30c98fe3b901290ae1fc05bdc55da8bf15ba10e1a3d10dd12da09cc2"},{"version":"070a5b980cf70e9e54d6291f31a634d1346707662aa0b906ebb47695316d94f5","signature":"0e92e6224e167b5d58f377e0513c39bb3b513cb2916a2166f87a20d3ac9cc629"},{"version":"cfca522a29f53430f1d0447baa732bf2fbfa5bebfe68d2e475432b228a496110","signature":"f8ac07e911fd9a3bff7d0a2cb3b8589e14a3b52a3d26a2fad493348f494edfac"},{"version":"431b70b424860910a8ba2560f83bd864a2939b87109f11ce22873964f1823b62","signature":"24cf0f162a2bcae8f5ae4b678da2ca97a91699cfc30898c606cda9ebec4d9f69"},{"version":"5d60d719297b5cca5a0e20daa44b23493c8d65f7e64593468e3c97293c236894","signature":"d316a8da36d661ba0c2110e7fa8961db330ee9c39732cdce1b693c8608a06100"},{"version":"703e7b32062955f1941d78af2bf1a972cee1905d9f64c8c945e0307b71c6c8f2","signature":"5c8e01f96eccbc91b7243158ddef84763ac292ad5e914a352f8422f4b374aa6a"},{"version":"5320b2c2ba15fc1d0250cfacd6deec6a1b242c1d03f5d5bcc2d7ea8186fb8787","signature":"7a977a3406b9510b629b97156a3917b9f347835d16ccc19e110f0bda76af6621"},{"version":"7b7a0b908bf6dbaee29816d012f9ef2ff0b0745ebe5977484e12ff0d2a1d4fd3","signature":"7b90b0d17d565ad2bcb84936503634ee9f77cf9f40b2353797af5c1dd26b6161"},{"version":"7ccdddbe6d980d43d92d5bbdacf45f1198b4142db652f6e90ab97375400ac468","signature":"ae5b73a3e026381b16450aa4673ea1b2ca1e7fe4ee196acc0a3f09c299081d51"},{"version":"043caefcacde199496905b469f7251c08948c4921f91e5a0f5c0df4f03cd2d55","signature":"08a95f68e870bfccbd65af83835d7f74d53e33f3a90926375171726c9394185f"},{"version":"d33e44d9c563ba82cceb0c3fd5a20de58d19a4ad63160482de55bd9c50c3ad2a","signature":"c53d5f0a2eac0af33a7fd617b3d035c3f95ed81fc152da2058542e01ced0fdb9"},{"version":"58cca2c47d5dd00d8585348eec7067b9e45f9fc89c9c430cbac18e2846c4bb80","signature":"7eca6e5608816544c2487977bcadb1118578578f54eb343e7ec2ab82302f82d2"},{"version":"002b0f75fc367ff79a5c95065b8d1a38cb78121d2588ba872110920bb9643024","signature":"512558fba7e0f5f8d0cfaad40f05937124ee8bf4c3a11dcab9f618afa626fc0f"},{"version":"920099117da73b53caf5e84b81cc4d2200bec4f82e818bc23b7d079a2a56907c","signature":"396d5e07f113ce101976ef3238521989b0162a3440f8f15e5115f00d67aba169"},{"version":"7444ab226ecde90756e4e31ca68280797132b5c3b38348dfafbb101346ff9c4a","signature":"41a5855424d478222c6ea0546f8f0e7563b8ea830f0b02418285fee2f6010104"},{"version":"4674b23baba8d8d1145d47b4d8db58a1161a0f0327cc5e05aaa3c70dea3aa4f2","signature":"597635cd2982b768c8075e33902d4bcad6b823ad6837b83bdd5df1108a8b5ef1"},{"version":"dbdf5c99dd4d0362a790a664fda2f7d80f0b90ec20d2dcf9f4e71e5d859ee247","signature":"7b9aa1a8a9728abd8faf699093ec32552e44ceb2e3e4eea7fd39fd4a105abc61"},{"version":"7007c577d3881953fee9f301de570abe4ba1f6a54fbe2873968dc002ab5e5629","signature":"e7dc47606500af2c3ea9d69e3d9ed293bdbbbf81bfcb0c48df6568c1ffee4b02"},{"version":"cfad3779365697cae7b23669d57fdb286de38dcd3c9b1fd53689f9ca3a91a2f0","signature":"51838b26378f28235d88da3177a2f581d6325b1c546464f2f5bcec82149eda0e"},{"version":"a6e432450d84b15cefce91791dc06498a08e357e05526537db4bc137807316d5","signature":"92cc6638c98debee2178e4ef0e1cd3859c86121ea2163b0b0685d41a96d14e73"},{"version":"2cdc33138be52678761d11065245a401d3499f110f502a2cb34cc2632e9c5e61","signature":"41f303a470c94ecbfca891884533f4544fb3ccf7cf97aef0ee9b65df992d97e8"},{"version":"236b3b7d3b7a86bfa27e5bbf1998dd7a09b9b6ae3ebcaf1040be305324dcb5bd","signature":"347ca2f28c70154d1081c55c3772b0e5073d3482e261847eb6ed655894401136"},{"version":"e22bf0ed5f47dd3f92ca02adde6e4459657a649c00c57071c4b401d6883009bc","signature":"c21b522e44f78bed8f3053a3824eeba6f32c27dd933d6b45cce037e8be0d0538"},{"version":"6f15ed3572bab8df8b145e2808ae12e8945eb7174add99ccd556f8bc1fa91b5a","signature":"dcc74f798751ba65a1cc7a24a795e46be9ec56f409261005e1d69b732b013560"},{"version":"d592e7b22c830bdf0c8da2ee4c4d5d3587675ef62db03bdc0c78df8e7f7b7c80","signature":"8f3dcdeaa6a4c6257d53aebff62fd88889876d55839a85929f5ae2d3a37d5a73"},{"version":"19edabca93b6826a91c26832d55037e487218d8f29f2172917ef87ff08f8f380","signature":"cb9da35a72a402b315b10d2569a304b12543538dcaf383f4d9f8dd5a8114927c"},{"version":"fae8d4ba3bdfd3f087c40507d8748236b0c08aae3d74c706651a15c0e27ba16f","signature":"2496283dc414126ef574138ede1396f27877de39dafe04d183e30d2c38e2cda8"},{"version":"9041eb411777fc80385d1b639173fbc6675ad3d7cbce257f52605d4b18616543","signature":"748edc1e544cacbc98bbdfd79c2a36b98f9e35ece62316b401da130aa0769631"},{"version":"3aaac2c7f4e18c47e5197948b4f1c4d1d569257499c1dcd2395bcb15849fdae4","signature":"bf80d1b3fd049b9db79c5bac94e6a4b2cc9df97720f65c91a62e095d793499b7"},{"version":"ced161d675dae30f24f7d001a14ad62504d69069b971753e9a8003b2200e7cc4","signature":"1c51e31907648173207e55a21000c17277311c053c658c91e5505c4f1cf4e9c6"},{"version":"9a5d95bb08f3be86b32723cdc4d4165a66d3008f375283cb31100fdabbf913d1","signature":"dd0fdb6f0c71a53e434d39a22ad54d9d196de67178d156fa5b13df073c527f19"},{"version":"22772822785aea051e4454632aa2bb73baab3d08d48cf7366cafd8f19e1e0c4b","signature":"7c89ddbb992896a2006feae6ff5ce82f22e2158dc35be692798b4744a624221b"},{"version":"08069afec7cba0f29e89b4fab6af533440854664607e1a6381781df96676115d","signature":"a78d3790cc5ae1b4930c299095023c07eccff89cca32e2939a4722a716c9cb55"},{"version":"cc5667037f805a2921883f7fa5e091aadf2ee8907c46d61a7d7992911965eb66","signature":"442965f5309d0a2d4d8def49f2aa35996d1b73d37c0b296eb305501cabe8829f"},{"version":"7ca021d1c2dce515eefcf872fd521c489e8ae3509706d5af45e4a53f7ff22549","signature":"094975115f35bbba2f336b32b78b856d111acc84119f62ae45f2261094e048a4"},{"version":"a2bc61e9998909cdcb970a57b46b9bb502242bc9d93a9a09be2c2f446baa729d","signature":"a7ea6ec679bdb15b1fbf1f1a938e8be370ec62bef5744c84361453a59c182a1f"},{"version":"736e4e795c9de961b77adbb54f1e457e47abfece7bb141eb1fcb6ca6fbe2c9ec","signature":"0f071aacf6b65da0adf7ede684da36b0ee0f310cf6b429e2fa37fbeb90ce79f8"},{"version":"82dbb5baa7af6aca1b1392a81acc3bbbc07f50ccd8cff2b3eff2ceb1c5db2182","signature":"e9708da92b0cb69d4b46485f491a6f053fa07145eadaa8101b16fa6738100f9e"},{"version":"fd3444a7b0304c83565c7d69296748987a09c2a55377bc9e6c4d32961f8c99cc","signature":"669ed183124d2bd3cc638ee9422002a758efdd672388d6d6121187f2d073d024"},{"version":"65f0fb9a264a44649bc963291e7d6e810c6c06350748007de4e1575eaba8d319","signature":"814699b1fd707185dac005f3047ce5badb2b34bbe355ec84b4c3cadc82008b8b"},{"version":"08fd220005ac42cce029a0f6ee96478b45715a8a39ebeb71e42aa726597ba44c","signature":"d81983bdc0492ab963061b9fa1fc64926ff2ccf744fc3ce3922061f6016ff571"},{"version":"904f2be841ddcf41a9358e1ffd053618fd6bb1ff1446d429fc2f253af774c5f2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2ca66d64ada0abbc61f1e626ec99774d85b81f4090bb27f7f3d29c599970bb3a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8e27dfd3b35176a3a2e4307206a9ec3909995b23657330dc835e7b5fd50ae89a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"72ac7a0ae5374dad1652ef8e41ef145bb371e9b8af2394b91a3b6e0220b5f39e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"943d9fea1a3def860d7398ac4c9c72712f969552e4b6e913da7f37b20defaaa2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cd34778c118327f37b7d398e6f0eeb648ded19b5398bbf6aca8af751167a18fe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f9527355cd60b21f51b107ab7097d05e058c0d4a38f82738d111c2c9c47c4a87","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e37af3d628ddbcd48c89790cb73dc028f308dba050d3ba3ae64d1cdd491dd7c2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5681b305d9e4a3f2a27695871b6685c547771c8df3321ba62aba2569d3537076","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"90e42071689c6160951272f117347d7859a2ef54dc83f3879eacbffcbfee1868","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"faee2c92e93b04bcf0bc3cf951a6ab15c80022850773ba6721dba52e84a5ba41","signature":"1342888987d6078504543599fffb3dd6029c2c0f768b489cd232fb10bedb675a"},{"version":"e353b7b008a1c093f02f51d6c46b2c1ef2c28fddbbd38889a6a7e4c224916779","signature":"8c40edee0d9d2d04dee8654ba2f8f239f1662ba5d78ee296068d1e17dece3391"},{"version":"b2ab2a7ba089dbf1480307647bb4fe436039011171626946c12efa7ac7ef23ad","signature":"ecacb7a344532575d0bcd497fdd22d7e66c42117aadfa1fd211a47dbde3b364f"},{"version":"b858241a65512e697bd928eb1675541fa5fcf2b516aa837f90cca9ccf23162f3","signature":"6a9bb53e41455547f9529dccd266b05e7cfd3fd72264f41bbc97581094096369"},{"version":"045b680cd4cc18bf4d40193feb610f9692e31e055ea84a89bac2f417831c7ed2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e6107e7fa315d770a69b9edc7dd077036f115479e102f2380306a7a92629329e","signature":"0717bfe8ecec022eb7f964ce697b1b0d749e217864e7a11b438c462ca1e55412"},{"version":"b26bd1398c6f71549fa23175f3c8b8245fdd2d2092a6380794cf8ae8ee67666e","signature":"99173fef2fa963dad3c3a06cef6bff8e4a9e4fea656ec6414aea2f84126ffc23"},{"version":"002de79e07e5851f180fd44a17d1f855b5a17fb9a00f9a17cd53ca055d27ab8f","signature":"08e14aa9343103c4781efad6b4d287b2d577a8266f51f389b2ed4db8957cb5ed"},{"version":"ca319732ee32ab8064188cb5e284f7d8994c1e05e67bd2f7f55203eaf67b33de","signature":"0de5e5a2fd2db15c16147aff67475c913395e62c14bd7c5313880b001a88e009"},{"version":"04ff795f13235dcc2df104c2363bc370338976c37fd408129eee133fd481b1b6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d043ae7012e9b61fc3a47946b043e6feabd01d8a1d43f5613bcfe1fb1d144fd0","signature":"ca0cc7ec2073444d6a6e3edc6a759fd98917651115e6c1b56e805d7226d122f9"},{"version":"e48cbc2646a758124caa3c87b05a722e8da250afbc6fd5f4eefda072772b4616","signature":"2a57bf7b0ebfba810bdcc71f9eed2403e5a2aba006c55788b48195c51aaab8f1"},{"version":"056fc04ab05389b453bdac4ec2e3c1eedd8bb661c20c9fe2e184125c8d69dfb0","signature":"abed2e07ab4d9ae16437a73aaa4382973966df349afdf5eaa654b3f766c30625"},{"version":"cf4d4045a6ef47b776863026fea118f50fefbf94bfaca15b330d5c939ebeae61","signature":"f670d82642bcedf7ae7e34c49a5dec771f607f89b47602cd0b7508aa981ec2ce"},{"version":"b0f38f471c06b85328632e90a5823eec4d4bbdaca72b634b477325cdd68d62bf","signature":"704594b25466b609c3bccd775f15b2118e1ac95cbfbca960e5819c93ebc1f8ee"},{"version":"2d3dd03df960f48735d9ea246405ce7f2f6501675599c7342965217e6873ac28","signature":"95e604b1fe25d3994cb3ff463ec3c46968a7ffcc3615814407b7a78359431ea1"},{"version":"dc2a05a4d3db8795c9c161d8e05a72d0548cf61e5b5a1e992b958d287d148f20","signature":"3844cd66a0ac7b19cd62be77527a4a53499fb22a7a122d735603ae6979064756"},{"version":"2c008ab0e0b102c7d0752086dd258c08086879c4ee036b40eb909f53cd444f76","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"078f20355a4a20354fb1a4ac5e7b0398aa43eb0d374aa77846c74b1496e7fb79","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3e5cd51e11096be95c01de7fee203750f0b365f46dad987f3afe9fb535b99122","signature":"c84bce3c55622bfec01668fa58087cc8073885a20ba030d9e9b519debfecdd96"},{"version":"e50fe642408753de3208274d3a7c83c42bf821821740b79012eb581ccb425bd9","signature":"8674781878cf01b59ae950a13994a74b11e766bdc3d6a87ecfc77d1e4e0fb7a7"},{"version":"6ada175c0c585e89569e8feb8ff6fc9fc443d7f9ca6340b456e0f94cbef559bf","impliedFormat":99},{"version":"e56e4d95fad615c97eb0ae39c329a4cda9c0af178273a9173676cc9b14b58520","impliedFormat":99},{"version":"bd821b87e2c0fb5f509cedf47da465c447451835ce0fe2a752c4fc53a9f95a5b","impliedFormat":99},{"version":"f1d7352c0f7041abb43e1054abb14fb8c53a13dd54bcc1d67b97d2c02bb5028c","impliedFormat":99},{"version":"fc820b2f0c21501f51f79b58a21d3fa7ae5659fc1812784dbfbb72af147659ee","impliedFormat":99},{"version":"08f88f75fc2f516413477606a4122b6d3f6eb6680e8eb79f3fda5a5d2ed306df","impliedFormat":99},{"version":"6ab9821afd2a06879620eb4e041b9492a90f294e9b733ae5eb022edaa3964a45","impliedFormat":99},{"version":"003533cc3fa10cc457668d4256d21a65706a67a04251962cfe85d240502f8d67","impliedFormat":99},{"version":"6c00cb8a4b187505dfe21aff242b07f69f84f5c832e8ab4357af69daaee1b0df","impliedFormat":99},{"version":"de14ddf9d780367c6a117bd8a1718d491aff66094186523b3eea680ea7035a7c","impliedFormat":99},{"version":"ee06b94d0521cfaf91e4b003518eeefc45bbd594b0c22955fe35be282958252b","impliedFormat":99},{"version":"9e5f8fdaeb03f1699392b4724a58ca7b47c5cbb6762920d2bfc722c265495ede","impliedFormat":99},{"version":"d05bd4d28c12545827349b0ac3a79c50658d68147dad38d13e97e22353544496","impliedFormat":99},{"version":"a1597b0039f39e9f3eeaf120f02d0c94a826fad30b027a2abfdb8d580c89be70","impliedFormat":99},{"version":"04ace6bedd6f59c30ea6df1f0f8d432c728c8bc5c5fd0c5c1c80242d3ab51977","impliedFormat":99},{"version":"57a8a7772769c35ba7b4b1ba125f0812deec5c7102a0d04d9e15b1d22880c9e8","impliedFormat":99},{"version":"1de82ba3718b2b3bc5333c5bc35da5cfc46d1b654edc012de46bbce48126fcce","impliedFormat":99},{"version":"36eafdce35542335372c9104a44db2597b5ecbdb11af1177da13d457efc94fb3","signature":"d6ab9d3f4bfde85a62fea7182d4e68ba2104946541f47eab58799009a38ba2db"},{"version":"d153e1cec75d95055701de32dec8d0ba9c9a89ce85bd371b7d51fa15e495137c","signature":"2eefd9c7b8dddc8d713b7ec2f408480a4c1ff14f1975c85f91834b8886963f8d"},{"version":"fbe6ac1edabe62f9565a5dcf644865c3b839c759c6b81a0991e7314186c77c14","signature":"5e5a13138a956d69dc4e30dcc820b816b253b6907b02e168e1067a6f026bd4b3"},{"version":"86d4ff8ba66b5ea1df375fe6092d2b167682ccd5dd0d9b003a7d30d95a0cda32","impliedFormat":99},{"version":"736097ddbb2903bef918bb3b5811ef1c9c5656f2a73bd39b22a91b9cc2525e50","impliedFormat":1},{"version":"4340936f4e937c452ae783514e7c7bbb7fc06d0c97993ff4865370d0962bb9cf","impliedFormat":1},{"version":"b70c7ea83a7d0de17a791d9b5283f664033a96362c42cc4d2b2e0bdaa65ef7d1","impliedFormat":1},{"version":"eb541103f61ea69ee1795085a473b727d46fe49ebc7091c721623b5ecd87c0f7","impliedFormat":99},{"version":"014ba72e2add59d6d2d2e82166647982c824639e2902ccd7b3103cf720a0cb65","impliedFormat":99},{"version":"e22273698b7aad4352f0eb3c981d510b5cf6b17fde2eeaa5c018bb065d15558f","impliedFormat":99},{"version":"b78c801c3c21015ee487f6494448bcff55bb6b61f41172dfc2c26f2218d99138","impliedFormat":99},{"version":"de97e016d8dd4869febd5bccce02eb96957089d04b74ea5d1dc0e66112493b64","impliedFormat":99},{"version":"671ccab2e6a253d2516c0e4699b3077fc30cdb70b4436d8c79d76c91266a1a94","impliedFormat":99},{"version":"a11fbd8ffbee6e5a7fe4c7c23e6a391be615de2e710a6946d7d1f947a85a1374","impliedFormat":99},{"version":"2d383c515b9b606aefcde23da9c312a69bc7976b75abb85c02592f7a8589a343","impliedFormat":99},{"version":"e760f7860d08e9d42b6ecd7dd341602fbc0c13d60eb30beaf1153f1c7c44d66d","impliedFormat":99},{"version":"fb04e1ca667399e7302c033656cc285e6c1cff9c29f264cf229dd25e3962a762","impliedFormat":99},{"version":"ca6fb77e3480af8f2287ccb756ac88d047ba8a8bcc0512f6720ac1216e274ea2","impliedFormat":99},{"version":"c0cc44b0ad2fd65c933d187c4faad6157efbed33c3c21023802aa6a89d9b9d13","impliedFormat":99},{"version":"ddaf5d3ddc45282b19fb0fecec91c87fc9b4d1f45c2ee611677345c81383c5c5","impliedFormat":99},{"version":"5668033966c8247576fc316629df131d6175d24ccf22940324c19c159671e1c1","impliedFormat":99},{"version":"d76df1670eeb97afbab6c87b8cd31bbd09dbf9026ff0ca533b5d7d3fc0291f79","impliedFormat":99},{"version":"e9b947b944887cf2cdea2d6188f412a25125eade82f2fe2334658af86f14cded","impliedFormat":99},{"version":"bc05fb9d657d30e61d50d690615f379b0d0415b8f29e69196e1dc6bfc664dc57","impliedFormat":99},{"version":"e315bab2f28d53f9ab473d9de610c455b6c414757bb19589b31ec8f490cebd4d","impliedFormat":99},{"version":"d999dd5abf4befbdab5f1248193cbea69b323b71131a02bb120f9462807fcd5a","impliedFormat":99},{"version":"031f1805f87171e8a9125cd99105bea4a869018ab2356c2e29dca7c86925510c","impliedFormat":99},{"version":"bdcb070ed484b40b84dad668b58e4861f7c3d36f38632072dad5f905bd8cc0cb","impliedFormat":99},{"version":"54a97fd1e2f33041d7b4cbbe8fe3235f97fdf1a05e9fa41e78417851bb8f1c78","impliedFormat":99},{"version":"f63e0743e2ac9f7eb4d3b8bc110834465f164eb9e75e4f531046e4ff9822f3a4","impliedFormat":99},{"version":"0372d6d6a41ae89f9aa86eef998b44bc0ba035d9d09c9226e0a150ef4578fc3d","impliedFormat":99},{"version":"cd8a4297d0ab56dc571dadd2845e558c9d979fe1e120a0dec537935bc8a36dd2","impliedFormat":99},{"version":"079a12cb0e0c42655d77da5185e882b4cc94bd5c6c2131171a9289fc1f4287fc","impliedFormat":99},{"version":"50cf14b8f0fc2722c11794ca2a06565b1f29e266491da75c745894960ebbce06","impliedFormat":99},{"version":"d4ce52c42c23981d958206037138e05f7b48d41faa1cfaba7e9eecce8c2e5489","impliedFormat":99},{"version":"8a3be5afe0275ce84a6a6298010e66d54d2d2f8e927df6bcde0ac326b5e81792","impliedFormat":99},{"version":"167edfac7664bec77aa2efb2ce9d515c41b5cc4269091a946b3fa6ec4e7e8738","impliedFormat":99},{"version":"218997a627f0efc4b8be5e6bc0b58e0c9edf250baa3674b661d3f7e6a7ef21e8","impliedFormat":99},{"version":"c3f1acbd39f587a7539d435d6c78ce8647b3bfdc5435df153a70cb2656b52b80","impliedFormat":99},{"version":"a1f568855887e2b5121e8b5c4cacd66dc5428259981c47026d95be1d844adf71","impliedFormat":99},{"version":"1db6f2cf99c83598669df0c7166405b0e83c153a03b59115000df3cb1afba79c","impliedFormat":99},{"version":"49af73d71b88a99b1a211ec02bacd321c21397062d253c605bb8d140082eb7b0","impliedFormat":99},{"version":"a50de7f1a7eadab7732d80dcf9c8a0c0d7d00e33315425316757006b5bec6e46","impliedFormat":99},{"version":"4c6fe268c2a984b3f14031a4b09ae7b2d9e51673258f4b4352e48d0c6ebed679","impliedFormat":99},{"version":"3bc3d81dbfb842bcf15454aacbe37cfeac57f1d15e829812ad02a05cc42be873","impliedFormat":99},{"version":"016952415e1ad35f9070b4d454946e79cd3881f3c76c0978d759b168fe033018","impliedFormat":99},{"version":"6bf5dc0c9f6b6c79fce77b56c985dadca4d4d474c9abf9139ae0785cb5c01992","impliedFormat":99},{"version":"d507276467f554e383b4fa058f42b8fdf5c15f1d1db84a2372bb569ce9d57a66","impliedFormat":99},{"version":"b3af5d182c9ef267d58cf43f3e51ab73986862436790a4dbf076b5994758d53f","impliedFormat":99},{"version":"1c7f26a88f861afdccd8f9d9e793f1affef4635d16d2609c487480c41c42f253","impliedFormat":99},{"version":"4d4551dcb3fd19a4f22aaa63c6c391d42ce44a15602a6f6a19d582709edb24d9","impliedFormat":99},{"version":"a76075b5aba8187b1fc5c8f565745daed6e4341e64b44e6ec41412a16d575d62","impliedFormat":99},{"version":"f836bf3653e31c3bba120071196c95d416b83c5d860ce27549975f8785cd670a","impliedFormat":99},{"version":"058d970583137cface729371715449aac0c1388bf7a5ba15e0be952677485fe3","impliedFormat":99},{"version":"31c45c074a9acb94dbe340d9336d3c915635eac2df3308916fcf41f2ba6ab84c","impliedFormat":99},{"version":"774256d456ca1d8266f6e2170a51bad2659cb7116334d1e7977595999533a5d0","impliedFormat":99},{"version":"07916d3ee50b94b3217c0bac71fa9d70d48f51586481c7cf61af2a8ebc2a9db5","impliedFormat":99},{"version":"f687f35c2206a319dc7d8f0b751e182638c912838ff54034fb782beae50f7cac","impliedFormat":99},{"version":"1ea2d362005804d980325c2fe6ca0abbb145197b856a64d80016554129966c97","impliedFormat":99},{"version":"7a81f15892b1c8d0cbfb35605038ce5c6d0cf93542946aa0b8c415dbefdea1cd","impliedFormat":99},{"version":"22ef1a1604bed6e226888a2414676ef477a7ad5d6ed907a62d6e40c831797366","impliedFormat":99},{"version":"2ab500573da35083b48fa8f4fe719860099d1502df3384f977eb22ab6b14caf7","impliedFormat":99},{"version":"9fd7d60e314f01c950ba31932c150dcec5db2c82de3c7fe0d0d24ee8b54f1fca","impliedFormat":99},{"version":"841d1f32f66723468772802e1558eb36ae0226c95d0527a3ebfcfa2c75b6f6d0","impliedFormat":99},{"version":"d3327c9f7dce1e11f5ce85b8ca921668a13068980f7f4ea4daedbc2c81589e9b","impliedFormat":99},{"version":"a698ef4e27ad6053ad8c189b53c468f857501046aacb554eb52be0403ba7f262","impliedFormat":99},{"version":"94b576c860480aac3eafdf904cd81755f5c9b16c3e0ef3253953a8f4fd8cecce","impliedFormat":99},{"version":"8dc7c54b72cb2a49a7639dccd99a559c243667a74abfb09545cf8afaecc58056","impliedFormat":99},{"version":"d2166d3793936235216ee5d014bcf0d8695f3a954ad54c01b1976c05f544ceea","impliedFormat":99},{"version":"41bf8c3193b575946682ca243de53370f61917035c3ff3fb747067bc680f2509","impliedFormat":99},{"version":"916fafd410b9c3a04bb3720774b0cca93d1dee94bc88b4ecb6edf56cd5585abb","signature":"50e5d708858d82cbd8bd30ca7a76597632b0dff659403765266a4891b35a712d"},{"version":"ef82bdd9d674d855785bbfcbec2181e8d602c430bf73b4b65ef581d78ecdc64a","signature":"24644a17b266badb345ca337c9d9c80300473c40b2c87a28e5a3ddc011551909"},{"version":"846affbec83fefdf905e16b3fbdf845edaa248b5895279498aa6ac733ff2a4b8","signature":"71e597ff732221dcbf043d2de4000ccc5326c9ac63b12f2a27f89b5adf18e609"},{"version":"227f3a03b267191752ed1a2381855cd73b0915794ed51151e5ce82ffd786dbde","signature":"22c51e70701555882fd248a93bda5c759c024c0b88a58ce37a54ef186729e795"},{"version":"4a700720ced7ebe4c0c973bfc450c6a7ae31f82fd447e0f464c7171562e8aa53","signature":"212577e3f6db3f7bfb26e82ef9385a9c0c241b3906ccb6d80e4ae6bdd657e00d"},{"version":"b69c8778c50bf0caee3dd1d2da2fc7d5f6157498b51cdf51fac81476850c715f","signature":"0270d8376c084b2e07697ef2de94f943eef66b6de4b77fb20147d306f645e990"},{"version":"39bc5068f51c657236fa9a763dde5bdae05b46bdd49d0390c4a72fa9dcb45dbe","signature":"2f67546822e0445ed6a5fc1d2e96bea837385d7b11803f8214835933d03ede63"},{"version":"2a6012a4f4a4695bc0a97d29f47861bda054359a9a60d295ab26f416f95e8940","signature":"dfb01e57f4a98a678b16d78007abe78b8600ec7545e63331d72a0daa6ce961ad"},{"version":"094e1a72a14a0f38f950e388d9a4e8f6118b493a5918235de9781d5c47f327c6","signature":"d7cd6120b5ccddff937be1aa22a538829f8a93ffc9b4715519f67bd21da26689"},{"version":"60523c590e7ec5b89c49c0728f8b64ec2132482709ea5c5909752d6d68c93401","signature":"2315efae7ec760b18fa4c15f987003721972b75388eb00f80f3a419e91159751"},{"version":"96118b858afcbe20db893025dcd75e19fd530bd5540c65029500e8cc251c34e1","signature":"bad3fb3837da6b89c49e110430e58827c321031273ba09aeb1c83a1e0e9dec70"},{"version":"17ae4d2c0c3487e077a7f6db647df1ebc56194a135262b8af15b19a3a1072452","signature":"7d5919c7ef0b53f308a78754ac25e352473a2de6bf6e25e109cd03dd493aab54"},{"version":"a389c1e6da14dc436285d19455229c3ecb445f0d26b4de5e4df0c223e43545c4","signature":"7e2734061c31bb7fcc162aa37af53a181cb8db1bc2ea1168ecf1c816cf52e045"},{"version":"c40cca5deab8288e95cacc2e5f8d1d2717f9b49e3617cb3ac992847d5a143fc3","signature":"d23b1f070ca79bf4cececb66c23b78eb3f35e10b3ef7d0119549521c9fd2ccbf"},{"version":"be28318b8f96ff27ce32fc1882bf6f18e306dc8ae65ca3361d7769ff98c933b7","signature":"36b9e77029aced4614bb01342291bcc4fc65360f32e9d3d639c8b38edfb86169"},{"version":"f8fcc667b4a4cc586bfbf3d76cc17e91bad6749ee634f736ca957ea7377cab3f","signature":"4f3963b6ccad89bd71ea9c5e491a83c9b448df7d36a01ec887aea29400c52cdc"},{"version":"fee446e0178c52a271d63c9d12598620eeba7a0a0178def71ab7eb70837d7f26","signature":"5eccb4db63e70774c70de6e6e6f67f3f4b26f2801767073541a772077c2b8458"},{"version":"dc1d17e168090dd4df773ba839410a2e106ff0f43163a994f681a0044b4de578","signature":"6113e636ad4fffa72648f2a41eb42ef83262089973d54e080d130cb4219dab4c"},{"version":"01ba304e845f2081cf6ce244153824e727d70d9acbb973de8e2b6340e4355185","signature":"9adbd23317b76a09a9364daa02f7ad358ef30c277dca1b05e8df356f7751702e"},{"version":"e8ac8a0a426c433de3f592188e1fabc47b43cc63be440be87f36f5f90980fc56","signature":"098fb9262c019dd7c7d2bc1efe85f61d7fef30a8c6ea0267398aee95321cf1f5"},{"version":"5e8cfa753701ab1bdd8545e9436da3c53f24c179978efab36a5d919484516735","signature":"03f8247a05f82c8f96aac14f06944d2dab5061d08fe71d98e429251144b7d9d8"},{"version":"e9ff70874950ac2fa288fe64a6fd622a06d3579bfd781be96bea79fee7fd1381","signature":"1b758ade259220a7723152591ae4997ead9ed62664cd36c08289f94fbaaa5511"},{"version":"574fede341569986c736fd5468e28194913dfe16685e804a5d5fb0a24e71384c","signature":"cb0d718f6419c7cf0ae1316734875540e46cc33e10e9aeecdf79e2f29f6eab4f"},{"version":"2a02d329b308ce5a74632fe2062c72c049e672e5941c5a8204bd14408859c3b3","signature":"291d5759f6ff0d7180456b40f53ac4ec52d6fb91b6688aa2748e70feabcc8124"},{"version":"3970978fc2242f1ee735a0b0f0dfa8a42e17bf5bc8d9200e4e7059c63877f4d9","signature":"f8328683d5b3f602c387cf200cb1726c422dc90197a7fdb0d578fcb7c9bc6786"},{"version":"7f4483fd17f3160331ba5ace194e18e3687009b63ff4f66489586a25f497c60e","signature":"4550911de88ba268a6ebe2afc50d958d54100677ee698557cc2d5a6a36e100d9"},{"version":"9607e2d3418c1e50af1dac762f78b031f5f9c24f13ca4990b062f27c4f09a340","signature":"4cb3d1e907efe7537c8b4603e87bba3e9afd8e3294a436401b5b95fd2bdebdfd"},{"version":"3f9ae10a4a447dc5fd8d079cdac3d973bfbfb61149d6d34421ca8ccb9fc25a8c","signature":"6fb16d7f85050f01ba2e8248d33306db324277a87040aed2ac58e20343a0c2ac"},{"version":"d8a627c1f6473ead38c4a7fc6c22a1718e4f4b83855f85eea45cf645aee63cb8","signature":"c404855e249e727a187122c5a1809d1e93cf3bb3af6d64d68dabae61754a2da7"},{"version":"b9de8a489cf0a4692ea10cebeb35b2dfe35539ba92c47f23aae36f1aeedffc3f","signature":"37fa56790fd8a57b9e8e21bd7f2aa4cdd33b7a833ae9626d8bcb9eb41e0288e2"},{"version":"7ebcbdfd5763421e021e5472bdcde0bb7dadd2fc6bb2d81f70309a89362155a2","signature":"d1e471f636d7ec618d53420476543d28b575d5c30f18b86931f648214ced21b0"},{"version":"826e0ef6771e8bdb186b153dfac8f181926f29570c7443560bbb665099eee80b","signature":"39dcbe7a573f3d3df729c6028108cea477260aba94ca082a95de9d02a267ef27"},{"version":"f955a769066260ff6a27a22deed2c93ed071342093caf86e7a6309d35eaaa480","signature":"3b93cb46f96d399b4cd9ed35122df5e43b202839ee7c8282632c5c47c4e697e7"},{"version":"60ba574b03771c2da031380bda16f8ebe86e64be2a04f31c53d572edf987d8d4","signature":"c81efaefef37848e456f15aca0b42ecc599fb9fb73ed61c95fa7f7851c280506"},{"version":"316b866c3bfbe957ec585f572fab4b2f7a35e8d9cb266dffe597e57927a5d66a","signature":"19d7ddc11ff468813dcf97fb05f4e51d6f78e16a0030933a608aa0fb9f2ff9ad"},{"version":"a17a4fdad4f5f7be2b342254233644413c8ef984661db43c951953933083d8e9","signature":"209ff798fc5f35a3705982a320e8dbcb321571e046a96de4092b4465b74fcdb6"},{"version":"db6562108a47f4a746b4bea1694912ec1ac7ec51b48e3a31b274b4c8102ab772","signature":"e1a2f10bdb3e04997994496c5f189b4eaa3bcd92e06761164845c59133af8c4c"},{"version":"85592302683b0f3d636e53a571bee7fe59803339b8b3eeaa9a5f3e43717bf81b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"77e9654c2e90c0915a4894800e66a9c269ffd3f0fe06bb17c14bdc23ef7f5d1e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"861c7b678c64d3cdfa0ad2a3f529dc1f57ad0252f6bf7db739be18e14c79c617","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8ab61002066e314d8b266725a4ed3db857e41c7aa11927aa4a38386370204af6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"62845c5b09ae355ab3bc4c4745dc5585b77b447706ebffb09ea3641e5c963da0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ca973ee48e138941af5747d17cc31073d1d08f57241a81b8a6370fba035c57c8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"734738e3b2da030816fedead484741535108b3bc904a7b90a6f8b2176d05970f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a2cb0d579cfd7ee8015c6adea94ddfeb2d7e79c040ae9ea9b57275096512bf0d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1d95bd896b6216d08fbd7ec10a33b40d09d711e3fa102786292ceb82e4b8193f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6dbc20023316e17c6ae6382458fa64ee65049a6367dd648e89ec443cd59ca18a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5c6eff88e726a6c9ccb73bd9f6b02dd0e248fba87dc47e7b3e211f3e9680b24c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"31cf5e25aaf868c1ba0e195b7b62f5cb516b70a5bd6e2ffd8eebad1bb011c24b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1896da12486c6e51bb02c20eaf22d1826fe48349e584f2c59c8506e925172b44","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"daf5363c1b026284d12cf8d49242827d18004e624ffd100bcb74536ea92645c7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5903b40fa3676f924372e37bbdca65ba67e3191a92f52852d5b70a2153f664c2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f7e7141383221df9f2f32fa96d8ac65ab8cda4e4f19eab9f69c24e09ca31b172","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ac0bb5930db453fdd87419f223d44c23e8852223300428032ca09c4a497d9ade","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f3acc54979130b5a6786b3cb3a1f47f0330b6acecf6c509a09c071a1760e3f09","signature":"73351372b4295fa8b882bc93e30276d7a911cadee0f013b17f66d50ae3de6a29"},{"version":"c7b6e3a82a16fd54330388cc5023d8686071c102d3a4cb1899a74064910e7704","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7739faa1d7e4719d14729d52aef996a6d8c8b1b1447dd9441728c642f46d4f79","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d1e9b0eaec1fc9821665f79d5cb10b16f5aedda997b77f67cfc634c219be45cf","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"eddd32b79454e4df90e6e3bd8d43a997c8813aa61e2be0c79875c87a5d9f7b2a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2c217dd4af49f75ea5671d76d7129d3d3154589fd0193c323ec2c687ed13ca62","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"70879fcfe03c15515033be18baa3afa57f4f4a6d6bce8801e87050b02e04df55","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"811af3b90fea77ad0ddc26b1a7f884d9366a44b20efff4c2a15de5bc9b35bb2e","signature":"16a8c433300e1e2ba1998062452df2b0ef51cfd21584e8bdb0553d9b0aa8bd5c"},{"version":"03a54222bd4814fd65957c052e3f6f68b85189a69e50bcca23e1fbee4238ac1a","signature":"ede3e24a18d5288414797441a3b532bcf9dc229cb41a5bcad089a4814f438a3d"},{"version":"e292ff26b159b2acb49baf29c18d233486c78afdd409e727b71ef6cffd21378f","signature":"2fd39dc262c1fc3f21d6e25374b30919d9315210346a585dc31a758918996577"},{"version":"4ed889a15e24a9e0f20d3642768a080cbf47254ba81997f4c88a37d1a7d0a7d0","signature":"f1088a946445f681d8bfd7cac8fc99d0549d70cff4e47179d361377e529118a9"},{"version":"a161552e025ab65f8b854f9a3338f8c69229c0493b13c323252d07407ecbb1c1","signature":"b5984247ba3e47fb79e844881c939f38398dc60958d0b29f9cb87d0e29fe73f5"},{"version":"154af56b732ad2cf00fb80508d1f3158f0497507c9309670b66758fdc0461bd1","signature":"66383839201674f99a40f904e89c5c9454d3d344ed91210206f28c5776fae9f3"},{"version":"70e051b3ac6969f054669d0eec72f57662efbeeebaec77174e96cb91dd3d7b9f","signature":"de7b7c00fc17f6accb9531e5271897cc70db0063fddf8a17d735db6fcf91b395"},{"version":"e8dae3ce6fefe470c1bcef3bd55eed9f3bd5b5211ab5ece54899783b80cd7b1e","signature":"31c27b104652e1136c1f2c56ef27f83380ae8587517ed95205649ad261a45812"},{"version":"1747682b50a243bbda982e8ef09306e5dc2bf9b0a0def44da795c851ef31d6e1","signature":"fb891c7c97af75b1638fcbf3b1cf51815a1f7aa9192f202bc4fbe295827537c6"},{"version":"8acf6816ce4505a5ef68bb1ccd8d4fc30815a83e00da1353b90102ae5160da81","signature":"997cba142aed5347c9d15f4e15f6daef2889c2fd037587841778b8ba476ea168"},{"version":"0c82b7ea29dce9d9c5ad81687769d14ff730a377fb9bc3c03cb16fb8ebdfdcb4","signature":"9ad6faef6958e6870ea4aba7cf6c40cf2399cf55b92f7bfcbad371186edd9636"},{"version":"80c8854964c4a39f42fdcf47a985104612a776c8de5b7e08e929c4389331a06a","signature":"8bccf01cb22376a08d48b284667f4b812ed8d38f2b723d6aeaa8c96d133d2ab8"},{"version":"ff619d9cbb2254ea51e7d71384abbbd5d72f2c93c071fea9c32b64ec3342888d","signature":"542542c33ba947f131b795d3630b41a32a460aa588a03aeaa5eecc38f3592041"},{"version":"5c0d00b05aedbf7b0bc483dcbb388e94b5948cfb1fbff930af60dfd9298dfc50","signature":"064b44fe73f0f7a084ed8e01bfb2ccbf0a6203b191fd521fc2f9c76d21e652c2"},{"version":"e89aeb89eb7cf6060c0af880e07093c91b08938d8b3a82a8a9b8fd5ae1d056f5","signature":"f3a1e1f72b8affbcc2c49613db77c9ff271fb02b9db2615fcfc355b194855b08"},{"version":"e8373b5d06c8923b34324f9df29eb35bea64a6a995b607a0b0fb2fb8c3a3a140","signature":"d86e8a694d25e37475962cecce34728a00aad3f3ae57454fb83e80c2e559cf84"},{"version":"6be1b5921e30052b789c02a63eda3517e0686c0d8e359d9ba5bbbee4e738d1b0","signature":"38d9938720a626eaf80d7c415abdc14d165e67cc0a3a5fcc4f40f0a16d2ce5ff"},{"version":"0b208ce8494b358652ba9030cf0e14619451807547d25b3b5b720aa57bb940cf","signature":"327348398994bb43ed73a2877af0f313518ed43453dfd4c68b77f47b77611738"},{"version":"d985bff3e70be34ddba319f5e9209e8eb799e392218201acb3afbd77b6ad4d5f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5f45030e7b52dbd77b0e101bccf5bbc08537605f8fb10927b0281a51fb2abbd6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6897d0d0498030dd4d7b6190a78010c071e924f62811f51897f63268faca2248","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cbc7b28d500a1738964097922cd6c6db2adb129dadcfdba9c1d56b77697afbfc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a219c1949667d439c27329b94cfdc416e2839e8214497fb621c491eb24cf3bc1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"20769f36dc2e033c8fcc237bc2d7a75682dfba17022efbe30ae06f22767869b3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"05a2797ae1b679bba91ebd96c9fee9bcfeee3b3dd3e400ebb3ddbedbba606306","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ae2e40a1fdc7fd8cdab6be243e4541f50b54445387834299471885785e3b2489","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"96fa384dc9c129874b902106257491e15eb6cc80bf921cbf2906a779ac96e60d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"db3b9f398ad210cb961c2b5d638e28f99792cefe9c50c81ed383d2942aa226e1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b3277a8f5c5b9d50cdda98e93cc145820c3983f5e8aaffd31f4316eeb0ce465c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bb6288a9c750095a16037444e6026de8bbdee3e77af676ceb41d4ab7a8aa465d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3780496f95996c8b34890fae81d23cf74833dddcdff259a695f7952e18c9607a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ab9cef431c5ba3ad0da377558211af661ac8ef1b0e3bc5c66bb36f4cfc3ad177","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"13d80d19b3c6cf01d42d51623f934ba1ce71c75aadba05d91d0d67860d86e629","signature":"0f8aeb5191b3424d8865e154335c144f70d1d1bd13be767471d43557fe97ed96"},{"version":"f2b455abf7da931e2c6af9e90e22ace14c7f357bd2cffbddd865d15b442030b5","signature":"5776f350899f8d645398751759ebb4fd2af323fb1171a47aacb022f5d707473f"},{"version":"70b399f171d822aca03548b1644217d752bbfde4d4ec2cfed1a214d7fc79840f","signature":"cd78f41f9d6f04e36cc052c74bfefb7c2db0779f89d2659aa2c3179b054b1c8f"},{"version":"d02be3bb5d64b49b5ba9e768fa8448b5c127d09e2b8ba14026773c1fabde1592","signature":"cabe51c63a81cba61c652a509354e7ab4fb262ecc04a5433dafe9edccc3ce1e2"},{"version":"a88dcc474c044ec5c3ac8536ae40771d408085bba71d322d73bf2204ea023dc1","signature":"39000aa5f4d43f9f6cc8762b6cd8029cea4b6f0511e7d1f47e4d6ed7da095a15"},{"version":"b1d512503e816355be4952330e0a427949fafd8cb3ee124017b7a535dbb26209","signature":"89f540ca38000b4ab06d97bef735703c375a50b0b4aacf9f4d28c14cd138e59e"},{"version":"842955471f601e4c1d21afb2cbb3d250ef424ced61416e8d7ffda5addbda9eb2","signature":"25e6d9fa0f3dcbfeef48b9738ad3a3efb1f07f8c32381d838fed05543afc20f3"},{"version":"493e9f05ac502360eeea2d5c72d28984c6bb2e03dd0c1bff35e2e5265cd8a6ce","signature":"a27763fffd538d56a65d2ee0de520e77a21958e31a87f4cd0c57efa7b9cc348f"},{"version":"5dc03bbe2c52976d8b054be1fdfafa1b7e43f328bf48a19d5f62f0563dfee905","signature":"b013ce777eb845733b2d4fb5608890fe38f7a0829738da416cfed813adf39080"},{"version":"3e06d8650c98a672c6d811bc035a7fa2561bc2c87ad01172e5df460a8629d489","signature":"4b4d1c5dcc9a153360f0bea18e847d0123bab4e18678d00780beeaa4e8ab01bd"},{"version":"feb5cba45f6c40b8b4601f40eb48697fa7e2f7e3db51337f15c308cf2800da36","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c0638b8d32100f3827e3535c4307c24b5ea5e7ae6b33476db318a8d706386626","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0fb10fe09a6f0c5fb2f5f7bfc0855cbabc27cc4fb9fa3c56e5956f0673746ddb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7b0daa318777bc57de0c9198d4ca71d7f1ee1e3f02c5bd860ec5bf390e08fce2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4c5ca16923d08d3df7ce8003095ee7cf136956b93a3a87d06e046c967a07d379","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6ddec7fe4e03cf6c98a431bd4b7998cc9a11ad1f5aace1c73f6a0784c7c9d503","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"048955e8ea4fff7090afeacc7a8d9a0d843199be15fb1ab402679f63d2a18a54","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ca2ccf002342ee1f87be1682f2aab080fab7316eb8f37aaa6a76a58859b3de76","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e85d9d5252dfb1dde90672424170e3b89cd14b07086f790d3c45aa3f023a93a1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"789e5210de191f9b2c090a2acc40b4d8a1e86e02626cf05cbc6b60079b132f3c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3fbbbf2353edb06efd1d6d25286c1cc267f3346dd7a73424e35606ef0fc04eb9","signature":"d1caf598b76a5d9cb02c68f802fccbe10bafe10d88cb6c0b78350e1b63f44ba9"},{"version":"d86965dd2a2cbcc99a7fe6d86c26dfc2509fe3b640d6c290e141cf41d0df613e","signature":"226dcb5115901da170f4421a0382fc00bc2ba83c97d9bdb91ecfb29ef4e8f2c4"},{"version":"a65cbbec2ad16ed203df68cc7474b8e45722ec2a6d2a6a6f6c0625754c9a63aa","signature":"1c1fc02bd4a8c8331473b19ae20b92b8473d8337bcab684d5bbc262b7a31f07f"},{"version":"4185e0d631c6b1e03a2ab79d937f8d47f25695cd2065f88744c5d237f0dcc4bf","signature":"dfbb24194637a7659be87169ccb8b885de89d979ef098ef29e16b314938b16ec"},{"version":"5efb20d08f4e3e7a5454d5403a500512464a4d59d30dc442a162e9f9873beece","signature":"19483d92b38fb23f95f7dcf3d6bd88df63a983d3d8c11fa3b4bea4c6d8141059"},{"version":"a50db966163020665ec8a68d0ecd79d8a9fb0d059c0f4d25ba53bdcd7e43cd75","signature":"d406c57bf0c60a88a4cddaf0944ed69a7554658e068fb62559e2d823f9255236"},{"version":"5880d909fcd7aa478c019c0916f68012f10427b2d90d203a9060517bb9ce4de5","signature":"109a47009ff1ea87255fbb9bd75f5f3a918ac7ab44ed123b521028f580aff53b"},{"version":"1bf7d9677be10753f2ede73e0578c4eff97e9b8fe1b7628d0d49cceeced9f051","signature":"8146af3e7bf09f628b043910cfa6b72b86f8109aa7b06167b9eb51a3f0a75da4"},{"version":"d97193507f74ca55d696adf4c7bf4dcaa581cc38da8993320385450a4837b988","signature":"5e25c87cc967b7bcd7949f75916a6757b59aada3685fddd966093696c85163b1"},{"version":"b7426a25a7942fd04027ef39d6e57d3652de5850a59c04b7a3b74ad2f335db99","signature":"ed09ce0bd7cf961caa2bbaa0265743b1a22acb59fe82fc227698550a7f0b1e14"},{"version":"d7e15183b1073666220cad96a18914084528dc05dc1e2af175c863afa3023e07","signature":"7afb481364c9e976ea5c55b9b02006f2496e68cc009eedc57f38264121a77836"},{"version":"b56e4b6d8d241dc9428b20e7be5d13487de4d263c5999f91d547983fffd8bed9","signature":"91ee1b220ead097d3cc5b596db9be622f0dccd9c05e4f4cf069f2e1db077511a"},{"version":"c5391ca708a239529f9f132919def5d73d4cd67786f87536da7e539d247bf149","signature":"44248c8a13f35779d07d3168c64fe9a1040ea2e66bfc4ff92567095c5b243e55"},{"version":"7fd84794a97f879f03f3067cc042ac622063d821e7b60b27100ce300bc65d833","signature":"78361a8f013fc8aea9c04034475febbc49998b63c3fe09f56dacba1e1c73f8fe"},{"version":"eaae5968ffd536215d143ee0c4a295cc4ab730c6306c0ff39da500a259fffe48","signature":"11fa086538a611fe1a99a34d1378e2579a4de6eac405ad7fb9eeaa51836977c0"},{"version":"169d8b256bea5a05efb2049b4bf5b8d916d986a97fe9000ad3af60c1804deb62","signature":"6840721f787baca46b15289facff041cf00967e6d746b6e2fcf657881b6e6c5d"},{"version":"0ad029b491ecca9c3bc7994015f376562f9fe7196e2c7815a7e7914545fcdb65","signature":"87d3a353f4a5033a14c02bebecb39e225f521c82a998c294c33481b9c5198271"},{"version":"505f10cf78d9caaf7df503e3c495055785de4c93e0286843574106d787d9f97a","signature":"4929cd61e267755bf505ff0a66adda55af5d318b84798ab1b46ead808203bc59"},{"version":"cb431697c9e94cf9faf8cb15dc79c36f21d951f2ae68a6cfa106b93edc373044","signature":"d21e563fc29f32dab8756bf5797d4c39b98ff0828fe02f8b766a8cd0f2130729"},{"version":"3c5a7c91728aa49db1d5eadc0e9f0d724dbb50b01ac203b8c577781846962d23","signature":"b524e9c8c9572a85e539e60885e7cd27a4a3734d72040582434a25e486702df4"},{"version":"c5e33bc47d97c9161f3cf286f89238e4097589e4dc86632a8a575135353883d7","signature":"bc3dc9e5cb7a7493571d35b9b2fa5a1f39cc7ad76f998ad62e7a98b56fb8df6c"},{"version":"a422e96804615648c7cfcaf2e23d5353ce5dbd305ef5f5467c7fff7ab39f5bdf","signature":"063c721b1237aa52f454a374210ba793cc38a5267af12e5f937c7f36bb33b6c6"},{"version":"0ef1d6c0063b12f4dea951dc976267bd8e11aca63fddb3aa10213ebd2abedf04","signature":"3e0b6c4d0b2d1c058853b3054d0ca2f00a36d93b462a4cbc97e0e20de4917691"},{"version":"a1d10e7fa181933ae7eeb34361f76d99ad2872cf6da8542528df84e4311da86d","signature":"de0b7ec69d1de3d88340668f43c9b8ef7086f96671d741ffb16d82ef844fc18e"},{"version":"db4c881c4d0036d8676e76f60ff17c6fcf240dbea3e48b5961e17d9a3b73831c","signature":"a2885e55e65c47dde0e39e7dbe3f9d931149d439b3b3c2a4480ae20b609bdc83"},{"version":"c50edce2d99b12b4447e3de82a3cbfb87332a0eed710e31f3a5306ebeedfee9a","signature":"3d577b57ecd8ee26a71f8dcaa01d354301d4155aa8fc228210f7980278d5a40e"},{"version":"c82897568bbe3658edfc608ed84b0615593c444b3dfe66fb884fe1f6f9ea0254","signature":"71ed8f0856c314b1c9270b9feb94da47f13e458a6b7041e75ea43a5a48e6d8e7"},{"version":"83b0bd3a78d0d2fd8794a9983dafa4f7fe6ba8ee95f7cd956dbc1b5ef1ae3f76","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cc1c0d6d5a958523960410c45f1e15874e8d8091120d3d7ef90f6d510b00438f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bcf28bc54cdceca8071e58514b793a72685363a07a190e0a093f90a74a9236e2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3daa25bd4bf7c1253c5bc577e84a9c3e08b4898204163d94e8f06bf93fe4657d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"20fdd22451018bcdf123b42bcf8f3607b54ec5bfc1a40ce6f3aa195114fee50d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"77aa32ef7822978656a1cf7a8955056e16072d0b6b3c71c8fe81998678532695","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c2bd3da8d18b5839c651f5dfffc391a3f583de5e4a3d7f856d908a60f47b04ba","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c840fa95d2134e19a21130e67e75c7d75715d95f35921d62b1d50262d7e34cf0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a6bbc1073d12694e6b875ef3444737c39391622528871c6663c36bd214779309","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6449a0751d077973e8e32ee57044cc4d03a50623c5a291196dbd19178fc3e649","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c8bb1a6ad7f07b0c3af284d80c5b76724ec9b6c2dbc1720d1af4018b571cabe7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a5585e289b764f82b17802e044380f2f72b584c02f0a9e5e5f9994fa14079179","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fc40f72f1c03ad660c6ad52cc2ec092594bd05e49bc5c960a4b0d30620dc55c7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"968339f16a5177a5ee35cf9b77108d92938ec1da02bd41e361585030b4f00da4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bdcb2c9e692ee3ee605a7704fdb479fa10ef6d4271ff6b9ff995d355d40e2206","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"044ad589837559611012aad8bd6a946acdc485aef131a351c8a01c1bcfad9db4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"53a1989193c0f9f558c62b7eee59b3ecf57cc7c3bea2fdd469ed4fa2aafeb0fe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5af67615072b85cf169a9b15a5bc2f54f874f32ff594fc80135b0229d46ed148","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c2b3d3b1134cd416e62ad730ba82293706888320b0ab860aa34a61c02aa48789","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"eef952dea22ec228c085f41b939f8824d7a8a9d5d53edf570d0fd162be862e8b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"41c362e66d4ade6b4727a2d3dcf1a3249ab336e6812cd51cad747284736a3610","signature":"d257d8bae8cfbd36ea0ea1c5333150f5b290f7fdc60c41083a81153a4ca4cbbb"},{"version":"c83c8f01896aed99315ae67c6fb0a5c948bada628c8f7b19665a228711c2d340","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"03c10706dc050b16e0ab8f3c5adde2d44fd9c4510394ded88c1254b29614bcf4","signature":"a257a955f81d30464899ba91ac6e7caa9c165d10f49b0e06bc9cae4cdad3bafd"},{"version":"5c45a3522e7ed545e4c383833e4e9590374dee2c6d2a4d8d50080bfd4a41188d","signature":"a8dd6879adaddc6d84af4fff927c3da912e5c65198c208823a713fb268cfb047"},{"version":"9d2e9036f50ec7b8066dc9536bd50a76f1a2e503c4fa7ee1c92725b694600d94","signature":"4f2f07fd2750e73d86f4763ee55f0ba88d59585ca882aac5cf6b5218af52a735"},{"version":"43aeb84aea08c738c5373e8ea3b4487907677f3e3e619f5f583d60925d221504","signature":"c1f55fce6df97a3f32d64e8e2b485c90ede6b9b6feadd640a3c16bb6329c192e"},{"version":"cf6a54f50ebe9b1fa179e3ae972e17bb5132bc1dddc612dfc2d868ca309999d5","signature":"619b2bf107c7c61e145876687ac47175b223d7cbbe8414b8d1ab5186064bd02f"},{"version":"6b3d4163477739b98bbda0e3722c1df15427f4fcdbcc044d4ae093622fc07691","signature":"0e81f3c44d6d754411d9b3fda7802a8c6c9567fcc7298542fafd23d879519d12"},{"version":"c4af4eaa49b5afdd70def3eb9ee71b509fa90dec11ea33591f7a1b1822400fd1","signature":"ba31eeb48994cf91f0acacad869ecb708d6840d7a8df864ecb86522256949502"},"920205f073d9e7a7cec8b42adeda1b66e3287253232703d75676ae0ee280ce5b",{"version":"8e209754fc98efbd0db41c1da78eb4f46d55d6da176ad9c6988b5947c02ea59c","signature":"34bad0db72824bba1a3419664c94ebe765c196975aca12cc24a7bf309f3fd68c"},{"version":"c4368b3ccf0073b6a7f2526ea0aa22e8184e98b099727db5884414724ee62151","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cc0c38dbb4436cef6d4ad0462c0b9230363a23303589e36042685c1132f33696","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9a999f9be568ad3a72ecf729bcd348b4bcee26719790f21290a16b5bc7dfe839","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fb37ccdae5fa6b7325f5aaf5d1b28caaea4c148957839827fc5b1f7ab2b2e2d1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"177cba3134e2dee9afd65d1d508127f10141c81769cef693f3493e5f691892b3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b54e6b18ed48a74d2d6129ca2ddda0aff1c30d2c46e7640113d2fe6669a5974f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"69a9d780f923c1940e4c1676d0d7d02b961490eb84cadbd80376c98b07d1ae23","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6b34a6f3217920c7cab89d81ea67dc64b48ebca1b4fe06e38852af49ea78068b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8a354ab401139f416005ba61675a503152089ecad7ac237da3d508779c29957b","signature":"b30c66f8aeae088710859fc3c16836dedd29bdc025e7304fc50dce105b9c04e6"},{"version":"76b797eb5bc8fe7158378f9ae1a16f98a76f4963b2a6eb40e1afce5f7574dc6f","signature":"f665a621665bf4b9ac13011827bc5cd5cb272d0adc1cf91afe269a599e6be31d"},{"version":"ba4ad41a9d1e344185c41db9d8bcd77361758caf451a76ac4844e690f096192b","signature":"6f3369ea3292063709715ccdc83ccf6bed46b409fbde2ac5c8b23bd5ca192401"},{"version":"fdc0244b111f72144b4b5ffeb4be73d77985a2c9839d87630366739702a7d069","signature":"0d7b280414b0cb316adfab6c3609f4d3b0c34aa5f942a74f5d0b330aee061cbc"},{"version":"2c7d3b8e7fac27fcfcce5e3b5a0043f58baf786e20e951be19087e05956faf52","signature":"6bf95bd5997a54eacb05169d05e4a3ac009a2ee4b1202cf0e609c84e711d28cd"},{"version":"feec6c48848e9e9fd2cc1dce253451511a02574223035461557a4bb97f173c2d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"213c42f7d5367619fc1f7a022520c0cfaf8828c3dd910d8abb68b229c44f97ed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fe4805a16fec6d9ceaa3834ce1ab4d8d3ec80c3c41ad093c2d09e7d7a00fe81b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ebc3418849ce69c3e4935c9c8ae98abd05c1bda372d9ad08cd259635d6bcf475","signature":"4a68e8778087ddeb83520b7ed367b8e7a5413545211f1c12181b036b98b46972"},{"version":"29775ef79bb6d19d569a24c59922476271b093a1afffb1678254d66e938e6980","signature":"1a01f741b2cf1e9d7d9a1bef2e8547b013e1ad3bcc8fbcfe1389c9eece787977"},{"version":"426e0fc6a30225af3662c056bd33bc2af1b58e67ebc1dc7c4d47aa9075a3df94","signature":"edc9cbb7eb4f1ec26911e7cdfb0673eb04ab03be7a74654ca4b68935792dfde8"},{"version":"bbffefcf2d2194e3c9cae686f981935765cee13a5f390c97363fed32cad90d63","signature":"c10afa01e312d1ec1d2e455117340bd869610913a3ddee3e1903060237b2d330"},{"version":"3ad0a06341e90f9e25826449160fccecf9e83b3ae923de99bf6318f2a0adcac1","signature":"8f0537d31f337710a710ca9da779d9fbe37da09f82a2bccb3159ce054a303771"},{"version":"754e504a4f7e802cc110ec7cfab158438903677b6d1e5320e3d06b4215f42d7c","signature":"f50252519f170601d78c919fbbcc6aba2864e344ef66c8dbae519080a9ab6763"},{"version":"a3e35d26f2d2ba764a55bf9af3fb0c22806c238a222679a83b40c838c30c7499","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4d1729c06dd03ddd24e92985fec6aa5863373fdce658884e07eb4827df021f67","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b80c68e22c6ef3a8c82b3e48dece693fd7b4e628542ac28b02dff88b31385882","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"22f5bdac2994c065f821a3c19074445873b02b4c89c5c4d26f95fb7319bd7298","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"da3a4a4651c78a13301492b76a30f6b89aff6919a14ce20dc48fab5473b99bd0","signature":"b9e301a99266862c3a04eac2c53d225b50d31203c93c570bb44b51e6df966f6f"},{"version":"084a417795f6930358662f806575b15cbe6975483daa9c40ae3764a1ae955b68","signature":"9e21029095d6b935b82ef9e8dabc88e552da4446f8551bb8e66bb608e221e7ee"},{"version":"9ffd818baa22a5a4a3494bda2daf646849c2635ad622ea25e34f4ee2c9a8f400","signature":"f1223da6f0fc1fca4ad9ae12e3c41227b7da0de5f55b38d9b871f3c651464039"},{"version":"3efe902a8539920b21bd44d2d0bed08ef8a95d3c4601ede6848a192af8563536","signature":"651a00540b7a7805a8de79cc6972dede798970c3b718c8374be90813037437cb"},{"version":"0bbcbcd6dd929d9dad0cf660bb39c2c578888071f5a7d80db51857ebc1c57923","signature":"9205ed03aeab041ae8db74ab3df06c747ba006d4bf2ec67df0fe59daa1a87d56"},{"version":"41f8f38651d123496907c1aa1b37c2361f1dd2cedc3aba6cb53c9ee253c7e4e8","signature":"7199bac5eac9213b52fe3a6d9481a0d20ab76d2bb99cbdafaf6ead4e5914e7a1"},{"version":"4631ce1c40a09fbe889057b38bf9d6daa45137ae0fa25fbf1b066ca41ce6f2b6","signature":"c685b52193d4c3022b8210703605d2b21a467ae9387aa15a8d9940785400fbde"},{"version":"1ff67eb52f40826c7d5512f924be11f6bec373c92896df0df557a13a8658f693","signature":"9d0fecb8068df90d9ab52aad97173c385ff2a17baf50297fc75cc31b3938c945"},{"version":"7025dad7d78fd9ad96f064ff669d353f930ddddbb39aa3c4984144fc6760118a","signature":"7d3b48b39ec46eacc882956538307aeec6db56edc31f31be7d6289ec2c92a385"},{"version":"8a9404494ea982c2bff41003f8de1daf258f83a54e917a6df73e6a6201862cbd","signature":"c93a0c999b510d141f69facbcc4d763280501bfbf78b8f1cdc4270af272d805d"},{"version":"b1f167490ed130cf9c920ee60fb21e9dd2ea9e601e9567e457f609a61f2f062d","signature":"96ac0d54822a7637a651aad1726587e96e5adeb6fd3e92f04e0c957313aaa83d"},{"version":"786aa97ed22b1c1aadb445ee997a12785c863377f4dd4a45365a1a90e1bdfe98","signature":"3d8b96ad1cab0524e81ed5283ba02e42100191203cd7f5e1280500f36a9abfc4"},{"version":"01f31174c59202f69635b2957a7a556a01c2ea194906befae45997b6d3c470b2","signature":"12f9e010df1bc3628cdb97e06e5b41a3bd149a6b61eb4ed5d9eab248bf5e2b67"},{"version":"6abbb171efa9fad3d88c9320ec5eccb199b726f832482379414fd55bdd485a66","signature":"f9fbed20734c2279dbee3f4186691fe847ff186337649bdccf7de42363d02022"},{"version":"08bcfa6546d768789b5134c6344f18ae851abd4513e3431e91b5e955f07d7eb9","signature":"90e7eec60d281be24fac0f9230a9c60c67de9d04a915ad11aadac11ea2715da3"},{"version":"d9bfe44b7126fd3ce4741db90af68d24cf8a56104826770276ee19f133496d37","signature":"3bc6339203e14955ed7d1f9bed916418c12a6a692d269a609b3a33dc4a863951"},{"version":"ae6be9a07e940a6f4b0743220077f33259542ae908744ab349a1a70d22f723f5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"70442efc456b60235ae408e157a6f0caa8ceacfac13f5af9d27265a6a3f57dc8","signature":"926011fd4f1072faf06ef6de9b938f68b655974be67f2ea7cd63d5ee58d69338"},{"version":"31409dc7d6946f1566b501934ee84e4d61916cb6893791c2da2731b12ef24b89","signature":"cb9d2bede0762fcbf1f1f6e59445d5671d08ddfd920f8ea01612d81b3a4384e1"},{"version":"36c6b6a3bbee15a10e445a9aad4f1287d7d1039b6b58224a01524fc62446e533","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"11fe25d3baf912c908f4a63d2e05da668fd9a9838f258130cca57cc5be4744d2","signature":"3c7233fe8f8bb2292a9100dc596c64a103235700d7d34096d5db17ec0cb9cb9e"},{"version":"ebaecc11e0bd3f3451f11514cc0ca76bb2c763d10240b59cb187e587b9e01f66","signature":"a406bd45d11ccf4449bed75df91936ec197ad7de0facd342bb0ac55597dd7cb4"},{"version":"56dd3ebfbb1e02d419ccbf55a7a298872f8090d3803610e2faf132a5fb07389a","signature":"823c47cdde5eb643974b725bbfada0576890962d21434906d18ce26b06bd9544"},{"version":"635713a99868407271583323a9aaca2958b2abe2ddd43d7f2ea987160f6ff89f","signature":"866bc33412b93d1a44a41d1a8ee37e688082c1979b4dafc2ef88edc53a7a999d"},{"version":"fb888ca2d1491a87202204b095f2816e2e8041f8a0dc67718d21e3e963afeaf2","signature":"f6eb9961bbb1fc5507f52b8081d6e82eb3ef5eab264a5d926518296dc4244127"},{"version":"4dc46afaf28a8d1726a39f5e7e00b37df65de1ed452b673bd74c90797d4c6201","signature":"a0a545639911994ff57683e710206d749c7d92037f30c37ab2cc070178a7fc3d"},{"version":"63b2318993b6e0dcf67bc21cc8aa94e41c7de4936bc0d33feed3828d589d33ac","signature":"1184cd7ebecbdb9ed966cea0f626822a3247878fe0a83dee49e89b0f89a92973"},{"version":"454b346ab7c6e6fea1963daa8abc997bf20a61e172018cc3fb4f3da99adc3ec1","signature":"29c3c744e646ac31f51cb4ae4b0cf912d8d251972c6a958b100df797025a94ac"},{"version":"fc357aeb6dafb0b0088062750d702118459f2385d31434c8ce94ed1c1e7914be","signature":"995cd4a56687721b9ebcde8c6499921201e7bdae56f437f08f6a2ec2b1e1ca0a"},{"version":"6ba0e711d73e317b739a4b0b083a109fc3fd294985c81e7f3c284ce4bc6427d4","signature":"08470625f34c0ff0200976ad34ce7d65a1fc9286f8b8a884e0553e19a4662610"},{"version":"bdbdf92aecef77ec1ce77d842bad71821d8c11bb84335c99cbab6e6519885583","signature":"d507737f7aa3a9dc2f94c67379888cd7e1e6ee3c96ca265ed0dea283869e2642"},{"version":"f46250a3d3cd3bd34994208caa7d245088a529540bdf459e7225f4752509085b","signature":"4c117079aad8524348f5b782625f9663c24202732204ab321cc56f0913c99318"},{"version":"2fc8086bb1e429d2786b7d38419c2dd195328c42631f4c03800ba8b7d691fc6a","signature":"55853877ee77b90e1a143d58d14c4f5c2003b54d251cc9ef4c809f8ecbd4aa1d"},{"version":"e558a1a4ba6ab77b902576da0a477e0a728245e47785eb24bcb352f5204fd506","signature":"fcf5168806c6395e7acce0b4494632bd61d8dd3b7be90a4b3b86fd645f898b3f"},{"version":"93835e1c93a13d4c2e896a885c90698d28d07b65788cdc1a9b55636eb05e57bd","signature":"3b65f98cd92e0cddcfa1ed665b6b2d2ab06584d87079d3ca16d473c24009b2bb"},{"version":"c15e4b4deaf1fb4877793b7cf7d89f6254a54419ce5357ef98a2f800c97825c4","signature":"0a6956cb83f672f2aaf173e306a81c48ef904b9444d51c28c5d07a7a90321840"},{"version":"df70517f2532151afcebc39b9984bfa3c5ee4677c6e9938df86d17dcbe6a8222","signature":"c55b5dd40b4e4244911fb70bec24eab327488b4b6414513f29d5c0d4669d8399"},{"version":"b6bcd22d528966ac3b3226ce4368fa2548b9d27086496f200acb6778b4be9e37","signature":"67c1fa6b68da9af0b53802564e5571e32e74faebcad03bee79ddba76534c2c28"},{"version":"6b79a17847fdbf4ba0c82d9a7ee2d987197680ca5317c2a6a68ebb5bc9a4a829","signature":"a7d6ad6e9eb8f49ed5a46f2764a8fba42de8ef651c256c04a16abc78d4b787b5"},{"version":"d99e261147a8ca0295f772e82edaae16711db8d30e2511b98c59131f6583c216","signature":"46c0c755480b2e33e77428563ef84a0fa949e17e287c83baed1c53d6e9fa9014"},{"version":"caa900f1d326dfd6bc47d123e685680bcc21d4462bcda44c92ca7cb4318efcbe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f4e14e16a2da8fcfa4982c8cfc32231f7ec7651440030365345e64956a86d5c3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"963e29c03860a04f42f2ca7723bf2f6c8aabcce3c2aed54a011716e20c1d65df","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3571219af6edabae2c952146660e1734804bad8169857f4b8ebe6433463ec3a2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5f610e2b5a184bf4fa504123d543d6c34a35afa82f6a58cde23d70942c8d77d9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b6a64100f55b037a2788401e6a59d3850ce656c85f3e4a0a8eaf66a750c6ed0d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"85e753e4bd1bde91bdfd4f516cbde1f0eda265356b0898e7e7623e45a1fa446f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"16ed5d4e6d9bf022f732e27adf9081604d593b3ec37e9c7a2094c67d115d6e51","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ba2e7bb3085f0acf77f6e173b0318d0db592580a32aed9c1d9a4bee49693996c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dc529f36460fcb82d608cbf7dfca17bf60caa2efcfa2ffc62dae265cf1eedc81","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d36609a41f06c7968f01527e56c8a1cb6d5697867d2c64fe0d46e0d8efba55ed","signature":"4eadc8f12f74708d36f7a73dbd6a4dba984b83f96a8c0875a27b54e88331c516"},{"version":"7dafd83200a4776fbc6fd2bbda38b6bf4743cd754535adc2d0ac4a5cae258aca","signature":"bb8f5c8174b21b9b1a9d318205301ddeec2d0cf85ba3a7cd68ca9bfa0517f36a"},{"version":"b3f296dacd56947df11418f474b12eb09c180449cc833fbbb203c13e657b96bb","signature":"35f2abe6c86b8ee3741319a9d7a8c3eb0230e9b42f7bd7d543db7a94dc4e9051"},{"version":"8ca12e1da31b750904a7e9c542da66d01d735bbe9b798bcfbf9753bc9451aa66","signature":"74a54bff2e7775037930699111384bbb5d74f4de57c1359b5880f0f86182f56e"},{"version":"4e64be35164e01cacc75bdc277a3412e76636435de76c0b193b3f3c5d4290d48","signature":"feb2d5fdc50e327f8560baa4feed95edc4e786e9b164d7718d7857a96f27fd15"},{"version":"5be3881384cca3b8cb54b455055859bb3417730c43fb7ea39b57102833b60ab7","signature":"ca02a04122eca135259518c85da5210e6d924d9a19cac98e0f1cd55cd75efdaf"},{"version":"2d4000626b78819a6a26c46ab8fd01ea13296c078a8ac19ca144933e47826a28","signature":"45988a2c99eceb92797c0825e6351b563dc059cde42a94107c00c34530b64500"},{"version":"d50a41d0c949f23759d25178df952afebb72b777ff9aa3dcdebbcd298b6dcdae","signature":"cd09cb9b335e1a378ede556e1a96dfd9fd412e9caa02bf73cc09d256252beb47"},"1cf312f6363cab7b3a2e6c67480fa5a5924eb6488bb43b766244327a6ab75db3",{"version":"dcbd3305da0fa9f9bfa2b6775179a16b18185d820810dd243be17ca852b7bd99","signature":"7837dd9c018c571283ac6e26b11fd830ca92edacdfde40f0dbf8ad4e9643b736"},{"version":"da3e0ab10454bff69d784689a6017755f62f51f9270bc5ca33a780d8f1effed6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c3514987210d9b0ff763ffd0a1aecd4fa7a2b4fc1f199c39b86fec4a717bd2ba","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0c282008ac1468a683e39296fa4bfecf89cb06be55359f6b6f7cd97acf4ac453","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"402e78c9fc8f2d232f0ba377e70c2ebba520dfde76cdf4cf3d71e28515c8f33c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9279672d35d72514a5d65cb870ae38fc12b87f6e814f1c8f60769021d49629be","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"77d640a224467919d1eaecefed3e3bddbcdd6ed34ae045f4c6c879b03ea8552c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6a5acc53f383e0d586667ea6e6a22dc8ee83f9cbeb851f73a287c833818465e5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"917d97183b85b400b895ef923475bda325f1a593dee6be6cda4163e0c649e1e6","signature":"925c75746c624e162e30df934c8b19a5b81be5a03978e80325c4e9f09bac61db"},{"version":"83d32bb6c68c36dc2c27d16caf470e429254d3de8c5c8f9ef91134d33299aae5","signature":"59ee2a3667021f2c7ed7061717eb0e9c7f8b0a4abccd93a8aade0900766e5e91"},{"version":"2af1638b9ec1f87086b8ff286b71fb279cad7a6256fbb6ec7192f31306f146ca","signature":"8edda68fc04a498391fe3e3d486b469c92b2e4afcbdd0b4a5a8bfe78cff9be0b"},{"version":"b2b32e7018f450b50765d979cfbedb39d6ef0443d6223b79e626db3d80c708f7","signature":"8490537159f5b3a3fd14f628b32e977a351e70a3bd09b890fae5616aaf894cca"},{"version":"b27e07ee2b259916d71aa0453912cae34b32a3492b7509feda6c537b796f957d","signature":"cf65707352be96547e90a932227cfc57bb9a3a71bccc6659328bd161ebffc36a"},{"version":"b26b234c627799e3b90925ea36bd4cec5e57e26e3eb4b95d598b0ee19d1fdedc","signature":"a476746c1a3430e74dc7d6764252eb4efaab3f931bb8ed285d82185b2efb30ba"},{"version":"ae3c82b6b88fe1315f8e92eb498df792923d5da97e77376a8e6434ac660bbc62","signature":"a5b40c328c53179858f4850879d0e77ea5f554c6076eb084c7a077ba81adfbf3"},{"version":"5d1a09f5ab37a76ea0dbcc20cd08dd4dc224e263389aeac1c61ff592cc825690","signature":"8e81241cc6e2de102991340c8878879924b204883de36540bb6d9c3931611147"},{"version":"d89ff4c66bb8ce9ecce1e47d62c2a11000e5cb57d27604af1ee22374cc7d6a32","signature":"eb07f404debd5b6bdaa86469be47c6b2a1e1ebe7c4d263730ba3fb4b32cf85df"},{"version":"7e488d2c1064204830ff271a055a09dc04d15aa208f1c2aa19be88ba19f57bde","signature":"2a346de3340c2b612e54eaf8f283d10e06620b02d5ad511a26faa5178f473b06"},{"version":"6a52477ffa08adc8d4bd84879ac20a5436f46333996cde7ca4a2e53e4e2f1776","signature":"b07b68f5938a55bf423545b75b3a448653410a1b1a09533ed9b00bfdd4c0ed64"},{"version":"b718744f7bdf7e86cd910c7e57698380454e0e94be0df2e1a4e658125ffe5516","signature":"5171645eaf377c98ada2fe1f7b90520dcf9b9356d4f4044b7cff35580836b049"},{"version":"ff3eda4c49da2ab76d049b988fa44b8a7658527d9abb1a22cd93c7917cd8dc27","signature":"a13db128a389010a8c44cc19ca53aee045a3f3309a1cf4468ab110962054254a"},{"version":"c11954f6c73d0bfdcafe0036d47648d71e8ca4f1a70b1ae88c815a703fa9ab80","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fc9adafb376ae31c4ea9501ef266f0faaf29de7d76aefde50ec9c6ceb67655fe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"af03f35fd7d1a7a1b59e8fefab3d87aa8fc45501cde4d5f42657a0af2dbd3b85","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"765ccc4e3f7042c4bf9a0288838c93f3841d85e2c3fd10e15a17ef5da7e348a3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bc705423f5e581231d5ab8472659fb37060aa3ade1a052f301ccfaa5eb836748","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"23f751ecd2c2b9ce4449c843400093a3359bd77b541c50c815b3f3bb234ddbcc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c1f283a5f8e29c8def3e16de0233029b469cb0c493d586c737e4d9c373e7cffa","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e93b1c62e445dda55a6dfb00d29b489d986c6b2fae90a1aec65f9020cdcd6a59","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"566e62da419b55e2c0504baa8b1e36b7af570e68ff1efecd7db3fdbd67d75984","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bd48793e5b03253b7fbdd55f3d0cc8aa7c66077cb7c43b9afafd4fc69aa65c13","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d3a4e8bf48ecbb6823f47d4883928e917080125816b24714745069c8a7f05627","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ff310bdb1d2c5121653e826dd2e72cd137c909bb92fbbcaa12d612e6008eca9d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ca5e466b1cd54a780167ecb1b23e6be6ebb99ccd3e500bdb6909343f4eb08e70","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"86bebba5823cd4c0c8c264ab1e5ca89532125029e01b3701365d7c8b57ff7b03","signature":"80e816ce6ab347f332104a3b4295fcd234484a8c0f78af931f6f679bc819854c"},{"version":"15c6a3bcc2ccaba6a79ea23cc968005bd86ae7c98e1851abbddacda91561027f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ba697cec2494efd4491a9b92cf45a2b453b381938429db999e6b0ad8eb91b607","signature":"462de0ebec39f4608d311ce5efe2f7996417ee2ba050330f9589541e27badc9a"},{"version":"3b9f374fb01fb21e7d3dc1ac1bda5a6ca485e8a42d80c5857c0a907fb1d56d9e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e3ff7ae3ddb410e74e0c46707084795c01be15481fcaa355cfeef2da8a92dac1","signature":"27c8473ae6d0631063de4e25ab27c0203e687c741721337d833ee7a8d114d9ec"},{"version":"726922c369c19248989afd3de749124e1acbcbf0b9916cee980e10b8cfc02557","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"178b68ad3da8447deb3fa36b903515c68a878693390dce2c3c51887138a4d358","signature":"d80742a6a41d9f569db06f2a6f1ce341e38a8023e1f172dfc596ebf59b320d89"},{"version":"2fe04834987c803287fedee95429f29ed93194477634301a80acea18732b0584","signature":"c8cea0f80c3f03c528c1ee5bcd4584ac807b414b573259bc90cd3787ca4645b5"},{"version":"64e96839e33ffc472581904d3d5f5101ea95a39fdac17d42bf3b8080ed452416","signature":"4ad6c2671041ef9cb7425418a493fca3c8b38243087e4533aeae67d0a9da5616"},{"version":"c632abd896e5fc858119334bc27fe15d828dd2ecb2efa72b19ade831564e4a56","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ad56f09ec02b513928021933ba8ccb5322184a5f145211adbb54bec8ab7c939e","signature":"02d62b21f2b1b3ae90d6f4c2a2177c849c94a135893850b697a16146152533b6"},{"version":"4d9ac1ae59eaa55088de16aa37191db82e512889ea2e3475c923acb0fb5dd1ec","signature":"e6ec51d846f163b420d420782dd42e40aee266aeff314d141b11a0307a86fe09"},{"version":"2b207ad5750863999cb3b248b98e29d8cf15b832e77bee46c23dd5c712094bcc","signature":"8494e8d1afa0d76f70eea09873120b790df6fe7b084458941c2ce07b55155b33"},{"version":"8a5547b3ef575d99d9981c3f2cec446e2b348ed27d728e3758ce04518afe2236","signature":"418948af8b278bd71d186eb1cd3e77e0692f5d950fb4f486a805fcbfe934e4f6"},{"version":"0ec34d2579a19920da577b641066c31e0bf05a7b1f5b2700f0cc325daf817a31","signature":"d3d6c1bd0b87f5ba67cd605f6bec93838c20c6f6086cf1480f783117cdf7056f"},{"version":"a109dbde88de9c839d3a7dc6422ffa7c7d5edb1f2de052d90c4f5a4f227831a3","signature":"e6c6732d88e711c29a07fdcf4cf22361827cdb0527c668da5e91b2c63923674c"},{"version":"9f9cfe8a6db296e29b8d96c60a6b2571f64ccc19bbb365971270c013cae6cd81","signature":"9a87bb6fb82202486cd4a2c71006169c516a1eeaf1a3e2303b374c2ed9f3b7c2"},{"version":"d165ab27ea9971500334bb5c37f5c56741d65f4c70ad1d6788e4582a222d2794","signature":"79ebf04474cb0d7a058c41fb366280437bd079dd47f2138ec94f3918daa05ae3"},{"version":"3b40021cf5c4b492aa5cd8fa0871ab438f0da413ca344de421849513e4332ba7","signature":"ba994537d2ab9e6ef4ac8ffc86dc36ba2b9fdd034a5725d1986d97759876b755"},{"version":"9a6a75a9d4cbcfe725e96855f3af3803559790aa6b7e48a6314be4497e3aeb8c","signature":"b05b871bd13173d03b8a6ccfd9d1d187d6f612bf672f565eff21e0da7055aa3d"},{"version":"579925bdfaa8ffdf328f0aaf7a2b98a43acd6c7e56f4902c31f81cb93597fb98","signature":"4478ca9bdbf267e8ba293c55d26d03b720b9006964a13d4ee05afbed4509335e"},{"version":"f749d4843e5fa4533b0e8bae2784314231b4404e332e8d56abc2692272965212","signature":"46c0c755480b2e33e77428563ef84a0fa949e17e287c83baed1c53d6e9fa9014"},{"version":"71c8ad895db3c65dfbefa63d75e779b2ce821e8badb100fdcdc6bc241f2f4544","signature":"01fedc4512be58611b781ddf06d6575cce9825bb18f1492ddc0b7174273b8f31"},{"version":"25ff64eed6d319715fece8d041173a27719a7616837f57626e812d1ec3c6faa1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"02f5133b4da482864f51bd4c061e1af4d67011eced5db16da712c5ca6d0af7d3","signature":"9ba498bea3aed8b2794b31437cf2cc47c2e1e500cd72521b38dd4e8a772d2459"},{"version":"2160b2891069ac50f73bd737ea2d4a26ed3f8cd43f483066e9715018ad38fd53","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2006492de4323a0166b032c02a1f8f5f6433b5e9756bfde1d98f7902aad7643a","signature":"45b373ad2e114de335dd3eaf62f9658266d71c2f34537489f88f3b4815fa72f8"},{"version":"38dfac0e60c6379a3276ffe33739a19e2c81f3359a73f80370b7dbd615239da2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7ecfb0fe515e7462466e1fffddf551ab3cbbb0120b9c60d8b0882da1184d719b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2d6e05af866b9a2fba0ca03edd8461339787f298085067eac7179ff66826fbf6","signature":"845a8c55efa3e6c366c3d4fe6aba5af60a822523d537e8a3930466b565b738a4"},{"version":"c7a40c6af045ffba5250fd4b2805c5e57e5f7ce518690f180c83b65018840f3a","signature":"cf231aee194a0a458e33d6b2a8017c04c869079c965b00b9d294016e5f331617"},{"version":"fa4fd1a6c106daad4d2048e50518a1707039d574d96f2282addc0157b2143b29","signature":"b59523722261669df66b7a54b3d8686823768c90c5a8a04fd1a7c0bc07064fb0"},{"version":"22c9076e33fb7efff1af1d8c1a9c3e38eb017a1a07e62d78b8b64ab33664f2d3","signature":"53e646710346887942688dfceeb46259c4d04547c3f4909366bf5a9e3ac41392"},{"version":"78baac76996d1d214302749ad18c6424d1952fc441004bc8b1ff78e16ae94f2a","signature":"e0fa0f834bef15145ff38c4f94b555e406815bff1d72c3cc4b911bed38024c17"},{"version":"a41f813b81e3ee6f2fe6051c05f77671ef035853004832795377479c61cbcb81","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e374ce00606b66ae99a8dd321694504f11749fa9f407bcc445dd4eb6c6b3b5f4","signature":"6a2a0e9055a691ef8a292a143dd336005e96f4cfed93373adb6d1fb2f7d67cee"},{"version":"bc8339d6590cff26e515017178e6a430e53c0fe8f4f858355180bc22278a1bcb","signature":"0c25e09a2b6916bfd4fb6138feb16d394bfedda3d5fce6464478918e2f3a32ef"},{"version":"50a97612fd2557f947fff7bc53118552d9eeae0f349814c410151cc6dc305f81","signature":"b3deb4cfcdd96ff391f83c5cbe1f6880f7c11facf2ecf8e8c60983ba70664cbb"},{"version":"93b01da13427e2d00a8d73767869b61102d6ced5b8e6ef297006047e7a36b63a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4ed34dcbd916c8746407bbe31966464ba2a40992a7d3eafc7b89fe9487322e0f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"980ff563c04a7ee054838de6d5581a1c74f879aa573e49083b767661eb497b06","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"073d7d72dada0f47cf563f302854c2f4a56a0fbdb4ca0bb02878abb996b14c71","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"81f5e70b2efef928bafed1f1ed517b5f3d7dbe1bbae734620b1680b8cdd0bc66","signature":"31055f7d0532f460a1f2ec3229a6c990bfec524fb95332e3108bb50913d60c09"},{"version":"2f9876fe775220881f9a1dc662c4d45a1fc6c69dcbdf3394d4dfa7d38e7abf08","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dd7cc12f27a14a8b4160e7eaefb41b48701477ae419951c0063c8625d0ce9ba3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"20e933198c82a21395745ca8398ef702bab9881d5224febcdffdded08766df03","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"23a5597b552c4fa9218e4ff75c6fde15c735a495a7eba796b21a28102ab16307","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"23fa3382c09d278365b7a211300808076300a0d16e6b7a7aceb22bbd6a5e2850","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"32890338fb3db8ba265d19c7192bfa9a11bc5ee4c15154a4db81a4ddf1c8b38a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ebefa20d8e7844bf717e29dea823d72e0e3851abec67bd7442f18d5e1c929979","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4cfb9e24d12ee634464b2e685f0e830f3871b28e0173cc89558416f194d49f73","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ef7f54e0c441529398e2666a264256395d244f143f2f97ce5737b8ba12f9dfb3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9a2f242d01ce2d89d7afdfd1fd83653b8d751731fe8484472e55caff6fca829c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dec2391eb73f6d626e7679f9c1a15a5a3939f799b408ee2ace519ebb16802d9a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6c8520dc79618d2ef97bd41bd2d9f9615e8d7c31289ad6ff40202de2520d8a0d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d7c9c2e7dc4c35e0a79a12add067b79cb96493da0593a7e063db435257c7ece0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5afdadab59d5ba59e11b585459cc467cc1f23eafce2e1357e7ae9173112cb540","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ed8ce303eb9c07bea6cfa724060c049d83421b0a03c671040208438adcc1ddd0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bc2358aa66dfb3288e71f8568e09cbf493eb412a7ec67ffa33cdc24b0eac922a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"71c4bc806bfef481e0a6ad07ad37d0be53ac5d8b0d19fb843e6a9549080dcefb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3e133c38d7312361e47e684f52022933865ca28b6d5d1bac3fa6e306c64e54e6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bf92a5c54601a670a6c8b9c02336b7a63a05b0cb9a05cf290d1cfaa95f28f284","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e8991aae569d37156933dbe3b3f24083dbb48ce7d8382f671da7c0ad563dbbb7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c3557cba180e795e1819088a6d2897084fe66b04f6c0c03d72eb196d960790b1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9da4f2245cc7c86108bcfa87bffa093cfd5b532a90783a7f52c3baf79cf42393","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6b55ad93c7c4c1b77f78a46e1d78564d3dae464706a767f3d25ffa5e3dcec0cd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ee249a2e5c93e9110ec235c1e89cfde32b81e509c667abb08fe9c1f2e324a810","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8f33490c1ff132d5483cf4e1eb80e6e6495eeee76803ad9e0bf039c16f6214f0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9530414f935f2d4311ff2b25d6d8fe9b119e40eb052183336306fc8be3c84e88","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dc772c4125fc9d21fa0bce97821fb5879dd123fdfdc00b666165d69a62c5006b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6ad131fba9f64b1c6efecc01403b93c63b294fca637e29d8d515eef286d78348","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"453246aeba13c55733a15b8aa23939b666b7dad48e20c2b940f6895f2d8c1a85","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a376d7cc82fea71186921ef0f2779295f1ae28d8685f2dcf5aecebd6ed897e7a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0c9b5c4082f748ded869361cbb3f97d405998ff5512bff4ec98ea95213085ae9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b3f09f17c91d57b6a841936dd215929d1ddb25b6cc36e2d5af8c2ad22efaea57","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e878fc887c74d6c43af5acbaa72e7f0d5e598447ee16c0c4eedc223bfee16a0c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4ffba69cef9d354ab21efcc26daafa01e3426d6ce70629064bc121269544e2f0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f5fdf3fdfbda3df361d340493fb1df84a700ba2d928cf6d48fb3d47804cca24c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d098b2c0bf6c0333deacbc30836b8b0cd048f48b76cc4f7f32c5c448bbb9a3c5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"83780a3b4577d40f2094e631b3929043444b0bb16097fcb8c7eca08dcb3c1427","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c0acf3d7f2a5d62332da4fc79bcf475ec142934b00b1b0c8bfd3893f64bd1c24","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b64de355d67c8f5b9c0bbba60fbfa188e0e16f107bdc4a72632a213c5ab9bd09","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d767afd9e2f82e7e899edc3775e1d86e5acb4c7e6268acfa95c551fc7c02d676","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"34a2803e9127b665802f3808b668a5474c0e95e2efa58720312bed19f4461187","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"49c7bcb065270b2137eb524c7f0dc6c2de882ed30fbb903686d9fcfc71b1ae1c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c6c4b346b7396de0a88562c85f142a3e6c71f0f0c3a51d8956d9d3d656bece75","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"17428ad5e6272b4958e99bad33e44b2c65c554fc5a4511c5ca18f6ee88277296","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"32a475b7d9fb82fdd7df4de62fab6df47c2906058f70f5b68b7dd4796e0407c5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"87525aac3b68b128ede1c21fe4f43b896ffb651c5507ec5bf554021789f0ec68","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2b52485c59bb8f5f8ecebc36f9eebd5bb9e839006267e67f14f40bf57c21e545","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6230518eb3bb41f00853f984b9208154c9180a11639ac532d115aa34daf08a4c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"63e54be11fb7b740bfdeadd63e8f451830470fb4add677af84ca53813253f593","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"41222d3dac2c14b6a1f71e0b5105f2e3f860186aa3db1aff6ec4d95f833bf6ba","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"59d51e5e8361f7051ead0c29c8a03483e6929dbb6cefc3b77c2c497f2d895762","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"34bd75b6379933f0a0371170d95905d43f72c8a3a2ee431fba5129470947bc84","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4ac0ebe63335c8cf5fd698cefa7904ccccca2f9e5d27dc9e0e18ae1cbb5ba066","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"44da1db5f81f80f935eb95e20e3c925d71d68ab43379c478ef6aea748a3a0b92","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bd6074dbcef6177b94d63a539d60447a71cc249f93982528095f888e24d1fd9f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"405ab3515b5d2f07531943438c5ecf082bd61434adbf4860e3f83cea145175dd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7356415ae2693e3f94e126d3fb31d42990d0efd882d063661d8a588124fecb67","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d0dd8957db84d11780ab6f4fa208bc3827c49b5986f0b5efd5bb98171bb5a944","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"25028aa767cb234fb49871cb5dd6784ad018d94609a519cdc5334f590085d21a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"28f03986bd300c037d0dfaa877d4e1ec84e84f56f87e6a28354988c4dd313325","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7f9e276f89ff3b71df956676c5779ac82c1a96513a360e6fa66de0de81195adf","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"13bd658bf78bc035a961497fdb6d06b0329edfbc22985c2ce9bac485ab8e1cf3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9cd1dcce9c4466749da349558f09c55628157e19f3755e2043833faca3f3d3a2","signature":"407c70ddc24d5c90bc55d198041d27c6ce2cb0f42fe30a091ef7533f5ac3686f"},{"version":"ad3602cf898d906862e748a847deed9392213387659382a47bda93090c24d2e7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"47beeb485aff1ad4e1fbd8ac6e8d36a24e150de10a30b6899840faa301655414","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"487344aba34994c6bea6db3099ad923d847a27e7c900106a17d5273f8cccaa06","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"90f9cb383eb3d9b17a982664056a8b1f86b8fb7b391e2bacca3b58e220a2a38b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0568baac3cdb203dd85282b137b649bbf8171ae2f1a61264cfdfdc1dd5f6c1ab","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2d05701eea88d40fa2962c3e988e6e8c751892445eeceaebb8f76bf10d8fb47e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"73167e4dcf4eda771cdd7eacdefb1f35871c2fe41104840ed700732f69feca46","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"10074d86632d44722c8168202803d73c95697b4d6117ecbd68e5a433031eb52c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a5f1a3661ab26f668a8195833f02b4085991113b44a0bc7f790e9c9af257f07a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"77a216493a9f360de8197a4996159822af842dbab96da3d7aa502563ec12e64f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8fbf898b003bf3d70416df534552735d946ee7c578766469039551b5b5989a16","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d43aea7abe28c92b8494f0fdd74762bc1d3ec18b972711d2a883ede1ca8ae628","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"598384c7786700c7d6208cac6007b37f123131de52f69441e496d3086f01599d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8237ed9ac77a0e6c957c04ae1939d077b1eb214150e0f2ee2330dcc698ebdb6e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9db5db65827dce6c3005c0ab5feb8dbc60776a2767d1f3779e4e56b6ac0eee26","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"133e42066e3a748c1236a82709776425d0592eaa5a1bc0195b102df8160076a7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a57babc764911fd4f52710a78128abaec189a6b12a0adcc0e06267741434f4a6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"750e7f25638270d4fba9ee9fa59e79d2d97cc88e655bc8bf27573dce9ecf52d1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"745001d456418763f9801cd2f8e00a519d597d29efac153f41db8ca2b4cb5cbe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"32552e3b687bed279decc2ff81ba12b6d194998a102b5592537ef0a7bc246ed0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"17000b2a7cbc8febc1c38e79ca4aff5a824bca523973aa7b5c4be0313c10278c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ae3187226d80dbd2906f54a87fe586f0b33961a92b99f74baddf23943ddf197b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6290d7ae201e2cb37a3462e8f0474823749c74478df2c024483ba0f66b9201b7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a1ed513cffd7653b442cb3d8865b226957b6a84390ad18d7702cb73bf169402b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c3ffb3ab371ef4a1c49f3d70e6cf58152abbcf97f79b87b81fcecf0e349c9e47","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"43c903a3a3e6bd110c6e1e0edf3f119bc3863e25f534de171957fceb9373b791","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6060795a11dbdc1b053619d909275140681f310638413d7f75dae71c0698a0fc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9b536f09a14585f7a60c6198eb73475cfda55bdb6eb7982562b14d9745ab3f58","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7f21b2e5f827fd2bfa35959f943d5f7c38bd76195247f63a7e00c35c869583c4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c93a6690c5ddf530c35ab275c70a4a15ac6ca4a74275d3a0205d1acdc8f99d2f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5a1d4f7b55a0f4585ce971998ad5602b25f56fa82c105750c8f770fd89f61fdb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3a25ecb2df0785bca73dcc65bb1971e06ab8dec22390defd7bc0fb6e56bae645","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"71cf715fe7ac9cbda3398c62715e9e41e205bb9f66c14db522c05d33d9bed871","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c170d6a07e32644b63485cb4fec95a7b4210c95b0106bf604f77f60be4590609","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7e8ad08f464b4d38665019d1a2e7abcf8431a2fafd4af65bcd93e71e9defe276","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"284afc03d292b1476a7abafc7a199b1374eece1304d742dfa2fffe29d1ef0c25","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"724ba566f050f9a5c9d59f094d43c5986a190bc913ea545fadd79e99201c1cb7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"63c289c6931d3546d36c0cb59ea38f2d22ce5df282547200bf86dadb4cf442aa","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e9a831e721e46b46f371bea50434b366775486045b009ed500a273a0c87cbc6f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"220c41cb6d922f9df023fc9633b25d3f277be8ca0b6959d35510aa0ce0d7f435","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fcf4e15889b48532a4fab0550d27792e595ac7e53b656745561bbf0499175c37","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9f93119e73d9aae89eb4897d9fcacebfc8131e4fd6add6bd0af2f085efbc1b5d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bed5a24b28678ac3060e6247e7f1028d52c3cd0a5da6f8de620813357bef52ac","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8e8323f9bf61781a5c665b85254c338ac0bc879cf252c408a9155fcde6d3926d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"58da354bb341bfae058822830e25841c7d4e322f2c01b523533d976788288a79","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5905fe6fd8787042e9f2a85ac2c37182f7a47b7dac70e7929ccea5bfa8fc51ab","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a45c928a098549927b5e3e27d83b1abaafa8ffe97af5ff78d51dc9e983f1cb41","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"de10d6cc7a07ce5c5d961316be25ee61e38b528aefc5b78bf4890f24c0749f6b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"569beb54f189db6412e1bd14225b3c003cb7ea7a8b8ac9d2bb4a98d443a1202a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2bcf3b15c705b78d2624ca829055672f638ce38a4ec0bb25d7f776265ac833c6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d7a0f90adedfb247320507bc1f490cbff7e5c0236bf52363e4dcfae1219bb9d4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b2a4650779610aa8626f855bdead2a9ee445074ac77f0df56d4c3d74d471ac27","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1a138b16a062718039f7b4a0189c173d4612c918f1391c15a13ff9d74d76c0cb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0f2066a8d178c8a82454b1b1ec4593e7dc6b52d802df5d6073180fc4affe3942","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0cb88121abd9fc2f1e7d2aa933930cff72eef65fa010ed23f923307e56193e66","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"570d900e54c02bb666819963695f97ab355d3a10137e4c90d48647fbef5a8bf1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"279259cf3f1278d44774d4e4ca7738ca2a7b23d5df3422c8f04a4ff8e435460d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dacd876d911d48bed49a48b86cf54c0ef8ddbd05ff05a7396ed34f3f1709400b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"535301d34adfb07b738b5511b1aeea6996f5cd1e1ab900f49e43419d8944fc2d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1b5f939daee2f045b7d23a12a0d3650dd49e4f9bc351d2f22e696469051d4eb1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6c3b470578a5bd66ef16829c185d96ccefc3d2a3377d9976410f500610ab9628","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f13b437a12555b0ec040c3d4f6d3aed3eda3ac447ef37cdfb0e458b697a97b8f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fc8cb3e5ebcc82df41bf9b42982dcadda2f7715d8b1b2df23b707388de721fed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"972a8cd8b3335703b18119089e6d0ea65460a6b0502350734fdb77941bb0762d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1003da076e1cc06db0b7632ad85dad4c0af857ecd949856c40e87a733c9ad2dc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"27a14d107fa2f36104dddfe0d0f3ad259d6a5a8cf3ff91cce99b5e493f9395c6","signature":"c754e6829c741e6b805b1868f57d8dccbecec8f04c2bea49c8fd3906a9b4bb9c"},{"version":"00796a0b209c7a10afa20dd2ea1561eccaaee5ab2798c4b2b7641d5eb16d9205","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"26db1d864473277cb2b8327fe5a4ca010b2894eb1be5087e817095a95f74938b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b03d1c836d3624a6ab8fd8395bcd1df2106a4c7da12ad82bbc7fe448968e7f41","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"20d1bd40bc36713f75dde61ff02bdda74cee057be3c13af6ee23fecdae565d53","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4f70f7a7defcca6061e9a022f5847772015898ff7f582b3e9f9991181cc23b7f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b0f5206a0b32d6320d4207fec04712a52460492e4a27e4af8d7e864bf09acd24","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"26085d6a7b985e91fad21164ed5cba66427dbeded7e0a672532ecff63d2e7c4c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"87dbf0346d5746894eca4b429e98201f34a03e11331cf456d13e71c81212e426","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dbee469d488b262f97f892153e62cd20ee4724dd8b7d253ba771770ac8114c67","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"37c5b9f699c69780b09804adbd63f971d028523664a9a769f92d9b407915c2ac","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"061d0dbd29081c82e7739053859db508c8bd9038d05d659505671fff58e03a90","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3a13b36699f297594179427601a6b8cec07112c1638ca4ac3ad451a7164bdb3e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"52d51a3b16158da8046ce3051acfd48586035a6ec20e3186b40f9fb0a622bc7b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"95658c91b67a72ec6af1ede02ccb5802d685bf848391710bb006f1ff1de9cc67","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6b1c0e19c69e765adb21c7b24a911f38d3ec08f8a4c5bbad7cd7c15e416f106e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"baa9f93cd885deed2211a1f17e2b64074d45217f6f95784d9d7db3b9adf39f7b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cdd21ddbcdf8e5073e31fe7f730fe3c4023c66309625b87b5b9785fe140190db","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cac72a71dd33cd4dcf93a4c06a34590d661d2ce406b9734cca33f567ddcc7208","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"508859ba82d5f926349e4a9d51add2f33fec2eb154fed40a6a80f12df4d99bec","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9fa26168f88bfa67f9b9f82b7cdc70c643822adc48535a76c320bc7d262ad78c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9f11d83ac8f4908d460984e703c13f43b69aca1572d2949292bc9b95ecb7a2b0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"47c08e2137ad5ac7254ddef770e68221d041e397acf64e2059b92018c4d38b4d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5458b79d513a3c28249bd399e109764da57de09097034437d65d13753035ec7b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"abd6d932a4f5ffeb10ea89ddc43d53467aabd67f1424f03634d2bdb9e91bb39f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e7484d772180a4fe32c9d12b3701087ec6479a1fb4027d02443b362d6748f265","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d22df3d0d4a171faea1356d2ed06746654b7b54a6f134ad5ea64f2bbffbe282c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e3ae440b75e800d2a80437da11c4e3a68270925cd4382fbadfc5e2a426b144ac","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6256390bc79dff5190177864fca522b99f1ff8c690ab411abb268d2660660479","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c871c193395edb4f0bc64f8dedd55c8d15a51a9519046dec95c4904242d7b2c6","signature":"3b5031a79ad3b873f4979dd714732927534e3a6d3ae7a9ec689c5725ca791ea6"},{"version":"3cd49322854ce1d737e709347cfd3aea195ff6e1b262d5958bb256c8beecfa0b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0f7d44afa8a022241eb790ad981f9ca901650e125283ee25ecef8dfba63b1b1f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2c144fb7b835575d4eb400187da6e88cb37e0e58c7f2d430bfaa511f7f471fda","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ec563dab247f022b8527fe82436349f3792b975c4e939886ce128d095583abf0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f815436168a53475078bbd0aa903c756c66bca0ec8c468ff534ca4312eca4bb8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fa2aa9a0b964ca9bd71c8f1b2554010f338e89979fc0581e1f273a56897086f8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"01655680390019da612e557fa6c87313dd411791e200ec4a960546fa1c73860b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"79bdfee706e5a2f5afc91eff7c3a186da1c451fc3827038d6bcead0160ead42e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4f719002191cbec9176949717a9a57b621e3a1d307a74ede4cc94dcb78c249c8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cb67219ebb0d8339188f55657be0482d7eaa73cfa5fbbabfc76bf0757fe217a9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9cd6b43d528855022541993f9db3ff28d73c7183ab1043df00fc36073d6555d3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dc96489dddaa0e3e3c918fea22584a4823f0f5b0ea27df04311ca25224969acc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9e68ae5f6432e9c50c43e9d2835536cfa152255e680aceddeb3ba2c13b5a24b1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7fb4e3677e240e9cbc6268542e651d8d7142cafc9b716002ecb94db2923231f8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4fab0835f3f0569611b4185f74019264c8ac4338acdae9786c36fc5e73165f72","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"095cc9d0709e52c5869e31a60f719773a43b60940f726480b689e301eb661d9c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c6e4a5152f75e9d77ddeec6158887b08565816164545f301243fb653d7c57c47","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b850436d0a9d744cd3fb92eb3be65206791e3b4c21cd66fa1af395074ccf9520","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5ae00f9eb7202bad96cf17277b37f8eb7ea8dea3e1d29766ca904c2b81f54043","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"33195f2e0363a39a049cb3839f69891f3e92cdef82661f683823b7d4f2f3d3cc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1a91b7d838f633711b64538b4a4fdfa77eb8ffc9e1a5cad23d66a43cc9d1bbf5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e30d7ffd5f108b4f12429dca91377297ac7b070fa87b5680201b4c3da07ff6db","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6eaea317fe5b6bbceeddd6440306eb6dfe56c86796b5e90b0c86f03d98fff955","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"539ea0ca2bc54254c4751432472c80d8a6336e592b9701695ac473aa6c9b4001","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8b10a43468849bb4be5c7ebf6e582671999c7378ba459e6307db755757aef954","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ce30fa93f285427c6251e073491cecdaf1e80751e13ebb7da419092fce4393ae","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1eed9281109c026b9f052241336e80589c39df225980919ec591a01ae388f11b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b2a018598e7116c04eb2caaff9bc3de6378bf8646bd96429ac82c6ff2668b9e0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a0f194912ffe562a67d5570ee74538fc74e5b9ac3eda0c8188b314e72bc0b1a4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9c63668af53291cbd777acdc086a76266b1f9c51e354ea2787619ffc3c10cd24","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"eafd0838df9188d3b117c9fe53e0c77b707f5a985d3b8af99f664de7a4bbed33","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"83e6f2be1beac83f30dd5f1e56d42e907c7ce21c05ac72970b6ebd370e5432d7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4f59b8b9f9609eced7551d65f5a9d36c47c3e8e8f946304c4a9202d8748c87e4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ccc6e094800a0ec7e18a71109ed4efc28f2070b608838fb625afe4ccf0dc9b87","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"313a574dfe32f592b23877fc0677f33c8656ea9970e4af30ef78b96e17e0a032","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3fcdf7901f8d9f9e77895e5b0743e77242c2710c17d8ac73beba8a79e433b57c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5a991ecc505e086074ced217226118fbee9bd97d37d94e9a73cbe73cefc82b23","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3c91bf1628b9af6723816e7f06ec22cbf5627ea3c793e802eee02aea37406231","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cea188c7223d92eaeaf903844518a77ba6daa9160538bcf25d4f7e2990a8be01","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0daabc5b3a23e77755acff32e410a9365d2266246af76bfc96de9738a6915bfa","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ca5a2320c781052b195b38bd95a7424e01468dc5e78ef946d8eae4d218437eb8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0dbc717d091c928ea25aac5b7118713c489b0b07f74b6ae3a57803d4d704c841","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7fa3cf6e959f107cb2e099dbdd80e4d78f6aab3a0c012a77a7b0d1288917c2b6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"29a34d2e9a40f421e4983fb1849a26f666b67513417ae53112b81f859d0ed18f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2f0f520b1cab36fdc9f80c54b74f17e7189921be14e5e6384c9e76dd694c5df1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1f20c6cfef7afb6001fe5361b024b2f4863f2c37eb8bd6e3d3c2f7891ee91317","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9210170f2fa566053f02e2c5c3a77faed4e7e51d8366ec02adcce7953297fa56","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b01dbe7929b0a92420ded501af329eacee87e3465038b6b1a0950bc7c8f90421","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"92e3daa4f9c571d990375be84c4f55ac2d68e6eebb26ba066b18e6712b4ad2f6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d3e64b1b1acb43e370665f3816223c516a760ff13f6d29f08043d1cb18afc95a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"803ce94cdd49ca8ed653e63004ed3fcb16ef302b983ede0d5291257babef6bcd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ebd54d09bcb969873eff5f50835348d41ed084785a1a53110b155cfe0875b7f1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b1d57277c40e6512296d6635e280c6228aeb08d789aff2eb4ef865eecf86cc78","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d71d12f9748990e5a21ef6fae3483650f1da187533e520785ba561f8e8f177af","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a3c1c6e2d0c647263a8aee2d16655f525c930d6b9784eb6080c93ccac28a7c9b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a93fe9b1bfbc124b8f4777276537084f37469f91fb5ea6ba8637f62222f9d378","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"89e0c0b9430b0635a17c439eb81fe536ac9ad69c9229a832c1a661dab780a362","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3c21d7eca64d0c7d8abaf6e2a69f445aa6e514be6f0e4feb54c76781a507b3e9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b57719fe1738f95cf28675ad0e55eb81a991bf372a7a5dda6c45b162bd094d96","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fee02d6d186cd9b1dc4824242b05768bb2edc61614f01ad6207145744366a731","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a83e4dd75c54300e6314ea2c0c5813b418d1a2244391acb001f263c9b1b37521","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"27c25a73ab8e8e6ea25f0679d1ef24c446a929a7b9da8fc842af72349beb9ef1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f0eef830593e6ca3e34d7c4af265fcbcd5d7ec2a6c980a442a8a395c98b7d872","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"752bc0bd543fc478323820a8595d137ea1fb8fd0d8ceb0ea05c3ded4bf1d3729","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4410ddaa3d6e3c1441fc5f669ea5c3e3390fd75f6127f06b1240625558160a9e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"36919106e6e5c86f0628d2542222b4f6a09cf7955bd96c53a9f17a09b62f3903","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f6be390877d224b02106db41d582eca38b6d52215c0843d3e6d78d210c956f95","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"477bc6781d39427cdfb58b00ac6744fd72a76ddb9add5ee2b6fd7c0123e8c133","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d77a4e85ac4e7465ee559c7aa33e9b67794fb42eb006094e41de859e0f574567","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"efaba8801c46c71114040269fdbc963f3496d01a5b185ef05612d3d71f6c1fbe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b4f65e7dc37a2e488233381af3c9fdd7ca6e0171adc8a9cb2d49d2fa17cd7d67","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"90866b821f4e9d5b76370bf9af71b3bf92fc1fa9e4498452ce90b1035a4e4737","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d534cfa39fd1fa8f70dbd3349d67920554475cefd0087e7c3e418d544eeb3adf","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6cda8181158bb3331be538ad07a3a6627c80322ea32904bc0807eae370c510ba","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4733417da9fe7eeed82209b53ddbf53bb76c0a7706f747945278a2c037ba2bcc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b78fa0476aae9c90f1ba345f48912c46ff37b4c95cd6242b75cb57efd8f2bc4b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c9959681b8cffae14e821fbfdf3daac7759ccd92bd04413f45100301d8d08d20","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"57ebd17c1bf0e09d222252bb67deb0cef31476b2476c680ea5ceeca29cfa3efe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2813a4305e2d3d23e4997d9a2f482fde783962eec4c66dcd112a3348a1b1f6a8","signature":"b84cea73e43cd5d152e01d2870e7736075b6c5ffd9355dfe2660b98078c17e9d"},{"version":"c6a12b1489747a7b0203fd3e8ce5905c18b426aa79fdf9c47578dbf0f91058e9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"48392b4f5473115f4cbd2da11efb0fda7bb0610c15185a5838260c9c2b2e5745","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"931e404359129b88ae22b707a39298f2d8351f150a5ad6feabb975603272beeb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b522f77d275268e582dd53f3dc4f93082eb2f79a0022d066bcadb94a59b6c88b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"002c7bb5c3156eee0fe39dff54c4741c297b56aa6d4378a8acd34507505ea63e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c77cad2e3e80373964256a967f064b23ff95f5fc46788636eac8b765b2fea524","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"75424e9c1527a998d62a8b265f710ad1302623e4de736fdccb156f78d2909ba4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"35c528989066fabdde1d1aafeb745a6d41d5909a454992c1107c318f59f80f8d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"69b3922251aa575049849afbf72d429c17965f5827fcfc0b6636263d0a261779","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a23b58c3087d419c5d21fba70096b8a9eb42977ad61f22f6f7fba5e09e0e6ae3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"055e5ac1ac33ae174595f55c76aa1e371ca8819456cc5b97a69872037139ac72","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"45f9d9bf9998e2ec7147ad27c7edc2e5ed387302c4018c9f4f7ff088eb22af8a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"90e9e2b93e5bae19a5e66972efb5e6ec11dc1b50b9e8259f882055ccdb3d4aac","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f397adc5718aa5b8ea60ec16afed311eafe510111cdc0de0378994c629ff4eff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7a83e8d50eecbdb731f8da23ee08494be2d247cb9e8e5c3857da7cd9e07fdc50","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"671265f64fc5c31cd317267ead0afc5c6c4634fb51204bfb54e3bac5d19d4db7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f1f4a3bf8d46ac603eaefa297ebfafb18a111a4854577d169bc3c0358bb373aa","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"89a4895e643533b7b76f782b52aa9b695d0961f2695ca7720dc50b48e9e55215","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dd12c9b4822755161ebb4ba65818948561a5982f5f493eca9f6f0db242a468b3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"114445bc0794c2c9a3f03a42134748f545ea788a004e4667d7b9eff39211a61f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3a47055c95d43ba5fec9e3d06bdf5e2458db8a93246b28c9efd0525fffb77f74","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"09b4d72988c4682aa5713bc6a6df7892a7b8e2f10e1af3dc02985c6d4aef84ad","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"baefb08b615c9fc53a31c27981bf8899af8e01a0c9e2ab60c23cf0d324d77274","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"465069c75ef1e4b084bce885c0a2ee70520c5ebb8f201fe6f85090a28fb34703","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1e1d5c76c59c49b2b6f32b7065d0a95bfd229908da2db72526ebdb9312ed2cdd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"99eedc96ba7fa339e3ac82727c73628382af56287cc1219589004ea36e1b0c64","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"40510866633ba6c635e0495e40994f4e3f30d9378f23cc26887b3ff5e56391a3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c9cc3eb942b0cb85daf312f463a21c77b019a156a10240188e26976bc5f274b9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"288e930c1a2661f6345d07635585f1fe13c2deda86e2ccfc349413716c420555","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7118997a67de7cc91539da74dd524459d5cf3f9e2a8b90efcf140600ec9a4d82","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"beabd1db71dc8e0911944d9400ced2cd02de425ffeb61c6ca0d2124cbe64d785","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"945b34137fa3efb0ae22928275c1c42a722dbb81a26c57fb02f64f71e5daa3ce","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ccbb103828aaddf117b3d694cb122ae8657298732bd550ae10bd1284366080b6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"294041d51d1910e6986cfe979cd3732a5f7eae7f329589ca4f2799248e5a7265","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4516f87a2270fb1f66171535148068380734007309d9568c4b81e42124e08cd1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ab4a93c3142d6f517f1d764f00e83b838fe91e8533dd3b006c2b3c2c901b4a6f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"de385ab9ee98320c98662be263959c7d9c55bb2cba5e8d7f9107def8bb82e4f6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c86fd4b8708bcfe085c01b61dc75aff81070eb58ac0ee7bde066589d1876b7a9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2309f180e82b613a4f9276b83512a7a85241e7651e734437bc3e89f2cfb342e1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"78228dfdc1a7024c9e4eabee22c057409886b9014ebd22319ee8a0a9ed31e799","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"18beabc03110b8f4e1d1eb5a556e6de09834d365995b2f10b17d26a574dba141","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"70e8e8b44c2fca9791db4f3c06c3cb310556b19335d656bfe3cfe6aee8d65622","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"995394b9345e8eae0c2413b22ea07faa239769d45a58bb219b9222d86bf2d9b7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"04b5ec07def664c916b3a73a5b4b31f3930a626739ddb528569bdd33f0300456","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"aa82aeaf9be0588a652ec636bca8e6d7be86a81f85bb22e857b02a469e8ab2b8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"028ca5c0547be6d2d8d1d20306614c16c3dee3d6501f2c38031e03aa6c9ccbed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f4b54f4c0273db6878a1823ac888998ad7a0dd816f1c45a2fa24e0417702fc7c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"32b1d470365f006b5fb2ad91d9097eefce0fad34bba93764e4501f2611104482","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"41b4c577be164a41be457fa1eff74c8923c8f08e8ba7e5e57d894424f48de2a9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"15c805dfe9b0eedb507e5a9d32ae6e321327d77673ba6181de4710f2c2634cc2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5cc1c46b57a52eba565ad27fa54cf2e09d763de1f3412354357e6085e0d89ec4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dd7ca639d42b7f9566dc6f58188e1919ddc8f3349dc7163c0efa1cb4205cbfdf","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5597c2bdc52f017f791a1962e3595e58f628d60de80407f11e0c3020565b52f8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"47c0e01bceee2d7e95b691b2417954d55251167544413855e8440495dd67a5a8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c2b1d972a2b83eb6f85a7d894c57331aa5be4e9b93d1b9b16d697112b52069bc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4599379d9c9ef5f827a03600471298c7f5ce06580b0ef6364a0f1e82423ad9c5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"931f024c731f1ec9fe2618f279ec2ad7c4a74e5378728dd2a60f427afc066227","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a1ff1783f054a0cc5e2a938d35db521e61071d522c9b9ba9036bf33844dedd99","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"35bd7fa89a59ca3b136c221ef604ba3a8e30cc88bb03cbc4d26fee0659d02ce7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d596725c15eee936539fe4bcb0ec9f08b2d8392f0e9bce03effb76ed734910ed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b08b6177c9234876e6836895b0bbf4465e14c9b64bbb7467da5b89b9b5b11d89","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ed0d11407fc46bc08b4b3b36c4d168f207ebdcf78496adef7dc126513efb6877","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a85610ba8978234a59d37629fafe05c27fb2154cb62b6b775eb8119802e0a38e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"adfa78b8af8a8be5116202f634a2f113d7801ed20c47767339f1505f952ebcc1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f328b3203d26b5f709e5a082bc956c2e95fbd9fbdca6abee0802b59b633e0dc0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4435ab8057ea5a31a324cc45bf3845138b7589c4ba8399f4f554b17b8fb79489","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d54083855fcc2ed66dedb389bf2efc33b892dcf572829e43758309fe7bcaabb3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f44df634432426f2b1249398b735f171a84c3902b4e0452ea2f7cc3d02568bd1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7952ed742b48930403868cdd2e09a9b5aa543c9adbed9f012618d6b58c289dff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"32466921bab35fe4558bf82916cc03ac02d5d6d87d592fe1c4a141ae801ba5ed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ba0d14c18aebe4a5cba52b4a7b902247dd5a91106737e06d6e2112b1b4cbcacf","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3b8983ef3b44f8779b91c7604f70379f8c40f88da3d6863e4bb7a5d7f95b2c98","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"046287f8ba5cdeb90b848a6eae7311c5a34b343e63b3fe23d3eea19eb59ab284","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6889f8eea64d18bcd198ca1ad22b306cf48f4e8e213e15d3c789e98d67e4c87b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ecaaff298281e8bd8bc234e03d4bc1ba565a804edb846005ea6566cbcc47fc73","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ceded34bed1c475b90671a320a8fd84a6a4a4d7c56c3f3f88d9a6804e933eba4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6679ae1bd78dea53dc058ae235a3708f27ac7f87da929ddd38f7d4c222c18f9b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9a40eaa7da4b2085746448671fad7ca6da6a84c58cb1d0e2ebfba17888d040a2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b2dd58af1e54e5d04b9130dbef33c83234955d0faa6c32018be9934e12ac2015","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"16e6cada57ba26e2ea6c272e08dc7017b9df78f114061b8144ef1c0537bc3274","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9815b675507e394e469b6bc395afbe8c63d6736cc7290a73f56cfaaca549b027","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b0248daeeaac242de0ea72ac0f093a31b55e70b43020d40380dbf609803a45e7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5a31a79a9691f6153276381e906dd27e985f53c6920adab35199527cbfaeace8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3414d416e51f2846b21f4aa1492fd44bf9ffeaece26743ae1527154e3da6f7fe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6309b32da582c7b3e5afdf30678bd7d456cd9a1118ea1c660dd73ee32770d683","signature":"4c372df16f354b44e6e653a4442eb9f26b95f2d43efcbaa75b59506276b92df7"},{"version":"26f213bee14ac8092e7a36473db58d1955fa4867bf5b091950ad8dfd31956809","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4b332cfe58c80b9e5abef88dfe157a88f9170f64035fd2a83dc395b334c440fc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"262dc2495f719b674acd7919e678de874580311f4a0cb71f04c69995bf61650e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c1161b186fb7ef72c0dbd14af1652937e6cb3453231dd6f56d396f43d46d638f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"89ac6a7385062683575fc5ad85a18f77e6c9617a3786f49aba644d55ae277f4e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3e33a62342fe8bc07fd5ffb6e870ed8f0d906f8021115bea5b4ef5cbd3632d04","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8bad29d0d9cfb3316a408a53c38ce6c56dc4d90b1155f5adc9a72c5e6ef6476e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b247732a1ae37a5e0307d4333ef15e3d5393951e3236be0287adeaa48d553b35","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"d1986184a09a52db8228cb2bb2a61a8c05c9354e5b93cec8e2628d8579c892d7",{"version":"1e11adf04ff8cb1c16d42cd6efe0e1039cc1335c26671292bc2c96ecbf9a0b30","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b1538a92b9bae8d230267210c5db38c2eb6bdb352128a3ce3aa8c6acf9fc9622","impliedFormat":1},{"version":"6fc1a4f64372593767a9b7b774e9b3b92bf04e8785c3f9ea98973aa9f4bbe490","impliedFormat":1},{"version":"ff09b6fbdcf74d8af4e131b8866925c5e18d225540b9b19ce9485ca93e574d84","impliedFormat":1},{"version":"d5895252efa27a50f134a9b580aa61f7def5ab73d0a8071f9b5bf9a317c01c2d","impliedFormat":1},{"version":"1f366bde16e0513fa7b64f87f86689c4d36efd85afce7eb24753e9c99b91c319","impliedFormat":1},{"version":"fb893a0dfc3c9fb0f9ca93d0648694dd95f33cbad2c0f2c629f842981dfd4e2e","impliedFormat":1},{"version":"89e326922cadcc2331d7e851011cf9f0456a681aaf3c95b48b81f8d80e8cdfba","impliedFormat":1},{"version":"5d08a179b846f5ee674624b349ebebe2121c455e3a265dc93da4e8d9e89722b4","impliedFormat":1},{"version":"96d14f21b7652903852eef49379d04dbda28c16ed36468f8c9fa08f7c14c9538","impliedFormat":1},{"version":"7fa8d75d229eeaee235a801758d9c694e94405013fe77d5d1dd8e3201fc414f1","impliedFormat":1}],"root":[531,532,613,614,[630,634],[636,640],[1034,1038],[1040,1048],[1083,1138],[1175,1245],[1312,1336],[1340,1368],[1371,1380],[1400,1431],[1453,1576],[1654,1656],[1662,1701],[1933,1956],[1958,1989],[1991,2056],[2060,2068],[2084,2130],[2275,2302],[2304,2316],[2319,2379],[2638,2652],[2654,2667],2672,2673,2675,2677,2678,2682,2684,2686,2688,2690,2692,2694,2695,[2700,2722],[2810,2960],[3038,3404],[3422,3424],[3492,4156]],"options":{"allowJs":true,"esModuleInterop":true,"jsx":4,"module":99,"skipLibCheck":true,"strict":true,"target":4},"referencedMap":[[4155,1],[531,2],[4156,3],[532,4],[3490,5],[3438,6],[3436,7],[3439,8],[3443,9],[3432,10],[3442,11],[3455,12],[3491,13],[3425,2],[3454,14],[3453,2],[3430,2],[3437,15],[3433,16],[3431,17],[3441,18],[3429,19],[3440,20],[3434,21],[3463,22],[3464,23],[3460,24],[3459,25],[3480,26],[3483,27],[3482,28],[3484,26],[3481,29],[3479,30],[3449,31],[3465,32],[3448,33],[3486,34],[3444,35],[3445,36],[3478,37],[3466,38],[3450,35],[3452,39],[3451,40],[3462,41],[3467,42],[3485,43],[3446,35],[3468,44],[3471,45],[3470,46],[3469,47],[3474,48],[3473,49],[3472,36],[3447,35],[3475,35],[3477,50],[3476,51],[3487,52],[3489,53],[3458,54],[3456,55],[3457,56],[3461,57],[3488,35],[3435,2],[651,58],[655,59],[654,60],[650,61],[653,62],[646,63],[652,58],[707,64],[719,65],[718,66],[708,67],[716,68],[752,69],[751,70],[731,71],[743,72],[722,73],[729,71],[723,74],[755,75],[754,76],[757,77],[756,78],[753,72],[758,72],[759,79],[764,80],[765,81],[763,82],[762,83],[761,84],[760,80],[769,85],[768,86],[767,87],[648,88],[649,89],[766,90],[740,91],[737,92],[779,72],[778,72],[777,72],[733,92],[745,74],[746,72],[742,72],[741,72],[732,72],[782,93],[781,94],[773,71],[730,71],[776,92],[775,72],[771,95],[734,72],[739,96],[736,97],[738,91],[721,98],[770,73],[749,99],[750,2],[744,72],[735,72],[774,71],[772,74],[813,100],[812,101],[810,102],[788,103],[811,72],[814,104],[816,105],[815,106],[709,92],[710,72],[711,72],[818,107],[817,108],[712,109],[713,97],[706,110],[705,111],[704,112],[714,72],[715,113],[717,92],[820,114],[822,115],[821,116],[823,92],[824,72],[825,72],[826,72],[828,72],[827,72],[841,117],[840,118],[832,119],[833,97],[834,104],[830,120],[831,121],[835,122],[836,72],[837,113],[838,92],[839,104],[845,80],[844,95],[843,123],[849,124],[848,125],[847,95],[842,95],[727,126],[846,127],[853,128],[852,129],[851,72],[850,72],[695,130],[674,131],[677,132],[673,133],[693,134],[672,135],[688,136],[696,137],[678,135],[679,138],[697,135],[691,139],[680,135],[684,140],[685,135],[686,141],[683,142],[689,143],[698,144],[690,145],[699,146],[692,147],[694,148],[687,135],[682,149],[725,150],[726,151],[1029,152],[855,153],[854,154],[643,155],[819,74],[748,2],[724,156],[675,2],[972,72],[641,2],[642,157],[720,74],[681,2],[645,158],[676,159],[647,74],[783,91],[784,92],[792,92],[791,160],[794,72],[793,72],[809,161],[808,162],[795,72],[796,72],[797,96],[798,97],[799,91],[800,160],[802,92],[801,72],[790,163],[786,164],[789,165],[785,166],[804,167],[803,168],[807,72],[805,169],[806,72],[857,170],[856,160],[787,171],[859,172],[858,72],[866,173],[865,174],[862,175],[864,175],[860,72],[861,175],[863,175],[877,91],[875,92],[870,92],[879,72],[881,176],[880,177],[869,72],[878,72],[868,72],[876,178],[872,97],[873,91],[867,63],[871,72],[874,72],[886,179],[884,179],[885,179],[891,180],[890,181],[887,179],[883,182],[889,179],[888,179],[882,2],[896,183],[895,184],[894,185],[893,186],[892,2],[905,91],[906,92],[909,72],[908,72],[912,187],[911,188],[904,96],[902,97],[903,91],[900,189],[899,190],[898,191],[907,72],[901,192],[910,72],[921,91],[922,92],[925,193],[924,194],[920,178],[917,195],[919,91],[915,196],[914,197],[913,198],[918,199],[923,72],[932,200],[931,201],[928,202],[930,202],[926,72],[927,202],[929,202],[938,203],[937,80],[936,204],[935,205],[934,206],[933,95],[942,207],[944,72],[946,208],[945,209],[939,72],[941,207],[943,72],[940,207],[960,91],[953,92],[964,72],[963,72],[951,72],[966,210],[965,211],[958,92],[959,72],[957,72],[948,95],[956,72],[955,96],[952,97],[954,91],[947,98],[961,72],[962,72],[949,71],[950,72],[780,212],[747,72],[970,213],[976,214],[975,215],[974,213],[968,213],[967,80],[973,216],[971,213],[969,213],[980,217],[979,218],[977,219],[978,220],[987,221],[986,222],[983,223],[985,224],[984,225],[982,226],[981,224],[998,72],[1000,91],[997,72],[994,72],[990,227],[995,72],[1002,228],[1001,229],[999,195],[988,230],[991,231],[993,232],[996,72],[989,233],[992,72],[1006,234],[1005,63],[1004,235],[1003,63],[1010,236],[1009,236],[1014,237],[1013,238],[1012,236],[1011,236],[1008,72],[1007,239],[1022,91],[1026,240],[1025,241],[1021,178],[1019,195],[1020,91],[1023,74],[1017,242],[1016,243],[1015,244],[1018,245],[1024,72],[644,246],[1028,247],[1027,159],[916,97],[703,248],[671,249],[702,250],[700,2],[701,251],[728,252],[829,74],[659,74],[657,253],[658,254],[664,255],[662,256],[660,2],[663,257],[661,258],[665,74],[897,2],[2669,259],[667,260],[669,261],[670,262],[666,2],[668,2],[1702,74],[1703,74],[1704,74],[1705,74],[1706,74],[1707,74],[1708,74],[1709,74],[1710,74],[1711,74],[1712,74],[1713,74],[1714,74],[1715,74],[1716,74],[1722,74],[1717,74],[1718,74],[1719,74],[1720,74],[1721,74],[1723,74],[1724,74],[1725,74],[1726,74],[1727,74],[1728,74],[1730,74],[1731,74],[1729,74],[1732,74],[1733,74],[1734,74],[1735,74],[1736,74],[1737,74],[1738,74],[1739,74],[1740,74],[1741,74],[1742,74],[1743,74],[1744,74],[1745,74],[1746,74],[1747,74],[1748,74],[1749,74],[1750,74],[1751,74],[1752,74],[1753,74],[1754,74],[1755,74],[1756,74],[1758,74],[1757,74],[1759,74],[1760,74],[1762,74],[1761,74],[1763,74],[1764,74],[1765,74],[1766,74],[1767,74],[1769,74],[1768,74],[1770,74],[1771,74],[1772,74],[1773,74],[1774,74],[1775,74],[1776,74],[1777,74],[1778,74],[1779,74],[1780,74],[1781,74],[1782,74],[1783,74],[1788,74],[1784,74],[1785,74],[1786,74],[1787,74],[1789,74],[1790,74],[1791,74],[1792,74],[1793,74],[1794,74],[1795,74],[1796,74],[1797,74],[1798,74],[1800,74],[1799,74],[1801,74],[1802,74],[1803,74],[1804,74],[1805,74],[1806,74],[1807,74],[1808,74],[1811,74],[1809,74],[1810,74],[1812,74],[1813,74],[1814,74],[1815,74],[1816,74],[1817,74],[1818,74],[1819,74],[1821,74],[1820,74],[1932,263],[1822,74],[1823,74],[1824,74],[1825,74],[1826,74],[1827,74],[1828,74],[1829,74],[1830,74],[1831,74],[1832,74],[1834,74],[1833,74],[1835,74],[1836,74],[1837,74],[1838,74],[1839,74],[1840,74],[1841,74],[1842,74],[1844,74],[1843,74],[1845,74],[1846,74],[1847,74],[1848,74],[1849,74],[1850,74],[1851,74],[1852,74],[1853,74],[1857,74],[1854,74],[1855,74],[1856,74],[1858,74],[1859,74],[1860,74],[1862,74],[1861,74],[1863,74],[1864,74],[1865,74],[1866,74],[1867,74],[1868,74],[1869,74],[1870,74],[1871,74],[1872,74],[1873,74],[1874,74],[1875,74],[1876,74],[1877,74],[1878,74],[1879,74],[1880,74],[1881,74],[1882,74],[1883,74],[1884,74],[1885,74],[1886,74],[1887,74],[1888,74],[1889,74],[1890,74],[1891,74],[1892,74],[1893,74],[1894,74],[1895,74],[1896,74],[1897,74],[1898,74],[1899,74],[1900,74],[1901,74],[1902,74],[1903,74],[1904,74],[1905,74],[1906,74],[1907,74],[1908,74],[1909,74],[1910,74],[1911,74],[1912,74],[1913,74],[1914,74],[1915,74],[1917,74],[1916,74],[1918,74],[1919,74],[1920,74],[1921,74],[1922,74],[1923,74],[1924,74],[1925,74],[1926,74],[1927,74],[1928,74],[1929,74],[1930,74],[1931,74],[2083,264],[2082,265],[405,2],[374,2],[2145,266],[2144,267],[1660,2],[1447,268],[1446,2],[625,2],[626,269],[1452,270],[1449,271],[1450,272],[1451,272],[1448,273],[627,274],[628,275],[1443,276],[1432,74],[1445,277],[1442,276],[1439,278],[1440,278],[1441,2],[1444,2],[1174,279],[1433,2],[1435,280],[1438,281],[1437,2],[1436,280],[1434,282],[1153,283],[1163,284],[1160,284],[1161,285],[1145,285],[1159,285],[1140,284],[1146,286],[1149,287],[1154,288],[1142,286],[1143,285],[1156,289],[1141,286],[1147,286],[1150,286],[1155,286],[1157,285],[1144,285],[1158,285],[1152,290],[1148,291],[1173,292],[1151,293],[1162,294],[1139,285],[1164,285],[1165,285],[1166,285],[1167,285],[1168,285],[1169,285],[1170,285],[1171,285],[1172,285],[1395,2],[1392,2],[1391,2],[1386,295],[1397,296],[1382,297],[1393,298],[1385,299],[1384,300],[1394,2],[1389,301],[1396,2],[1390,302],[1383,2],[2681,303],[2680,304],[2679,297],[1399,305],[1639,306],[1640,306],[1642,307],[1641,306],[1634,306],[1635,306],[1637,308],[1636,306],[1614,2],[1613,2],[1616,309],[1615,2],[1612,2],[1579,310],[1577,311],[1580,2],[1627,312],[1581,306],[1617,313],[1626,314],[1618,2],[1621,315],[1619,2],[1622,2],[1624,2],[1620,315],[1623,2],[1625,2],[1578,316],[1653,317],[1638,306],[1633,318],[1643,319],[1649,320],[1650,321],[1652,322],[1651,323],[1631,318],[1632,324],[1628,325],[1630,326],[1629,327],[1644,306],[1648,328],[1645,306],[1646,329],[1647,306],[1582,2],[1583,2],[1586,2],[1584,2],[1585,2],[1588,2],[1589,330],[1590,2],[1591,2],[1587,2],[1592,2],[1593,2],[1594,2],[1595,2],[1596,331],[1597,2],[1611,332],[1598,2],[1599,2],[1600,2],[1601,2],[1602,2],[1603,2],[1604,2],[1607,2],[1605,2],[1606,2],[1608,306],[1609,306],[1610,333],[1381,2],[602,334],[4157,2],[4158,2],[4159,2],[4160,335],[2154,2],[2132,336],[2155,337],[2131,2],[4161,2],[4163,338],[600,2],[4164,339],[546,2],[2724,340],[2668,2],[4165,2],[2734,340],[4162,2],[3427,2],[3428,341],[140,342],[141,342],[142,343],[97,344],[143,345],[144,346],[145,347],[92,2],[95,348],[93,2],[94,2],[146,349],[147,350],[148,351],[149,352],[150,353],[151,354],[152,354],[153,355],[154,356],[155,357],[156,358],[98,2],[96,2],[157,359],[158,360],[159,361],[191,362],[160,363],[161,364],[162,365],[163,366],[164,367],[165,368],[166,369],[167,370],[168,371],[169,372],[170,372],[171,373],[172,2],[173,374],[175,375],[174,376],[176,17],[177,377],[178,378],[179,379],[180,380],[181,381],[182,382],[183,383],[184,384],[185,385],[186,386],[187,387],[188,388],[99,2],[100,2],[101,2],[139,389],[189,390],[190,391],[1990,392],[1957,74],[195,393],[460,74],[196,394],[194,395],[462,396],[461,397],[1398,74],[1369,398],[192,399],[458,2],[193,400],[83,2],[85,401],[457,74],[226,74],[2723,2],[4166,2],[542,402],[589,403],[587,2],[588,2],[534,2],[584,404],[581,405],[582,406],[603,407],[594,2],[597,408],[596,409],[608,409],[595,410],[533,2],[541,411],[583,411],[536,412],[539,413],[590,412],[540,414],[535,2],[601,2],[1033,415],[1032,416],[1030,2],[84,2],[2468,417],[2447,418],[2544,2],[2448,419],[2384,417],[2385,417],[2386,417],[2387,417],[2388,417],[2389,417],[2390,417],[2391,417],[2392,417],[2393,417],[2394,417],[2395,417],[2396,417],[2397,417],[2398,417],[2399,417],[2400,417],[2401,417],[2380,2],[2402,417],[2403,417],[2404,2],[2405,417],[2406,417],[2408,417],[2407,417],[2409,417],[2410,417],[2411,417],[2412,417],[2413,417],[2414,417],[2415,417],[2416,417],[2417,417],[2418,417],[2419,417],[2420,417],[2421,417],[2422,417],[2423,417],[2424,417],[2425,417],[2426,417],[2427,417],[2429,417],[2430,417],[2431,417],[2428,417],[2432,417],[2433,417],[2434,417],[2435,417],[2436,417],[2437,417],[2438,417],[2439,417],[2440,417],[2441,417],[2442,417],[2443,417],[2444,417],[2445,417],[2446,417],[2449,420],[2450,417],[2451,417],[2452,421],[2453,422],[2454,417],[2455,417],[2456,417],[2457,417],[2460,417],[2458,417],[2459,417],[2382,2],[2461,417],[2462,417],[2463,417],[2464,417],[2465,417],[2466,417],[2467,417],[2469,423],[2470,417],[2471,417],[2472,417],[2474,417],[2473,417],[2475,417],[2476,417],[2477,417],[2478,417],[2479,417],[2480,417],[2481,417],[2482,417],[2483,417],[2484,417],[2486,417],[2485,417],[2487,417],[2488,2],[2489,2],[2490,2],[2637,424],[2491,417],[2492,417],[2493,417],[2494,417],[2495,417],[2496,417],[2497,2],[2498,417],[2499,2],[2500,417],[2501,417],[2502,417],[2503,417],[2504,417],[2505,417],[2506,417],[2507,417],[2508,417],[2509,417],[2510,417],[2511,417],[2512,417],[2513,417],[2514,417],[2515,417],[2516,417],[2517,417],[2518,417],[2519,417],[2520,417],[2521,417],[2522,417],[2523,417],[2524,417],[2525,417],[2526,417],[2527,417],[2528,417],[2529,417],[2530,417],[2531,417],[2532,2],[2533,417],[2534,417],[2535,417],[2536,417],[2537,417],[2538,417],[2539,417],[2540,417],[2541,417],[2542,417],[2543,417],[2545,425],[2381,417],[2546,417],[2547,417],[2548,2],[2549,2],[2550,2],[2551,417],[2552,2],[2553,2],[2554,2],[2555,2],[2556,2],[2557,417],[2558,417],[2559,417],[2560,417],[2561,417],[2562,417],[2563,417],[2564,417],[2569,426],[2567,427],[2568,428],[2566,429],[2565,417],[2570,417],[2571,417],[2572,417],[2573,417],[2574,417],[2575,417],[2576,417],[2577,417],[2578,417],[2579,417],[2580,2],[2581,2],[2582,417],[2583,417],[2584,2],[2585,2],[2586,2],[2587,417],[2588,417],[2589,417],[2590,417],[2591,423],[2592,417],[2593,417],[2594,417],[2595,417],[2596,417],[2597,417],[2598,417],[2599,417],[2600,417],[2601,417],[2602,417],[2603,417],[2604,417],[2605,417],[2606,417],[2607,417],[2608,417],[2609,417],[2610,417],[2611,417],[2612,417],[2613,417],[2614,417],[2615,417],[2616,417],[2617,417],[2618,417],[2619,417],[2620,417],[2621,417],[2622,417],[2623,417],[2624,417],[2625,417],[2626,417],[2627,417],[2628,417],[2629,417],[2630,417],[2631,417],[2632,417],[2383,430],[2633,2],[2634,2],[2635,2],[2636,2],[2059,431],[2058,432],[2057,2],[2653,433],[2267,2],[551,2],[2671,434],[2670,435],[621,436],[623,437],[622,438],[620,439],[619,2],[3426,440],[2142,2],[635,2],[574,2],[576,441],[575,2],[1039,74],[2803,2],[2777,442],[2776,443],[2775,444],[2802,445],[2801,446],[2805,447],[2804,448],[2807,449],[2806,450],[2762,451],[2736,452],[2737,453],[2738,453],[2739,453],[2740,453],[2741,453],[2742,453],[2743,453],[2744,453],[2745,453],[2746,453],[2760,454],[2747,453],[2748,453],[2749,453],[2750,453],[2751,453],[2752,453],[2753,453],[2754,453],[2756,453],[2757,453],[2755,453],[2758,453],[2759,453],[2761,453],[2735,455],[2800,456],[2780,457],[2781,457],[2782,457],[2783,457],[2784,457],[2785,457],[2786,458],[2788,457],[2787,457],[2799,459],[2789,457],[2791,457],[2790,457],[2793,457],[2792,457],[2794,457],[2795,457],[2796,457],[2797,457],[2798,457],[2779,457],[2778,460],[2770,461],[2768,462],[2769,462],[2773,463],[2771,462],[2772,462],[2774,462],[2767,2],[2303,2],[1370,74],[483,464],[488,1],[495,465],[478,466],[230,2],[238,467],[378,468],[381,469],[353,2],[366,470],[373,471],[255,2],[355,2],[236,2],[352,472],[398,473],[237,2],[228,474],[380,475],[382,476],[383,477],[455,478],[347,479],[300,480],[360,481],[361,482],[359,483],[358,2],[354,484],[379,485],[239,486],[425,2],[426,487],[266,488],[240,489],[267,488],[303,488],[206,488],[376,490],[375,2],[365,491],[473,2],[215,2],[494,492],[433,493],[434,494],[430,495],[512,2],[330,2],[435,104],[431,496],[517,497],[516,498],[511,2],[281,2],[333,499],[332,2],[510,500],[432,74],[286,501],[293,502],[295,503],[285,2],[290,504],[292,505],[294,506],[289,507],[287,2],[291,508],[513,2],[509,2],[515,509],[514,2],[284,510],[504,511],[507,512],[274,513],[273,514],[272,515],[520,74],[271,516],[260,2],[522,2],[2697,517],[2696,2],[523,74],[524,518],[198,2],[362,519],[363,520],[364,521],[202,2],[367,2],[222,522],[197,2],[447,74],[204,523],[446,524],[445,525],[436,2],[437,2],[444,2],[439,2],[442,526],[438,2],[440,527],[443,528],[441,527],[235,2],[232,2],[233,488],[387,2],[392,529],[393,530],[391,531],[389,532],[390,533],[385,2],[453,104],[227,104],[482,534],[489,535],[493,536],[321,537],[320,2],[315,2],[469,538],[477,539],[348,540],[349,541],[428,542],[337,2],[451,543],[325,74],[342,544],[454,545],[338,2],[341,546],[339,2],[452,547],[449,548],[448,2],[450,2],[345,2],[424,549],[210,550],[323,551],[327,552],[343,553],[346,554],[335,555],[328,556],[476,557],[401,558],[319,559],[207,560],[475,561],[203,562],[394,563],[386,2],[395,564],[413,565],[384,2],[412,566],[91,2],[407,567],[231,2],[427,568],[402,2],[216,2],[218,2],[357,2],[411,569],[234,2],[258,570],[344,571],[264,572],[324,2],[410,2],[388,2],[415,573],[416,574],[356,2],[418,575],[420,576],[419,577],[368,2],[409,560],[422,578],[318,579],[408,580],[414,581],[243,2],[247,2],[246,2],[245,2],[250,2],[244,2],[253,2],[252,2],[249,2],[248,2],[251,2],[254,582],[242,2],[310,583],[309,2],[314,584],[311,585],[313,586],[316,584],[312,585],[223,587],[302,588],[472,589],[470,2],[499,590],[501,591],[465,592],[500,593],[211,594],[208,594],[241,2],[225,595],[224,596],[220,597],[221,598],[229,599],[257,599],[268,599],[304,600],[269,600],[213,601],[212,2],[308,602],[307,603],[306,604],[305,605],[214,606],[456,607],[256,608],[464,609],[429,610],[459,611],[463,612],[351,613],[350,614],[331,615],[317,616],[299,617],[301,618],[298,619],[421,620],[322,2],[487,2],[219,621],[423,622],[471,623],[329,2],[259,624],[336,625],[334,626],[261,627],[396,628],[466,2],[262,629],[397,629],[485,2],[484,2],[486,2],[468,2],[467,2],[399,630],[326,2],[296,631],[217,632],[275,2],[201,633],[263,2],[491,74],[200,2],[503,634],[283,74],[497,104],[282,635],[480,636],[280,634],[205,2],[505,637],[278,74],[279,74],[270,2],[199,2],[277,638],[276,639],[265,640],[340,371],[400,371],[417,2],[404,641],[403,2],[288,510],[209,2],[297,74],[474,522],[481,642],[86,74],[89,643],[90,644],[87,74],[88,2],[377,645],[372,646],[371,2],[370,647],[369,2],[479,648],[490,649],[492,650],[496,651],[2698,652],[498,653],[502,654],[530,655],[506,655],[529,656],[508,657],[518,658],[519,659],[521,660],[525,661],[528,522],[527,2],[526,662],[2699,663],[1659,663],[1658,664],[1657,74],[1661,665],[2962,2],[2968,666],[2961,2],[2965,2],[2967,667],[2964,668],[3037,669],[3031,669],[2992,670],[2988,671],[3003,672],[2993,673],[3000,674],[2987,675],[3001,2],[2999,676],[2996,677],[2997,678],[2994,679],[3002,680],[2969,668],[3032,681],[2983,682],[2980,683],[2981,684],[2982,685],[2971,686],[2990,687],[3009,688],[3005,689],[3004,690],[3008,691],[3006,692],[3007,692],[2984,693],[2986,694],[2985,695],[2989,696],[3033,697],[2991,698],[2973,699],[3034,700],[2972,701],[3035,702],[2974,703],[3012,704],[3010,683],[3011,705],[2975,692],[3016,706],[3014,707],[3015,708],[2976,709],[3019,710],[3018,711],[3021,712],[3020,713],[3024,714],[3022,713],[3023,715],[3017,716],[3013,717],[3025,716],[2977,692],[3036,718],[2978,713],[2979,692],[2995,719],[2998,720],[2970,2],[3026,692],[3027,721],[3029,722],[3028,723],[3030,724],[2963,725],[2966,726],[1338,727],[1339,728],[1337,2],[569,729],[567,730],[568,731],[556,732],[557,730],[564,733],[555,734],[560,735],[570,2],[561,736],[566,737],[572,738],[571,739],[554,740],[562,741],[563,742],[558,743],[565,729],[559,744],[1388,745],[1387,2],[1049,2],[1065,746],[1066,746],[1067,746],[1068,746],[1082,747],[1069,748],[1070,748],[1071,749],[1062,750],[1060,751],[1051,2],[1055,752],[1059,753],[1057,754],[1064,755],[1052,756],[1053,757],[1054,758],[1056,759],[1058,760],[1061,761],[1063,762],[1072,748],[1073,748],[1074,748],[1075,746],[1076,748],[1077,748],[1050,748],[1078,2],[1080,763],[1079,748],[1081,746],[2317,764],[2318,765],[2766,766],[2765,767],[2171,768],[2264,769],[2262,770],[2169,2],[2170,771],[2263,2],[2265,772],[2173,773],[2172,774],[2176,775],[2243,776],[2238,777],[2139,778],[2209,779],[2202,780],[2259,781],[2137,782],[2208,783],[2197,784],[2196,774],[2242,785],[2239,786],[2190,787],[2201,788],[2244,789],[2245,789],[2246,790],[2254,791],[2248,791],[2256,791],[2260,791],[2247,791],[2249,792],[2252,792],[2255,792],[2251,793],[2253,791],[2257,794],[2250,795],[2148,796],[2223,74],[2220,797],[2224,74],[2159,791],[2149,791],[2215,798],[2138,799],[2158,800],[2162,801],[2222,791],[2135,74],[2221,802],[2219,74],[2218,791],[2150,74],[2269,803],[2233,795],[2213,804],[2274,805],[2231,2],[2229,2],[2234,806],[2232,807],[2228,808],[2230,809],[2235,810],[2237,811],[2227,74],[2157,812],[2134,791],[2226,791],[2175,813],[2225,74],[2198,812],[2258,791],[2192,814],[2146,815],[2151,816],[2203,817],[2205,814],[2184,818],[2187,814],[2163,819],[2186,820],[2194,821],[2195,822],[2191,823],[2206,824],[2193,825],[2168,826],[2214,827],[2210,828],[2211,829],[2207,830],[2185,831],[2174,832],[2178,833],[2152,834],[2182,835],[2183,836],[2179,837],[2153,838],[2164,839],[2204,822],[2147,840],[2212,2],[2177,841],[2167,842],[2199,2],[2271,843],[2272,844],[2273,771],[2240,2],[2270,771],[2261,2],[2188,2],[2160,2],[2236,845],[2189,2],[2140,771],[2268,846],[2166,847],[2200,848],[2165,849],[2241,850],[2180,2],[2216,2],[2217,851],[2161,2],[2181,2],[2266,2],[2136,74],[2143,852],[2141,2],[2809,853],[2808,854],[2764,855],[2763,856],[656,2],[548,857],[547,339],[406,858],[629,74],[553,2],[1031,2],[604,2],[537,2],[538,859],[2731,860],[2730,2],[81,2],[82,2],[13,2],[14,2],[16,2],[15,2],[2,2],[17,2],[18,2],[19,2],[20,2],[21,2],[22,2],[23,2],[24,2],[3,2],[25,2],[26,2],[4,2],[27,2],[31,2],[28,2],[29,2],[30,2],[32,2],[33,2],[34,2],[5,2],[35,2],[36,2],[37,2],[38,2],[6,2],[42,2],[39,2],[40,2],[41,2],[43,2],[7,2],[44,2],[49,2],[50,2],[45,2],[46,2],[47,2],[48,2],[8,2],[54,2],[51,2],[52,2],[53,2],[55,2],[9,2],[56,2],[57,2],[58,2],[60,2],[59,2],[61,2],[62,2],[10,2],[63,2],[64,2],[65,2],[11,2],[66,2],[67,2],[68,2],[69,2],[70,2],[1,2],[71,2],[72,2],[12,2],[76,2],[74,2],[79,2],[78,2],[73,2],[77,2],[75,2],[80,2],[117,861],[127,862],[116,861],[137,863],[108,864],[107,865],[136,662],[130,866],[135,867],[110,868],[124,869],[109,870],[133,871],[105,872],[104,662],[134,873],[106,874],[111,875],[112,2],[115,875],[102,2],[138,876],[128,877],[119,878],[120,879],[122,880],[118,881],[121,882],[131,662],[113,883],[114,884],[123,885],[103,886],[126,877],[125,875],[129,2],[132,887],[2733,888],[2729,2],[2732,889],[3421,890],[3405,2],[3406,2],[3408,891],[3409,2],[3407,2],[3410,891],[3411,891],[3413,892],[3412,891],[3414,891],[3415,892],[3416,891],[3417,2],[3418,891],[3419,2],[3420,2],[2726,893],[2725,340],[2728,894],[2727,895],[2133,896],[2156,897],[606,898],[592,899],[593,898],[591,2],[544,900],[580,901],[550,902],[545,900],[543,2],[549,903],[578,2],[573,2],[577,904],[552,2],[579,905],[612,906],[605,907],[598,908],[607,909],[586,910],[616,911],[617,912],[609,913],[618,914],[610,915],[599,916],[615,917],[611,918],[2674,919],[624,920],[585,2],[2073,921],[2080,922],[2075,2],[2076,2],[2074,923],[2077,924],[2069,2],[2070,2],[2081,925],[2072,926],[2078,2],[2079,927],[2071,928],[1305,929],[1308,930],[1306,930],[1302,929],[1309,931],[1310,932],[1307,930],[1303,933],[1304,934],[1298,935],[1250,936],[1252,937],[1296,2],[1251,938],[1297,939],[1301,940],[1299,2],[1253,936],[1254,2],[1295,941],[1249,942],[1246,2],[1300,943],[1247,944],[1248,2],[1311,945],[1255,946],[1256,946],[1257,946],[1258,946],[1259,946],[1260,946],[1261,946],[1262,946],[1263,946],[1264,946],[1265,946],[1267,946],[1266,946],[1268,946],[1269,946],[1270,946],[1294,947],[1271,946],[1272,946],[1273,946],[1274,946],[1275,946],[1276,946],[1277,946],[1278,946],[1279,946],[1281,946],[1280,946],[1282,946],[1283,946],[1284,946],[1285,946],[1286,946],[1287,946],[1288,946],[1289,946],[1290,946],[1291,946],[1292,946],[1293,946],[2683,948],[2685,267],[2687,267],[2689,267],[2691,267],[2693,267],[2676,267],[2867,949],[2858,950],[1314,951],[1313,952],[1312,953],[2864,954],[2857,955],[2855,956],[2866,957],[2856,958],[2865,959],[2861,960],[2860,961],[2859,962],[1245,267],[2862,963],[2895,964],[2893,965],[2894,966],[2812,967],[2911,968],[2912,969],[2901,970],[2913,971],[2899,972],[1315,267],[2914,973],[2903,974],[1317,975],[1316,976],[2898,977],[2915,978],[2916,979],[2904,980],[1320,981],[1319,982],[2917,983],[2902,984],[2896,985],[2909,986],[2907,987],[2910,988],[2906,989],[2905,990],[2897,991],[2900,992],[2908,993],[2918,994],[2851,995],[2919,996],[2924,997],[2921,998],[2920,999],[2923,1000],[2932,1001],[2925,1002],[2933,1003],[2929,1004],[1322,1005],[1321,267],[2931,1006],[2927,1007],[2926,1008],[1323,267],[2934,1009],[2928,1010],[2930,1011],[2949,1012],[2946,1013],[2950,1014],[2936,1015],[2939,1016],[2938,1017],[1324,267],[1326,1018],[1325,1019],[2952,1020],[2953,1020],[2940,1021],[2951,1022],[2937,1023],[1327,267],[2942,1024],[2941,1025],[2954,1026],[2943,1027],[1329,1028],[1328,1029],[2955,1030],[2956,1031],[2944,1032],[1087,267],[2948,1033],[2945,1034],[2935,104],[2947,1035],[2719,1036],[1331,1037],[1330,1038],[3051,1039],[2831,1040],[3052,1041],[3047,1042],[1334,1043],[1333,1044],[3053,1045],[3054,1045],[3049,1046],[1336,1047],[1335,267],[3055,1048],[3048,1049],[3056,1050],[2959,1051],[3057,1052],[2829,1053],[2828,1054],[3058,1055],[2830,1056],[3059,1057],[2958,1058],[1344,1059],[1343,1060],[3060,1061],[3061,1062],[1342,1063],[1346,1064],[1345,1065],[3050,1066],[3063,1067],[1358,1068],[3064,1069],[3065,1070],[1356,1071],[3066,1072],[3067,1072],[1378,1073],[3068,1074],[1373,1075],[1379,1076],[3071,1077],[1367,1078],[3072,1079],[1365,1080],[3073,1081],[1364,1082],[1403,1083],[1363,1084],[1359,1085],[1404,1086],[1366,1087],[3069,1088],[1355,1089],[1380,1090],[1374,1091],[3070,1092],[1357,1089],[1349,267],[1400,1093],[1377,1094],[1401,1095],[1375,1096],[1402,1097],[1376,1096],[3062,1098],[3149,1099],[3136,1100],[3151,1101],[3150,1102],[3152,1103],[3142,1104],[3154,1105],[3145,1106],[3155,1107],[3144,1108],[3153,1109],[3140,1110],[3156,1111],[3143,1112],[3148,1113],[3147,1114],[3112,1115],[3113,1116],[3090,1117],[1409,267],[3093,1118],[3124,1119],[3081,1120],[3079,1121],[3125,1122],[3082,1123],[3126,1124],[3094,1125],[3127,1126],[3095,1127],[3128,1128],[3129,1129],[3075,1130],[3130,1131],[3076,1132],[3078,1133],[3131,1134],[3074,1135],[3077,1118],[3132,1136],[1697,1137],[3133,1138],[3080,1139],[3134,1140],[1410,1141],[1411,1142],[3114,1143],[3102,1144],[3115,1145],[3100,1146],[1405,267],[1408,1147],[1407,1148],[3116,1149],[3101,1150],[3117,1151],[3118,1152],[3096,1153],[3119,1154],[1406,1155],[3084,1156],[3120,1157],[3085,1158],[3121,1159],[3092,1160],[3083,1161],[3109,1162],[3104,1163],[3091,1164],[3106,1165],[3098,1166],[3107,1167],[3099,1168],[3108,1169],[3097,1170],[3086,1171],[3122,1172],[3087,1173],[3123,1174],[3088,1175],[3110,1176],[3111,1177],[3103,1178],[3135,1179],[3089,1180],[3105,1181],[1422,1182],[1423,1183],[1421,1184],[1424,1185],[1425,1185],[1427,1186],[1426,1187],[1428,1188],[1114,1189],[1429,1190],[1431,1191],[1430,1192],[1455,1193],[1457,1194],[1456,1195],[1459,1196],[1458,1190],[1461,1197],[1460,1190],[1463,1198],[1462,1190],[1466,1199],[1465,1200],[1467,1201],[1107,267],[3158,1202],[1454,1203],[1468,1204],[1469,976],[1470,1205],[1471,1206],[1473,1207],[1472,1208],[1474,1206],[1475,1207],[1476,1209],[1478,1210],[1477,1211],[1480,1212],[1479,1213],[1481,1214],[1129,1211],[1483,1215],[1482,1216],[1484,1211],[1220,1189],[1486,1217],[1485,1211],[1488,1218],[1489,1219],[1487,1220],[1490,1221],[1492,1222],[1491,1221],[1239,1189],[1493,1223],[1494,1190],[1495,1211],[1496,1189],[1498,1224],[1497,1211],[1500,1225],[1499,1226],[1502,1227],[1501,1228],[1503,1228],[1504,1229],[1506,1230],[1505,1231],[1507,1230],[1509,1232],[1508,1189],[1510,1233],[1189,1211],[1512,1234],[1511,1235],[1513,1236],[1218,1211],[1516,1237],[1515,1238],[1518,1239],[1517,1238],[1520,1240],[1519,1241],[1521,1242],[1514,1243],[1523,1244],[1522,1238],[1525,1245],[1524,1189],[1527,1246],[1526,1211],[1222,1247],[1529,1248],[1528,1211],[1530,1190],[1532,1249],[1534,1250],[1533,1195],[1536,1251],[1535,1252],[1538,1253],[1537,1223],[1540,1254],[1539,1211],[1542,1255],[1541,1223],[1543,1256],[1545,1257],[1544,1258],[1547,1259],[1546,1260],[1548,1261],[1217,1262],[1549,1263],[1108,1189],[1552,1264],[1551,1265],[1553,1266],[1550,1189],[1555,1267],[1554,1189],[1412,1268],[1413,1269],[1109,1270],[1414,1271],[1231,1272],[1232,1272],[1415,1273],[1229,1272],[1416,1274],[1233,1272],[1417,1272],[1418,1275],[1219,1276],[1223,1277],[1557,1278],[1556,1189],[1559,1279],[1558,1211],[1561,1280],[1560,1243],[3157,1281],[1420,1282],[2815,1283],[2813,1284],[2811,1285],[1244,1286],[1243,1287],[3172,1288],[3192,1289],[3197,1290],[3240,1291],[3241,1292],[3218,1293],[1563,1294],[1562,1295],[1566,1296],[1565,1297],[3203,1298],[1568,1299],[1569,1300],[1567,1301],[3242,1302],[3215,1303],[3206,1304],[1571,1305],[1570,267],[3219,1306],[3238,1307],[3258,1308],[3220,1309],[3259,1310],[3208,1311],[3260,1312],[3229,1313],[3261,1314],[3207,1315],[3262,1316],[3223,1317],[3263,1318],[3264,1319],[3222,1320],[3265,1321],[3224,1322],[3266,1323],[3232,1324],[3267,1325],[3209,1326],[3268,1327],[3237,1328],[1573,1329],[1572,1330],[3257,1331],[1574,1332],[3245,1333],[3243,1334],[3214,1335],[3244,1336],[3228,1337],[3246,1338],[3211,1339],[3247,1340],[3221,1341],[3248,1342],[3193,1343],[3194,1344],[3250,1345],[3196,1346],[3249,1347],[3195,1348],[1576,1349],[1575,1350],[3251,1351],[3201,1352],[3198,1353],[3213,1354],[3252,1355],[3212,1356],[3253,1357],[3204,1358],[3210,1359],[1654,1360],[3199,1361],[3205,1362],[3233,1363],[1656,1364],[1655,1365],[3254,1366],[3234,1367],[3255,1368],[3202,1369],[3200,1370],[3256,1371],[3231,1372],[3269,1373],[1564,1343],[3239,1374],[3277,1375],[3270,1376],[3278,1377],[3271,1378],[3279,1379],[3273,1380],[3272,1381],[3280,1382],[3274,1383],[3276,1384],[3275,1385],[3298,1386],[3368,1387],[3367,1388],[1666,1389],[1665,1390],[3375,1391],[3325,1392],[3376,1393],[3324,1394],[1670,1395],[1669,1396],[3379,1397],[3332,1398],[3331,1399],[3330,1400],[1672,1401],[1671,267],[3377,1402],[3363,1403],[3323,1404],[3378,1405],[3371,1406],[1663,1407],[1662,1408],[3374,1409],[3373,1410],[3380,1411],[3369,1412],[3381,1413],[3342,1414],[3326,1415],[3382,1416],[3333,1417],[3383,1418],[3362,1419],[3347,1420],[3366,1421],[3364,1422],[3358,1423],[3372,1424],[1664,1425],[1674,1426],[1673,267],[1216,976],[3388,1427],[3386,1428],[3387,1429],[3401,1430],[3399,1431],[3402,1432],[3398,1433],[3397,1434],[3393,1435],[3392,1436],[3400,1437],[2853,1438],[2852,1439],[3508,1440],[3530,1441],[3500,1442],[3531,1443],[3522,1444],[3532,1445],[3509,1446],[3533,1447],[3501,1448],[1676,1449],[3510,1450],[3502,1451],[3534,1452],[3503,1453],[3535,1454],[3517,1455],[3536,1456],[3521,1457],[3537,1458],[3511,1459],[3504,1460],[3538,1461],[3505,1462],[3539,1463],[3506,1464],[3540,1465],[3507,1466],[3541,1467],[3520,1468],[3515,1469],[3518,1451],[3514,1453],[3516,1470],[3519,1471],[1678,1472],[1677,267],[3542,1473],[3527,1474],[3543,1475],[3525,1476],[3544,1477],[3523,1478],[3545,1479],[3526,1480],[3547,1481],[3546,1482],[3548,1483],[3524,1484],[1681,1485],[1680,1486],[3404,1487],[1686,1488],[1685,1489],[1688,1490],[3424,1491],[3549,1492],[3492,1493],[3550,1494],[3493,1495],[3551,1496],[3494,1497],[3552,1498],[3495,1499],[1679,976],[3496,1497],[3497,1497],[3499,1499],[3529,1500],[3528,1501],[3573,1502],[3563,1503],[3574,1504],[3557,1505],[3575,1506],[3568,1507],[3571,1508],[3560,1509],[3559,1510],[1691,1511],[1690,1512],[3576,1513],[3566,1514],[3577,1515],[3558,1516],[3578,1517],[3561,1518],[3579,1519],[3569,1520],[3580,1521],[3555,1522],[3581,1523],[3556,1524],[3582,1525],[3565,1526],[3583,1527],[3564,1528],[3572,1529],[3554,1530],[3553,1531],[1693,1532],[1692,267],[3584,1533],[3567,1534],[3562,1137],[3570,1535],[3595,1536],[3590,1537],[3596,1538],[3589,1539],[3597,1540],[3588,1541],[3587,1542],[3600,1543],[3601,1544],[3585,1545],[3602,1546],[3603,1547],[3586,1548],[3604,1549],[1973,1550],[1694,953],[1975,1551],[1974,1552],[3598,1553],[3593,1554],[3599,1555],[3592,1556],[3591,1557],[3594,1558],[3633,1559],[3610,1560],[3634,1561],[3630,1562],[3629,1563],[3646,1564],[3619,1565],[3651,1566],[3624,1567],[3647,1568],[3620,1569],[3648,1570],[3623,1480],[3649,1571],[3621,1572],[1979,1573],[1980,1574],[3650,1575],[3618,1139],[3622,104],[3638,1576],[3616,1577],[3626,1578],[3628,1579],[3639,1580],[3613,1581],[3640,1582],[3608,1583],[3641,1584],[3612,1585],[3642,1586],[3617,1587],[3643,1588],[3625,1589],[3644,1590],[3614,1591],[1976,267],[1978,1592],[1977,1593],[3645,1594],[3627,1595],[3635,1596],[3609,1597],[3605,1598],[3632,1599],[3607,1600],[3606,1601],[3636,1602],[3611,1603],[3637,1604],[3615,1605],[3631,1606],[3653,1607],[3046,1608],[3652,1609],[3663,1610],[3664,1611],[3655,1612],[3661,1613],[3665,1614],[3654,1615],[1983,1616],[1982,1617],[3669,1618],[3670,1618],[3660,1619],[3666,1620],[3657,1621],[3656,1622],[3667,1623],[3658,1624],[3668,1625],[3659,1626],[1981,1038],[3662,1627],[3678,1628],[3671,1629],[3676,1630],[3674,1631],[3677,1632],[3673,1633],[3672,1634],[3675,1635],[3688,1636],[3682,1637],[3686,1638],[3683,1639],[3687,1640],[3679,1641],[3685,1642],[3681,1643],[3680,1644],[3684,1645],[3696,1646],[3703,1647],[3706,1648],[3705,1649],[3704,1650],[3709,1651],[3708,1652],[3707,1653],[3732,1654],[3716,1655],[3733,1656],[3717,1655],[3734,1657],[3718,1658],[3731,1659],[3719,1660],[3735,1661],[3723,1662],[1987,1663],[1989,1664],[1988,1665],[3736,1666],[3724,1667],[1992,1668],[1991,1669],[3722,1670],[3737,1671],[3721,1672],[1985,1673],[1984,267],[3720,267],[3729,1674],[3725,1675],[3730,1676],[3727,1677],[3738,1678],[3726,1679],[1993,1680],[1341,1681],[3728,1682],[3749,1683],[3740,1684],[3752,1685],[3742,1686],[1996,1687],[1995,1688],[1997,1689],[1994,953],[3747,1690],[3750,1691],[3739,1692],[3751,1693],[3746,1694],[3754,1695],[3755,1696],[3745,1697],[3753,1698],[3744,1699],[3743,1700],[3748,1701],[3769,1702],[3770,1703],[3765,1704],[3771,1705],[3763,1706],[3762,1707],[3779,1708],[3767,1709],[1210,1710],[3772,1711],[1209,1712],[1208,1713],[3773,1714],[3764,1715],[3774,1716],[3766,1717],[3780,1718],[3781,1719],[3761,1720],[3775,1721],[3776,1722],[3759,1723],[3777,1724],[3758,1725],[3757,1726],[3778,1727],[3760,1728],[3768,1729],[3785,1730],[3784,1731],[3783,1732],[3782,1733],[3793,1734],[3795,1735],[3799,1736],[3787,1737],[3786,1738],[3801,1739],[3791,1740],[3790,1741],[3803,1742],[3805,1743],[3804,1744],[3807,1745],[3806,1746],[2703,1747],[3809,1748],[3810,1749],[3808,1750],[3811,1751],[3812,1752],[3813,1753],[3814,1754],[3816,1755],[3815,1756],[3820,1757],[3819,1758],[3821,1759],[3822,1760],[3818,1761],[3823,1762],[3817,1763],[3824,1764],[3844,1765],[3711,1766],[2085,1137],[1104,1767],[3952,1768],[3329,1769],[3340,267],[3942,1770],[3341,1771],[3954,1772],[3334,1773],[1095,1774],[3955,1775],[3300,1776],[1667,267],[2041,1777],[2040,1778],[3943,1779],[3328,1780],[2044,1781],[2043,1782],[2046,1783],[2045,267],[2047,1784],[1136,1785],[2042,1786],[1130,267],[3956,1787],[3304,1788],[1121,1789],[1119,1790],[3944,1791],[1112,1792],[2048,1793],[1111,267],[1118,1794],[1120,1795],[2049,1796],[1093,1797],[2050,1798],[1134,1799],[3945,1800],[1132,1801],[1131,1802],[3957,1803],[3335,1804],[1122,1774],[1127,1805],[3327,1806],[3958,1807],[3337,1808],[2051,1809],[1116,267],[3946,1810],[1117,1811],[1135,1812],[3959,1813],[3336,1814],[1100,1815],[3960,1816],[3338,1817],[2086,1137],[1096,1815],[3947,1818],[1113,1819],[3961,1820],[3339,1821],[1123,1815],[3948,1822],[2087,1823],[3949,1824],[1128,1825],[3950,1826],[1124,1774],[2052,1827],[1133,1828],[2053,1829],[1125,1830],[1098,1831],[3951,1832],[1126,1833],[1097,1834],[1099,1023],[3845,1835],[3353,1836],[3962,1837],[1698,1838],[1318,1038],[3867,1839],[3281,1840],[3873,1841],[3282,1842],[3874,1843],[3284,1844],[3875,1845],[3286,1846],[3868,1847],[3283,1840],[3869,1848],[3297,1849],[3870,1850],[3287,1840],[3293,1851],[3871,1852],[3291,1853],[3872,1854],[3290,1855],[3162,1856],[3963,1857],[3161,1858],[3825,1859],[1230,1860],[3846,1861],[3741,1862],[1936,1155],[3788,1863],[2062,1864],[3964,1865],[2061,1866],[3965,1867],[3797,1868],[3966,1869],[3798,1870],[2060,1871],[3792,1872],[3967,1873],[3800,1874],[3968,1875],[3796,1876],[3969,1877],[3789,1878],[3794,1879],[2054,1880],[3802,1881],[2063,1882],[2055,1883],[3970,1884],[3295,1885],[3512,1886],[1675,267],[3971,1887],[3513,1888],[3972,1889],[1683,1890],[1684,1573],[2065,1891],[2064,1892],[3294,1893],[3292,1894],[640,1038],[3847,1895],[3712,1896],[3876,1897],[3168,1898],[3877,1899],[3878,1900],[3165,1901],[3879,1902],[3163,1466],[3164,1903],[3880,1904],[3167,1905],[2004,1906],[2003,267],[3881,1907],[3882,1908],[3166,1909],[1464,267],[1372,1910],[1699,1911],[2833,1912],[1700,1023],[1085,1913],[3973,1914],[2818,1915],[3974,1916],[2834,1917],[3995,1918],[3389,1919],[3996,1920],[3390,1921],[3997,1922],[3391,1923],[2066,1332],[3998,1924],[3288,1925],[3999,1926],[3289,1927],[3975,1928],[1701,1929],[3976,1930],[2819,1931],[3977,1932],[2718,1933],[3318,1934],[3978,1935],[3311,1936],[3979,1937],[1934,1938],[3980,1939],[1933,1940],[3981,1941],[1084,1942],[3983,1943],[3982,1860],[3984,1944],[1952,1945],[3985,1946],[3352,1947],[1935,1838],[3351,1948],[3986,1949],[1939,1950],[1953,1951],[3987,1952],[1940,1953],[3988,1954],[1950,1955],[2068,1956],[2067,1957],[3989,1958],[2835,1959],[3991,1960],[1350,1961],[3992,1962],[1951,1963],[3993,1964],[2826,1965],[3994,1966],[3308,1967],[3990,1968],[2827,1969],[2868,104],[3826,1970],[1959,1971],[3827,1972],[2715,1973],[3828,1974],[2720,1975],[3883,1976],[3175,1977],[3884,1978],[3174,1979],[3173,1980],[3885,1981],[3178,1982],[3886,1983],[3177,1984],[3176,1985],[3829,1986],[2922,1987],[2089,1988],[2090,1989],[2088,1990],[4000,1991],[2091,1992],[2092,1993],[638,1994],[3848,1995],[3159,1996],[3887,1997],[2012,1998],[3888,1999],[2008,2000],[3889,2001],[2009,2002],[3890,2003],[2010,2004],[2014,2005],[2007,2006],[3891,2007],[2013,2008],[2015,2009],[2011,2010],[4001,2011],[2843,2012],[2093,267],[3830,2013],[3312,2014],[3137,2015],[3141,2016],[3892,2017],[3138,104],[2016,267],[3139,2018],[2018,2019],[2017,1665],[3831,2020],[1368,1565],[3849,2021],[2292,267],[1998,2022],[1197,267],[4002,2023],[1960,2024],[1961,2025],[4005,2026],[1089,976],[2096,2027],[2095,2028],[1215,2029],[4003,2030],[2094,2031],[1046,2032],[2098,2033],[2097,2034],[4004,2035],[1962,2036],[2100,2037],[2099,2038],[2102,2039],[2101,104],[3850,2040],[3348,2041],[3851,2042],[1242,2043],[3832,2044],[2722,2045],[4006,2046],[3403,2047],[1687,267],[4007,2048],[1090,2049],[2104,2050],[2103,1343],[4008,2051],[3498,2052],[3852,2053],[2836,2054],[2105,2055],[1966,2056],[2106,2057],[4009,2058],[1963,2059],[4010,2060],[1967,2061],[4011,2062],[3230,2063],[1088,267],[1965,2064],[4012,2065],[3422,2066],[4013,2067],[1086,267],[2108,2068],[2107,1091],[4014,2069],[3343,2070],[4015,2071],[3346,2072],[4016,2073],[3345,2074],[3344,2075],[4017,2076],[3301,2077],[4018,2078],[3361,2079],[4019,2080],[3360,2081],[3359,2082],[4020,2083],[3322,2084],[2109,267],[3285,2085],[3365,2086],[3853,2087],[3307,2088],[3305,2089],[3893,2090],[2854,2091],[2020,2092],[2019,267],[4021,2093],[3299,1923],[4022,2094],[1354,2095],[4023,2096],[3038,2097],[3160,2098],[3854,2099],[2717,2100],[3895,2101],[2706,2102],[3896,2103],[2709,2104],[3897,2105],[2707,2106],[2021,2107],[1234,267],[2022,267],[3898,2108],[2710,2109],[3899,2110],[2716,2111],[3894,2112],[2712,2113],[3900,2114],[2714,2115],[1999,2116],[1214,2117],[3833,2118],[2721,2119],[1047,1038],[2840,2120],[3855,2121],[1958,2122],[4026,2123],[4027,2124],[1972,2125],[2110,2126],[1970,2127],[4024,2128],[4025,2129],[2841,2130],[2112,2131],[2111,267],[2113,2132],[1971,267],[2116,2133],[2115,2134],[4029,2135],[3395,2136],[2118,2137],[2117,2138],[4030,2139],[3394,2140],[2114,953],[4028,2141],[3396,2142],[2000,267],[2002,2143],[2001,2144],[3856,2145],[3354,2146],[3901,2147],[3356,2148],[3355,2149],[3902,2150],[3357,2151],[3857,2152],[3714,2153],[4031,2154],[2839,2155],[2121,2156],[2120,2157],[4032,2158],[2838,2159],[2837,2160],[4033,2161],[2844,2162],[1689,267],[3858,2163],[3370,2164],[3859,2165],[1352,2166],[3860,2167],[3296,2168],[2123,2169],[2122,1192],[2126,2170],[2125,2171],[2124,2172],[3861,2173],[3349,2174],[3862,2175],[3350,2176],[4039,2177],[2960,2178],[4034,2179],[1941,1139],[4035,2180],[1942,1139],[4036,2181],[1945,2182],[4037,2183],[1943,1023],[4038,2184],[1944,2185],[4042,2186],[3045,2187],[4040,2188],[3044,2189],[2128,2190],[2127,2191],[4041,2192],[3043,2193],[3042,2194],[3041,2195],[2129,267],[1531,267],[3834,2196],[2869,2197],[4043,2198],[3313,2199],[3863,2200],[3171,2201],[2023,267],[3903,2202],[2886,2203],[2884,1466],[3904,2204],[2885,2205],[2024,267],[3905,2206],[2887,2207],[3906,2208],[2889,2209],[3907,2210],[2888,1466],[3908,2211],[2870,2212],[3909,2213],[3226,2214],[3910,2215],[3225,2216],[2026,2217],[2025,1499],[3911,2218],[3227,2219],[2028,2220],[2027,2221],[3912,2222],[2890,2223],[2029,953],[2030,1155],[3918,2224],[2873,2225],[3919,2226],[2872,2227],[3920,2228],[2874,2229],[3921,2230],[3922,2231],[2875,2232],[3913,2233],[2876,1923],[3914,2234],[2877,2235],[3915,2236],[2880,2237],[3916,2238],[2878,1466],[3917,2239],[2879,2240],[2032,2241],[2031,2242],[3923,2243],[2881,2244],[3924,2245],[2882,2246],[3925,2247],[2883,2248],[3926,2249],[3170,2250],[3169,2251],[2033,267],[3927,2252],[1949,2253],[3928,2254],[1946,2255],[3929,2256],[3039,2257],[1947,2258],[3931,2259],[3040,2260],[3930,2261],[1948,2262],[3146,104],[4059,2263],[2821,2264],[4044,2265],[1956,2266],[4045,2267],[2816,2268],[4060,2269],[3713,1763],[4068,2270],[2277,2271],[4069,2272],[2278,2271],[4070,2273],[2279,2274],[4071,2275],[2276,2276],[2130,267],[4072,2277],[2280,2271],[2282,2278],[4073,2279],[2281,2271],[4046,2280],[1235,1921],[4047,2281],[1968,2282],[1176,2283],[4061,2284],[4062,2285],[1180,2286],[4063,2287],[1182,2288],[4064,2289],[1179,2290],[4065,2291],[1184,2292],[4066,2293],[1187,2294],[4067,2295],[1186,2296],[1185,2297],[1188,2298],[1175,2299],[2006,267],[4048,2300],[1195,2301],[2842,267],[4074,2302],[1954,1942],[3309,2303],[3303,2304],[4049,2305],[1200,2306],[4050,2307],[1201,2308],[4051,2309],[1091,1137],[1937,1139],[4052,2310],[2814,2311],[4053,2312],[2825,2313],[4054,2314],[1696,2313],[4055,2315],[2957,2316],[2871,2317],[2823,2318],[4056,2319],[1041,1137],[4057,2320],[1226,2321],[2822,2322],[4075,2323],[1190,2324],[1191,2325],[4076,2326],[1192,2327],[4077,2328],[1194,2329],[4078,2330],[1196,2331],[1204,2332],[4079,2333],[1198,2334],[4080,2335],[1199,1665],[4081,2336],[1202,2337],[4082,2338],[1203,2339],[4058,2340],[2705,2341],[1695,2342],[3932,2343],[1238,2344],[3836,2345],[1241,2346],[3835,2347],[2891,2348],[4083,2349],[3423,2350],[637,267],[4084,2351],[3691,2352],[3690,2353],[3689,2354],[2846,2355],[4085,2356],[4086,2356],[3314,2357],[3310,2358],[4087,2359],[1938,2360],[4092,2361],[3316,2362],[2284,2363],[2283,267],[4088,2364],[3317,2365],[4093,2366],[3315,267],[2286,2367],[2285,267],[4089,2368],[3321,2369],[4090,2370],[3319,2371],[2287,2372],[2119,267],[4091,2373],[3320,2374],[2288,1223],[3838,2375],[3695,2376],[2035,2377],[2034,2378],[3933,2379],[3694,2380],[3693,2381],[3837,2382],[3692,2383],[2290,2384],[2289,267],[4098,2385],[2847,2386],[4099,2387],[4100,2388],[2848,2389],[4094,2390],[2832,2391],[2291,267],[2294,2392],[2293,2393],[2845,2394],[4095,2395],[2820,2396],[4096,2397],[4097,2398],[2824,2399],[3934,2400],[2713,2401],[3839,2402],[3698,2403],[3935,2404],[3697,2405],[3936,2406],[3701,2407],[2036,1190],[3937,2408],[3700,2409],[3938,2410],[3699,2411],[3840,2412],[3702,2413],[4101,2414],[1347,2415],[1955,2416],[4102,2417],[1236,2418],[4103,2419],[1115,2420],[4104,2421],[2704,999],[2708,2422],[4105,2423],[1035,2424],[1092,1925],[4106,2425],[2275,2426],[1183,2427],[1101,2428],[1040,2429],[1110,2430],[1362,2431],[4107,2432],[1044,2433],[2817,2434],[1038,2435],[1036,1925],[1042,1925],[1237,2436],[1102,2437],[4108,2438],[1225,2439],[4109,2440],[1045,2441],[1043,2442],[1181,2430],[1177,2443],[1103,2444],[2702,2445],[1094,2446],[1178,1925],[1348,2447],[1037,1925],[4110,2448],[1048,2449],[4111,2450],[1361,2451],[3841,2452],[2892,2453],[3842,2454],[3864,2455],[3302,2456],[3940,2457],[3385,2458],[3939,2459],[3710,2460],[2037,2461],[1986,2462],[1332,267],[2039,2463],[2038,267],[3865,2464],[3715,2465],[3843,2466],[2810,2467],[1138,267],[4112,2468],[1969,2469],[3866,2470],[3756,2471],[4125,2472],[3181,2473],[4113,2474],[3182,2475],[4114,2476],[3180,2477],[3179,2478],[2301,267],[4115,2479],[2313,104],[2295,267],[4116,2480],[2312,2481],[2311,2482],[2299,2483],[4126,2484],[2298,104],[2309,2485],[2308,104],[4127,2486],[2310,2487],[4128,2488],[2307,104],[4121,2489],[4122,2489],[3191,2490],[4123,2491],[3183,2492],[2296,1408],[4129,2493],[2302,2494],[4130,2495],[2332,2496],[2300,267],[4131,2497],[2305,2498],[4132,2499],[2335,2500],[2342,2501],[4133,2502],[2336,2503],[4134,2504],[2319,2505],[4135,2506],[2340,2507],[4136,2508],[2341,2509],[4137,2510],[2337,2511],[2329,267],[2330,2512],[4138,2513],[2339,2514],[4139,2515],[2338,2516],[4140,2517],[1211,2518],[4141,2519],[2331,2520],[2304,2521],[4142,2522],[2334,2523],[4143,2524],[2333,2525],[4144,2526],[2316,267],[4145,2527],[2315,2528],[2306,2529],[2343,2530],[2320,267],[4124,2531],[3184,2532],[3185,2533],[4117,2534],[3186,2535],[4118,2536],[3190,2537],[3189,2538],[4119,2539],[3188,2540],[2323,2541],[2328,2542],[2324,2543],[2325,2544],[2326,2545],[4146,2546],[2327,2547],[2321,267],[2344,2548],[2322,2549],[4120,2550],[3187,267],[2297,2551],[2314,2552],[3306,267],[3384,2553],[2849,2554],[3941,2555],[2850,2556],[2700,2557],[2056,2558],[4147,2559],[2711,2560],[2701,2561],[1224,2562],[2350,2563],[2348,2563],[2347,2563],[2349,2564],[2346,2563],[2345,2563],[2351,976],[4151,2565],[2354,2566],[1360,104],[4148,2567],[3216,2568],[4149,2569],[1371,2570],[3217,2571],[4150,2572],[3235,2573],[3236,2574],[2352,104],[2353,2575],[2355,2576],[1351,2577],[2357,2578],[1137,2579],[2358,2580],[1034,2581],[2362,2582],[2361,2583],[2364,2584],[2363,267],[4152,2585],[2084,2586],[2365,2587],[2366,2587],[1340,2588],[2367,2589],[630,267],[2368,2590],[1212,267],[2369,2591],[1213,2592],[639,2],[2370,2593],[2359,2594],[1353,2595],[1205,267],[2360,2596],[631,2597],[614,267],[2371,2598],[2372,2599],[1227,2600],[2373,2601],[634,2602],[2374,2603],[1193,2604],[1453,267],[2375,2605],[1207,1287],[2376,267],[2378,2606],[2377,267],[2379,2607],[636,2608],[2639,2609],[2638,2610],[2641,2611],[2640,267],[2642,2612],[1240,267],[2643,2613],[1228,267],[2644,267],[2646,2614],[2645,267],[2647,2615],[633,2616],[2648,2617],[1964,267],[2649,2618],[1668,2619],[2650,267],[2651,2620],[1682,267],[2652,2621],[1221,976],[2655,2622],[2654,2623],[2658,2624],[2657,2625],[2659,2626],[2656,267],[2660,2627],[1105,267],[2661,2628],[1106,976],[2663,2629],[2662,267],[632,267],[2664,2630],[1419,1287],[2665,2631],[2005,2038],[2666,2632],[1083,267],[2667,2633],[1206,2634],[4153,2635],[2684,2636],[2686,2637],[2688,2638],[2690,2639],[2692,2640],[2694,2641],[2672,2642],[2673,2643],[2675,2644],[2677,2645],[2356,2646],[2695,2454],[3953,1330],[2678,2643],[2682,2647],[2863,2648],[4154,2649],[613,2650]],"semanticDiagnosticsPerFile":[[1344,[{"start":2507,"length":35,"messageText":"Type instantiation is excessively deep and possibly infinite.","category":1,"code":2589},{"start":2507,"length":38,"messageText":"Type instantiation is excessively deep and possibly infinite.","category":1,"code":2589}]],[1486,[{"start":5247,"length":6,"code":2322,"category":1,"messageText":"Type 'string' is not assignable to type 'undefined'."}]],[1489,[{"start":1996,"length":15,"code":2322,"category":1,"messageText":"Type 'number' is not assignable to type 'ModelBudgetConfig'."},{"start":3425,"length":10,"code":2322,"category":1,"messageText":"Type 'number' is not assignable to type 'ModelBudgetConfig'."}]],[1540,[{"start":643,"length":6,"code":2739,"category":1,"messageText":"Type '{ google_client_id: string; google_client_secret: string; microsoft_client_id: string; microsoft_client_secret: string; microsoft_tenant: string; generic_client_id: string; generic_client_secret: string; ... 7 more ...; team_mappings: { ...; }; }' is missing the following properties from type 'SSOSettingsValues': saml_idp_metadata_url, saml_idp_metadata_xml, saml_sp_entity_id, saml_allow_unsolicited, generic_scope","relatedInformation":[{"file":"./src/app/(dashboard)/hooks/sso/usessosettings.ts","start":1521,"length":6,"messageText":"The expected type comes from property 'values' which is declared here on type 'SSOSettingsResponse'","category":3,"code":6500}],"canonicalHead":{"code":2322,"messageText":"Type '{ google_client_id: string; google_client_secret: string; microsoft_client_id: string; microsoft_client_secret: string; microsoft_tenant: string; generic_client_id: string; generic_client_secret: string; ... 7 more ...; team_mappings: { ...; }; }' is not assignable to type 'SSOSettingsValues'."}},{"start":7416,"length":6,"code":2739,"category":1,"messageText":"Type '{ google_client_id: null; google_client_secret: null; microsoft_client_id: null; microsoft_client_secret: null; microsoft_tenant: null; generic_client_id: null; generic_client_secret: null; ... 7 more ...; team_mappings: { ...; }; }' is missing the following properties from type 'SSOSettingsValues': saml_idp_metadata_url, saml_idp_metadata_xml, saml_sp_entity_id, saml_allow_unsolicited, generic_scope","relatedInformation":[{"file":"./src/app/(dashboard)/hooks/sso/usessosettings.ts","start":1521,"length":6,"messageText":"The expected type comes from property 'values' which is declared here on type 'SSOSettingsResponse'","category":3,"code":6500}],"canonicalHead":{"code":2322,"messageText":"Type '{ google_client_id: null; google_client_secret: null; microsoft_client_id: null; microsoft_client_secret: null; microsoft_tenant: null; generic_client_id: null; generic_client_secret: null; ... 7 more ...; team_mappings: { ...; }; }' is not assignable to type 'SSOSettingsValues'."}}]],[1561,[{"start":1402,"length":5,"code":2322,"category":1,"messageText":{"messageText":"Type '{ user_id: string; user_email: string; user_alias: null; user_role: string; spend: number; max_budget: null; key_count: number; created_at: string; updated_at: string; sso_user_id: null; budget_duration: null; }[]' is not assignable to type 'UserInfo[]'.","category":1,"code":2322,"next":[{"messageText":"Property 'models' is missing in type '{ user_id: string; user_email: string; user_alias: null; user_role: string; spend: number; max_budget: null; key_count: number; created_at: string; updated_at: string; sso_user_id: null; budget_duration: null; }' but required in type 'UserInfo'.","category":1,"code":2741,"canonicalHead":{"code":2322,"messageText":"Type '{ user_id: string; user_email: string; user_alias: null; user_role: string; spend: number; max_budget: null; key_count: number; created_at: string; updated_at: string; sso_user_id: null; budget_duration: null; }' is not assignable to type 'UserInfo'."}}]},"relatedInformation":[{"file":"./src/components/networking.tsx","start":32568,"length":6,"messageText":"'models' is declared here.","category":3,"code":2728},{"file":"./src/components/networking.tsx","start":32875,"length":5,"messageText":"The expected type comes from property 'users' which is declared here on type 'UserListResponse'","category":3,"code":6500}]}]],[1978,[{"start":328,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":784,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":1182,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":1684,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":2044,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":2529,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":3149,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: { temperature: number; max_tokens: number; top_p: number; }; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: { temperature: number; max_tokens: number; top_p: number; }; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":3683,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":4135,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":4538,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: { name: string; description: string; json: string; }[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: { name: string; description: string; json: string; }[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":5174,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}}]],[1993,[{"start":4983,"length":43,"code":2352,"category":1,"messageText":{"messageText":"Conversion of type 'SpendMetrics' to type 'Record' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.","category":1,"code":2352,"next":[{"messageText":"Index signature for type 'string' is missing in type 'SpendMetrics'.","category":1,"code":2329}]}}]],[2037,[{"start":56,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":95,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":131,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":251,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":316,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":431,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":505,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2041,[{"start":1826,"length":24,"code":2739,"category":1,"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic_v2\"; }' is missing the following properties from type 'ComplexityRouterConfigPayload': classification_mode, session_affinity, deployment_affinity, modality_routing, modality_pin_override","relatedInformation":[{"file":"./src/lib/autorouter_presets.ts","start":1068,"length":24,"messageText":"The expected type comes from property 'complexity_router_config' which is declared here on type 'AutoRouterPreset'","category":3,"code":6500}],"canonicalHead":{"code":2322,"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic_v2\"; }' is not assignable to type 'ComplexityRouterConfigPayload'."}}]],[2042,[{"start":170,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":225,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":293,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":389,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":607,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":753,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":973,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1119,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1359,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1507,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1738,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1890,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1941,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2009,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2101,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2336,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2434,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2669,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2771,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2964,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3060,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3613,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3677,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3756,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4194,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4290,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4528,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4598,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4669,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4945,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2044,[{"start":497,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":553,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":681,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":729,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":788,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":835,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":886,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":935,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1009,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1135,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1374,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1431,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1486,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2046,[{"start":425,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":474,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":690,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":955,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1264,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1463,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1694,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1795,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1851,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2039,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2311,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2503,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2770,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2871,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3139,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3482,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3755,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4094,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4354,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4619,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4691,"length":12,"messageText":"Parameter 'defaultModel' implicitly has an 'any' type.","category":1,"code":7006},{"start":4905,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5006,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5320,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5590,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5957,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6026,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2047,[{"start":613,"length":10,"code":2739,"category":1,"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: string[]; COMPLEX: string[]; REASONING: string[]; }; tierLabels: undefined; classifierType: \"heuristic\"; classifierLlmConfig: undefined; classifierContextWindowSize: undefined; ... 16 more ...; returnRawModelName: false; }' is missing the following properties from type 'BuildComplexityRouterConfigParams': defaultModel, planModeMinTier, classificationExamples, heuristicFirstMaxTier, classificationMode","canonicalHead":{"code":2322,"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: string[]; COMPLEX: string[]; REASONING: string[]; }; tierLabels: undefined; classifierType: \"heuristic\"; classifierLlmConfig: undefined; classifierContextWindowSize: undefined; ... 16 more ...; returnRawModelName: false; }' is not assignable to type 'BuildComplexityRouterConfigParams'."}},{"start":1362,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1412,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1879,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1922,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2185,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2250,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2318,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2428,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2537,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2701,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2765,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2989,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3082,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3273,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3330,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3576,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3669,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3929,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3977,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4076,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4478,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4554,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4850,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4913,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5321,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5378,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5436,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5503,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5564,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6011,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6070,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6138,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6549,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6616,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6689,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7035,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7102,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7175,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7566,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7630,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8085,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8342,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8405,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8472,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8892,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8949,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9023,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9075,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9521,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9583,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9635,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9692,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9797,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9859,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9919,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10639,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10839,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11199,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11244,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11297,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11355,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11414,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11568,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11631,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11752,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11863,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11945,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12063,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12189,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12309,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12396,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12536,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12737,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12790,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":13001,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13055,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":13210,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13268,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":13614,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13654,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13728,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13781,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13836,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14217,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14257,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14319,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14384,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14427,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14491,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14575,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14648,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14816,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14904,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15092,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15228,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15401,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15468,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15596,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15718,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15801,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15951,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16016,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16186,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16274,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16445,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16528,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16686,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16733,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16798,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17065,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":17158,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17408,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":17482,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17667,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":17856,"length":6,"messageText":"Parameter '_label' implicitly has an 'any' type.","category":1,"code":7006},{"start":17864,"length":8,"messageText":"Parameter 'keywords' implicitly has an 'any' type.","category":1,"code":7006},{"start":17883,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18136,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":18211,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18225,"length":24,"messageText":"Expected 2 arguments, but got 1.","category":1,"code":2554,"relatedInformation":[{"file":"./src/components/add_model/build_complexity_router_config.ts","start":11634,"length":24,"messageText":"An argument for 'rows' was not provided.","category":3,"code":6210}]},{"start":18570,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":18659,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18814,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":19058,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":19240,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19319,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":19545,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19625,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":19884,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19968,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":20094,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20180,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":20415,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":20824,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20922,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21005,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21335,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21416,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21488,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21639,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21796,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21882,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21981,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22222,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22299,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22448,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22534,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22777,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22934,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23301,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23392,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23787,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23873,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23986,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":24430,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24528,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24650,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":24747,"length":5,"messageText":"Parameter 'extra' implicitly has an 'any' type.","category":1,"code":7006},{"start":24874,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24973,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25007,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25086,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25172,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25468,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25521,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25753,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25833,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25931,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26139,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26194,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26235,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26281,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26340,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26492,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26552,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26641,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26735,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26834,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26928,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27007,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27098,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27170,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27262,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27353,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27425,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27465,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27536,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27599,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27641,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27765,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27935,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28014,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28064,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28136,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28206,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28262,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28327,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28808,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28951,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29009,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29074,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29111,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29200,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29303,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29411,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29516,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29625,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29790,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29960,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30120,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30182,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30258,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30431,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30476,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30668,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30734,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30872,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30932,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31123,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31191,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31331,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31381,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31492,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31548,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31658,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31759,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31882,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31950,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32032,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32131,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32382,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32423,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32584,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32630,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32716,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32803,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32879,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32969,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":33065,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33218,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":33548,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33728,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":33781,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33934,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34151,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34340,"length":25,"messageText":"Parameter 'supportedReasoningEfforts' implicitly has an 'any' type.","category":1,"code":7006},{"start":34367,"length":13,"messageText":"Parameter 'expectedError' implicitly has an 'any' type.","category":1,"code":7006},{"start":34577,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34626,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34663,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34768,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34830,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34940,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":35018,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":35196,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":35301,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":35481,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":35586,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":35733,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36126,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36245,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36305,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36370,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36552,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36646,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36705,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36768,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36835,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":37127,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37201,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":37487,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":37595,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37684,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37807,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":37972,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38023,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38117,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38184,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38582,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38654,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38696,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38830,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38893,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38999,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39151,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39253,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":39983,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40130,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40227,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40418,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40473,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40585,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40670,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40767,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40847,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40943,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41167,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41272,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41377,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41649,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41726,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41871,"length":14,"messageText":"Parameter 'classifierType' implicitly has an 'any' type.","category":1,"code":7006},{"start":42061,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":42137,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":42604,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":42714,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":43028,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":43104,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":43470,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":43618,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":43834,"length":5,"messageText":"Parameter '_name' implicitly has an 'any' type.","category":1,"code":7006},{"start":43841,"length":3,"messageText":"Parameter 'key' implicitly has an 'any' type.","category":1,"code":7006},{"start":44691,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":44810,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":44866,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":45088,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45156,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45210,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":45411,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45471,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45571,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":45666,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45765,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":45860,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45955,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":46061,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":46162,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":46369,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":46532,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":46722,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":46791,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":46834,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":46950,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47035,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":47721,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47957,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":48073,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":48393,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":48480,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":48847,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":48902,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":48977,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":49044,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":49260,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49327,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49393,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49474,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":49787,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49843,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49895,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49962,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":50166,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50222,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50288,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50371,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":50409,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":50526,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50631,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50749,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50873,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":50943,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51142,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":51246,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51303,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51380,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":51566,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":51780,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51854,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":52297,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":52371,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":52620,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2048,[{"start":196,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":238,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":501,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":595,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":679,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":786,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":890,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":976,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1134,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1208,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1349,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1425,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1463,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1587,"length":12,"messageText":"Parameter 'systemPrompt' implicitly has an 'any' type.","category":1,"code":7006},{"start":1601,"length":8,"messageText":"Parameter 'expected' implicitly has an 'any' type.","category":1,"code":7006},{"start":1620,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1707,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1746,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1823,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1920,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2040,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2116,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2089,[{"start":11595,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ session_affinity_ttl_seconds: number; tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: string; keyword_tier_rules: { keywords: string[]; tier: string; }[]; ... 4 more ...; some_future_backend_key: { ...; }; }' is not assignable to parameter of type 'StoredComplexityRouterConfig'.","category":1,"code":2345,"next":[{"messageText":"Types of property 'classifier_type' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type 'string' is not assignable to type 'ClassifierType | undefined'.","category":1,"code":2322}]}]}},{"start":13641,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ modality_routing: boolean; modality_pin_override: boolean; tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: string; keyword_tier_rules: { keywords: string[]; tier: string; }[]; ... 4 more ...; some_future_backend_key: { ...; }; }' is not assignable to parameter of type 'StoredComplexityRouterConfig'.","category":1,"code":2345,"next":[{"messageText":"Types of property 'classifier_type' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type 'string' is not assignable to type 'ClassifierType | undefined'.","category":1,"code":2322}]}]}},{"start":14094,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ classification_mode: string; tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: string; keyword_tier_rules: { keywords: string[]; tier: string; }[]; escalation_keywords: string[]; semantic_keyword_matching: boolean; embedding_model: string; match_threshold: number...' is not assignable to parameter of type 'StoredComplexityRouterConfig'.","category":1,"code":2345,"next":[{"messageText":"Types of property 'classifier_type' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type 'string' is not assignable to type 'ClassifierType | undefined'.","category":1,"code":2322}]}]}},{"start":14515,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ classification_mode: string; tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: string; keyword_tier_rules: { keywords: string[]; tier: string; }[]; escalation_keywords: string[]; semantic_keyword_matching: boolean; embedding_model: string; match_threshold: number...' is not assignable to parameter of type 'StoredComplexityRouterConfig'.","category":1,"code":2345,"next":[{"messageText":"Types of property 'classifier_type' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type 'string' is not assignable to type 'ClassifierType | undefined'.","category":1,"code":2322}]}]}},{"start":31386,"length":12,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { CASUAL: string[]; AUDIT: string[]; }; tier_definitions: { name: string; description: string; }[]; fallback_tier: string; classifier_type: string; classifier_llm_config: { model: string; timeout_ms: number; reasoning_effort: string; }; }' is not assignable to parameter of type 'StoredComplexityRouterConfig'.","category":1,"code":2345,"next":[{"messageText":"Types of property 'tiers' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type '{ CASUAL: string[]; AUDIT: string[]; }' has no properties in common with type 'Partial>'.","category":1,"code":2559}]}]}},{"start":31954,"length":12,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { CASUAL: string[]; AUDIT: string[]; }; tier_definitions: { name: string; description: string; }[]; fallback_tier: string; classifier_type: string; classifier_llm_config: { model: string; timeout_ms: number; reasoning_effort: string; }; }' is not assignable to parameter of type 'StoredComplexityRouterConfig'.","category":1,"code":2345,"next":[{"messageText":"Types of property 'tiers' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type '{ CASUAL: string[]; AUDIT: string[]; }' has no properties in common with type 'Partial>'.","category":1,"code":2559}]}]}},{"start":32495,"length":12,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { CASUAL: string[]; AUDIT: string[]; }; tier_definitions: { name: string; description: string; }[]; fallback_tier: string; classifier_type: string; classifier_llm_config: { model: string; timeout_ms: number; reasoning_effort: string; }; }' is not assignable to parameter of type 'StoredComplexityRouterConfig'.","category":1,"code":2345,"next":[{"messageText":"Types of property 'tiers' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type '{ CASUAL: string[]; AUDIT: string[]; }' has no properties in common with type 'Partial>'.","category":1,"code":2559}]}]}},{"start":32661,"length":21,"code":2339,"category":1,"messageText":"Property 'classification_prompt' does not exist on type '{ tiers: { CASUAL: string[]; AUDIT: string[]; }; tier_definitions: { name: string; description: string; }[]; fallback_tier: string; classifier_type: string; classifier_llm_config: { model: string; timeout_ms: number; reasoning_effort: string; }; }'."},{"start":32748,"length":23,"code":2339,"category":1,"messageText":"Property 'classification_examples' does not exist on type '{ tiers: { CASUAL: string[]; AUDIT: string[]; }; tier_definitions: { name: string; description: string; }[]; fallback_tier: string; classifier_type: string; classifier_llm_config: { model: string; timeout_ms: number; reasoning_effort: string; }; }'."},{"start":32832,"length":21,"code":2339,"category":1,"messageText":"Property 'classification_prompt' does not exist on type '{ tiers: { CASUAL: string[]; AUDIT: string[]; }; tier_definitions: { name: string; description: string; }[]; fallback_tier: string; classifier_type: string; classifier_llm_config: { model: string; timeout_ms: number; reasoning_effort: string; }; }'."},{"start":32916,"length":23,"code":2339,"category":1,"messageText":"Property 'classification_examples' does not exist on type '{ tiers: { CASUAL: string[]; AUDIT: string[]; }; tier_definitions: { name: string; description: string; }[]; fallback_tier: string; classifier_type: string; classifier_llm_config: { model: string; timeout_ms: number; reasoning_effort: string; }; }'."}]],[2090,[{"start":2335,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2392,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2586,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2656,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2844,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2916,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3012,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3051,"length":15,"messageText":"Spread types may only be created from object types.","category":1,"code":2698},{"start":3147,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3184,"length":12,"code":2559,"category":1,"messageText":"Type 'string' has no properties in common with type 'StoredComplexityRouterConfig'."},{"start":3244,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3460,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3570,"length":15,"messageText":"Spread types may only be created from object types.","category":1,"code":2698},{"start":3683,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3739,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3840,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3886,"length":15,"messageText":"Spread types may only be created from object types.","category":1,"code":2698},{"start":3985,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4022,"length":12,"code":2559,"category":1,"messageText":"Type 'string' has no properties in common with type 'StoredComplexityRouterConfig'."},{"start":4087,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4313,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4428,"length":15,"messageText":"Spread types may only be created from object types.","category":1,"code":2698},{"start":4551,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4612,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4830,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4895,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5080,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5160,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5336,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5410,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5724,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2094,[{"start":1600,"length":17,"code":2322,"category":1,"messageText":{"messageText":"Type '{ budget_limit: number; time_period: string; } | { max_budget: number; budget_duration: string; }' is not assignable to type 'ModelBudgetConfig'.","category":1,"code":2322,"next":[{"messageText":"Type '{ max_budget: number; budget_duration: string; }' is missing the following properties from type 'ModelBudgetConfig': budget_limit, time_period","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ max_budget: number; budget_duration: string; }' is not assignable to type 'ModelBudgetConfig'."}}]}},{"start":2144,"length":12,"code":2322,"category":1,"messageText":{"messageText":"Type 'string | number' is not assignable to type 'number'.","category":1,"code":2322,"next":[{"messageText":"Type 'string' is not assignable to type 'number'.","category":1,"code":2322}]},"relatedInformation":[{"file":"./src/components/key_team_helpers/modelmaxbudgeteditor.tsx","start":506,"length":12,"messageText":"The expected type comes from property 'budget_limit' which is declared here on type 'ModelBudgetConfig'","category":3,"code":6500}]},{"start":2388,"length":12,"code":2322,"category":1,"messageText":"Type 'string' is not assignable to type 'number'.","relatedInformation":[{"file":"./src/components/key_team_helpers/modelmaxbudgeteditor.tsx","start":506,"length":12,"messageText":"The expected type comes from property 'budget_limit' which is declared here on type 'ModelBudgetConfig'","category":3,"code":6500}]},{"start":3742,"length":8,"code":2739,"category":1,"messageText":"Type '{ max_budget: number; budget_duration: string; tpm_limit: number; }' is missing the following properties from type 'ModelBudgetConfig': budget_limit, time_period","canonicalHead":{"code":2322,"messageText":"Type '{ max_budget: number; budget_duration: string; tpm_limit: number; }' is not assignable to type 'ModelBudgetConfig'."}}]],[2357,[{"start":16399,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":18287,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":18967,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":20970,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":26215,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":32436,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; match_threshold: number; escalation_keywords: never[]; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; match_threshold: number; escalation_keywords: never[]; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":32734,"length":356,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: false; deployment_affinity: true; enable_context_window_escalation: false; context_window_escalation_buffer: number; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: false; deployment_affinity: true; enable_context_window_escalation: false; context_window_escalation_buffer: number; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":33824,"length":45,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ classification_mode: \"user_turn\"; tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ classification_mode: \"user_turn\"; tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":33999,"length":4,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":34250,"length":265,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: false; deployment_affinity: true; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: false; deployment_affinity: true; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":35426,"length":64,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tier_labels: { SIMPLE: string; REASONING: string; }; tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tier_labels: { SIMPLE: string; REASONING: string; }; tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":35675,"length":4,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":36212,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":36912,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: string[]; }; tier_model_configs: { REASONING: { model_name: string; litellm_params: { reasoning_effort: string; }; }[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boo...' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: string[]; }; tier_model_configs: { REASONING: { model_name: string; litellm_params: { reasoning_effort: string; }; }[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boo...' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":37948,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: never[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: string[]; }; tier_model_configs: { REASONING: { model_name: string; litellm_params: { reasoning_effort: string; }; }[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: bool...' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: never[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: string[]; }; tier_model_configs: { REASONING: { model_name: string; litellm_params: { reasoning_effort: string; }; }[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: bool...' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":39150,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: never[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: string[]; }; tier_model_configs: { REASONING: ({ model_name: string; litellm_params: { reasoning_effort: string; temperature: number; }; } | { ...; })[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: ...' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: never[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: string[]; }; tier_model_configs: { REASONING: ({ model_name: string; litellm_params: { reasoning_effort: string; temperature: number; }; } | { ...; })[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: ...' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":39954,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}}]],[2366,[{"start":3271,"length":4,"code":2322,"category":1,"messageText":{"messageText":"Type '{ key_alias: string; }' is not assignable to type '{ access_group_ids?: string[] | null | undefined; agent_id?: string | null | undefined; aliases: ({ [x: string]: unknown; } & {}) | null; allowed_cache_controls: unknown[] | null; allowed_passthrough_routes?: unknown[] | ... 1 more ... | undefined; ... 46 more ...; user_id?: string | ... 1 more ... | undefined; } & {}'.","category":1,"code":2322,"next":[{"messageText":"Type '{ key_alias: string; }' is missing the following properties from type '{ access_group_ids?: string[] | null | undefined; agent_id?: string | null | undefined; aliases: ({ [x: string]: unknown; } & {}) | null; allowed_cache_controls: unknown[] | null; allowed_passthrough_routes?: unknown[] | ... 1 more ... | undefined; ... 46 more ...; user_id?: string | ... 1 more ... | undefined; }': aliases, allowed_cache_controls, allowed_routes, auto_rotate, and 7 more.","category":1,"code":2740,"canonicalHead":{"code":2322,"messageText":"Type '{ key_alias: string; }' is not assignable to type '{ access_group_ids?: string[] | null | undefined; agent_id?: string | null | undefined; aliases: ({ [x: string]: unknown; } & {}) | null; allowed_cache_controls: unknown[] | null; allowed_passthrough_routes?: unknown[] | ... 1 more ... | undefined; ... 46 more ...; user_id?: string | ... 1 more ... | undefined; }'."}}]},"relatedInformation":[{"file":"./node_modules/openapi-fetch/dist/index.d.mts","start":3474,"length":4,"messageText":"The expected type comes from property 'body' which is declared here on type '{ params?: { query?: undefined; header?: { \"litellm-changed-by\"?: string | null | undefined; } | undefined; path?: undefined; cookie?: undefined; } | undefined; } & { body: { access_group_ids?: string[] | ... 1 more ... | undefined; ... 50 more ...; user_id?: string | ... 1 more ... | undefined; } & {}; } & { ...; }...'","category":3,"code":6500}]},{"start":3928,"length":4,"code":2322,"category":1,"messageText":{"messageText":"Type '{ key_alias: string; }' is not assignable to type '{ access_group_ids?: string[] | null | undefined; agent_id?: string | null | undefined; aliases: ({ [x: string]: unknown; } & {}) | null; allowed_cache_controls: unknown[] | null; allowed_passthrough_routes?: unknown[] | ... 1 more ... | undefined; ... 46 more ...; user_id?: string | ... 1 more ... | undefined; } & {}'.","category":1,"code":2322,"next":[{"messageText":"Type '{ key_alias: string; }' is missing the following properties from type '{ access_group_ids?: string[] | null | undefined; agent_id?: string | null | undefined; aliases: ({ [x: string]: unknown; } & {}) | null; allowed_cache_controls: unknown[] | null; allowed_passthrough_routes?: unknown[] | ... 1 more ... | undefined; ... 46 more ...; user_id?: string | ... 1 more ... | undefined; }': aliases, allowed_cache_controls, allowed_routes, auto_rotate, and 7 more.","category":1,"code":2740,"canonicalHead":{"code":2322,"messageText":"Type '{ key_alias: string; }' is not assignable to type '{ access_group_ids?: string[] | null | undefined; agent_id?: string | null | undefined; aliases: ({ [x: string]: unknown; } & {}) | null; allowed_cache_controls: unknown[] | null; allowed_passthrough_routes?: unknown[] | ... 1 more ... | undefined; ... 46 more ...; user_id?: string | ... 1 more ... | undefined; }'."}}]},"relatedInformation":[{"file":"./node_modules/openapi-fetch/dist/index.d.mts","start":3474,"length":4,"messageText":"The expected type comes from property 'body' which is declared here on type '{ params?: { query?: undefined; header?: { \"litellm-changed-by\"?: string | null | undefined; } | undefined; path?: undefined; cookie?: undefined; } | undefined; } & { body: { access_group_ids?: string[] | ... 1 more ... | undefined; ... 50 more ...; user_id?: string | ... 1 more ... | undefined; } & {}; } & { ...; }...'","category":3,"code":6500}]}]],[2367,[{"start":1322,"length":3,"messageText":"Tuple type '[]' of length '0' has no element at index '0'.","category":1,"code":2493},{"start":1327,"length":4,"messageText":"Tuple type '[]' of length '0' has no element at index '1'.","category":1,"code":2493},{"start":1491,"length":4,"messageText":"'init' is possibly 'undefined'.","category":1,"code":18048},{"start":1616,"length":4,"messageText":"'init' is possibly 'undefined'.","category":1,"code":18048},{"start":1945,"length":26,"messageText":"Object is possibly 'undefined'.","category":1,"code":2532},{"start":1969,"length":1,"messageText":"Tuple type '[]' of length '0' has no element at index '1'.","category":1,"code":2493},{"start":2301,"length":4,"messageText":"Tuple type '[]' of length '0' has no element at index '1'.","category":1,"code":2493},{"start":2345,"length":4,"messageText":"'init' is possibly 'undefined'.","category":1,"code":18048},{"start":2383,"length":4,"messageText":"'init' is possibly 'undefined'.","category":1,"code":18048},{"start":4907,"length":4,"messageText":"Tuple type '[]' of length '0' has no element at index '1'.","category":1,"code":2493},{"start":4951,"length":4,"messageText":"'init' is possibly 'undefined'.","category":1,"code":18048}]],[2660,[{"start":272,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":354,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":907,"length":9,"messageText":"Cannot find name 'afterEach'.","category":1,"code":2304},{"start":1076,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1114,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1199,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1276,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1338,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1481,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1560,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1665,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1712,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1757,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1836,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1918,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1976,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2023,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2370,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2447,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2755,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2802,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2838,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2914,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2969,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3051,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3148,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3523,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3642,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3690,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4031,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4159,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4484,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4533,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4878,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4940,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4977,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5400,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5476,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6141,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6218,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6485,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6532,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6573,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6639,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6703,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6766,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6823,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6888,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7012,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7166,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7255,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7313,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7379,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7452,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7497,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7552,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7599,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7663,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7736,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7807,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7880,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7925,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8020,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8403,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8481,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8933,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9013,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9490,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9631,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9757,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9835,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9876,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10661,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10731,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10785,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11070,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11113,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11970,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12047,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12318,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2661,[{"start":3595,"length":405,"code":2741,"category":1,"messageText":"Property 'spend' is missing in type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' but required in type 'Team'.","relatedInformation":[{"file":"./src/components/key_team_helpers/key_list.tsx","start":785,"length":5,"messageText":"'spend' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' is not assignable to type 'Team'."}},{"start":4010,"length":406,"code":2741,"category":1,"messageText":"Property 'spend' is missing in type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' but required in type 'Team'.","relatedInformation":[{"file":"./src/components/key_team_helpers/key_list.tsx","start":785,"length":5,"messageText":"'spend' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' is not assignable to type 'Team'."}},{"start":4616,"length":405,"code":2741,"category":1,"messageText":"Property 'spend' is missing in type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' but required in type 'Team'.","relatedInformation":[{"file":"./src/components/key_team_helpers/key_list.tsx","start":785,"length":5,"messageText":"'spend' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' is not assignable to type 'Team'."}},{"start":5031,"length":406,"code":2741,"category":1,"messageText":"Property 'spend' is missing in type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' but required in type 'Team'.","relatedInformation":[{"file":"./src/components/key_team_helpers/key_list.tsx","start":785,"length":5,"messageText":"'spend' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' is not assignable to type 'Team'."}}]],[2812,[{"start":3077,"length":4,"messageText":"Tuple type '[]' of length '0' has no element at index '0'.","category":1,"code":2493},{"start":3083,"length":4,"messageText":"Tuple type '[]' of length '0' has no element at index '1'.","category":1,"code":2493},{"start":3175,"length":4,"messageText":"'opts' is possibly 'undefined'.","category":1,"code":18048}]],[3051,[{"start":7973,"length":4,"code":2345,"category":1,"messageText":{"messageText":"Argument of type 'Element' is not assignable to parameter of type 'HTMLElement'.","category":1,"code":2345,"next":[{"messageText":"Type 'Element' is missing the following properties from type 'HTMLElement': accessKey, accessKeyLabel, autocapitalize, autocorrect, and 129 more.","category":1,"code":2740}]}},{"start":8039,"length":4,"code":2345,"category":1,"messageText":{"messageText":"Argument of type 'Element' is not assignable to parameter of type 'HTMLElement'.","category":1,"code":2345,"next":[{"messageText":"Type 'Element' is missing the following properties from type 'HTMLElement': accessKey, accessKeyLabel, autocapitalize, autocorrect, and 129 more.","category":1,"code":2740}]}}]],[3057,[{"start":3761,"length":7,"code":2741,"category":1,"messageText":"Property 'by_router' is missing in type '{ by_tier: { group: string; turn_count: number; real_win_rate_pct: number; shadow_win_rate_pct: number; tie_rate_pct: number; avg_judge_confidence: number; real_spend: number; shadow_spend: number; cache_hit_turns: number; }[]; ... 7 more ...; shed_count: number; }' but required in type '{ by_current_model: { avg_judge_confidence: number; cache_hit_turns: number; group: string; real_spend: number; real_win_rate_pct: number; shadow_spend: number; shadow_win_rate_pct: number; tie_rate_pct: number; turn_count: number; }[]; ... 9 more ...; withheld_count?: number | ... 1 more ... | undefined; }'.","relatedInformation":[{"file":"./src/lib/http/schema.d.ts","start":1378747,"length":9,"messageText":"'by_router' is declared here.","category":3,"code":2728},{"file":"./src/lib/http/schema.d.ts","start":1372928,"length":7,"messageText":"The expected type comes from property 'results' which is declared here on type '{ baseline_model?: string | null | undefined; created_at: string; direction: \"reverse\" | \"forward\"; ends_at: string; error_count?: number | null | undefined; job_id: string; judge_model: string; ... 10 more ...; targets: { ...; }[]; }'","category":3,"code":6500}],"canonicalHead":{"code":2322,"messageText":"Type '{ by_tier: { group: string; turn_count: number; real_win_rate_pct: number; shadow_win_rate_pct: number; tie_rate_pct: number; avg_judge_confidence: number; real_spend: number; shadow_spend: number; cache_hit_turns: number; }[]; ... 7 more ...; shed_count: number; }' is not assignable to type '{ by_current_model: { avg_judge_confidence: number; cache_hit_turns: number; group: string; real_spend: number; real_win_rate_pct: number; shadow_spend: number; shadow_win_rate_pct: number; tie_rate_pct: number; turn_count: number; }[]; ... 9 more ...; withheld_count?: number | ... 1 more ... | undefined; }'."}},{"start":28794,"length":7,"code":2741,"category":1,"messageText":"Property 'by_router' is missing in type '{ by_tier: never[]; by_current_model: never[]; overall_shadow_win_rate_pct: number; overall_tie_rate_pct: number; sampled_real_spend: number; sampled_shadow_spend: number; }' but required in type '{ by_current_model: { avg_judge_confidence: number; cache_hit_turns: number; group: string; real_spend: number; real_win_rate_pct: number; shadow_spend: number; shadow_win_rate_pct: number; tie_rate_pct: number; turn_count: number; }[]; ... 9 more ...; withheld_count?: number | ... 1 more ... | undefined; }'.","relatedInformation":[{"file":"./src/lib/http/schema.d.ts","start":1378747,"length":9,"messageText":"'by_router' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ by_tier: never[]; by_current_model: never[]; overall_shadow_win_rate_pct: number; overall_tie_rate_pct: number; sampled_real_spend: number; sampled_shadow_spend: number; }' is not assignable to type '{ by_current_model: { avg_judge_confidence: number; cache_hit_turns: number; group: string; real_spend: number; real_win_rate_pct: number; shadow_spend: number; shadow_win_rate_pct: number; tie_rate_pct: number; turn_count: number; }[]; ... 9 more ...; withheld_count?: number | ... 1 more ... | undefined; }'."}}]],[3058,[{"start":6233,"length":5,"code":2322,"category":1,"messageText":{"messageText":"Type '{ sessions: number; turns: number; avg_turns_per_session: number; avg_session_seconds: number; avg_tokens_per_session: number; spend: number; saved_spend: number; baseline_spend: number; saved_pct: number; saved_per_session: number; cache: { ...; }; }' is not assignable to type '{ avg_session_seconds: number; avg_tokens_per_session: number; avg_turns_per_session: number; baseline_spend: number; cache: { coverage_pct: number; first_visit: { hit_rate_pct: number; hits: number; turns: number; }; ... 8 more ...; unordered_turns: number; }; ... 9 more ...; turns: number; } | { ...; }'.","category":1,"code":2322,"next":[{"messageText":"Property 'classifier_cost' is missing in type '{ sessions: number; turns: number; avg_turns_per_session: number; avg_session_seconds: number; avg_tokens_per_session: number; spend: number; saved_spend: number; baseline_spend: number; saved_pct: number; saved_per_session: number; cache: { ...; }; }' but required in type '{ avg_session_seconds: number; avg_tokens_per_session: number; avg_turns_per_session: number; baseline_spend: number; cache: { coverage_pct: number; first_visit: { hit_rate_pct: number; hits: number; turns: number; }; ... 8 more ...; unordered_turns: number; }; ... 6 more ...; turns: number; }'.","category":1,"code":2741,"canonicalHead":{"code":2322,"messageText":"Type '{ sessions: number; turns: number; avg_turns_per_session: number; avg_session_seconds: number; avg_tokens_per_session: number; spend: number; saved_spend: number; baseline_spend: number; saved_pct: number; saved_per_session: number; cache: { ...; }; }' is not assignable to type '{ avg_session_seconds: number; avg_tokens_per_session: number; avg_turns_per_session: number; baseline_spend: number; cache: { coverage_pct: number; first_visit: { hit_rate_pct: number; hits: number; turns: number; }; ... 8 more ...; unordered_turns: number; }; ... 6 more ...; turns: number; }'."}}]},"relatedInformation":[{"file":"./src/lib/http/schema.d.ts","start":883611,"length":15,"messageText":"'classifier_cost' is declared here.","category":3,"code":2728},{"file":"./src/app/(dashboard)/cost-optimization/_components/autorouterbenchmarks.ts","start":508,"length":5,"messageText":"The expected type comes from property 'stats' which is declared here on type 'BenchmarkView'","category":3,"code":6500}]}]],[3111,[{"start":2067,"length":42,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":2572,"length":41,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":3058,"length":34,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":3554,"length":8,"code":2322,"category":1,"messageText":"Type 'undefined' is not assignable to type 'string'.","relatedInformation":[{"file":"./src/app/(dashboard)/hooks/useauthorized.ts","start":1740,"length":50,"messageText":"The expected type comes from property 'userRole' which is declared here on type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'","category":3,"code":6500}]},{"start":4033,"length":42,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":5026,"length":34,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[3119,[{"start":3309,"length":15,"code":2339,"category":1,"messageText":{"messageText":"Property 'CustomGuardrail' does not exist on type 'Record | typeof GuardrailProviders'.","category":1,"code":2339,"next":[{"messageText":"Property 'CustomGuardrail' does not exist on type 'typeof GuardrailProviders'.","category":1,"code":2339}]}}]],[3120,[{"start":1123,"length":14,"code":2322,"category":1,"messageText":{"messageText":"Type '{ \"hide-secrets\": { ui_friendly_name: string; detect_secrets_config: { param: string; description: string; required: boolean; type: string; }; }; }' is not assignable to type 'ProviderParamsResponse'.","category":1,"code":2322,"next":[{"messageText":"Property '\"hide-secrets\"' is incompatible with index signature.","category":1,"code":2530,"next":[{"messageText":"Type '{ ui_friendly_name: string; detect_secrets_config: { param: string; description: string; required: boolean; type: string; }; }' is not assignable to type '{ [key: string]: ProviderParam; }'.","category":1,"code":2322,"next":[{"messageText":"Property 'ui_friendly_name' is incompatible with index signature.","category":1,"code":2530,"next":[{"messageText":"Type 'string' is not assignable to type 'ProviderParam'.","category":1,"code":2322}]}]}]}]},"relatedInformation":[{"file":"./src/app/(dashboard)/guardrails/_components/guardrail_provider_fields.tsx","start":1281,"length":14,"messageText":"The expected type comes from property 'providerParams' which is declared here on type 'IntrinsicAttributes & GuardrailProviderFieldsProps'","category":3,"code":6500}]}]],[3151,[{"start":198,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":366,"length":9,"messageText":"Cannot find name 'afterEach'.","category":1,"code":2304},{"start":417,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":500,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":569,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":702,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":944,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1191,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1266,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1353,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1621,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1713,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1981,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2069,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2260,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2354,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2467,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2569,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2908,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2988,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3401,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3480,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3592,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3750,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3254,[{"start":2766,"length":5,"code":2339,"category":1,"messageText":"Property 'value' does not exist on type 'HTMLElement'."},{"start":2896,"length":5,"code":2339,"category":1,"messageText":"Property 'value' does not exist on type 'HTMLElement'."},{"start":3912,"length":5,"code":2339,"category":1,"messageText":"Property 'value' does not exist on type 'HTMLElement'."}]],[3263,[{"start":10763,"length":423,"code":2352,"category":1,"messageText":{"messageText":"Conversion of type '{ status: \"healthy\"; last_health_check: string; health_check_error: null; teams: { team_id: string; }[]; allowed_tools: string[]; has_user_credential: true; approval_status: \"approved\"; submitted_by: string; ... 47 more ...; env_vars?: MCPEnvVar[] | null; }' to type 'MCPServer' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.","category":1,"code":2352,"next":[{"messageText":"Types of property 'approval_status' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type '\"approved\"' is not comparable to type '\"active\" | \"pending_review\" | \"rejected\" | null | undefined'.","category":1,"code":2678}]}]}}]],[3375,[{"start":4578,"length":15,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ isLoading: boolean; isAuthorized: boolean; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ isLoading: boolean; isAuthorized: boolean; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[3379,[{"start":4867,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":11772,"length":24,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ model_name: string; litellm_params: { model: string; complexity_router_config: { tiers: {}; classifier_type: string; }; }; model_info: { id: string; db_model: boolean; created_at: string; }; }[]' is not assignable to parameter of type '({ model_name: string; litellm_params: { model: string; complexity_router_config?: undefined; complexity_router_default_model?: undefined; auto_router_config?: undefined; auto_router_default_model?: undefined; }; model_info: { ...; }; } | { ...; } | { ...; } | { ...; } | { ...; })[]'.","category":1,"code":2345,"next":[{"messageText":"Type '{ model_name: string; litellm_params: { model: string; complexity_router_config: { tiers: {}; classifier_type: string; }; }; model_info: { id: string; db_model: boolean; created_at: string; }; }' is not assignable to type '{ model_name: string; litellm_params: { model: string; complexity_router_config?: undefined; complexity_router_default_model?: undefined; auto_router_config?: undefined; auto_router_default_model?: undefined; }; model_info: { ...; }; } | { ...; } | { ...; } | { ...; } | { ...; }'.","category":1,"code":2322,"next":[{"messageText":"Type '{ model_name: string; litellm_params: { model: string; complexity_router_config: { tiers: {}; classifier_type: string; }; }; model_info: { id: string; db_model: boolean; created_at: string; }; }' is not assignable to type '{ model_name: string; litellm_params: { model: string; auto_router_config: string; auto_router_default_model: string; complexity_router_config?: undefined; complexity_router_default_model?: undefined; }; model_info: { ...; }; }'.","category":1,"code":2322,"next":[{"messageText":"Types of property 'litellm_params' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type '{ model: string; complexity_router_config: { tiers: {}; classifier_type: string; }; }' is missing the following properties from type '{ model: string; auto_router_config: string; auto_router_default_model: string; complexity_router_config?: undefined; complexity_router_default_model?: undefined; }': auto_router_config, auto_router_default_model","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ model_name: string; litellm_params: { model: string; complexity_router_config: { tiers: {}; classifier_type: string; }; }; model_info: { id: string; db_model: boolean; created_at: string; }; }' is not assignable to type '{ model_name: string; litellm_params: { model: string; auto_router_config: string; auto_router_default_model: string; complexity_router_config?: undefined; complexity_router_default_model?: undefined; }; model_info: { ...; }; }'."}}]}]}]}]}}]],[3653,[{"start":2185,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2365,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2415,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":2652,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3085,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3188,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3521,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3674,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3768,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3861,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3998,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4041,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":4130,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4412,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4492,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3686,[{"start":2516,"length":2,"code":2345,"category":1,"messageText":"Argument of type '{}' is not assignable to parameter of type 'void'."}]],[3730,[{"start":11320,"length":300,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":20262,"length":14,"code":2322,"category":1,"messageText":{"messageText":"Type '(value: TagListResponse | PromiseLike) => void' is not assignable to type '(tags: Record) => void'.","category":1,"code":2322,"next":[{"messageText":"Types of parameters 'value' and 'tags' are incompatible.","category":1,"code":2328,"next":[{"messageText":"Type 'Record' is not assignable to type 'TagListResponse | PromiseLike'.","category":1,"code":2322,"next":[{"messageText":"Property 'then' is missing in type 'Record' but required in type 'PromiseLike'.","category":1,"code":2741,"canonicalHead":{"code":2322,"messageText":"Type 'Record' is not assignable to type 'PromiseLike'."}}]}]}]},"relatedInformation":[{"file":"./node_modules/typescript/lib/lib.es5.d.ts","start":71612,"length":239,"messageText":"'then' is declared here.","category":3,"code":2728}]},{"start":21563,"length":15,"code":2322,"category":1,"messageText":{"messageText":"Type '(value: TagListResponse | PromiseLike) => void' is not assignable to type '(tags: Record) => void'.","category":1,"code":2322,"next":[{"messageText":"Types of parameters 'value' and 'tags' are incompatible.","category":1,"code":2328,"next":[{"messageText":"Type 'Record' is not assignable to type 'TagListResponse | PromiseLike'.","category":1,"code":2322,"next":[{"messageText":"Property 'then' is missing in type 'Record' but required in type 'PromiseLike'.","category":1,"code":2741,"canonicalHead":{"code":2322,"messageText":"Type 'Record' is not assignable to type 'PromiseLike'."}}]}]}]},"relatedInformation":[{"file":"./node_modules/typescript/lib/lib.es5.d.ts","start":71612,"length":239,"messageText":"'then' is declared here.","category":3,"code":2728}]},{"start":22118,"length":308,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":33445,"length":331,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":34328,"length":331,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[3783,[{"start":3053,"length":46,"code":2352,"category":1,"messageText":{"messageText":"Conversion of type '[url: string][]' to type '[string, RequestInit][]' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.","category":1,"code":2352,"next":[{"messageText":"Type '[url: string]' is not comparable to type '[string, RequestInit]'.","category":1,"code":2678,"next":[{"messageText":"Source has 1 element(s) but target requires 2.","category":1,"code":2618}]}]}}]],[3810,[{"start":9097,"length":9,"messageText":"Cannot find name 'afterEach'.","category":1,"code":2304}]],[3828,[{"start":401,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":442,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":647,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":711,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":957,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1064,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1307,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1383,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1651,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1701,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1948,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1998,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2220,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2297,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2528,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3833,[{"start":792,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":835,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1063,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1122,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1226,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1306,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1527,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1712,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1913,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2009,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2261,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2311,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2510,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2560,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2731,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3843,[{"start":780,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":813,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":891,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1067,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1117,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1321,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1371,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1570,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1620,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1789,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1848,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1981,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2053,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2107,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2176,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2342,"length":8,"messageText":"Parameter 'severity' implicitly has an 'any' type.","category":1,"code":7006},{"start":2352,"length":9,"messageText":"Parameter 'iconClass' implicitly has an 'any' type.","category":1,"code":7006},{"start":2505,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2584,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2857,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3006,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3063,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3512,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3571,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3661,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4075,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3880,[{"start":2005,"length":27,"code":2769,"category":1,"messageText":{"messageText":"No overload matches this call.","category":1,"code":2769,"next":[{"messageText":"Overload 1 of 2, '(id: Matcher, options?: SelectorMatcherOptions | undefined): HTMLElement', gave the following error.","category":1,"code":2772,"next":[{"messageText":"Argument of type 'string | null' is not assignable to parameter of type 'Matcher'.","category":1,"code":2345,"next":[{"messageText":"Type 'null' is not assignable to type 'Matcher'.","category":1,"code":2322}]}]},{"messageText":"Overload 2 of 2, '(id: Matcher, options?: SelectorMatcherOptions | undefined): HTMLElement', gave the following error.","category":1,"code":2772,"next":[{"messageText":"Argument of type 'string | null' is not assignable to parameter of type 'Matcher'.","category":1,"code":2345,"next":[{"messageText":"Type 'null' is not assignable to type 'Matcher'.","category":1,"code":2322}]}]}]},"relatedInformation":[]},{"start":2084,"length":26,"code":2769,"category":1,"messageText":{"messageText":"No overload matches this call.","category":1,"code":2769,"next":[{"messageText":"Overload 1 of 2, '(id: Matcher, options?: SelectorMatcherOptions | undefined): HTMLElement', gave the following error.","category":1,"code":2772,"next":[{"messageText":"Argument of type 'string | null' is not assignable to parameter of type 'Matcher'.","category":1,"code":2345,"next":[{"messageText":"Type 'null' is not assignable to type 'Matcher'.","category":1,"code":2322}]}]},{"messageText":"Overload 2 of 2, '(id: Matcher, options?: SelectorMatcherOptions | undefined): HTMLElement', gave the following error.","category":1,"code":2772,"next":[{"messageText":"Argument of type 'string | null' is not assignable to parameter of type 'Matcher'.","category":1,"code":2345,"next":[{"messageText":"Type 'null' is not assignable to type 'Matcher'.","category":1,"code":2322}]}]}]},"relatedInformation":[]}]],[3888,[{"start":162,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":205,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":319,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":384,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":517,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":602,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":751,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3889,[{"start":119,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":155,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":397,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":451,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":699,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":788,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":880,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1165,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1233,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1492,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1560,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1820,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3890,[{"start":211,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":252,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":384,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":454,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":615,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":698,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":788,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":875,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1063,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1159,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1503,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1570,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1738,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3891,[{"start":877,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":917,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1015,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1106,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1371,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1444,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1769,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1848,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2010,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2082,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2521,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2584,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2872,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2940,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3009,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3082,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3483,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3633,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3667,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3766,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4264,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4439,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4498,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4989,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5022,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3892,[{"start":144,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":177,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":286,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":359,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":488,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":554,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":618,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":732,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":793,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":961,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1031,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1172,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1248,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1396,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1468,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1599,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3898,[{"start":236,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":276,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":330,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":583,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":735,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":802,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":859,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":931,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1173,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1267,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1590,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1754,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1854,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2168,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2268,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2981,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3925,[{"start":1201,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1242,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":1294,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1434,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1517,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1627,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1810,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1875,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1963,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2273,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2391,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2426,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2681,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2876,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2924,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3198,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3285,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3314,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3448,"length":8,"messageText":"Parameter 'severity' implicitly has an 'any' type.","category":1,"code":7006},{"start":3458,"length":5,"messageText":"Parameter 'label' implicitly has an 'any' type.","category":1,"code":7006},{"start":3609,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3933,[{"start":5200,"length":36,"messageText":"Object is possibly 'null'.","category":1,"code":2531}]],[3935,[{"start":208,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":242,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":313,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":387,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":451,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":525,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":605,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":681,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":716,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":864,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":932,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1101,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1167,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1339,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1423,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1485,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1663,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1726,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1772,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1825,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1880,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1947,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1998,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3940,[{"start":1780,"length":8,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":15138,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; token: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; token: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[3941,[{"start":3402,"length":15,"code":2322,"category":1,"messageText":"Type 'number' is not assignable to type 'ModelBudgetConfig'."},{"start":3423,"length":7,"code":2322,"category":1,"messageText":"Type 'number' is not assignable to type 'ModelBudgetConfig'."}]],[3942,[{"start":3610,"length":8,"code":2741,"category":1,"messageText":"Property 'spend' is missing in type '{ team_id: string; team_alias: string; models: string[]; max_budget: number; budget_duration: string; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: never[]; }' but required in type 'Team'.","relatedInformation":[{"file":"./src/components/key_team_helpers/key_list.tsx","start":785,"length":5,"messageText":"'spend' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ team_id: string; team_alias: string; models: string[]; max_budget: number; budget_duration: string; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: never[]; }' is not assignable to type 'Team'."}},{"start":5344,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":5861,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":6795,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":7743,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":8690,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":9485,"length":43,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":10249,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":10894,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":11581,"length":56,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":12876,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[3943,[{"start":1457,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1501,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":1553,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1638,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1723,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2186,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2268,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2372,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2435,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2539,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3054,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3159,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3589,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3689,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3944,[{"start":837,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":1657,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1766,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1811,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2092,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2179,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2275,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2634,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2723,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3113,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3206,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3312,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3386,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3463,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3808,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3886,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4002,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4375,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4449,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4642,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4701,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5015,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5192,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5251,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5400,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3945,[{"start":1408,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1453,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1553,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1641,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1763,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1828,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1893,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1959,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2032,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2160,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2220,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2300,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2389,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2466,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2678,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2761,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2980,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3046,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3117,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3188,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3259,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3580,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3618,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3673,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3718,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3924,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4009,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4090,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4467,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4582,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5320,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5383,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5802,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5847,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6204,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6280,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6356,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6458,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6535,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7105,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7175,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7245,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7339,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7430,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7505,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7602,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8188,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8233,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8290,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8377,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8839,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8914,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9002,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9526,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9609,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10227,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10339,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10883,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10960,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11056,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11516,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11616,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11900,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11988,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12565,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12610,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12705,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12988,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13067,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13164,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":13729,"length":5,"messageText":"Parameter 'label' implicitly has an 'any' type.","category":1,"code":7006},{"start":13736,"length":11,"messageText":"Parameter 'replacement' implicitly has an 'any' type.","category":1,"code":7006},{"start":13749,"length":8,"messageText":"Parameter 'expected' implicitly has an 'any' type.","category":1,"code":7006},{"start":14281,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14316,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14428,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14510,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15129,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15170,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15380,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15464,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15810,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15867,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15931,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16652,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16732,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":17485,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17587,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":17813,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17889,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17984,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":18393,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18475,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18599,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18687,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":19160,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19287,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19325,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19404,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":20092,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20216,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":20742,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20803,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20950,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21018,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21303,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21382,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21457,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21541,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21821,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21890,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21968,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22546,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22613,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22642,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23087,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23172,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23254,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23388,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23474,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23943,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24032,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":24496,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24591,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":24910,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24994,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25068,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25439,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25512,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25587,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25739,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25835,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26078,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26360,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26456,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26829,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26867,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26944,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27541,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27666,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27969,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28057,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28745,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28825,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29313,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29407,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29886,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29924,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29997,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30245,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30516,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30596,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30689,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30781,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31178,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31325,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31738,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31888,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32296,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32379,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32477,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32838,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33002,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33040,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33117,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":33567,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33669,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34220,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34616,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34698,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34793,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":35371,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":35416,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":35465,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":35547,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":35784,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":35857,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36463,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36589,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36634,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36683,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36762,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":37118,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37211,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":37701,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37746,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37803,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37893,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38263,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38355,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38456,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38601,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38704,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38939,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":39117,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39181,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39244,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39315,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39394,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":39566,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39653,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39748,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40033,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40117,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40436,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40474,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40547,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40711,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40810,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40951,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41043,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41284,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41343,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41406,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41768,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41885,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41945,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":42340,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":42408,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":42501,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":42754,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":43050,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":43418,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":43512,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":43562,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":44018,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":44090,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":44181,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":44503,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":44617,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":44677,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":44930,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45045,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45162,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":45543,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45640,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":45907,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":46031,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":46389,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":46502,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":46573,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":46677,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":47108,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47224,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47327,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":47743,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47847,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":48002,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":48173,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":48283,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":48608,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":48725,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":49059,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49097,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49168,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":49616,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49654,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49719,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":49991,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50062,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":50622,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50743,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":51226,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51350,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":51552,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":51844,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51931,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":52339,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":52457,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":52518,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":52996,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":53083,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":53177,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":53459,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":53576,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":53648,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":53911,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":53965,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":54356,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":54407,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":54850,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":55011,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":55567,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":55668,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":56214,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":56482,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":56561,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":56733,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":57069,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":57238,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":57698,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":57756,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":58198,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":58349,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":58465,"length":6,"messageText":"Parameter 'action' implicitly has an 'any' type.","category":1,"code":7006},{"start":58919,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":58975,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":59193,"length":5,"messageText":"Parameter 'model' implicitly has an 'any' type.","category":1,"code":7006},{"start":59200,"length":6,"messageText":"Parameter 'effort' implicitly has an 'any' type.","category":1,"code":7006},{"start":59208,"length":5,"messageText":"Parameter 'label' implicitly has an 'any' type.","category":1,"code":7006},{"start":59215,"length":7,"messageText":"Parameter 'warning' implicitly has an 'any' type.","category":1,"code":7006},{"start":59367,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":59507,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":59571,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":59678,"length":5,"messageText":"Parameter 'model' implicitly has an 'any' type.","category":1,"code":7006},{"start":59802,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":59950,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":60019,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":60173,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":60457,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":60763,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":60917,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":60989,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":61374,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":61448,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":61881,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":62183,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":62591,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":62906,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":63185,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":63332,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":63738,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":64338,"length":6,"messageText":"Parameter '_label' implicitly has an 'any' type.","category":1,"code":7006},{"start":64346,"length":5,"messageText":"Parameter 'value' implicitly has an 'any' type.","category":1,"code":7006},{"start":64398,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":64482,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":64845,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":64937,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":65991,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":66137,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":66232,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":66455,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":66604,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":66964,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":67088,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":67264,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":67348,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":67444,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":67699,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":67756,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":67813,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":68094,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":68251,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":68308,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":68447,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":68519,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":68569,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":68785,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":68977,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":69074,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":69153,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":69442,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":69526,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":69648,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":69919,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":69997,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":70113,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":70549,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":70650,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":71037,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":71115,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":71233,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":71310,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":71435,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":71531,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":71824,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":71911,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":72476,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":72592,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":73177,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":73299,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":73895,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":73960,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":74178,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":74275,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":74386,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":74463,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":74933,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":75001,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":75046,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":75192,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":75500,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":75601,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":75691,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":75817,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":76249,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":76345,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":76435,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":76556,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":77078,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":77170,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":77271,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":77467,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":77558,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":77682,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":78289,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":78568,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":78606,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":78728,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":78815,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":79163,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":79454,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":79644,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":80140,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":80175,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":80278,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":80468,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":80723,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3946,[{"start":10172,"length":28,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is not assignable to parameter of type 'UseQueryResult'.","category":1,"code":2345,"next":[{"messageText":"Type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is not assignable to type 'QueryObserverRefetchErrorResult | QueryObserverSuccessResult | QueryObserverPlaceholderResult<...>'.","category":1,"code":2322,"next":[{"messageText":"Type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is missing the following properties from type 'QueryObserverPlaceholderResult': error, isLoading, isLoadingError, isRefetchError, and 18 more.","category":1,"code":2740,"canonicalHead":{"code":2322,"messageText":"Type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is not assignable to type 'QueryObserverPlaceholderResult'."}}]}]}},{"start":11331,"length":28,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is not assignable to parameter of type 'UseQueryResult'.","category":1,"code":2345,"next":[{"messageText":"Type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is not assignable to type 'QueryObserverRefetchErrorResult | QueryObserverSuccessResult | QueryObserverPlaceholderResult<...>'.","category":1,"code":2322,"next":[{"messageText":"Type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is missing the following properties from type 'QueryObserverPlaceholderResult': error, isLoading, isLoadingError, isRefetchError, and 18 more.","category":1,"code":2740,"canonicalHead":{"code":2322,"messageText":"Type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is not assignable to type 'QueryObserverPlaceholderResult'."}}]}]}}]],[3947,[{"start":1213,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":1412,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1478,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1878,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2020,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2305,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2574,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2996,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3076,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3207,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3717,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3910,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4162,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4286,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4544,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4630,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4727,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5116,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5238,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5478,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5600,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5817,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6317,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6562,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6708,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6776,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6866,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7042,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7129,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7231,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7331,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7640,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7722,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8356,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8618,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8663,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8736,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8982,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9239,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9341,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9448,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9710,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9883,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10017,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10109,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10204,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10624,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10809,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11251,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3949,[{"start":670,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":716,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":995,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1089,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1162,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1222,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1294,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1454,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1549,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1753,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1842,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2056,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3950,[{"start":450,"length":5,"code":2322,"category":1,"messageText":{"messageText":"Type '{ SIMPLE: string; MEDIUM: string; COMPLEX: string; REASONING: string; }' is not assignable to type 'ComplexityTiers'.","category":1,"code":2322,"next":[{"messageText":"Types of property 'SIMPLE' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type 'string' is not assignable to type 'string[]'.","category":1,"code":2322,"canonicalHead":{"code":2322,"messageText":"Type '{ SIMPLE: string; MEDIUM: string; COMPLEX: string; REASONING: string; }' is not assignable to type 'ComplexityTiers'."}}]}]},"relatedInformation":[{"file":"./src/components/add_model/complexityrouterconfig.tsx","start":14750,"length":5,"messageText":"The expected type comes from property 'tiers' which is declared here on type 'ComplexityRouterConfigValue'","category":3,"code":6500}]},{"start":833,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":884,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":974,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1098,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1205,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1331,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1389,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1460,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1504,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1589,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1629,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1724,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1894,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1939,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2113,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2415,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2460,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2655,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3021,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3066,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3177,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3520,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3558,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3625,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3894,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3932,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4009,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4133,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4196,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4276,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4450,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4545,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4747,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4845,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4883,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3952,[{"start":3331,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3861,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6669,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6708,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":7325,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7445,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7610,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7698,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8039,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8117,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8214,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8985,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9032,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9086,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9795,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10087,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10266,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10549,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10665,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10755,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11066,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11155,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11246,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11379,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11459,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11673,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11742,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12087,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12683,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12742,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12857,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":13419,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13477,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13544,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14178,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14378,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14463,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14526,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14595,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15088,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15444,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15941,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16107,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16200,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16267,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16761,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16949,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17033,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17130,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":17917,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18019,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18109,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":18796,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18888,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18978,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":19715,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19774,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19977,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":20478,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20571,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20824,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21467,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21560,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21627,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22064,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22260,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22319,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22476,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23025,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23191,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23250,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23423,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":24161,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24325,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24400,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24480,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25331,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25390,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25561,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25604,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26053,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26199,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26278,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26362,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27089,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27235,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27306,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27382,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28080,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28284,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28430,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28501,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28776,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29473,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29532,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29697,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30557,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30616,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30813,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31424,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31483,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31649,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32097,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32286,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32345,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32503,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32929,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33166,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33225,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33384,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34015,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34074,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34234,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34696,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34755,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34948,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":35735,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":35794,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36109,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36352,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36530,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36567,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36938,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37026,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38304,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38451,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38492,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38553,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38611,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38758,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38875,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38946,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":39752,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40011,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40103,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40209,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40250,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40712,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40772,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41120,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41217,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41438,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41629,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41689,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41789,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":42193,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":42531,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":42657,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":42743,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":43048,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":43370,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":43467,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":43778,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":43995,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":44093,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":44412,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":44587,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":44944,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":45477,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45538,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":46240,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":46701,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47376,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47544,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47589,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47645,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47716,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":48260,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":48321,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":48628,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":49308,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":49459,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":49852,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50175,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50365,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50436,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":50771,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51113,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51281,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51326,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51373,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51448,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":51491,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":51592,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":52050,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":52111,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":52276,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":52956,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":53017,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":53194,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":53991,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":54371,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":54461,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":54564,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":54974,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":55113,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":55395,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":55456,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":55910,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":56324,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":56520,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":56716,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":56920,"length":6,"messageText":"Parameter '_label' implicitly has an 'any' type.","category":1,"code":7006},{"start":56928,"length":9,"messageText":"Parameter 'modelName' implicitly has an 'any' type.","category":1,"code":7006},{"start":57278,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":57365,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":57452,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":58005,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":58369,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":58459,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":58562,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":58925,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":59255,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":59316,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":59780,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":60250,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":60309,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":60432,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":60531,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":60696,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":61146,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":61301,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":61453,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":61610,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":61660,"length":9,"messageText":"Cannot find name 'afterEach'.","category":1,"code":2304},{"start":61751,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":62011,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":62124,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":62200,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":62461,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":62540,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":62871,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":63038,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":63075,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":63210,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3955,[{"start":907,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":954,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":1006,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1338,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1383,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1456,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1533,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1610,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1716,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2009,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2084,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2163,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2237,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2746,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2827,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2918,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3025,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3104,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3519,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3978,[{"start":3670,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ metadata: { key: string; value?: string | undefined; }[]; }' is not assignable to parameter of type '{ metadata?: MetadataPair[] | undefined; }'.","category":1,"code":2345,"next":[{"messageText":"Types of property 'metadata' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type '{ key: string; value?: string | undefined; }[]' is not assignable to type 'MetadataPair[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ key: string; value?: string | undefined; }' is not assignable to type 'MetadataPair'.","category":1,"code":2322,"next":[{"messageText":"Types of property 'value' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type 'string | undefined' is not assignable to type 'string'.","category":1,"code":2322,"next":[{"messageText":"Type 'undefined' is not assignable to type 'string'.","category":1,"code":2322}],"canonicalHead":{"code":2322,"messageText":"Type '{ key: string; value?: string | undefined; }' is not assignable to type 'MetadataPair'."}}]}]}]}]}]}}]],[3984,[{"start":806,"length":13,"code":2322,"category":1,"messageText":{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }[]' is not assignable to type 'Organization[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is missing the following properties from type 'Organization': updated_by, litellm_budget_table, teams, users, members","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is not assignable to type 'Organization'."}}]},"relatedInformation":[{"file":"./src/components/common_components/organizationdropdown.tsx","start":179,"length":13,"messageText":"The expected type comes from property 'organizations' which is declared here on type 'IntrinsicAttributes & OrganizationDropdownProps'","category":3,"code":6500}]},{"start":1045,"length":13,"code":2322,"category":1,"messageText":{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }[]' is not assignable to type 'Organization[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is missing the following properties from type 'Organization': updated_by, litellm_budget_table, teams, users, members","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is not assignable to type 'Organization'."}}]},"relatedInformation":[{"file":"./src/components/common_components/organizationdropdown.tsx","start":179,"length":13,"messageText":"The expected type comes from property 'organizations' which is declared here on type 'IntrinsicAttributes & OrganizationDropdownProps'","category":3,"code":6500}]},{"start":1459,"length":13,"code":2322,"category":1,"messageText":{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }[]' is not assignable to type 'Organization[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is missing the following properties from type 'Organization': updated_by, litellm_budget_table, teams, users, members","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is not assignable to type 'Organization'."}}]},"relatedInformation":[{"file":"./src/components/common_components/organizationdropdown.tsx","start":179,"length":13,"messageText":"The expected type comes from property 'organizations' which is declared here on type 'IntrinsicAttributes & OrganizationDropdownProps'","category":3,"code":6500}]},{"start":1920,"length":13,"code":2322,"category":1,"messageText":{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }[]' is not assignable to type 'Organization[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is missing the following properties from type 'Organization': updated_by, litellm_budget_table, teams, users, members","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is not assignable to type 'Organization'."}}]},"relatedInformation":[{"file":"./src/components/common_components/organizationdropdown.tsx","start":179,"length":13,"messageText":"The expected type comes from property 'organizations' which is declared here on type 'IntrinsicAttributes & OrganizationDropdownProps'","category":3,"code":6500}]},{"start":2291,"length":13,"code":2322,"category":1,"messageText":{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }[]' is not assignable to type 'Organization[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is missing the following properties from type 'Organization': updated_by, litellm_budget_table, teams, users, members","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is not assignable to type 'Organization'."}}]},"relatedInformation":[{"file":"./src/components/common_components/organizationdropdown.tsx","start":179,"length":13,"messageText":"The expected type comes from property 'organizations' which is declared here on type 'IntrinsicAttributes & OrganizationDropdownProps'","category":3,"code":6500}]},{"start":2754,"length":13,"code":2322,"category":1,"messageText":{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }[]' is not assignable to type 'Organization[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is missing the following properties from type 'Organization': updated_by, litellm_budget_table, teams, users, members","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is not assignable to type 'Organization'."}}]},"relatedInformation":[{"file":"./src/components/common_components/organizationdropdown.tsx","start":179,"length":13,"messageText":"The expected type comes from property 'organizations' which is declared here on type 'IntrinsicAttributes & OrganizationDropdownProps'","category":3,"code":6500}]}]],[3987,[{"start":221,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":265,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":376,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":454,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":589,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":667,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":802,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":880,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1023,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1104,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1445,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[4090,[{"start":2930,"length":304,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ isLoading: false; isAuthorized: true; userId: string; userRole: string; accessToken: string; token: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: false; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ isLoading: false; isAuthorized: true; userId: string; userRole: string; accessToken: string; token: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: false; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[4098,[{"start":5253,"length":13,"code":2739,"category":1,"messageText":"Type '{ token: string; token_id: string; key_name: string; key_alias: string; spend: number; max_budget: number; expires: string; models: never[]; aliases: {}; config: {}; user_id: string; team_id: null; max_parallel_requests: number; ... 44 more ...; key_rotation_at: undefined; }' is missing the following properties from type 'KeyResponse': project_id, key_type, last_active","canonicalHead":{"code":2322,"messageText":"Type '{ token: string; token_id: string; key_name: string; key_alias: string; spend: number; max_budget: number; expires: string; models: never[]; aliases: {}; config: {}; user_id: string; team_id: null; max_parallel_requests: number; ... 44 more ...; key_rotation_at: undefined; }' is not assignable to type 'KeyResponse'."}}]],[4099,[{"start":5009,"length":14,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; isLoading: boolean; isAuthorized: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; isLoading: boolean; isAuthorized: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":10433,"length":14,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; isLoading: boolean; isAuthorized: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; isLoading: boolean; isAuthorized: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[4100,[{"start":4046,"length":13,"code":2739,"category":1,"messageText":"Type '{ token: string; token_id: string; key_name: string; key_alias: string; spend: number; max_budget: number; expires: string; models: never[]; aliases: {}; config: {}; user_id: string; team_id: null; project_id: null; ... 45 more ...; key_rotation_at: undefined; }' is missing the following properties from type 'KeyResponse': key_type, last_active","canonicalHead":{"code":2322,"messageText":"Type '{ token: string; token_id: string; key_name: string; key_alias: string; spend: number; max_budget: number; expires: string; models: never[]; aliases: {}; config: {}; user_id: string; team_id: null; project_id: null; ... 45 more ...; key_rotation_at: undefined; }' is not assignable to type 'KeyResponse'."}},{"start":6416,"length":47,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; accessToken: string; userId: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; accessToken: string; userId: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":6903,"length":47,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; accessToken: string; userId: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; accessToken: string; userId: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":8038,"length":55,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; accessToken: string; userId: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; accessToken: string; userId: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":8468,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":9841,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":10515,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":10960,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":11621,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":12378,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":13010,"length":104,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":14297,"length":94,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":15073,"length":94,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":15868,"length":94,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":16630,"length":115,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":17972,"length":96,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":19102,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":22855,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":24098,"length":115,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":24544,"length":111,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":25000,"length":115,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":25484,"length":113,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":26592,"length":102,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":27013,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":27644,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":28274,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":28857,"length":112,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":30053,"length":102,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":30808,"length":102,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":31694,"length":112,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":32555,"length":112,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":33756,"length":112,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":37651,"length":112,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":45209,"length":112,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[4154,[{"start":1980,"length":293,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":2424,"length":10,"code":2739,"category":1,"messageText":"Type '{ showTags: false; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ showTags: false; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":2638,"length":10,"code":2739,"category":1,"messageText":"Type '{ showTags: true; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ showTags: true; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":2857,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":5736,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":6118,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":6926,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: never[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: never[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":7351,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: undefined; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: undefined; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":7757,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: null; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: null; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":8309,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":9294,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":9836,"length":10,"code":2739,"category":1,"messageText":"Type '{ showTags: true; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ showTags: true; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":10256,"length":10,"code":2739,"category":1,"messageText":"Type '{ showTags: false; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ showTags: false; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":10874,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: never[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: never[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}}]]],"affectedFilesPendingEmit":[4156,2683,2685,2687,2689,2691,2693,2676,2867,2858,1314,1313,1312,2864,2857,2855,2866,2856,2865,2861,2860,2859,1245,2862,2895,2893,2894,2812,2911,2912,2901,2913,2899,1315,2914,2903,1317,1316,2898,2915,2916,2904,1320,1319,2917,2902,2896,2909,2907,2910,2906,2905,2897,2900,2908,2918,2851,2919,2924,2921,2920,2923,2932,2925,2933,2929,1322,1321,2931,2927,2926,1323,2934,2928,2930,2949,2946,2950,2936,2939,2938,1324,1326,1325,2952,2953,2940,2951,2937,1327,2942,2941,2954,2943,1329,1328,2955,2956,2944,1087,2948,2945,2935,2947,2719,1331,1330,3051,2831,3052,3047,1334,1333,3053,3054,3049,1336,1335,3055,3048,3056,2959,3057,2829,2828,3058,2830,3059,2958,1344,1343,3060,3061,1342,1346,1345,3050,3063,1358,3064,3065,1356,3066,3067,1378,3068,1373,1379,3071,1367,3072,1365,3073,1364,1403,1363,1359,1404,1366,3069,1355,1380,1374,3070,1357,1349,1400,1377,1401,1375,1402,1376,3062,3149,3136,3151,3150,3152,3142,3154,3145,3155,3144,3153,3140,3156,3143,3148,3147,3112,3113,3090,1409,3093,3124,3081,3079,3125,3082,3126,3094,3127,3095,3128,3129,3075,3130,3076,3078,3131,3074,3077,3132,1697,3133,3080,3134,1410,1411,3114,3102,3115,3100,1405,1408,1407,3116,3101,3117,3118,3096,3119,1406,3084,3120,3085,3121,3092,3083,3109,3104,3091,3106,3098,3107,3099,3108,3097,3086,3122,3087,3123,3088,3110,3111,3103,3135,3089,3105,1422,1423,1421,1424,1425,1427,1426,1428,1114,1429,1431,1430,1455,1457,1456,1459,1458,1461,1460,1463,1462,1466,1465,1467,1107,3158,1454,1468,1469,1470,1471,1473,1472,1474,1475,1476,1478,1477,1480,1479,1481,1129,1483,1482,1484,1220,1486,1485,1488,1489,1487,1490,1492,1491,1239,1493,1494,1495,1496,1498,1497,1500,1499,1502,1501,1503,1504,1506,1505,1507,1509,1508,1510,1189,1512,1511,1513,1218,1516,1515,1518,1517,1520,1519,1521,1514,1523,1522,1525,1524,1527,1526,1222,1529,1528,1530,1532,1534,1533,1536,1535,1538,1537,1540,1539,1542,1541,1543,1545,1544,1547,1546,1548,1217,1549,1108,1552,1551,1553,1550,1555,1554,1412,1413,1109,1414,1231,1232,1415,1229,1416,1233,1417,1418,1219,1223,1557,1556,1559,1558,1561,1560,3157,1420,2815,2813,2811,1244,1243,3172,3192,3197,3240,3241,3218,1563,1562,1566,1565,3203,1568,1569,1567,3242,3215,3206,1571,1570,3219,3238,3258,3220,3259,3208,3260,3229,3261,3207,3262,3223,3263,3264,3222,3265,3224,3266,3232,3267,3209,3268,3237,1573,1572,3257,1574,3245,3243,3214,3244,3228,3246,3211,3247,3221,3248,3193,3194,3250,3196,3249,3195,1576,1575,3251,3201,3198,3213,3252,3212,3253,3204,3210,1654,3199,3205,3233,1656,1655,3254,3234,3255,3202,3200,3256,3231,3269,1564,3239,3277,3270,3278,3271,3279,3273,3272,3280,3274,3276,3275,3298,3368,3367,1666,1665,3375,3325,3376,3324,1670,1669,3379,3332,3331,3330,1672,1671,3377,3363,3323,3378,3371,1663,1662,3374,3373,3380,3369,3381,3342,3326,3382,3333,3383,3362,3347,3366,3364,3358,3372,1664,1674,1673,1216,3388,3386,3387,3401,3399,3402,3398,3397,3393,3392,3400,2853,2852,3508,3530,3500,3531,3522,3532,3509,3533,3501,1676,3510,3502,3534,3503,3535,3517,3536,3521,3537,3511,3504,3538,3505,3539,3506,3540,3507,3541,3520,3515,3518,3514,3516,3519,1678,1677,3542,3527,3543,3525,3544,3523,3545,3526,3547,3546,3548,3524,1681,1680,3404,1686,1685,1688,3424,3549,3492,3550,3493,3551,3494,3552,3495,1679,3496,3497,3499,3529,3528,3573,3563,3574,3557,3575,3568,3571,3560,3559,1691,1690,3576,3566,3577,3558,3578,3561,3579,3569,3580,3555,3581,3556,3582,3565,3583,3564,3572,3554,3553,1693,1692,3584,3567,3562,3570,3595,3590,3596,3589,3597,3588,3587,3600,3601,3585,3602,3603,3586,3604,1973,1694,1975,1974,3598,3593,3599,3592,3591,3594,3633,3610,3634,3630,3629,3646,3619,3651,3624,3647,3620,3648,3623,3649,3621,1979,1980,3650,3618,3622,3638,3616,3626,3628,3639,3613,3640,3608,3641,3612,3642,3617,3643,3625,3644,3614,1976,1978,1977,3645,3627,3635,3609,3605,3632,3607,3606,3636,3611,3637,3615,3631,3653,3046,3652,3663,3664,3655,3661,3665,3654,1983,1982,3669,3670,3660,3666,3657,3656,3667,3658,3668,3659,1981,3662,3678,3671,3676,3674,3677,3673,3672,3675,3688,3682,3686,3683,3687,3679,3685,3681,3680,3684,3696,3703,3706,3705,3704,3709,3708,3707,3732,3716,3733,3717,3734,3718,3731,3719,3735,3723,1987,1989,1988,3736,3724,1992,1991,3722,3737,3721,1985,1984,3720,3729,3725,3730,3727,3738,3726,1993,1341,3728,3749,3740,3752,3742,1996,1995,1997,1994,3747,3750,3739,3751,3746,3754,3755,3745,3753,3744,3743,3748,3769,3770,3765,3771,3763,3762,3779,3767,1210,3772,1209,1208,3773,3764,3774,3766,3780,3781,3761,3775,3776,3759,3777,3758,3757,3778,3760,3768,3785,3784,3783,3782,3793,3795,3799,3787,3786,3801,3791,3790,3803,3805,3804,3807,3806,2703,3809,3810,3808,3811,3812,3813,3814,3816,3815,3820,3819,3821,3822,3818,3823,3817,3824,3844,3711,2085,1104,3952,3329,3340,3942,3341,3954,3334,1095,3955,3300,1667,2041,2040,3943,3328,2044,2043,2046,2045,2047,1136,2042,1130,3956,3304,1121,1119,3944,1112,2048,1111,1118,1120,2049,1093,2050,1134,3945,1132,1131,3957,3335,1122,1127,3327,3958,3337,2051,1116,3946,1117,1135,3959,3336,1100,3960,3338,2086,1096,3947,1113,3961,3339,1123,3948,2087,3949,1128,3950,1124,2052,1133,2053,1125,1098,3951,1126,1097,1099,3845,3353,3962,1698,1318,3867,3281,3873,3282,3874,3284,3875,3286,3868,3283,3869,3297,3870,3287,3293,3871,3291,3872,3290,3162,3963,3161,3825,1230,3846,3741,1936,3788,2062,3964,2061,3965,3797,3966,3798,2060,3792,3967,3800,3968,3796,3969,3789,3794,2054,3802,2063,2055,3970,3295,3512,1675,3971,3513,3972,1683,1684,2065,2064,3294,3292,640,3847,3712,3876,3168,3877,3878,3165,3879,3163,3164,3880,3167,2004,2003,3881,3882,3166,1464,1372,1699,2833,1700,1085,3973,2818,3974,2834,3995,3389,3996,3390,3997,3391,2066,3998,3288,3999,3289,3975,1701,3976,2819,3977,2718,3318,3978,3311,3979,1934,3980,1933,3981,1084,3983,3982,3984,1952,3985,3352,1935,3351,3986,1939,1953,3987,1940,3988,1950,2068,2067,3989,2835,3991,1350,3992,1951,3993,2826,3994,3308,3990,2827,2868,3826,1959,3827,2715,3828,2720,3883,3175,3884,3174,3173,3885,3178,3886,3177,3176,3829,2922,2089,2090,2088,4000,2091,2092,638,3848,3159,3887,2012,3888,2008,3889,2009,3890,2010,2014,2007,3891,2013,2015,2011,4001,2843,2093,3830,3312,3137,3141,3892,3138,2016,3139,2018,2017,3831,1368,3849,2292,1998,1197,4002,1960,1961,4005,1089,2096,2095,1215,4003,2094,1046,2098,2097,4004,1962,2100,2099,2102,2101,3850,3348,3851,1242,3832,2722,4006,3403,1687,4007,1090,2104,2103,4008,3498,3852,2836,2105,1966,2106,4009,1963,4010,1967,4011,3230,1088,1965,4012,3422,4013,1086,2108,2107,4014,3343,4015,3346,4016,3345,3344,4017,3301,4018,3361,4019,3360,3359,4020,3322,2109,3285,3365,3853,3307,3305,3893,2854,2020,2019,4021,3299,4022,1354,4023,3038,3160,3854,2717,3895,2706,3896,2709,3897,2707,2021,1234,2022,3898,2710,3899,2716,3894,2712,3900,2714,1999,1214,3833,2721,1047,2840,3855,1958,4026,4027,1972,2110,1970,4024,4025,2841,2112,2111,2113,1971,2116,2115,4029,3395,2118,2117,4030,3394,2114,4028,3396,2000,2002,2001,3856,3354,3901,3356,3355,3902,3357,3857,3714,4031,2839,2121,2120,4032,2838,2837,4033,2844,1689,3858,3370,3859,1352,3860,3296,2123,2122,2126,2125,2124,3861,3349,3862,3350,4039,2960,4034,1941,4035,1942,4036,1945,4037,1943,4038,1944,4042,3045,4040,3044,2128,2127,4041,3043,3042,3041,2129,1531,3834,2869,4043,3313,3863,3171,2023,3903,2886,2884,3904,2885,2024,3905,2887,3906,2889,3907,2888,3908,2870,3909,3226,3910,3225,2026,2025,3911,3227,2028,2027,3912,2890,2029,2030,3918,2873,3919,2872,3920,2874,3921,3922,2875,3913,2876,3914,2877,3915,2880,3916,2878,3917,2879,2032,2031,3923,2881,3924,2882,3925,2883,3926,3170,3169,2033,3927,1949,3928,1946,3929,3039,1947,3931,3040,3930,1948,3146,4059,2821,4044,1956,4045,2816,4060,3713,4068,2277,4069,2278,4070,2279,4071,2276,2130,4072,2280,2282,4073,2281,4046,1235,4047,1968,1176,4061,4062,1180,4063,1182,4064,1179,4065,1184,4066,1187,4067,1186,1185,1188,1175,2006,4048,1195,2842,4074,1954,3309,3303,4049,1200,4050,1201,4051,1091,1937,4052,2814,4053,2825,4054,1696,4055,2957,2871,2823,4056,1041,4057,1226,2822,4075,1190,1191,4076,1192,4077,1194,4078,1196,1204,4079,1198,4080,1199,4081,1202,4082,1203,4058,2705,1695,3932,1238,3836,1241,3835,2891,4083,3423,637,4084,3691,3690,3689,2846,4085,4086,3314,3310,4087,1938,4092,3316,2284,2283,4088,3317,4093,3315,2286,2285,4089,3321,4090,3319,2287,2119,4091,3320,2288,3838,3695,2035,2034,3933,3694,3693,3837,3692,2290,2289,4098,2847,4099,4100,2848,4094,2832,2291,2294,2293,2845,4095,2820,4096,4097,2824,3934,2713,3839,3698,3935,3697,3936,3701,2036,3937,3700,3938,3699,3840,3702,4101,1347,1955,4102,1236,4103,1115,4104,2704,2708,4105,1035,1092,4106,2275,1183,1101,1040,1110,1362,4107,1044,2817,1038,1036,1042,1237,1102,4108,1225,4109,1045,1043,1181,1177,1103,2702,1094,1178,1348,1037,4110,1048,4111,1361,3841,2892,3842,3864,3302,3940,3385,3939,3710,2037,1986,1332,2039,2038,3865,3715,3843,2810,1138,4112,1969,3866,3756,4125,3181,4113,3182,4114,3180,3179,2301,4115,2313,2295,4116,2312,2311,2299,4126,2298,2309,2308,4127,2310,4128,2307,4121,4122,3191,4123,3183,2296,4129,2302,4130,2332,2300,4131,2305,4132,2335,2342,4133,2336,4134,2319,4135,2340,4136,2341,4137,2337,2329,2330,4138,2339,4139,2338,4140,1211,4141,2331,2304,4142,2334,4143,2333,4144,2316,4145,2315,2306,2343,2320,4124,3184,3185,4117,3186,4118,3190,3189,4119,3188,2323,2328,2324,2325,2326,4146,2327,2321,2344,2322,4120,3187,2297,2314,3306,3384,2849,3941,2850,2700,2056,4147,2711,2701,1224,2350,2348,2347,2349,2346,2345,2351,4151,2354,1360,4148,3216,4149,1371,3217,4150,3235,3236,2352,2353,2355,1351,2357,1137,2358,1034,2362,2361,2364,2363,4152,2084,2365,2366,1340,2367,630,2368,1212,2369,1213,2370,2359,1353,1205,2360,631,614,2371,2372,1227,2373,634,2374,1193,1453,2375,1207,2376,2378,2377,2379,636,2639,2638,2641,2640,2642,1240,2643,1228,2644,2646,2645,2647,633,2648,1964,2649,1668,2650,2651,1682,2652,1221,2655,2654,2658,2657,2659,2656,2660,1105,2661,1106,2663,2662,632,2664,1419,2665,2005,2666,1083,2667,1206,4153,2684,2686,2688,2690,2692,2694,2672,2673,2675,2677,2356,2695,3953,2678,2682,2863,4154,613],"version":"5.9.3"} \ No newline at end of file From 04bc354525142dc4fbf4c9fde094d65b9ef2f7ed Mon Sep 17 00:00:00 2001 From: joshua-berri Date: Fri, 2 Oct 2026 16:04:11 -0700 Subject: [PATCH 02/61] refactor(mcp): extract upstream preparation and support modern clients (#44232) * refactor(mcp): extract upstream preparation and support modern clients * fix(mcp): reject incompatible upstream transport before saving * fix(mcp): serialize protocol validation with server updates --------- Co-authored-by: Joshua Valluru <326636767+joshua-berri@users.noreply.github.com> --- litellm/experimental_mcp_client/client.py | 20 +- .../_experimental/mcp_server/contracts.py | 18 +- litellm/proxy/_experimental/mcp_server/db.py | 43 +- .../mcp_server/mcp_server_manager.py | 368 ++---------------- .../mcp_server/server_resolution.py | 39 ++ .../_experimental/mcp_server/upstream.py | 315 +++++++++++++++ litellm/proxy/_types.py | 25 ++ .../mcp_management_endpoints.py | 55 ++- litellm/types/mcp.py | 9 +- .../types/mcp_server/mcp_server_manager.py | 6 +- tests/integration/mcp/test_mcp_management.py | 94 +++++ tests/integration/mcp/test_mcp_transports.py | 49 +++ .../test_mcp_client.py | 94 ++++- .../mcp_server/test_mcp_logging.py | 6 +- .../mcp_server/test_mcp_partial_update.py | 39 +- .../mcp_server/test_mcp_server.py | 22 +- .../mcp_server/test_mcp_server_manager.py | 76 +++- .../mcp_server/test_openapi_tool_auth.py | 25 ++ .../mcp_server/test_operations.py | 2 +- .../mcp_server/test_server_resolution.py | 93 +++++ .../test_mcp_management_endpoints.py | 86 ++++ tests/unit/proxy/test__types.py | 12 +- 22 files changed, 1115 insertions(+), 381 deletions(-) create mode 100644 litellm/proxy/_experimental/mcp_server/upstream.py diff --git a/litellm/experimental_mcp_client/client.py b/litellm/experimental_mcp_client/client.py index f133e4837a6..bf6780c7812 100644 --- a/litellm/experimental_mcp_client/client.py +++ b/litellm/experimental_mcp_client/client.py @@ -36,6 +36,7 @@ from mcp.types import ( METHOD_NOT_FOUND, REQUEST_TIMEOUT, ClientCapabilities, + DiscoverResult, ElicitationCapability, FormElicitationCapability, GetPromptRequestParams, @@ -84,6 +85,7 @@ from litellm.types.mcp import ( MCPUpstreamProtocol, credential_redirect_hook, has_header, + validate_mcp_protocol_transport, without_header, ) @@ -401,7 +403,10 @@ class MCPClient: logging_callback: Callable | None = None, protocol_version: MCPUpstreamProtocol = "auto", ): - self.protocol_version: MCPUpstreamProtocol = TypeAdapter(MCPUpstreamProtocol).validate_python(protocol_version) + self.protocol_version: MCPUpstreamProtocol = TypeAdapter[MCPUpstreamProtocol]( + MCPUpstreamProtocol + ).validate_python(protocol_version) + validate_mcp_protocol_transport(self.protocol_version, transport_type) self.server_url: str = server_url self.transport_type: MCPTransport = transport_type self.auth_type: MCPAuthType = auth_type @@ -540,6 +545,17 @@ class MCPClient: return safe_env + async def _prepare_session(self, session: ClientSession) -> InitializeResult | DiscoverResult: + if self.protocol_version != "2026-07-28": + return await self._initialize_session(session) + discovery: Final = DiscoverResult.model_validate(await session.send_discover(self.protocol_version)) + if self.protocol_version not in discovery.supported_versions: + raise MCPError(code=-32022, message="Upstream did not accept the configured MCP protocol version") + session.adopt(discovery) + if session.protocol_version != self.protocol_version: + raise MCPError(code=-32022, message="Upstream selected an unsupported MCP protocol version") + return discovery + async def _initialize_session(self, session: ClientSession) -> InitializeResult: if self.protocol_version == "auto": automatic: Final = await session.initialize() @@ -623,7 +639,7 @@ class MCPClient: ) session: Final = await session_ctx.__aenter__() try: - init_result: Final = await self._initialize_session(session) + init_result: Final = await self._prepare_session(session) instructions: Final = getattr(init_result, "instructions", None) self._last_initialize_instructions = ( instructions.strip() or None if isinstance(instructions, str) else None diff --git a/litellm/proxy/_experimental/mcp_server/contracts.py b/litellm/proxy/_experimental/mcp_server/contracts.py index b55034e6dc9..82b1861c1e8 100644 --- a/litellm/proxy/_experimental/mcp_server/contracts.py +++ b/litellm/proxy/_experimental/mcp_server/contracts.py @@ -3,12 +3,28 @@ from copy import deepcopy from dataclasses import dataclass, field from datetime import datetime from types import MappingProxyType -from typing import Final, Protocol +from typing import TYPE_CHECKING, Final, Literal, Protocol from litellm.proxy._experimental.mcp_server.tool_outcome import WireCompat from litellm.proxy._types import UserAPIKeyAuth from litellm.types.mcp_server.mcp_server_manager import MCPServer +if TYPE_CHECKING: + from litellm.proxy._experimental.mcp_server.server_resolution import ResolvedMCPServer + + +class TargetCatalog(Protocol): + async def resolve( + self, + server_id: str, + caller: UserAPIKeyAuth, + *, + is_admin_view: bool, + not_found_detail: Mapping[str, str], + forbidden_detail: Mapping[str, str], + non_admin_missing: Literal["not_found", "forbidden"], + ) -> "ResolvedMCPServer": ... + def copy_caller(auth: UserAPIKeyAuth | None) -> UserAPIKeyAuth | None: if auth is None: diff --git a/litellm/proxy/_experimental/mcp_server/db.py b/litellm/proxy/_experimental/mcp_server/db.py index 481ebdb1ef2..bf1f6fa90c1 100644 --- a/litellm/proxy/_experimental/mcp_server/db.py +++ b/litellm/proxy/_experimental/mcp_server/db.py @@ -8,6 +8,7 @@ from datetime import datetime, timedelta, timezone from typing import TYPE_CHECKING, Any, Final, Literal, Protocol, TypedDict, cast from fastapi import HTTPException +from pydantic import TypeAdapter from typing_extensions import ReadOnly from litellm._logging import verbose_proxy_logger @@ -52,8 +53,8 @@ from litellm.repositories.verification_token_repository import ( VerificationTokenRepository, ) from litellm.types.llms.custom_http import httpxSpecialProvider -from litellm.types.mcp import MCPCredentials -from litellm.types.mcp_server.mcp_server_manager import PinnedMCPTool +from litellm.types.mcp import MCPCredentials, MCPTransportType, MCPUpstreamProtocol, validate_mcp_protocol_transport +from litellm.types.mcp_server.mcp_server_manager import MCPInfo, PinnedMCPTool if TYPE_CHECKING: from prisma import models as prisma_db_models @@ -600,6 +601,7 @@ async def _mcp_server_write_if_identifier_free( alias: str | None, exclude_server_id: str | None, write: "Callable[[TableActions[prisma_db_models.LiteLLM_MCPServerTable]], Awaitable[prisma_db_models.LiteLLM_MCPServerTable | None]]", + lock_server_id: str | None = None, ) -> "prisma_db_models.LiteLLM_MCPServerTable | McpIdentifierConflict | None": """Run ``write`` only when no other live row owns ``server_name``/``alias``. @@ -619,6 +621,10 @@ async def _mcp_server_write_if_identifier_free( ) if conflict is not None: return conflict + if lock_server_id is not None: + await tx.execute_raw( + 'SELECT server_id FROM "LiteLLM_MCPServerTable" WHERE server_id=$1 FOR UPDATE', lock_server_id + ) return await write(tx.litellm_mcpservertable) @@ -1100,6 +1106,27 @@ async def get_draft_mcp_server( return table +def _validate_mcp_protocol_write( + stored: "prisma_db_models.LiteLLM_MCPServerTable", data_dict: Mapping[str, object] +) -> None: + raw_info: Final = data_dict.get("mcp_info", stored.mcp_info) + adapter: Final = TypeAdapter[MCPInfo | None](MCPInfo | None) + try: + info: Final = ( + adapter.validate_json(raw_info) if isinstance(raw_info, str) else adapter.validate_python(raw_info) + ) + validate_mcp_protocol_transport( + TypeAdapter[MCPUpstreamProtocol](MCPUpstreamProtocol).validate_python( + (info or {}).get("protocol_version", "auto") + ), + TypeAdapter[MCPTransportType](MCPTransportType).validate_python( + data_dict.get("transport", stored.transport) + ), + ) + except ValueError as exc: + raise HTTPException(status_code=400, detail=str(exc)) from exc + + async def _update_mcp_server_row( prisma_client: PrismaClient, *, @@ -1107,29 +1134,36 @@ async def _update_mcp_server_row( data_dict: Mapping[str, object], ) -> "prisma_db_models.LiteLLM_MCPServerTable | McpIdentifierConflict | None": identifier_write: Final = any(field in data_dict for field in ("server_name", "alias")) + protocol_write: Final = bool({"transport", "mcp_info"}.intersection(data_dict)) async def _update( table: "TableActions[prisma_db_models.LiteLLM_MCPServerTable]", ) -> "prisma_db_models.LiteLLM_MCPServerTable | None": + if protocol_write: + stored: Final = await table.find_unique(where={"server_id": server_id}) + if stored is None: + return None + _validate_mcp_protocol_write(stored, data_dict) return await table.update( where={"server_id": server_id}, data=data_dict, ) - if not identifier_write: + if not identifier_write and not protocol_write: return await _update(_mcp_server_table_actions(prisma_client)) if "alias" in data_dict and not data_dict["alias"] and "server_name" not in data_dict: # Clearing the alias drops the prefix to the stored server_name, which # may already belong to another row, so that name needs the check too. existing: Final = await _db_find_mcp_server_row(prisma_client, server_id) if existing is None: - return await _update(_mcp_server_table_actions(prisma_client)) + return None return await _mcp_server_write_if_identifier_free( prisma_client, server_name=existing.server_name, alias=None, exclude_server_id=server_id, write=_update, + lock_server_id=server_id if protocol_write else None, ) return await _mcp_server_write_if_identifier_free( prisma_client, @@ -1137,6 +1171,7 @@ async def _update_mcp_server_row( alias=_identifier_field(data_dict, "alias"), exclude_server_id=server_id, write=_update, + lock_server_id=server_id if protocol_write else None, ) diff --git a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py index 375c010a9a8..816ae9aea59 100644 --- a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py +++ b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py @@ -58,12 +58,10 @@ from litellm.constants import ( MCP_CLIENT_TIMEOUT, MCP_HEALTH_CHECK_TIMEOUT, MCP_METADATA_TIMEOUT, - MCP_NPM_CACHE_DIR, - MCP_STDIO_ALLOWED_COMMANDS, MCP_TOOL_LISTING_TIMEOUT, ) from litellm.exceptions import BlockedPiiEntityError, GuardrailRaisedException -from litellm.experimental_mcp_client.client import MCPClient, MCPSigV4Auth, strip_auth_scheme, to_basic_credentials +from litellm.experimental_mcp_client.client import MCPClient, strip_auth_scheme, to_basic_credentials from litellm.integrations.custom_guardrail import ( _sync_guardrail_info_to_logging_obj, # pyright: ignore[reportPrivateUsage] - the same bridge @log_guardrail_information uses; reimplementing it here would fork the metadata-key logic ) @@ -91,8 +89,6 @@ from litellm.proxy._experimental.mcp_server.mcp_debug import describe_upstream_h from litellm.proxy._experimental.mcp_server.oauth2_token_cache import ( MCPPerUserTokenCache, mcp_per_user_token_cache, - resolve_mcp_auth, - resolved_token_header, ) from litellm.proxy._experimental.mcp_server.oauth_utils import ( _redact_mcp_resource_url, @@ -105,10 +101,8 @@ from litellm.proxy._experimental.mcp_server.outbound_credentials import ( UpstreamCredentialProvider, ) from litellm.proxy._experimental.mcp_server.outbound_credentials.adapter import ( - prepare_mcp_client, raise_public, raise_token_exchange_challenge, - raise_user_oauth_challenge, to_server_spec, to_subject, ) @@ -118,19 +112,14 @@ from litellm.proxy._experimental.mcp_server.outbound_credentials.oauth_token_sto from litellm.proxy._experimental.mcp_server.outbound_credentials.per_user_oauth_store import ( LazyPerUserOAuthTokenStore, ) -from litellm.proxy._experimental.mcp_server.outbound_credentials.resolver import resolve_credentials_with_source from litellm.proxy._experimental.mcp_server.outbound_credentials.token_exchange_provider import ( build_token_exchanger, ) from litellm.proxy._experimental.mcp_server.outbound_credentials.types import ( DEFAULT_CREDENTIAL_HEADER, - AuthorizationCodeConfig, AuthResolution, - ClientCredentialsConfig, - CredError, IdJagConfig, PassthroughConfig, - ServerSpec, TokenExchangeConfig, ) from litellm.proxy._experimental.mcp_server.result_conversion import ( @@ -145,7 +134,6 @@ from litellm.proxy._experimental.mcp_server.sampling_handler import ( from litellm.proxy._experimental.mcp_server.stdio_gate import ( MCP_STDIO_DISABLED_MESSAGE, is_mcp_stdio_blocked, - is_mcp_stdio_enabled, warn_if_mcp_stdio_blocked, ) from litellm.proxy._experimental.mcp_server.tool_catalog_guard import ( @@ -154,6 +142,13 @@ from litellm.proxy._experimental.mcp_server.tool_catalog_guard import ( pin_tool_catalog, scan_tool_descriptions, ) +from litellm.proxy._experimental.mcp_server.upstream import ( + passthrough_token_from_mcp_auth_header, + prepare_upstream_client, + resolve_upstream_auth, + take_forwarded_authorization, + to_server_spec_fail_closed, +) from litellm.proxy._experimental.mcp_server.utils import ( MCP_TOOL_PREFIX_SEPARATOR, MCPMissingUserEnvVarsError, @@ -204,10 +199,8 @@ from litellm.types.llms.custom_http import httpxSpecialProvider from litellm.types.mcp import ( DEFAULT_SUBJECT_TOKEN_TYPE, MCPAuth, - MCPStdioConfig, MCPTokenEndpointAuthMethod, MCPUpstreamProtocol, - has_header, without_header, ) from litellm.types.mcp_server.mcp_server_manager import ( @@ -1234,7 +1227,7 @@ def _resolve_openapi_tool_auth( Returns the ``Authorization`` value to inject, the extra headers to forward, and the credential to hand ``resolve_openapi_upstream_auth``, whose passthrough arm reads it via - ``_passthrough_token_from_mcp_auth_header``. The per-server Authorization travels only in the + ``passthrough_token_from_mcp_auth_header``. The per-server Authorization travels only in the credential, never also in the forwarded headers, because the resolver pops Authorization out of those and would otherwise have two sources to reconcile. """ @@ -1325,35 +1318,6 @@ def _client_forwarded_authorization_headers( return extra_headers -def _take_forwarded_authorization( - headers: dict[str, str] | None, -) -> tuple[str | None, dict[str, str] | None]: - """Pop the ``Authorization`` value out of ``headers`` (case-insensitive), returning it with the - remaining headers, so the passthrough resolver arm is the single Authorization source rather than - the header also riding in ``extra_headers`` (which the resolved auth would then defer to).""" - if not headers: - return None, headers - value: Final = next((v for k, v in headers.items() if k.lower() == "authorization"), None) - return value, without_header(headers, DEFAULT_CREDENTIAL_HEADER) - - -def _passthrough_token_from_mcp_auth_header( - mcp_auth_header: str | dict[str, str] | None, -) -> str | None: - """The caller's per-server upstream credential for a passthrough-mode server, or None. - - Sourced from ``x-mcp-{alias}-authorization`` (string or per-header dict form) or the deprecated - global ``x-mcp-auth`` fallback. Per-server headers are the multi-server shape: they bind one - token to one server, so an aggregate scope with several passthrough-mode servers never replays - a single credential across upstreams. The value is forwarded verbatim, so it must be the full - header value (e.g. ``Bearer ``).""" - if isinstance(mcp_auth_header, str): - return mcp_auth_header or None - if isinstance(mcp_auth_header, dict): - return next((v for k, v in mcp_auth_header.items() if k.lower() == "authorization"), None) - return None - - async def _materialize_auth_headers(auth: httpx2.Auth | None) -> dict[str, str] | None: """Extract the header a resolved ``httpx2.Auth`` would set, as a plain dict, or None. @@ -1418,25 +1382,6 @@ def _redacted_registry_dump(servers: dict[str, MCPServer]) -> dict[str, dict[str } -def _to_server_spec_fail_closed(server: MCPServer) -> ServerSpec | None: - """`to_server_spec`, except a half-configured `oauth2_id_jag` server refuses instead of deferring. - - ID-JAG has no v1 arm, so deferring to v1 would let `resolve_mcp_auth` honor a caller x-mcp-* - override or fall through to the static `authentication_token`, both of which bypass the per-user - identity assertion the mode promises. That is an operator misconfiguration, not a fallback. - """ - spec: Final = to_server_spec(server) - if spec is None and server.auth_type == MCPAuth.oauth2_id_jag: - raise_public( - CredError.of_misconfigured( - "oauth2_id_jag requires token_exchange_endpoint, id_jag_resource_token_endpoint, " - "client_id, and a client_secret or client_private_key; refusing to fall back to " - "a static credential." - ) - ) - return spec - - def _caller_authorization_fans_out( server: MCPServer, scope_servers: list[MCPServer] | None, @@ -4088,75 +4033,6 @@ class MCPServerManager: _write_user_env_vars_cache(user_id, server.server_id, values) return values - async def _resolve_v2_auth( - self, - *, - server: MCPServer, - spec: ServerSpec, - provider: UpstreamCredentialProvider, - subject_token: str | None, - user_api_key_auth: UserAPIKeyAuth | None, - extra_headers: dict[str, str] | None, - ) -> tuple[httpx2.Auth | None, dict[str, str] | None]: - """Resolve a v2-owned server's upstream credential into ``(resolved_auth, extra_headers)``. - - On a missing/rejected per-user credential this raises the mode's discovery challenge - (authorization_code's browser-OAuth 401, token_exchange's RFC 9728 challenge) or maps any - other ``CredError`` onto its public HTTP status; it never returns an error as a value. - """ - match await resolve_credentials_with_source(provider, to_subject(user_api_key_auth, subject_token), spec): - case Ok(credential): - auth: Final = credential.auth - # NoOpAuth has no header_name and so never conflicts. - header_name: Final[str | None] = getattr(auth, "header_name", None) - if header_name is None or not extra_headers: - source: Final = ( - AuthResolution.extra_headers - if credential.source == AuthResolution.no_auth and extra_headers - else credential.source - ) - record_auth_resolution(server.server_id, source) - return auth, extra_headers - if not has_header(extra_headers, header_name): - record_auth_resolution(server.server_id, credential.source) - return auth, extra_headers - if isinstance( - spec.config, - (TokenExchangeConfig, AuthorizationCodeConfig, IdJagConfig, ClientCredentialsConfig), - ): - # The resolver owns the credential here (token_exchange's exchanged token, - # authorization_code's stored token, id_jag's minted assertion, - # client_credentials' gateway-minted M2M token). It is authoritative: a - # guardrail such as MCPJWTSigner, static_headers, or any other injected - # Authorization must NOT shadow it (otherwise the upstream gets e.g. the - # signer's JWT instead of the minted token and rejects it, and for M2M the - # one-shot 401 refetch is lost with it). Drop only the header the resolved - # credential is about to occupy, so a static credential the operator aimed at a - # DIFFERENT header still reaches upstream. - record_auth_resolution(server.server_id, credential.source) - return auth, without_header(extra_headers, header_name) - # Other modes: an Authorization already supplied via extra_headers (a forwarded caller - # header or static_headers) is intentional and wins; v1 applies those last. - record_auth_resolution(server.server_id, AuthResolution.extra_headers) - return None, extra_headers - case Error(err): - record_auth_resolution(server.server_id, AuthResolution.failed) - if err.tag == "unauthorized" and isinstance(spec.config, AuthorizationCodeConfig): - # authorization_code's missing per-user token -> the per-server browser-OAuth - # challenge, built here where the full MCPServer is in hand. - raise_user_oauth_challenge(server, root_path=get_request_root_path()) - if err.tag == "unauthorized" and isinstance(spec.config, TokenExchangeConfig): - # token_exchange (OBO): a missing/rejected subject token -> the RFC 9728 challenge - # pointing at the IdP the client must SSO with to obtain one, rather than an opaque - # 401. No gateway-side browser flow. An IdP step-up rejection (Entra Conditional - # Access) threads its claims blob into the challenge for the client to satisfy. - raise_token_exchange_challenge( - server, - root_path=get_request_root_path(), - claims=err.unauthorized.claims, - ) - raise_public(err) - async def preflight_token_exchange( self, server: MCPServer, @@ -4196,7 +4072,7 @@ class MCPServerManager: case _: return resolved_server: Final = await self.ensure_oauth_metadata_discovered(server) - spec: Final = _to_server_spec_fail_closed(resolved_server) + spec: Final = to_server_spec_fail_closed(resolved_server) if spec is None or not isinstance(spec.config, (TokenExchangeConfig, IdJagConfig)): return if subject_token is None and isinstance(spec.config, TokenExchangeConfig): @@ -4226,202 +4102,33 @@ class MCPServerManager: client_ip: str | None = None, protocol_version_override: MCPUpstreamProtocol | None = None, ) -> MCPClient: - """ - Create an MCPClient instance for the given server. - - Auth resolution (single place for all auth logic): - 1. ``mcp_auth_header`` — per-request/per-user override - 2. OAuth2 Token Exchange (OBO) — exchange user token for scoped token - 3. OAuth2 client_credentials token — auto-fetched and cached - 4. ``server.authentication_token`` — static token from config/DB - - Args: - server: The server configuration. - mcp_auth_header: Optional per-request auth override. - extra_headers: Additional headers to forward. - stdio_env: Environment variables for stdio transport. - subject_token: Optional user JWT for token exchange (OBO) flow. - user_api_key_auth: Optional auth context for sampling callbacks. - - Returns: - Configured MCP client instance. - """ record_auth_resolution(server.server_id, AuthResolution.unresolved) resolved_server: Final = await self.ensure_oauth_metadata_discovered(server) - protocol_version: Final = ( - protocol_version_override if protocol_version_override is not None else resolved_server.protocol_version - ) - transport: Final = resolved_server.transport or MCPTransport.sse - spec = None if transport == MCPTransport.stdio else _to_server_spec_fail_closed(resolved_server) - provider: Final = cred_provider or self._cred_provider - # A caller-supplied per-request override (mcp_auth_header / x-mcp-*) defers to the v1 path - # so it wins - except for the modes the v2 resolver owns per-caller (authorization_code's - # stored token, token_exchange's RFC 8693 minted token, id_jag's minted assertion, and the - # passthrough modes' forwarded caller token). A caller must not be able to substitute another - # user's stored credential, nor silently disable the OBO / ID-JAG exchange and forward an - # arbitrary bearer upstream, so we keep the v2 spec and ignore the override for these; the - # REST tools preview supplies its not-yet-persisted token through the resolver - # (cred_provider), never this path. - if ( - spec is not None - and mcp_auth_header - and not isinstance( - spec.config, - (AuthorizationCodeConfig, IdJagConfig, PassthroughConfig, TokenExchangeConfig), - ) - ): - spec = None - auth_value: Final = await resolve_mcp_auth(resolved_server, mcp_auth_header) if spec is None else None - auth_header_name: Final = resolved_token_header(resolved_server, mcp_auth_header) if spec is None else None - - # Create sampling and elicitation callbacks for this client - sampling_cb = ( - _create_sampling_callback( - operation_context=OperationContext( - _caller=user_api_key_auth, raw_headers=raw_headers, client_ip=client_ip - ) - ) - if resolved_server.allow_sampling - else None - ) - elicitation_cb: Final = _create_elicitation_callback() if resolved_server.allow_elicitation else None - - # Handle stdio transport - if transport == MCPTransport.stdio: - if not is_mcp_stdio_enabled(): - raise HTTPException(status_code=403, detail=MCP_STDIO_DISABLED_MESSAGE) - resolved_env: Final = ( - stdio_env - if stdio_env is not None - else (dict(resolved_server.env) if resolved_server.env is not None else None) - ) - - # Ensure npm-based STDIO MCP servers have a writable cache dir. - # In containers the default (~/.npm or /app/.npm) may not exist - # or be read-only, causing npx to fail with ENOENT. - if resolved_env is not None and "NPM_CONFIG_CACHE" not in resolved_env: - resolved_env["NPM_CONFIG_CACHE"] = MCP_NPM_CACHE_DIR - # Defense-in-depth: block commands not in the allowlist. - # The Pydantic validator blocks new servers; this catches legacy - # config/DB records predating the allowlist. - if resolved_server.command: - base_command: Final = os.path.basename(resolved_server.command) - # Strip .exe/.cmd/.bat/.com suffix for Windows compatibility - base_command_no_ext = base_command.lower() - for ext in [".exe", ".cmd", ".bat", ".com"]: - if base_command.lower().endswith(ext): - base_command_no_ext = base_command[: -len(ext)].lower() - break - if ( - base_command.lower() not in MCP_STDIO_ALLOWED_COMMANDS - and base_command_no_ext not in MCP_STDIO_ALLOWED_COMMANDS - ): - raise HTTPException( - status_code=403, - detail=f"MCP stdio command '{resolved_server.command}' is not in the allowlist ({sorted(MCP_STDIO_ALLOWED_COMMANDS)}). " - f"Add it to LITELLM_MCP_STDIO_EXTRA_COMMANDS to allow this command.", + return await prepare_upstream_client( + resolved_server, + provider=cred_provider or self._cred_provider, + root_path=get_request_root_path(), + mcp_auth_header=mcp_auth_header, + extra_headers=extra_headers, + stdio_env=stdio_env, + subject_token=subject_token, + user_api_key_auth=user_api_key_auth, + protocol_version=( + protocol_version_override if protocol_version_override is not None else resolved_server.protocol_version + ), + sampling_callback=( + _create_sampling_callback( + operation_context=OperationContext( + _caller=user_api_key_auth, + raw_headers=raw_headers, + client_ip=client_ip, ) - - stdio_config: MCPStdioConfig | None = None - if resolved_server.command and resolved_server.args is not None: - stdio_config = MCPStdioConfig( - command=resolved_server.command, - args=resolved_server.args, - env=resolved_env, ) - - record_auth_resolution(server.server_id, AuthResolution.not_applicable) - return MCPClient( - server_url="", # Not used for stdio - transport_type=transport, - protocol_version=protocol_version, - auth_type=resolved_server.auth_type, - auth_value=auth_value, - timeout=(resolved_server.timeout if resolved_server.timeout is not None else MCP_CLIENT_TIMEOUT), - stdio_config=stdio_config, - extra_headers=extra_headers, - sampling_callback=sampling_cb, - elicitation_callback=elicitation_cb, - ) - else: - # For HTTP/SSE transports - server_url: Final = resolved_server.url or "" - - if spec is not None: - inbound_token = subject_token - if isinstance(spec.config, PassthroughConfig): - inbound_token, extra_headers = _take_forwarded_authorization(extra_headers) - per_server_token: Final = _passthrough_token_from_mcp_auth_header(mcp_auth_header) - if per_server_token is not None: - inbound_token = per_server_token - resolved_auth, extra_headers = await self._resolve_v2_auth( - server=resolved_server, - spec=spec, - provider=provider, - subject_token=inbound_token, - user_api_key_auth=user_api_key_auth, - extra_headers=extra_headers, - ) - return await prepare_mcp_client( - resolved_server, - MCPClient( - server_url=server_url, - transport_type=transport, - protocol_version=protocol_version, - auth_type=resolved_server.auth_type, - timeout=( - resolved_server.timeout if resolved_server.timeout is not None else MCP_CLIENT_TIMEOUT - ), - extra_headers=extra_headers, - resolved_auth=resolved_auth, - sampling_callback=sampling_cb, - elicitation_callback=elicitation_cb, - ), - ) - - # Create SigV4 auth if configured - aws_auth = None - if resolved_server.auth_type == MCPAuth.aws_sigv4: - aws_auth = MCPSigV4Auth( - aws_access_key_id=resolved_server.aws_access_key_id, - aws_secret_access_key=resolved_server.aws_secret_access_key, - aws_session_token=resolved_server.aws_session_token, - aws_region_name=resolved_server.aws_region_name, - aws_service_name=resolved_server.aws_service_name, - aws_role_name=resolved_server.aws_role_name, - aws_session_name=resolved_server.aws_session_name, - ) - - legacy_source: Final = ( - AuthResolution.aws_sigv4 - if aws_auth is not None - else AuthResolution.extra_headers - if extra_headers and has_header(extra_headers, auth_header_name or "Authorization") - else AuthResolution.per_request_header - if mcp_auth_header - else AuthResolution.static_token - if auth_value - else AuthResolution.extra_headers - if extra_headers - else AuthResolution.no_auth - ) - record_auth_resolution(server.server_id, legacy_source) - return await prepare_mcp_client( - resolved_server, - MCPClient( - server_url=server_url, - transport_type=transport, - protocol_version=protocol_version, - auth_type=resolved_server.auth_type, - auth_value=auth_value, - auth_header_name=auth_header_name, - timeout=(resolved_server.timeout if resolved_server.timeout is not None else MCP_CLIENT_TIMEOUT), - extra_headers=extra_headers, - aws_auth=aws_auth, - sampling_callback=sampling_cb, - elicitation_callback=elicitation_cb, - ), - ) + if resolved_server.allow_sampling + else None + ), + elicitation_callback=(_create_elicitation_callback() if resolved_server.allow_elicitation else None), + ) async def _get_tools_from_server( self, @@ -6457,7 +6164,7 @@ class MCPServerManager: token, token_exchange's exchanged token, passthrough's forwarded caller token) must be materialized into headers here. Returns ``(resolved_auth_headers, forwarded_headers)``: the resolved headers are authoritative over every other Authorization source (the same - rule ``_resolve_v2_auth`` applies on the MCPClient path) and ``forwarded_headers`` comes + rule ``resolve_upstream_auth`` applies on the MCPClient path) and ``forwarded_headers`` comes back with any header the resolver claimed already dropped. Unmigrated (v1) servers resolve through the stored-token lookup instead, and a missing per-user credential raises the same discovery challenge the MCPClient path serves, rather than egressing unauthenticated. @@ -6480,11 +6187,12 @@ class MCPServerManager: if isinstance(spec.config, (TokenExchangeConfig, IdJagConfig)): subject_token = self._extract_subject_token(oauth2_headers, raw_headers, user_api_key_auth) elif isinstance(spec.config, PassthroughConfig): - inbound_token, forwarded_headers = _take_forwarded_authorization(forwarded_headers) - per_server_token: Final = _passthrough_token_from_mcp_auth_header(mcp_auth_header) + inbound_token, forwarded_headers = take_forwarded_authorization(forwarded_headers) + per_server_token: Final = passthrough_token_from_mcp_auth_header(mcp_auth_header) subject_token = per_server_token if per_server_token is not None else inbound_token - resolved_auth, forwarded_headers = await self._resolve_v2_auth( + resolved_auth, forwarded_headers = await resolve_upstream_auth( + root_path=get_request_root_path(), server=mcp_server, spec=spec, provider=self._cred_provider, diff --git a/litellm/proxy/_experimental/mcp_server/server_resolution.py b/litellm/proxy/_experimental/mcp_server/server_resolution.py index 8168fea9068..54fd17fa280 100644 --- a/litellm/proxy/_experimental/mcp_server/server_resolution.py +++ b/litellm/proxy/_experimental/mcp_server/server_resolution.py @@ -120,3 +120,42 @@ async def authorize_mcp_server( ) return resolved + + +@dataclass(frozen=True, slots=True) +class MCPServerTargetCatalog: + manager: MCPServerRegistry + db_lookup: Callable[[str], Awaitable[LiteLLM_MCPServerTable | None]] | None = None + temp_lookup: Callable[[str], Awaitable[MCPServer | None]] | None = None + id_client_ip: str | None = None + name_client_ip: str | None = None + match_name: bool = False + + async def resolve( + self, + server_id: str, + caller: UserAPIKeyAuth, + *, + is_admin_view: bool, + not_found_detail: Mapping[str, str], + forbidden_detail: Mapping[str, str], + non_admin_missing: Literal["not_found", "forbidden"], + ) -> ResolvedMCPServer: + resolved: Final = await resolve_mcp_server( + server_id, + manager=self.manager, + db_lookup=self.db_lookup, + temp_lookup=self.temp_lookup, + id_client_ip=self.id_client_ip, + name_client_ip=self.name_client_ip, + match_name=self.match_name, + ) + return await authorize_mcp_server( + resolved, + caller, + manager=self.manager, + is_admin_view=is_admin_view, + not_found_detail=not_found_detail, + forbidden_detail=forbidden_detail, + non_admin_missing=non_admin_missing, + ) diff --git a/litellm/proxy/_experimental/mcp_server/upstream.py b/litellm/proxy/_experimental/mcp_server/upstream.py new file mode 100644 index 00000000000..89f433093e6 --- /dev/null +++ b/litellm/proxy/_experimental/mcp_server/upstream.py @@ -0,0 +1,315 @@ +from __future__ import annotations + +import os +from typing import Final + +import httpx2 +from fastapi import HTTPException + +from litellm.constants import MCP_CLIENT_TIMEOUT, MCP_NPM_CACHE_DIR, MCP_STDIO_ALLOWED_COMMANDS +from litellm.experimental_mcp_client.client import MCPClient, MCPSigV4Auth +from litellm.proxy._experimental.mcp_server.legacy_callbacks import ElicitationCallback, SamplingCallback +from litellm.proxy._experimental.mcp_server.mcp_debug import record_auth_resolution +from litellm.proxy._experimental.mcp_server.oauth2_token_cache import resolve_mcp_auth, resolved_token_header +from litellm.proxy._experimental.mcp_server.outbound_credentials import Error, Ok, UpstreamCredentialProvider +from litellm.proxy._experimental.mcp_server.outbound_credentials.adapter import ( + prepare_mcp_client, + raise_public, + raise_token_exchange_challenge, + raise_user_oauth_challenge, + to_server_spec, + to_subject, +) +from litellm.proxy._experimental.mcp_server.outbound_credentials.resolver import resolve_credentials_with_source +from litellm.proxy._experimental.mcp_server.outbound_credentials.types import ( + DEFAULT_CREDENTIAL_HEADER, + AuthorizationCodeConfig, + AuthResolution, + ClientCredentialsConfig, + CredError, + IdJagConfig, + PassthroughConfig, + ServerSpec, + TokenExchangeConfig, +) +from litellm.proxy._experimental.mcp_server.stdio_gate import MCP_STDIO_DISABLED_MESSAGE, is_mcp_stdio_enabled +from litellm.proxy._types import MCPTransport, UserAPIKeyAuth +from litellm.types.mcp import ( + MCPAuth, + MCPStdioConfig, + MCPTransportType, + MCPUpstreamProtocol, + has_header, + without_header, +) +from litellm.types.mcp_server.mcp_server_manager import MCPServer + + +def take_forwarded_authorization( + headers: dict[str, str] | None, +) -> tuple[str | None, dict[str, str] | None]: + """Pop the ``Authorization`` value out of ``headers`` (case-insensitive), returning it with the + remaining headers, so the passthrough resolver arm is the single Authorization source rather than + the header also riding in ``extra_headers`` (which the resolved auth would then defer to).""" + if not headers: + return None, headers + value: Final = next((v for k, v in headers.items() if k.lower() == "authorization"), None) + return value, without_header(headers, DEFAULT_CREDENTIAL_HEADER) + + +def passthrough_token_from_mcp_auth_header( + mcp_auth_header: str | dict[str, str] | None, +) -> str | None: + """The caller's per-server upstream credential for a passthrough-mode server, or None. + + Sourced from ``x-mcp-{alias}-authorization`` (string or per-header dict form) or the deprecated + global ``x-mcp-auth`` fallback. Per-server headers are the multi-server shape: they bind one + token to one server, so an aggregate scope with several passthrough-mode servers never replays + a single credential across upstreams. The value is forwarded verbatim, so it must be the full + header value (e.g. ``Bearer ``).""" + if isinstance(mcp_auth_header, str): + return mcp_auth_header or None + if isinstance(mcp_auth_header, dict): + return next((v for k, v in mcp_auth_header.items() if k.lower() == "authorization"), None) + return None + + +def to_server_spec_fail_closed(server: MCPServer) -> ServerSpec | None: + """`to_server_spec`, except a half-configured `oauth2_id_jag` server refuses instead of deferring. + + ID-JAG has no v1 arm, so deferring to v1 would let `resolve_mcp_auth` honor a caller x-mcp-* + override or fall through to the static `authentication_token`, both of which bypass the per-user + identity assertion the mode promises. That is an operator misconfiguration, not a fallback. + """ + spec: Final = to_server_spec(server) + if spec is None and server.auth_type == MCPAuth.oauth2_id_jag: + raise_public( + CredError.of_misconfigured( + "oauth2_id_jag requires token_exchange_endpoint, id_jag_resource_token_endpoint, " + "client_id, and a client_secret or client_private_key; refusing to fall back to " + "a static credential." + ) + ) + return spec + + +async def resolve_upstream_auth( + *, + server: MCPServer, + spec: ServerSpec, + root_path: str, + provider: UpstreamCredentialProvider, + subject_token: str | None, + user_api_key_auth: UserAPIKeyAuth | None, + extra_headers: dict[str, str] | None, +) -> tuple[httpx2.Auth | None, dict[str, str] | None]: + """Resolve a v2-owned server's upstream credential into ``(resolved_auth, extra_headers)``. + + On a missing/rejected per-user credential this raises the mode's discovery challenge + (authorization_code's browser-OAuth 401, token_exchange's RFC 9728 challenge) or maps any + other ``CredError`` onto its public HTTP status; it never returns an error as a value. + """ + match await resolve_credentials_with_source(provider, to_subject(user_api_key_auth, subject_token), spec): + case Ok(credential): + auth: Final = credential.auth + # NoOpAuth has no header_name and so never conflicts. + header_name: Final[str | None] = getattr(auth, "header_name", None) + if header_name is None or not extra_headers: + source: Final = ( + AuthResolution.extra_headers + if credential.source == AuthResolution.no_auth and extra_headers + else credential.source + ) + record_auth_resolution(server.server_id, source) + return auth, extra_headers + if not has_header(extra_headers, header_name): + record_auth_resolution(server.server_id, credential.source) + return auth, extra_headers + if isinstance( + spec.config, + (TokenExchangeConfig, AuthorizationCodeConfig, IdJagConfig, ClientCredentialsConfig), + ): + # The resolver owns the credential here (token_exchange's exchanged token, + # authorization_code's stored token, id_jag's minted assertion, + # client_credentials' gateway-minted M2M token). It is authoritative: a + # guardrail such as MCPJWTSigner, static_headers, or any other injected + # Authorization must NOT shadow it (otherwise the upstream gets e.g. the + # signer's JWT instead of the minted token and rejects it, and for M2M the + # one-shot 401 refetch is lost with it). Drop only the header the resolved + # credential is about to occupy, so a static credential the operator aimed at a + # DIFFERENT header still reaches upstream. + record_auth_resolution(server.server_id, credential.source) + return auth, without_header(extra_headers, header_name) + # Other modes: an Authorization already supplied via extra_headers (a forwarded caller + # header or static_headers) is intentional and wins; v1 applies those last. + record_auth_resolution(server.server_id, AuthResolution.extra_headers) + return None, extra_headers + case Error(err): + record_auth_resolution(server.server_id, AuthResolution.failed) + if err.tag == "unauthorized" and isinstance(spec.config, AuthorizationCodeConfig): + # authorization_code's missing per-user token -> the per-server browser-OAuth + # challenge, built here where the full MCPServer is in hand. + raise_user_oauth_challenge(server, root_path=root_path) + if err.tag == "unauthorized" and isinstance(spec.config, TokenExchangeConfig): + # token_exchange (OBO): a missing/rejected subject token -> the RFC 9728 challenge + # pointing at the IdP the client must SSO with to obtain one, rather than an opaque + # 401. No gateway-side browser flow. An IdP step-up rejection (Entra Conditional + # Access) threads its claims blob into the challenge for the client to satisfy. + raise_token_exchange_challenge( + server, + root_path=root_path, + claims=err.unauthorized.claims, + ) + raise_public(err) + + +def _stdio_config(server: MCPServer, stdio_env: dict[str, str] | None) -> MCPStdioConfig | None: + if not is_mcp_stdio_enabled(): + raise HTTPException(status_code=403, detail=MCP_STDIO_DISABLED_MESSAGE) + if server.command: + command: Final = os.path.basename(server.command) + lowercase: Final = command.lower() + normalized: Final = next( + ( + lowercase.removesuffix(suffix) + for suffix in (".exe", ".cmd", ".bat", ".com") + if lowercase.endswith(suffix) + ), + lowercase, + ) + if command not in MCP_STDIO_ALLOWED_COMMANDS and normalized not in MCP_STDIO_ALLOWED_COMMANDS: + raise HTTPException( + status_code=403, + detail=f"MCP stdio command '{server.command}' is not in the allowlist ({sorted(MCP_STDIO_ALLOWED_COMMANDS)}). " + "Add it to LITELLM_MCP_STDIO_EXTRA_COMMANDS to allow this command.", + ) + if not server.command or server.args is None: + return None + environment: Final = stdio_env if stdio_env is not None else server.env + return MCPStdioConfig( + command=server.command, + args=server.args, + env={"NPM_CONFIG_CACHE": MCP_NPM_CACHE_DIR, **environment} if environment is not None else None, + ) + + +async def prepare_upstream_client( + server: MCPServer, + *, + provider: UpstreamCredentialProvider, + root_path: str, + mcp_auth_header: str | dict[str, str] | None = None, + extra_headers: dict[str, str] | None = None, + stdio_env: dict[str, str] | None = None, + subject_token: str | None = None, + user_api_key_auth: UserAPIKeyAuth | None = None, + protocol_version: MCPUpstreamProtocol, + sampling_callback: SamplingCallback | None = None, + elicitation_callback: ElicitationCallback | None = None, +) -> MCPClient: + transport: Final[MCPTransportType] = server.transport or MCPTransport.sse + server_spec: Final = None if transport == MCPTransport.stdio else to_server_spec_fail_closed(server) + spec: Final = ( + None + if server_spec is not None + and mcp_auth_header + and not isinstance( + server_spec.config, (AuthorizationCodeConfig, IdJagConfig, PassthroughConfig, TokenExchangeConfig) + ) + else server_spec + ) + auth_value: Final = await resolve_mcp_auth(server, mcp_auth_header) if spec is None else None + auth_header_name: Final = resolved_token_header(server, mcp_auth_header) if spec is None else None + timeout: Final = server.timeout if server.timeout is not None else MCP_CLIENT_TIMEOUT + if transport == MCPTransport.stdio: + config: Final = _stdio_config(server, stdio_env) + record_auth_resolution(server.server_id, AuthResolution.not_applicable) + return MCPClient( + server_url="", + transport_type=transport, + protocol_version=protocol_version, + auth_type=server.auth_type, + auth_value=auth_value, + timeout=timeout, + stdio_config=config, + extra_headers=extra_headers, + sampling_callback=sampling_callback, + elicitation_callback=elicitation_callback, + ) + if spec is not None: + inbound_token, forwarded_headers = ( + take_forwarded_authorization(extra_headers) + if isinstance(spec.config, PassthroughConfig) + else (subject_token, extra_headers) + ) + per_server_token: Final = ( + passthrough_token_from_mcp_auth_header(mcp_auth_header) + if isinstance(spec.config, PassthroughConfig) + else None + ) + resolved_auth, resolved_headers = await resolve_upstream_auth( + server=server, + spec=spec, + provider=provider, + root_path=root_path, + subject_token=per_server_token if per_server_token is not None else inbound_token, + user_api_key_auth=user_api_key_auth, + extra_headers=forwarded_headers, + ) + return await prepare_mcp_client( + server, + MCPClient( + server_url=server.url or "", + transport_type=transport, + protocol_version=protocol_version, + auth_type=server.auth_type, + timeout=timeout, + extra_headers=resolved_headers, + resolved_auth=resolved_auth, + sampling_callback=sampling_callback, + elicitation_callback=elicitation_callback, + ), + ) + aws_auth: Final = ( + MCPSigV4Auth( + aws_access_key_id=server.aws_access_key_id, + aws_secret_access_key=server.aws_secret_access_key, + aws_session_token=server.aws_session_token, + aws_region_name=server.aws_region_name, + aws_service_name=server.aws_service_name, + aws_role_name=server.aws_role_name, + aws_session_name=server.aws_session_name, + ) + if server.auth_type == MCPAuth.aws_sigv4 + else None + ) + source: Final = ( + AuthResolution.aws_sigv4 + if aws_auth is not None + else AuthResolution.extra_headers + if extra_headers and has_header(extra_headers, auth_header_name or "Authorization") + else AuthResolution.per_request_header + if mcp_auth_header + else AuthResolution.static_token + if auth_value + else AuthResolution.extra_headers + if extra_headers + else AuthResolution.no_auth + ) + record_auth_resolution(server.server_id, source) + return await prepare_mcp_client( + server, + MCPClient( + server_url=server.url or "", + transport_type=transport, + protocol_version=protocol_version, + auth_type=server.auth_type, + auth_value=auth_value, + auth_header_name=auth_header_name, + timeout=timeout, + extra_headers=extra_headers, + aws_auth=aws_auth, + sampling_callback=sampling_callback, + elicitation_callback=elicitation_callback, + ), + ) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index da69b77286e..2084f6b6ee3 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -16,6 +16,7 @@ from pydantic import ( JsonValue, PositiveInt, PrivateAttr, + TypeAdapter, field_validator, model_validator, ) @@ -46,6 +47,8 @@ from litellm.types.mcp import ( MCPCredentials, MCPTransport, MCPTransportType, + MCPUpstreamProtocol, + validate_mcp_protocol_transport, ) from litellm.types.mcp_server.mcp_server_manager import MCPInfo from litellm.types.proxy.agent_identity import ManagedAgentContext @@ -1673,6 +1676,16 @@ class NewMCPServerRequest(LiteLLMPydanticObjectBase): description="Server-managed: set by the endpoint; caller values are overridden.", ) + @model_validator(mode="after") + def validate_protocol_transport(self) -> "NewMCPServerRequest": + validate_mcp_protocol_transport( + TypeAdapter[MCPUpstreamProtocol](MCPUpstreamProtocol).validate_python( + (self.mcp_info or {}).get("protocol_version", "auto") + ), + self.transport, + ) + return self + @model_validator(mode="before") @classmethod def validate_transport_fields(cls, values): @@ -1756,6 +1769,18 @@ class UpdateMCPServerRequest(LiteLLMPydanticObjectBase): timeout: float | None = None max_concurrent_requests: int | None = None + @model_validator(mode="after") + def validate_protocol_transport(self) -> "UpdateMCPServerRequest": + if not {"transport", "mcp_info"}.issubset(self.model_fields_set): + return self + validate_mcp_protocol_transport( + TypeAdapter[MCPUpstreamProtocol](MCPUpstreamProtocol).validate_python( + (self.mcp_info or {}).get("protocol_version", "auto") + ), + self.transport, + ) + return self + @model_validator(mode="before") @classmethod def validate_transport_fields(cls, values): diff --git a/litellm/proxy/management_endpoints/mcp_management_endpoints.py b/litellm/proxy/management_endpoints/mcp_management_endpoints.py index 8ed8ec8752e..0d358302fa2 100644 --- a/litellm/proxy/management_endpoints/mcp_management_endpoints.py +++ b/litellm/proxy/management_endpoints/mcp_management_endpoints.py @@ -44,6 +44,7 @@ from fastapi import ( status, ) from fastapi.responses import JSONResponse +from pydantic import TypeAdapter from typing_extensions import ReadOnly, TypedDict try: @@ -137,6 +138,7 @@ if MCP_AVAILABLE: def validate_tool_name(name: str) -> _ToolNameValidationResult: return _ToolNameValidationResult() + from litellm.proxy._experimental.mcp_server.contracts import TargetCatalog from litellm.proxy._experimental.mcp_server.db import ( McpIdentifierConflict, approve_mcp_server, @@ -178,6 +180,7 @@ if MCP_AVAILABLE: global_mcp_server_manager, ) from litellm.proxy._experimental.mcp_server.server_resolution import ( + MCPServerTargetCatalog, authorize_mcp_server, resolve_mcp_server, ) @@ -237,7 +240,9 @@ if MCP_AVAILABLE: MCPCredentials, MCPGatewaySessionsResponse, MCPGatewaySessionsTerminateResponse, + MCPUpstreamProtocol, normalize_upstream_header_name, + validate_mcp_protocol_transport, ) from litellm.types.mcp_server.mcp_server_manager import MCPServer, PinnedMCPTool @@ -2185,18 +2190,16 @@ if MCP_AVAILABLE: from litellm.proxy.auth.ip_address_utils import IPAddressUtils client_ip: Final = IPAddressUtils.get_mcp_client_ip(request) if request is not None else None - resolved: Final = await resolve_mcp_server( - server_id, + catalog: Final[TargetCatalog] = MCPServerTargetCatalog( manager=global_mcp_server_manager, temp_lookup=get_cached_temporary_mcp_server, id_client_ip=None, name_client_ip=client_ip, match_name=True, ) - authorized: Final = await authorize_mcp_server( - resolved, + authorized: Final = await catalog.resolve( + server_id, user_api_key_dict, - manager=global_mcp_server_manager, is_admin_view=_user_has_admin_view(user_api_key_dict), not_found_detail={"error": f"MCP server {server_id} not found"}, forbidden_detail={"error": f"Access denied to MCP server {server_id}"}, @@ -2739,15 +2742,13 @@ if MCP_AVAILABLE: 404, so server ids can't be enumerated), using the same allowed-server resolution the MCP gateway enforces on tool calls. """ - resolved: Final = await resolve_mcp_server( - server_id, + catalog: Final[TargetCatalog] = MCPServerTargetCatalog( manager=global_mcp_server_manager, db_lookup=lambda sid: get_mcp_server(prisma_client, sid), ) - authorized: Final = await authorize_mcp_server( - resolved, + authorized: Final = await catalog.resolve( + server_id, user_api_key_dict, - manager=global_mcp_server_manager, is_admin_view=_user_has_admin_view(user_api_key_dict), not_found_detail={"error": f"MCP Server {server_id} not found"}, forbidden_detail={ @@ -2938,6 +2939,32 @@ if MCP_AVAILABLE: statuses.append(status_obj) return statuses + def _validate_mcp_protocol_update( + payload: UpdateMCPServerRequest, + fields_set: set[str], + stored: LiteLLM_MCPServerTable | None, + read_failed: bool, + ) -> None: + if not {"transport", "mcp_info"}.intersection(fields_set): + return + if read_failed: + raise HTTPException( + status_code=503, detail="Cannot validate MCP configuration while stored state is unavailable" + ) + if stored is None: + return + effective_transport: Final = payload.transport if "transport" in fields_set else stored.transport + effective_info: Final = payload.mcp_info if "mcp_info" in fields_set else stored.mcp_info + try: + validate_mcp_protocol_transport( + TypeAdapter[MCPUpstreamProtocol](MCPUpstreamProtocol).validate_python( + (effective_info or {}).get("protocol_version", "auto") + ), + effective_transport, + ) + except ValueError as exc: + raise HTTPException(status_code=400, detail=str(exc)) from exc + @router.put( "/server", description="Allows deleting mcp serves in the db", @@ -2986,9 +3013,9 @@ if MCP_AVAILABLE: }, ) - # Snapshot the pre-update identity so we can detect a mint-relevant change below. The read is - # advisory (it only feeds the stale-token purge decision), so a failure skips the purge with a - # warning instead of failing the edit, whose primary job is the update itself. + # Snapshot stored configuration for protocol validation and mint-relevant changes below. + # Protocol or transport edits require this read; other edits may continue on read failure + # while skipping the best-effort stale-token purge. try: old_server_record = await get_mcp_server(prisma_client, payload.server_id) old_server_record_read_failed = False @@ -3001,6 +3028,8 @@ if MCP_AVAILABLE: old_server_record = None old_server_record_read_failed = True + _validate_mcp_protocol_update(payload, payload_fields_set, old_server_record, old_server_record_read_failed) + if payload.per_server_oauth_discovery and (old_server_record is not None or old_server_record_read_failed): relay_eligible: Final = old_server_record is not None and is_per_server_oauth_discovery_eligible( payload.auth_type if "auth_type" in payload_fields_set else old_server_record.auth_type, diff --git a/litellm/types/mcp.py b/litellm/types/mcp.py index fec5e84c8df..da7401e2a2e 100644 --- a/litellm/types/mcp.py +++ b/litellm/types/mcp.py @@ -63,7 +63,14 @@ DEFAULT_SUBJECT_TOKEN_TYPE: Final = "urn:ietf:params:oauth:token-type:access_tok MCPTransportType = Literal[MCPTransport.sse, MCPTransport.http, MCPTransport.stdio] MCPLegacyVersion = Literal["2024-11-05", "2025-03-26", "2025-06-18", "2025-11-25"] MCP_LEGACY_VERSIONS: Final[tuple[MCPLegacyVersion, ...]] = ("2024-11-05", "2025-03-26", "2025-06-18", "2025-11-25") -MCPUpstreamProtocol = MCPLegacyVersion | Literal["auto"] +MCPUpstreamProtocol = MCPLegacyVersion | Literal["auto", "2026-07-28"] + + +def validate_mcp_protocol_transport(protocol_version: MCPUpstreamProtocol, transport: MCPTransportType) -> None: + if protocol_version == "2026-07-28" and transport == MCPTransport.sse: + raise ValueError("Modern MCP requires HTTP or stdio transport") + + MCPAdvertisedVersions = Annotated[tuple[MCPLegacyVersion, ...], Field(min_length=1)] MCPSpecVersionType = Literal[ MCPSpecVersion.nov_2024, diff --git a/litellm/types/mcp_server/mcp_server_manager.py b/litellm/types/mcp_server/mcp_server_manager.py index 91ae95eff48..2ee19b3e59a 100644 --- a/litellm/types/mcp_server/mcp_server_manager.py +++ b/litellm/types/mcp_server/mcp_server_manager.py @@ -13,13 +13,14 @@ from litellm.types.mcp import ( MCPTransportType, MCPUpstreamProtocol, normalize_upstream_header_name, + validate_mcp_protocol_transport, ) # MCPInfo now allows arbitrary additional fields for custom metadata def _validate_mcp_protocol_metadata(value: dict[str, object]) -> dict[str, object]: if "protocol_version" in value: - TypeAdapter(MCPUpstreamProtocol).validate_python(value["protocol_version"]) + TypeAdapter[MCPUpstreamProtocol](MCPUpstreamProtocol).validate_python(value["protocol_version"]) return value @@ -277,9 +278,10 @@ class MCPServer(BaseModel): @model_validator(mode="after") def resolve_protocol_version(self) -> Self: if "protocol_version" not in self.model_fields_set and self.mcp_info is not None: - self.protocol_version = TypeAdapter(MCPUpstreamProtocol).validate_python( + self.protocol_version = TypeAdapter[MCPUpstreamProtocol](MCPUpstreamProtocol).validate_python( self.mcp_info.get("protocol_version", "auto") ) + validate_mcp_protocol_transport(self.protocol_version, self.transport) return self @model_validator(mode="after") diff --git a/tests/integration/mcp/test_mcp_management.py b/tests/integration/mcp/test_mcp_management.py index 67cdbbff5a4..bef882eae31 100644 --- a/tests/integration/mcp/test_mcp_management.py +++ b/tests/integration/mcp/test_mcp_management.py @@ -390,3 +390,97 @@ def test_ui_session_lists_and_fetches_team_granted_config_server( assert detail.status_code == 200, f"Team-granted server detail access should succeed: {detail.text}" assert detail.json()["server_id"] == server_id, detail.text assert detail.json()["alias"] == alias, detail.text + + +@pytest.mark.parametrize("explicit_transport", [False, True]) +def test_modern_sse_registration_rejected_without_saving(gateway: Gateway, explicit_transport: bool) -> None: + identity: Final = str(uuid.uuid4()) + with mcp_peer() as peer: + response: Final = gateway.request("POST", "/v1/mcp/server", { + "server_id": identity, "server_name": "invalid" + uuid.uuid4().hex[:8], + "url": peer.url, "mcp_info": {"protocol_version": "2026-07-28"}, + **({"transport": "sse"} if explicit_transport else {}), + }) + try: + assert response.status_code == 422, response.text + assert "Modern MCP requires HTTP or stdio" in response.text + assert identity not in _servers(gateway) + assert peer.drain() == (), "Rejected configuration reached upstream" + finally: + if response.status_code == 201: + delete_mcp(gateway, identity) + + +def test_protocol_transport_updates_validate_effective_configuration(gateway: Gateway) -> None: + from integration._support.database import read_rows + + with mcp_peer() as peer, gateway.scenario() as scenario: + identity: Final = register_mcp(scenario, peer, "protocol" + uuid.uuid4().hex[:8]) + modern: Final = gateway.request("PUT", "/v1/mcp/server", { + "server_id": identity, "mcp_info": {"protocol_version": "2026-07-28"}, + }) + assert modern.status_code == 202, modern.text + assert modern.json()["transport"] == "http" + changed: Final = gateway.request("PUT", "/v1/mcp/server", {"server_id": identity, "description": "renamed"}) + assert changed.status_code == 202, changed.text + assert changed.json()["mcp_info"]["protocol_version"] == "2026-07-28" + snapshot: Final = read_rows('SELECT transport, mcp_info, updated_at::text FROM "LiteLLM_MCPServerTable" WHERE server_id=%s', (identity,)) + peer.drain() + rejected: Final = gateway.request("PUT", "/v1/mcp/server", {"server_id": identity, "transport": "sse", "url": peer.url}) + assert rejected.status_code == 400, rejected.text + assert "Modern MCP requires HTTP or stdio" in rejected.text + assert read_rows('SELECT transport, mcp_info, updated_at::text FROM "LiteLLM_MCPServerTable" WHERE server_id=%s', (identity,)) == snapshot + assert tool_calls(peer.drain()) == () + key: Final = scenario.key(object_permission={"mcp_servers": [identity]}) + assert call_tool(gateway, key, identity, "add", ADD).status_code == 200 + + legacy: Final = gateway.request("PUT", "/v1/mcp/server", {"server_id": identity, "transport": "sse", "url": peer.url, "mcp_info": {}}) + assert legacy.status_code == 202, legacy.text + legacy_snapshot: Final = read_rows('SELECT transport, mcp_info, updated_at::text FROM "LiteLLM_MCPServerTable" WHERE server_id=%s', (identity,)) + rejected_protocol: Final = gateway.request("PUT", "/v1/mcp/server", {"server_id": identity, "mcp_info": {"protocol_version": "2026-07-28"}}) + assert rejected_protocol.status_code == 400, rejected_protocol.text + assert read_rows('SELECT transport, mcp_info, updated_at::text FROM "LiteLLM_MCPServerTable" WHERE server_id=%s', (identity,)) == legacy_snapshot + repaired: Final = gateway.request("PUT", "/v1/mcp/server", {"server_id": identity, "transport": "http", "url": peer.url, "mcp_info": {"protocol_version": "2026-07-28"}}) + assert repaired.status_code == 202, repaired.text + assert call_tool(gateway, key, identity, "add", ADD).status_code == 200 + + +@pytest.mark.parametrize("rename", [False, True]) +def test_concurrent_protocol_transport_edits_cannot_save_incompatible_configuration( + gateway: Gateway, peer: Gateway, rename: bool +) -> None: + import os + from concurrent.futures import ThreadPoolExecutor + + import psycopg + from integration._support.database import read_rows + + with mcp_peer() as upstream, gateway.scenario() as scenario: + identity: Final = register_mcp(scenario, upstream, "race" + uuid.uuid4().hex[:8]) + with ThreadPoolExecutor(max_workers=2) as pool: + # Hold the row so both workers read the old configuration before either can write. + with psycopg.connect(os.environ["DATABASE_URL"]) as blocker: + blocker.execute('SELECT server_id FROM "LiteLLM_MCPServerTable" WHERE server_id=%s FOR UPDATE', (identity,)) + protocol = pool.submit(gateway.request, "PUT", "/v1/mcp/server", { + "server_id": identity, "mcp_info": {"protocol_version": "2026-07-28"}, + **({"alias": "renamed" + uuid.uuid4().hex[:8]} if rename else {}), + }) + transport = pool.submit(peer.request, "PUT", "/v1/mcp/server", { + "server_id": identity, "transport": "sse", "url": upstream.url, + }) + eventually( + lambda: read_rows("SELECT pid FROM pg_stat_activity WHERE datname=current_database() AND wait_event_type='Lock' AND query LIKE %s", ('%LiteLLM_MCPServerTable%',)), + lambda rows: len(rows) >= 2, + seconds=3, + ) + responses: Final = [protocol.result(), transport.result()] + assert sorted(response.status_code for response in responses) == [202, 400], [r.text for r in responses] + saved: Final = read_rows('SELECT transport, mcp_info FROM "LiteLLM_MCPServerTable" WHERE server_id=%s', (identity,))[0] + assert saved["transport"] == "http" or saved["mcp_info"] != {"protocol_version": "2026-07-28"} + repaired: Final = gateway.request("PUT", "/v1/mcp/server", { + "server_id": identity, "transport": "http", "url": upstream.url, + "mcp_info": {"protocol_version": "2026-07-28"}, + }) + assert repaired.status_code == 202, repaired.text + key: Final = scenario.key(object_permission={"mcp_servers": [identity]}) + assert call_tool(gateway, key, identity, "add", ADD).status_code == 200 diff --git a/tests/integration/mcp/test_mcp_transports.py b/tests/integration/mcp/test_mcp_transports.py index 16004cdd501..b448b8b4a64 100644 --- a/tests/integration/mcp/test_mcp_transports.py +++ b/tests/integration/mcp/test_mcp_transports.py @@ -195,3 +195,52 @@ def test_pinned_revision_pairs_list_and_call_through_gateway( assert negotiations, "The operation must reach the upstream negotiation" assert all(request["params"]["protocolVersion"] == upstream for request in negotiations), negotiations assert len(tool_calls(observed)) == 1 + + +@pytest.mark.parametrize("downstream", ("2024-11-05", "2025-03-26", "2025-06-18", "2025-11-25")) +@pytest.mark.parametrize("peer_kind", ("http", "stdio")) +def test_legacy_gateway_calls_modern_upstream_without_initialize( + gateway: Gateway, + downstream: str, + peer_kind: PeerKind, +) -> None: + import asyncio + + from mcp.types import CallToolRequestParams + + from litellm.experimental_mcp_client.client import MCPClient + from litellm.types.mcp import MCPTransport + + with peer_of(peer_kind) as peer, gateway.scenario() as scenario: + alias: Final = "modern" + uuid.uuid4().hex[:8] + identity: Final = register_mcp(scenario, peer, alias, mcp_info={"protocol_version": "2026-07-28"}) + key: Final = scenario.key(object_permission={"mcp_servers": [identity]}) + client: Final = MCPClient( + server_url=str(gateway.client.base_url).rstrip("/") + "/mcp", + transport_type=MCPTransport.http, + protocol_version=downstream, + extra_headers={"Authorization": f"Bearer {key}"}, + ) + + async def exercise() -> None: + listed: Final = await client.list_tools(raise_on_error=True) + assert f"{alias}-add" in tuple(tool.name for tool in listed) + called: Final = await client.call_tool( + CallToolRequestParams(name=f"{alias}-add", arguments={"a": 2, "b": 3}), + raise_on_error=True, + ) + assert called.is_error is False + assert called.content[0].text == "5" + + peer.drain() + asyncio.run(exercise()) + observed: Final = peer.drain() + assert len(tool_calls(observed)) == 1 + assert all(row["body"].get("method") not in ("initialize", "notifications/initialized") for row in observed) + requests: Final = tuple(row for row in observed if "id" in row["body"]) + assert requests + for row in requests: + metadata: Final = row["body"]["params"]["_meta"] + assert metadata["io.modelcontextprotocol/protocolVersion"] == "2026-07-28" + assert "io.modelcontextprotocol/clientCapabilities" in metadata + assert b"mcp-session-id" not in row.get("headers", {}) diff --git a/tests/unit/experimental_mcp_client/test_mcp_client.py b/tests/unit/experimental_mcp_client/test_mcp_client.py index 508ab447326..015d12c3d5e 100644 --- a/tests/unit/experimental_mcp_client/test_mcp_client.py +++ b/tests/unit/experimental_mcp_client/test_mcp_client.py @@ -3028,9 +3028,101 @@ async def test_configured_upstream_revision_is_offered_and_checked(revision, acc await client.list_tools(raise_on_error=True) -@pytest.mark.parametrize("revision", ["2026-07-28", "unknown", "", None]) +@pytest.mark.parametrize("revision", ["unknown", "", None]) def test_upstream_protocol_configuration_rejects_unavailable_modes(revision): from pydantic import ValidationError with pytest.raises(ValidationError): MCPClient(protocol_version=revision) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("accepted", [True, False]) +async def test_modern_upstream_requests_are_self_contained_without_initialization(accepted: bool) -> None: + from queue import SimpleQueue + + from mcp.types import DiscoverResult, ToolsCapability + + methods: Final[SimpleQueue[str]] = SimpleQueue() + + def respond(request: httpx2.Request) -> httpx2.Response: + if request.method != "POST": + return httpx2.Response(405) + payload: Final = _JSONRPC_MESSAGE_ADAPTER.validate_json(request.content) + assert isinstance(payload, JSONRPCRequest) + methods.put(payload.method) + assert payload.method != "initialize", "Modern operations must not establish a legacy session" + assert "mcp-session-id" not in request.headers + assert request.headers["mcp-protocol-version"] == "2026-07-28" + assert request.headers["authorization"] == "Bearer upstream-credential" + assert payload.params is not None + metadata: Final = payload.params["_meta"] + assert metadata["io.modelcontextprotocol/protocolVersion"] == "2026-07-28" + assert "io.modelcontextprotocol/clientCapabilities" in metadata + if payload.method == "server/discover": + discovery: Final = DiscoverResult( + supported_versions=["2026-07-28"] if accepted else ["2025-11-25"], + capabilities=ServerCapabilities(tools=ToolsCapability()), + instructions="modern instructions", + ) + return httpx2.Response( + 200, + json={ + "jsonrpc": "2.0", + "id": payload.id, + "result": discovery.model_dump(by_alias=True, exclude_none=True), + }, + ) + assert accepted, "Rejected negotiation must prevent upstream execution" + if payload.method == "tools/list": + return httpx2.Response( + 200, + json={ + "jsonrpc": "2.0", + "id": payload.id, + "result": { + "resultType": "complete", + "cacheScope": "private", + "ttlMs": 0, + "tools": [{"name": "add", "inputSchema": {"type": "object"}}], + }, + }, + ) + assert payload.method == "tools/call" + assert payload.params["arguments"] == {"a": 2, "b": 3} + return httpx2.Response( + 200, + json={ + "jsonrpc": "2.0", + "id": payload.id, + "result": {"resultType": "complete", "content": [{"type": "text", "text": "5"}], "isError": False}, + }, + ) + + client: Final = _MockTransportClient( + respond, + server_url="https://example.com/mcp", + protocol_version="2026-07-28", + auth_type=MCPAuth.bearer_token, + auth_value="upstream-credential", + ) + params: Final = CallToolRequestParams(name="add", arguments={"a": 2, "b": 3}) + if accepted: + result: Final = await client.call_tool(params, raise_on_error=True) + assert result.content[0].text == "5" + assert not result.is_error + assert client._last_initialize_instructions == "modern instructions" + assert tuple(methods.get_nowait() for _ in range(methods.qsize())) == ( + "server/discover", + "tools/call", + "tools/list", + ) + else: + with pytest.raises((MCPError, RuntimeError), match="protocol version"): + await client.call_tool(params, raise_on_error=True) + assert tuple(methods.get_nowait() for _ in range(methods.qsize())) == ("server/discover",) + + +def test_modern_upstream_rejects_legacy_sse_transport() -> None: + with pytest.raises(ValueError, match="transport"): + MCPClient(protocol_version="2026-07-28", transport_type=MCPTransport.sse) diff --git a/tests/unit/proxy/_experimental/mcp_server/test_mcp_logging.py b/tests/unit/proxy/_experimental/mcp_server/test_mcp_logging.py index 41d0e2cb59b..44ba40afdd1 100644 --- a/tests/unit/proxy/_experimental/mcp_server/test_mcp_logging.py +++ b/tests/unit/proxy/_experimental/mcp_server/test_mcp_logging.py @@ -111,7 +111,7 @@ async def test_mcp_cost_tracking(): local_mcp_server_manager = MCPServerManager() with patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient", + "litellm.proxy._experimental.mcp_server.upstream.MCPClient", mock_client_constructor, ): # Load the server config @@ -244,7 +244,7 @@ async def test_mcp_cost_tracking_per_tool(): local_mcp_server_manager = MCPServerManager() with patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient", + "litellm.proxy._experimental.mcp_server.upstream.MCPClient", mock_client_constructor, ): # Load the server config with per-tool costs @@ -417,7 +417,7 @@ async def test_mcp_tool_call_hook(): local_mcp_server_manager = MCPServerManager() with patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient", + "litellm.proxy._experimental.mcp_server.upstream.MCPClient", mock_client_constructor, ): # Load the server config diff --git a/tests/unit/proxy/_experimental/mcp_server/test_mcp_partial_update.py b/tests/unit/proxy/_experimental/mcp_server/test_mcp_partial_update.py index af4f4cbeb17..a3c52dc16b7 100644 --- a/tests/unit/proxy/_experimental/mcp_server/test_mcp_partial_update.py +++ b/tests/unit/proxy/_experimental/mcp_server/test_mcp_partial_update.py @@ -13,6 +13,7 @@ from unittest.mock import AsyncMock, MagicMock import pytest from prisma import Json, models +from fastapi import HTTPException from litellm.proxy._experimental.mcp_server.db import ( create_mcp_server, @@ -35,7 +36,7 @@ def _mock_prisma(): mock_prisma.db.litellm_mcpservertable.update = AsyncMock(return_value=row) mock_prisma.db.litellm_mcpservertable.create = AsyncMock(return_value=row) mock_prisma.db.litellm_mcpservertable.find_first = AsyncMock(return_value=None) - mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=None) + mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=row) tx_client = MagicMock() tx_client.execute_raw = AsyncMock() tx_client.litellm_mcpservertable = mock_prisma.db.litellm_mcpservertable @@ -1141,6 +1142,42 @@ async def test_set_mcp_server_pinned_tools_writes_the_snapshot_and_null_clears_i @pytest.mark.asyncio async def test_set_mcp_server_pinned_tools_on_a_missing_server_writes_nothing(): mock_prisma = _mock_prisma() + mock_prisma.db.litellm_mcpservertable.find_unique.return_value = None assert await set_mcp_server_pinned_tools(mock_prisma, "ghost", None, "admin") is None mock_prisma.db.litellm_mcpservertable.update.assert_not_awaited() + + +@pytest.mark.asyncio +@pytest.mark.parametrize("protocol_only", [False, True]) +async def test_protocol_update_revalidates_current_stored_configuration_before_writing(protocol_only: bool): + prisma = _mock_prisma() + table = prisma.db.litellm_mcpservertable + table.find_unique.return_value = models.LiteLLM_MCPServerTable.model_construct( + server_id="test-server", transport="sse" if protocol_only else "http", + mcp_info={} if protocol_only else {"protocol_version": "2026-07-28"}, env={}, env_vars=[], + ) + payload = UpdateMCPServerRequest.model_validate({ + "server_id": "test-server", + **({"mcp_info": {"protocol_version": "2026-07-28"}} if protocol_only else {"transport": "sse", "url": "https://upstream.example/sse"}), + }) + with pytest.raises(HTTPException) as error: + await update_mcp_server(prisma, payload, "admin") + assert error.value.status_code == 400 + assert "Modern MCP requires HTTP or stdio" in str(error.value.detail) + table.update.assert_not_awaited() + + +@pytest.mark.asyncio +@pytest.mark.parametrize("clear_alias", [False, True]) +async def test_protocol_update_preserves_missing_server_without_writing(clear_alias: bool): + prisma = _mock_prisma() + table = prisma.db.litellm_mcpservertable + table.find_unique.return_value = None + payload = UpdateMCPServerRequest.model_validate({ + "server_id": "missing", "mcp_info": {"protocol_version": "2026-07-28"}, + **({"alias": None} if clear_alias else {}), + }) + result = await update_mcp_server(prisma, payload, "admin") + assert result is None + table.update.assert_not_awaited() diff --git a/tests/unit/proxy/_experimental/mcp_server/test_mcp_server.py b/tests/unit/proxy/_experimental/mcp_server/test_mcp_server.py index f8bf72428aa..d3679506a2f 100644 --- a/tests/unit/proxy/_experimental/mcp_server/test_mcp_server.py +++ b/tests/unit/proxy/_experimental/mcp_server/test_mcp_server.py @@ -71,7 +71,7 @@ async def test_mcp_server_manager_https_server(): return mock_client with patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient", + "litellm.proxy._experimental.mcp_server.upstream.MCPClient", mock_client_constructor, ): await mcp_server_manager.load_servers_from_config( @@ -179,7 +179,7 @@ async def test_mcp_http_transport_list_tools_mock(): return mock_client with patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient", + "litellm.proxy._experimental.mcp_server.upstream.MCPClient", mock_client_constructor, ): # Load server config with HTTP transport @@ -256,7 +256,7 @@ async def test_mcp_http_transport_call_tool_mock(): return mock_client with patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient", + "litellm.proxy._experimental.mcp_server.upstream.MCPClient", mock_client_constructor, ): # Load server config with HTTP transport @@ -322,7 +322,7 @@ async def test_mcp_http_transport_call_tool_error_mock(): return mock_client with patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient", + "litellm.proxy._experimental.mcp_server.upstream.MCPClient", mock_client_constructor, ): # Load server config with HTTP transport @@ -1093,7 +1093,7 @@ async def test_list_tools_only_returns_allowed_servers(monkeypatch): return mock_client with patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient", + "litellm.proxy._experimental.mcp_server.upstream.MCPClient", mock_client_constructor, ): # Call list_tools @@ -1390,7 +1390,7 @@ async def test_mcp_server_manager_alias_tool_prefixing(): return mock_client with patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient", + "litellm.proxy._experimental.mcp_server.upstream.MCPClient", mock_client_constructor, ): # Get tools from server @@ -1450,7 +1450,7 @@ async def test_mcp_server_manager_server_name_tool_prefixing(): return mock_client with patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient", + "litellm.proxy._experimental.mcp_server.upstream.MCPClient", mock_client_constructor, ): # Get tools from server @@ -1510,7 +1510,7 @@ async def test_mcp_server_manager_server_id_tool_prefixing(): return mock_client with patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient", + "litellm.proxy._experimental.mcp_server.upstream.MCPClient", mock_client_constructor, ): # Get tools from server @@ -2506,7 +2506,7 @@ async def test_filter_tools_by_allowed_tools_integration(): # Mock the MCPClient constructor with patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient", + "litellm.proxy._experimental.mcp_server.upstream.MCPClient", mock_client_constructor, ): # Call _get_tools_from_mcp_servers which should apply the filtering @@ -2620,7 +2620,7 @@ async def test_filter_tools_by_disallowed_tools_integration(): # Mock the MCPClient constructor with patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient", + "litellm.proxy._experimental.mcp_server.upstream.MCPClient", mock_client_constructor, ): # Call _get_tools_from_mcp_servers which should apply the filtering @@ -2722,7 +2722,7 @@ async def test_filter_tools_no_restrictions_integration(): # Mock the MCPClient constructor with patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient", + "litellm.proxy._experimental.mcp_server.upstream.MCPClient", mock_client_constructor, ): # Call _get_tools_from_mcp_servers which should apply the filtering diff --git a/tests/unit/proxy/_experimental/mcp_server/test_mcp_server_manager.py b/tests/unit/proxy/_experimental/mcp_server/test_mcp_server_manager.py index 46264de738a..4b460fc67ae 100644 --- a/tests/unit/proxy/_experimental/mcp_server/test_mcp_server_manager.py +++ b/tests/unit/proxy/_experimental/mcp_server/test_mcp_server_manager.py @@ -1,3 +1,4 @@ +from litellm.proxy._experimental.mcp_server.upstream import resolve_upstream_auth import importlib import asyncio import functools @@ -94,7 +95,7 @@ async def test_manager_sampling_preserves_explicit_headers_without_ambient_conte client.call_tool = AsyncMock(return_value=CallToolResult(content=[])) assert legacy_server.get_active_auth_context() is None with ( - patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient", return_value=client) as factory, + patch("litellm.proxy._experimental.mcp_server.upstream.MCPClient", return_value=client) as factory, patch("litellm.proxy._experimental.mcp_server.sampling_handler.handle_sampling_create_message", sampling), ): await MCPServerManager()._call_regular_mcp_tool( @@ -1343,7 +1344,7 @@ class TestMCPServerManager: "ensure_oauth_metadata_discovered", new=ensure_oauth_metadata_discovered, ), - patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient"), + patch("litellm.proxy._experimental.mcp_server.upstream.MCPClient"), ): await manager._create_mcp_client(server) @@ -3900,10 +3901,10 @@ class TestMCPServerManager: ) with ( patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.resolve_mcp_auth", + "litellm.proxy._experimental.mcp_server.upstream.resolve_mcp_auth", new_callable=AsyncMock, ) as mock_resolve, - patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient") as mock_client_cls, + patch("litellm.proxy._experimental.mcp_server.upstream.MCPClient") as mock_client_cls, ): await manager._create_mcp_client(server=server, extra_headers={"Authorization": "Bearer upstream-token"}) mock_resolve.assert_not_awaited() @@ -3953,10 +3954,10 @@ class TestMCPServerManager: ) with ( patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.resolve_mcp_auth", + "litellm.proxy._experimental.mcp_server.upstream.resolve_mcp_auth", new_callable=AsyncMock, ) as mock_resolve, - patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient") as mock_client_cls, + patch("litellm.proxy._experimental.mcp_server.upstream.MCPClient") as mock_client_cls, ): await manager._create_mcp_client( server=server, @@ -3996,10 +3997,10 @@ class TestMCPServerManager: ) with ( patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.resolve_mcp_auth", + "litellm.proxy._experimental.mcp_server.upstream.resolve_mcp_auth", new_callable=AsyncMock, ) as mock_resolve, - patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient") as mock_client_cls, + patch("litellm.proxy._experimental.mcp_server.upstream.MCPClient") as mock_client_cls, ): await manager._create_mcp_client( server=server, @@ -13707,7 +13708,8 @@ async def test_debug_resolution_matches_final_header_conflict_winner(_mcp_reques "none": NoneConfig(), }[config] try: - auth, remaining = await MCPServerManager()._resolve_v2_auth( + auth, remaining = await resolve_upstream_auth( + root_path="", server=MCPServer( server_id="s", name="s", @@ -15085,7 +15087,7 @@ async def test_client_sampling_does_not_fill_explicit_context_from_another_ambie try: legacy_server.set_auth_context(UserAPIKeyAuth(user_id="unrelated"), raw_headers={"authorization": "unrelated-credential"}, client_ip="192.0.2.99") with ( - patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient") as factory, + patch("litellm.proxy._experimental.mcp_server.upstream.MCPClient") as factory, patch("litellm.proxy._experimental.mcp_server.sampling_handler.handle_sampling_create_message", sampling), ): if legacy_factory: @@ -15800,3 +15802,57 @@ class TestToolCatalogGuard: proxy_logging_obj=proxy_logging_obj, server=server, ) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("command,args", [(None, []), ("python", None), ("blocked-executable", [])]) +async def test_upstream_preparation_rejects_blocked_or_preserves_incomplete_stdio_config( + monkeypatch: pytest.MonkeyPatch, + command: str | None, + args: list[str] | None, +) -> None: + monkeypatch.setenv("LITELLM_ENABLE_MCP_STDIO", "true") + server: Final = MCPServer(server_id="stdio", name="stdio", transport=MCPTransport.stdio, command=command, args=args) + if command == "blocked-executable": + with pytest.raises(HTTPException) as error: + await MCPServerManager()._create_mcp_client(server) + assert error.value.status_code == 403 + assert "not in the allowlist" in error.value.detail + else: + client: Final = await MCPServerManager()._create_mcp_client(server) + assert client.stdio_config is None + + +@pytest.mark.asyncio +async def test_upstream_preparation_preserves_windows_command_and_caller_environment( + monkeypatch: pytest.MonkeyPatch, +) -> None: + from litellm.constants import MCP_NPM_CACHE_DIR + + monkeypatch.setenv("LITELLM_ENABLE_MCP_STDIO", "true") + environment: Final = {"PEER_USER": "alice"} + server: Final = MCPServer( + server_id="stdio", name="stdio", transport=MCPTransport.stdio, command="python.exe", args=[] + ) + client: Final = await MCPServerManager()._create_mcp_client(server, stdio_env=environment) + assert client.stdio_config == { + "command": "python.exe", + "args": [], + "env": {"PEER_USER": "alice", "NPM_CONFIG_CACHE": MCP_NPM_CACHE_DIR}, + } + assert environment == {"PEER_USER": "alice"} + + +@pytest.mark.asyncio +async def test_upstream_preparation_honors_case_sensitive_extra_command(monkeypatch: pytest.MonkeyPatch) -> None: + from litellm.proxy._experimental.mcp_server import upstream + + monkeypatch.setenv("LITELLM_ENABLE_MCP_STDIO", "true") + monkeypatch.setattr(upstream, "MCP_STDIO_ALLOWED_COMMANDS", frozenset({"CustomRunner"})) + server: Final = MCPServer( + server_id="custom-stdio", name="custom-stdio", transport=MCPTransport.stdio, + command="/opt/tools/CustomRunner", args=[], + ) + client: Final = await MCPServerManager()._create_mcp_client(server) + assert client.stdio_config is not None + assert client.stdio_config["command"] == "/opt/tools/CustomRunner" diff --git a/tests/unit/proxy/_experimental/mcp_server/test_openapi_tool_auth.py b/tests/unit/proxy/_experimental/mcp_server/test_openapi_tool_auth.py index 15d3b67e641..a8ec7be55f0 100644 --- a/tests/unit/proxy/_experimental/mcp_server/test_openapi_tool_auth.py +++ b/tests/unit/proxy/_experimental/mcp_server/test_openapi_tool_auth.py @@ -851,3 +851,28 @@ def test_the_openapi_arm_keeps_the_shared_client_when_no_guard_is_needed(resolve assert not client.client.event_hooks.get("request") finally: _request_resolved_auth_headers.reset(token) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("mode", [MCPAuth.true_passthrough, MCPAuth.oauth_delegate]) +@pytest.mark.parametrize("per_server", [None, "Bearer per-server"]) +async def test_openapi_passthrough_preparation_preserves_credential_precedence( + mode: MCPAuth, per_server: str | None, +) -> None: + from typing import Final + + from litellm.proxy._experimental.mcp_server.mcp_server_manager import MCPServerManager + + server: Final = MCPServer( + server_id="openapi-passthrough", name="openapi-passthrough", transport=MCPTransport.http, + url="https://upstream.example", spec_path="https://upstream.example/openapi.json", auth_type=mode, + ) + headers: Final = {"authorization": "Bearer forwarded", "X-Trace": "trace"} + resolved, remaining = await MCPServerManager().resolve_openapi_upstream_auth( + mcp_server=server, oauth2_headers=None, raw_headers=None, + mcp_auth_header=per_server, user_api_key_auth=UserAPIKeyAuth(user_id="alice"), + forwarded_headers=headers, + ) + assert resolved == {"Authorization": per_server or "Bearer forwarded"} + assert remaining == {"X-Trace": "trace"} + assert headers == {"authorization": "Bearer forwarded", "X-Trace": "trace"} diff --git a/tests/unit/proxy/_experimental/mcp_server/test_operations.py b/tests/unit/proxy/_experimental/mcp_server/test_operations.py index bb900de4f98..b16b27ac919 100644 --- a/tests/unit/proxy/_experimental/mcp_server/test_operations.py +++ b/tests/unit/proxy/_experimental/mcp_server/test_operations.py @@ -136,7 +136,7 @@ async def test_prompt_sampling_receives_explicit_operation_caller_headers_and_ip sampling = AsyncMock() with ( patch.object(operations, "_get_allowed_mcp_servers", AsyncMock(return_value=[upstream])), - patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient", return_value=client) as factory, + patch("litellm.proxy._experimental.mcp_server.upstream.MCPClient", return_value=client) as factory, patch("litellm.proxy._experimental.mcp_server.sampling_handler.handle_sampling_create_message", sampling), ): result = await GatewayOperations().execute( diff --git a/tests/unit/proxy/_experimental/mcp_server/test_server_resolution.py b/tests/unit/proxy/_experimental/mcp_server/test_server_resolution.py index f88088a4fd8..853118b8dc2 100644 --- a/tests/unit/proxy/_experimental/mcp_server/test_server_resolution.py +++ b/tests/unit/proxy/_experimental/mcp_server/test_server_resolution.py @@ -10,7 +10,9 @@ from unittest.mock import Mock import pytest from fastapi import HTTPException +from litellm.proxy._experimental.mcp_server.contracts import TargetCatalog from litellm.proxy._experimental.mcp_server.server_resolution import ( + MCPServerTargetCatalog, ResolutionSource, ResolvedMCPServer, authorize_mcp_server, @@ -460,3 +462,94 @@ async def test_missing_alias_does_not_produce_a_resolution() -> None: manager: Final = _manager() assert await resolve_mcp_server("missing", manager=manager, match_name=True) is None manager.name_lookup_spy.assert_called_once_with("missing", None) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("source", ["db", "registry", "temp"]) +@pytest.mark.parametrize("allowed", [False, True]) +async def test_target_catalog_authorizes_canonical_identity(source: ResolutionSource, allowed: bool) -> None: + server: Final = _runtime_server() + manager: Final = _manager( + servers_by_name={"requested-alias": server}, + allowed_server_ids=(server.server_id,) if allowed else ("requested-alias",), + ) + + async def database_lookup(server_id: str) -> LiteLLM_MCPServerTable | None: + return _table_server(server.server_id) + + async def temporary_lookup(server_id: str) -> MCPServer | None: + return server + + catalog: Final[TargetCatalog] = MCPServerTargetCatalog( + manager=manager, + db_lookup=database_lookup if source == "db" else None, + temp_lookup=temporary_lookup if source == "temp" else None, + match_name=True, + ) + operation: Final = catalog.resolve( + "requested-alias", + _auth(), + is_admin_view=False, + not_found_detail={"error": "missing"}, + forbidden_detail={"error": "denied"}, + non_admin_missing="forbidden", + ) + if allowed and source != "temp": + result: Final = await operation + assert result.table.server_id == server.server_id + assert result.source == source + assert result.runtime is (None if source == "db" else server) + else: + with pytest.raises(HTTPException) as error: + await operation + assert (error.value.status_code, error.value.detail) == (403, {"error": "denied"}) + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "admin,missing,status_code", [(True, "forbidden", 404), (False, "forbidden", 403), (False, "not_found", 404)] +) +async def test_target_catalog_preserves_missing_target_policy( + admin: bool, + missing: Literal["forbidden", "not_found"], + status_code: int, +) -> None: + catalog: Final[TargetCatalog] = MCPServerTargetCatalog(manager=_manager()) + with pytest.raises(HTTPException) as error: + await catalog.resolve( + "missing", + _auth(), + is_admin_view=admin, + not_found_detail={"error": "missing"}, + forbidden_detail={"error": "denied"}, + non_admin_missing=missing, + ) + assert error.value.status_code == status_code + assert error.value.detail == {"error": "missing" if status_code == 404 else "denied"} + + +@pytest.mark.asyncio +async def test_target_catalog_does_not_reuse_admin_authorization_for_another_caller() -> None: + server: Final = _runtime_server() + manager: Final = _manager(servers_by_id={server.server_id: server}) + catalog: Final[TargetCatalog] = MCPServerTargetCatalog(manager=manager) + admin: Final = await catalog.resolve( + server.server_id, + UserAPIKeyAuth(user_id="admin"), + is_admin_view=True, + not_found_detail={"error": "missing"}, + forbidden_detail={"error": "denied"}, + non_admin_missing="forbidden", + ) + assert admin.runtime is server + with pytest.raises(HTTPException) as error: + await catalog.resolve( + server.server_id, + _auth(), + is_admin_view=False, + not_found_detail={"error": "missing"}, + forbidden_detail={"error": "denied"}, + non_admin_missing="forbidden", + ) + assert (error.value.status_code, error.value.detail) == (403, {"error": "denied"}) + manager.allowed_servers_spy.assert_called_once_with(_auth()) diff --git a/tests/unit/proxy/management_endpoints/test_mcp_management_endpoints.py b/tests/unit/proxy/management_endpoints/test_mcp_management_endpoints.py index 88ee36a0fef..2cbf1d578b2 100644 --- a/tests/unit/proxy/management_endpoints/test_mcp_management_endpoints.py +++ b/tests/unit/proxy/management_endpoints/test_mcp_management_endpoints.py @@ -11059,3 +11059,89 @@ class TestMCPServerResolutionCharacterization: health_check.assert_not_awaited() effects.assert_no_writes() assert httpx_mock.calls.call_count == 0 + + +@pytest.mark.parametrize("explicit_transport", [False, True]) +def test_modern_sse_create_is_rejected_before_persistence(explicit_transport: bool) -> None: + with pytest.raises(ValidationError, match="Modern MCP requires HTTP or stdio"): + NewMCPServerRequest.model_validate({ + "url": "https://upstream.example/sse", + "mcp_info": {"protocol_version": "2026-07-28"}, + **({"transport": "sse"} if explicit_transport else {}), + }) + + +@pytest.mark.parametrize("metadata", [False, True]) +def test_modern_sse_runtime_configuration_is_rejected(metadata: bool) -> None: + with pytest.raises(ValidationError, match="Modern MCP requires HTTP or stdio"): + MCPServer.model_validate({ + "server_id": "modern", "name": "modern", "transport": "sse", + **({"mcp_info": {"protocol_version": "2026-07-28"}} if metadata else {"protocol_version": "2026-07-28"}), + }) + + +@pytest.mark.parametrize("transport,version", [("http", "2026-07-28"), ("stdio", "2026-07-28"), ("sse", "2025-11-25"), ("sse", "auto")]) +def test_supported_protocol_transport_configurations_remain_valid(transport: str, version: str, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv("LITELLM_ENABLE_MCP_STDIO", "true") + payload: Final = NewMCPServerRequest.model_validate({ + "transport": transport, "url": "https://upstream.example/mcp", "command": "python", "args": ["peer.py"], + "mcp_info": {"protocol_version": version}, + }) + assert payload.transport == transport + assert payload.mcp_info == {"protocol_version": version} + + +@pytest.mark.asyncio +@pytest.mark.parametrize("protocol_only", [False, True]) +async def test_modern_sse_partial_update_rejected_without_writes(protocol_only: bool) -> None: + old_record: Final = LiteLLM_MCPServerTable( + server_id="srv-1", transport="sse" if protocol_only else "http", + mcp_info={"protocol_version": "auto" if protocol_only else "2026-07-28"}, + ) + payload: Final = UpdateMCPServerRequest.model_validate({ + "server_id": "srv-1", + **({"mcp_info": {"protocol_version": "2026-07-28"}} if protocol_only else {"transport": "sse", "url": "https://upstream.example/sse"}), + }) + update_mock: Final = AsyncMock(side_effect=HTTPException(status_code=418, detail="Unexpected persistence")) + p1, p2, p3, p4, p5 = _edit_endpoint_patches(old_record, update_mock) + with p1, p2, p3, p4, p5: + with pytest.raises(HTTPException) as error: + await mgmt_endpoints.edit_mcp_server(payload=payload, user_api_key_dict=UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN)) + assert error.value.status_code == 400 + assert "Modern MCP requires HTTP or stdio" in str(error.value.detail) + update_mock.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_protocol_partial_update_fails_closed_when_stored_configuration_is_unreadable() -> None: + update_mock: Final = AsyncMock(side_effect=HTTPException(status_code=418, detail="Unexpected persistence")) + p1, p2, p3, p4, p5 = _edit_endpoint_patches(RuntimeError("db unavailable"), update_mock) + with p1, p2, p3, p4, p5: + with pytest.raises(HTTPException) as error: + await mgmt_endpoints.edit_mcp_server( + payload=UpdateMCPServerRequest(server_id="srv-1", mcp_info={"protocol_version": "2026-07-28"}), + user_api_key_dict=UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN), + ) + assert error.value.status_code == 503 + update_mock.assert_not_awaited() + + +def test_modern_sse_complete_update_is_rejected() -> None: + with pytest.raises(ValidationError, match="Modern MCP requires HTTP or stdio"): + UpdateMCPServerRequest( + server_id="server", transport=MCPTransport.sse, url="https://upstream.example/sse", + mcp_info={"protocol_version": "2026-07-28"}, + ) + + +@pytest.mark.asyncio +async def test_protocol_update_on_missing_server_preserves_not_found() -> None: + update_mock: Final = AsyncMock(return_value=None) + p1, p2, p3, p4, p5 = _edit_endpoint_patches(None, update_mock) + with p1, p2, p3, p4, p5: + with pytest.raises(HTTPException) as error: + await mgmt_endpoints.edit_mcp_server( + payload=UpdateMCPServerRequest(server_id="missing", mcp_info={"protocol_version": "2026-07-28"}), + user_api_key_dict=UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN), + ) + assert error.value.status_code == 404 diff --git a/tests/unit/proxy/test__types.py b/tests/unit/proxy/test__types.py index 50c3eb2908c..70c5a153647 100644 --- a/tests/unit/proxy/test__types.py +++ b/tests/unit/proxy/test__types.py @@ -397,7 +397,7 @@ def test_mcp_advertised_versions_reject_unavailable_revisions(versions): ConfigGeneralSettings(mcp_advertised_versions=versions) -@pytest.mark.parametrize("revision", ["2026-07-28", "unknown", None]) +@pytest.mark.parametrize("revision", ["unknown", None]) def test_mcp_metadata_rejects_unavailable_upstream_protocol(revision): from litellm.proxy._types import NewMCPServerRequest, UpdateMCPServerRequest @@ -466,3 +466,13 @@ def test_an_http_mcp_server_is_unaffected_by_the_stdio_flag(monkeypatch, request def test_a_non_mapping_mcp_server_payload_gets_a_validation_error(request_model): with pytest.raises(ValidationError, match="valid dictionary"): request_model.model_validate("not-a-server") + + +@pytest.mark.parametrize("request_model", MCP_SERVER_REQUESTS) +def test_modern_http_upstream_protocol_is_available(request_model): + parsed = request_model.model_validate({ + "server_id": "modern", "transport": "http", "url": "https://example.com/mcp", + "mcp_info": {"protocol_version": "2026-07-28"}, + }) + assert parsed.mcp_info["protocol_version"] == "2026-07-28" + assert parsed.transport == "http" From aef0a538377031f93bb46618ee30fb9dd2efec0f Mon Sep 17 00:00:00 2001 From: moe-berri Date: Fri, 2 Oct 2026 16:05:00 -0700 Subject: [PATCH 03/61] fix(lens): default to traces and restore closed span details (#44260) --- .../src/app/(dashboard)/lens/page.test.tsx | 11 ++++++++--- .../src/app/(dashboard)/lens/page.tsx | 2 +- .../components/view_logs/TraceView/SpanTree.tsx | 11 ++++++++++- .../view_logs/TraceView/TraceDrawer.test.tsx | 15 +++++++++++++++ .../view_logs/TraceView/TraceDrawer.tsx | 1 + 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/lens/page.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/lens/page.test.tsx index 148a916c867..5a6fd9cc68f 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/lens/page.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/lens/page.test.tsx @@ -18,12 +18,11 @@ vi.mock("./_components/LensView", () => ({ describe("Lens navigation", () => { beforeEach(() => auth.mockReturnValue({ accessToken: "test-token", userRole: "Admin", isViewOnly: false })); - it("opens investigations and pauses trace polling when returning from traces", async () => { + it("opens traces by default and pauses polling while viewing investigations", async () => { const user = userEvent.setup(); const onUrlUpdate = vi.fn(); renderWithProviders(, { onUrlUpdate }); - expect(screen.getByRole("tab", { name: "Investigations" })).toHaveAttribute("aria-selected", "true"); - await user.click(screen.getByRole("tab", { name: "Traces" })); + expect(screen.getByRole("tab", { name: "Traces" })).toHaveAttribute("aria-selected", "true"); expect(screen.getByText("Trace polling active")).toBeVisible(); await user.click(screen.getByRole("tab", { name: "Investigations" })); expect(screen.getByText("Manage investigations")).toBeVisible(); @@ -39,6 +38,12 @@ describe("Lens navigation", () => { expect(screen.getByText("Manage investigations")).toBeVisible(); }); + it("honors an explicit traces tab even when a saved investigation is in the URL", () => { + renderWithProviders(, { searchParams: "?tab=traces&lens=saved-lens" }); + expect(screen.getByRole("tab", { name: "Traces" })).toHaveAttribute("aria-selected", "true"); + expect(screen.getByText("Trace polling active")).toBeVisible(); + }); + it.each(["Internal User", "Internal Viewer", "Org Admin"])( "preserves trace access without granting investigations to %s", async (userRole) => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/lens/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/lens/page.tsx index 01f5126e3ae..b29e498600f 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/lens/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/lens/page.tsx @@ -15,7 +15,7 @@ export default function LensPage() { parseAsStringLiteral(["traces", "investigations"]).withOptions({ history: "push" }), ); const [lensId] = useQueryState("lens", parseAsString); - const defaultTab = isProxyAdminTierRole(userRole ?? "") || lensId ? "investigations" : "traces"; + const defaultTab = lensId ? "investigations" : "traces"; const activeTab = tab ?? defaultTab; if (!accessToken) return null; return ( diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/SpanTree.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/SpanTree.tsx index 27a3d7fbfe0..3f7c798a0a0 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/SpanTree.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/SpanTree.tsx @@ -1,8 +1,9 @@ "use client"; -import { BarChart3, Clock, Coins, ListTree, MoreHorizontal, Timer } from "lucide-react"; +import { BarChart3, Clock, Coins, ListTree, MoreHorizontal, PanelRightOpen, Timer } from "lucide-react"; import { useEffect, useMemo, useRef } from "react"; +import { Button } from "@/components/ui/button"; import { Switch } from "@/components/ui/switch"; import { cn } from "@/lib/cva.config"; @@ -24,6 +25,7 @@ interface SpanTreeProps { onToggleSpan: (id: string) => void; onToggleGroup: (id: string) => void; onLoadMore: (groupId: string) => void; + onOpenDetails?: () => void; /** Inside the side drawer J/K switch runs, so spans move with the arrow keys. */ embedded?: boolean; } @@ -308,6 +310,7 @@ export function SpanTree({ onToggleSpan, onToggleGroup, onLoadMore, + onOpenDetails, embedded = false, }: SpanTreeProps) { const scrollRef = useRef(null); @@ -333,6 +336,12 @@ export function SpanTree({ aria-label="Hide framework spans" /> + {onOpenDetails && ( + + )}
diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/TraceDrawer.test.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/TraceDrawer.test.tsx index da3fce0d14b..50de602fa31 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/TraceDrawer.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/TraceDrawer.test.tsx @@ -135,6 +135,21 @@ describe("RunView", () => { expect(screen.queryByTestId("detail-pane")).not.toBeInTheDocument(); }); + it.each(["button", "Escape"])("reopens the selected step after closing details with %s", async (method) => { + const user = userEvent.setup(); + renderRun(research); + await screen.findByTestId("detail-pane"); + await user.keyboard("j"); + const selectedId = screen.getByTestId("detail-pane").getAttribute("data-row-id"); + expect(selectedId).not.toBe(rootSpanId(research)); + if (method === "button") await user.click(screen.getByRole("button", { name: "close detail" })); + else await user.keyboard("{Escape}"); + expect(screen.queryByTestId("detail-pane")).not.toBeInTheDocument(); + await user.click(screen.getByRole("button", { name: "Show details" })); + expect(screen.getByTestId("detail-pane")).toHaveAttribute("data-row-id", selectedId); + expect(screen.queryByRole("button", { name: "Show details" })).not.toBeInTheDocument(); + }); + it("keeps a way back to the runs table when a run fails to load", async () => { const user = userEvent.setup(); const onBack = vi.fn(); diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/TraceDrawer.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/TraceDrawer.tsx index 0aeea4e4cc7..a991c771a56 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/TraceDrawer.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/TraceDrawer.tsx @@ -253,6 +253,7 @@ function RunBody({ trace, accessToken, initialSpanId, embedded }: RunBodyProps) onToggleSpan={toggleSpan} onToggleGroup={toggleGroup} onLoadMore={loadMore} + onOpenDetails={detailOpen ? undefined : () => setDetailOpen(true)} embedded={embedded} /> {detailOpen && ( From 8b4de39ad7a68e6181fc0d6bc370443b0264224b Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2026 16:06:26 -0700 Subject: [PATCH 04/61] fix(bedrock): surface unrecognized converse-stream event frames instead of an empty turn (#44112) * fix(bedrock): surface unrecognized converse-stream event frames instead of an empty turn * fix(bedrock): track distinct unknown stream event types and cover the stream error builder * test(bedrock): audit converse-stream event frames on the proxy and the sync decoder --------- Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com> --- litellm/llms/bedrock/chat/invoke_handler.py | 69 +- litellm/llms/bedrock/common_utils.py | 102 +- tests/integration/_support/upstream.py | 68 +- ...drock_converse_stream_event_frames_wire.py | 935 ++++++++++++++++++ ...drock_converse_stream_sync_decoder_wire.py | 100 ++ .../llms/bedrock/chat/test_invoke_handler.py | 208 +++- .../llms/bedrock/test_bedrock_common_utils.py | 69 ++ 7 files changed, 1494 insertions(+), 57 deletions(-) create mode 100644 tests/integration/providers/test_bedrock_converse_stream_event_frames_wire.py create mode 100644 tests/integration/sdk/test_bedrock_converse_stream_sync_decoder_wire.py diff --git a/litellm/llms/bedrock/chat/invoke_handler.py b/litellm/llms/bedrock/chat/invoke_handler.py index 93804e20041..9f3d27cbfb9 100644 --- a/litellm/llms/bedrock/chat/invoke_handler.py +++ b/litellm/llms/bedrock/chat/invoke_handler.py @@ -42,9 +42,15 @@ from litellm.types.utils import GenericStreamingChunk as GChunk from ..common_utils import ( BedrockError, + BedrockEventStreamResponseDict, + bedrock_event_stream_header, + bedrock_event_stream_response, + bedrock_stream_event_error_status, build_bedrock_stream_error, + build_bedrock_stream_event_error, error_response_text, get_bedrock_response_stream_shape, + get_bedrock_stream_event_statuses, get_bedrock_tool_name, ) @@ -53,6 +59,7 @@ from litellm.llms.bedrock.chat.converse_transformation import AmazonConverseConf if TYPE_CHECKING: from botocore.eventstream import EventStreamMessage + from botocore.model import Shape converse_config: Final = AmazonConverseConfig() _STREAM_HEAD_BYTES: Final = 200 @@ -365,10 +372,14 @@ def _response_header(response_headers: Mapping[str, str] | None, name: str) -> s class _EventStreamTally: - def __init__(self) -> None: + def __init__(self, event_statuses: Mapping[str, int | None] | None) -> None: + self.event_statuses = event_statuses self.bytes_received = 0 self.bytes_decoded = 0 self.events = 0 + self.recognized_events = 0 + self.unrecognized_event_types: frozenset[str] = frozenset() + self.unrecognized_head = b"" self.head = b"" def add_chunk(self, chunk: bytes) -> None: @@ -376,13 +387,24 @@ class _EventStreamTally: if len(self.head) < _STREAM_HEAD_BYTES: self.head = (self.head + chunk)[:_STREAM_HEAD_BYTES] - def add_event(self, event: "EventStreamMessage") -> None: + def add_event(self, event: "EventStreamMessage", headers: Mapping[str, object]) -> None: self.events += 1 self.bytes_decoded += event.prelude.total_length + event_type: Final = bedrock_event_stream_header(headers, ":event-type") + if ( + self.event_statuses is None + or bedrock_event_stream_header(headers, ":message-type") != "event" + or (event_type is not None and event_type in self.event_statuses) + ): + self.recognized_events += 1 + return + self.unrecognized_event_types = self.unrecognized_event_types | {event_type or ""} + if not self.unrecognized_head: + self.unrecognized_head = event.payload[:_STREAM_HEAD_BYTES] def undecoded_stream_error(self, response_headers: Mapping[str, str] | None) -> BedrockError | None: undecoded: Final = self.bytes_received - self.bytes_decoded - if self.events and not undecoded: + if self.recognized_events and not undecoded: return None detail: Final = ( f"content-type={_response_header(response_headers, 'content-type')!r}, " @@ -397,6 +419,15 @@ class _EventStreamTally: f"({detail}, first bytes={self.head!r})" ), ) + if not self.recognized_events: + return BedrockError( + status_code=502, + message=( + f"Bedrock answered the stream with HTTP 200 but none of its {self.events} events carried a known " + f"event type (event types={sorted(self.unrecognized_event_types)}, {detail}, " + f"first payload={self.unrecognized_head!r})" + ), + ) return BedrockError( status_code=502, message=f"Bedrock stream ended with {undecoded} undecoded bytes after {self.events} events ({detail})", @@ -749,13 +780,12 @@ class AWSEventStreamDecoder: from botocore.eventstream import EventStreamBuffer event_stream_buffer: Final = EventStreamBuffer() - tally: Final = _EventStreamTally() + tally: Final = _EventStreamTally(get_bedrock_stream_event_statuses()) for chunk in iterator: event_stream_buffer.add_data(chunk) tally.add_chunk(chunk) for event in event_stream_buffer: - tally.add_event(event) - message = self._parse_message_from_event(event) + message = self._decode_event(event, tally) if message: # sse_event = ServerSentEvent(data=message, event="completion") _data = json.loads(message) @@ -771,13 +801,12 @@ class AWSEventStreamDecoder: from botocore.eventstream import EventStreamBuffer event_stream_buffer: Final = EventStreamBuffer() - tally: Final = _EventStreamTally() + tally: Final = _EventStreamTally(get_bedrock_stream_event_statuses()) async for chunk in iterator: event_stream_buffer.add_data(chunk) tally.add_chunk(chunk) for event in event_stream_buffer: - tally.add_event(event) - message = self._parse_message_from_event(event) + message = self._decode_event(event, tally) if message: _data = json.loads(message) yield self._chunk_parser(chunk_data=_data) @@ -785,7 +814,7 @@ class AWSEventStreamDecoder: if undecoded_stream_error is not None: raise undecoded_stream_error - def _parse_message_from_event(self, event) -> str | None: + def _response_stream_shape(self) -> "Shape": response_stream_shape: Final = get_bedrock_response_stream_shape() if response_stream_shape is None: raise BedrockError( @@ -795,11 +824,29 @@ class AWSEventStreamDecoder: "Ensure botocore is correctly installed." ), ) - response_dict: Final = event.to_response_dict() + return response_stream_shape + + def _decode_event(self, event: "EventStreamMessage", tally: _EventStreamTally) -> str | None: + response_stream_shape: Final = self._response_stream_shape() + response_dict: Final = bedrock_event_stream_response(event) + tally.add_event(event, response_dict["headers"]) + return self._parse_message_from_response(response_dict, response_stream_shape) + + def _parse_message_from_event(self, event: "EventStreamMessage") -> str | None: + response_stream_shape: Final = self._response_stream_shape() + return self._parse_message_from_response(bedrock_event_stream_response(event), response_stream_shape) + + def _parse_message_from_response( + self, response_dict: BedrockEventStreamResponseDict, response_stream_shape: "Shape" + ) -> str | None: parsed_response: Final = self.parser.parse(response_dict, response_stream_shape) if response_dict["status_code"] != 200: raise build_bedrock_stream_error(response_dict, response_stream_shape) + event_type: Final = bedrock_event_stream_header(response_dict["headers"], ":event-type") + event_error_status: Final = bedrock_stream_event_error_status(event_type) + if event_type is not None and event_error_status is not None: + raise build_bedrock_stream_event_error(event_type, event_error_status, response_dict["body"]) if "chunk" in parsed_response: chunk = parsed_response.get("chunk") if not chunk: diff --git a/litellm/llms/bedrock/common_utils.py b/litellm/llms/bedrock/common_utils.py index b876bdb2a54..323890624a0 100644 --- a/litellm/llms/bedrock/common_utils.py +++ b/litellm/llms/bedrock/common_utils.py @@ -9,11 +9,15 @@ import functools import json import os import re -from collections.abc import Mapping, Sequence -from typing import TYPE_CHECKING, Any, Final, Literal, TypedDict +from collections.abc import Iterator, Mapping, Sequence +from types import MappingProxyType +from typing import TYPE_CHECKING, Any, Final, Literal + +from typing_extensions import ReadOnly, TypedDict if TYPE_CHECKING: - from botocore.model import Shape + from botocore.eventstream import EventStreamMessage + from botocore.model import ServiceModel, Shape from litellm.types.llms.bedrock import BedrockCreateBatchRequest @@ -1468,10 +1472,77 @@ def get_bedrock_response_stream_shape(): return _load_bedrock_response_stream_shape() +_BEDROCK_STREAM_OUTPUT_SHAPES: Final = ("ConverseStreamOutput", "ResponseStream") + + +def _modeled_error_status(member: Shape) -> int | None: + status: Final = (member.metadata or {}).get("error", {}).get("httpStatusCode") + return None if status is None else int(status) + + +def _structure_members(shape: Shape | None) -> Mapping[str, Shape]: + from botocore.model import StructureShape + + return shape.members if isinstance(shape, StructureShape) else {} + + +def _bedrock_stream_output_members(service_model: ServiceModel) -> Iterator[tuple[str, Shape]]: + for shape_name in _BEDROCK_STREAM_OUTPUT_SHAPES: + yield from _structure_members(service_model.shape_for(shape_name)).items() + + +def _load_bedrock_stream_event_statuses() -> Mapping[str, int | None] | None: + try: + from botocore.loaders import Loader + from botocore.model import ServiceModel + + service_description: Final = TypeAdapter(Mapping[str, object]).validate_python( + Loader().load_service_model("bedrock-runtime", "service-2") + ) + service_model: Final = ServiceModel(service_description) + return MappingProxyType( + {name: _modeled_error_status(member) for name, member in _bedrock_stream_output_members(service_model)} + ) + except Exception as e: + verbose_logger.warning( + "litellm: could not load the bedrock-runtime stream event types, " + "so unrecognized Bedrock stream events will pass through undetected. Error: %s", + e, + ) + return None + + +@functools.lru_cache(maxsize=1) +def get_bedrock_stream_event_statuses() -> Mapping[str, int | None] | None: + """Every modeled Bedrock stream event type mapped to its error status (None for a content event).""" + return _load_bedrock_stream_event_statuses() + + +def bedrock_stream_event_error_status(event_type: str | None) -> int | None: + statuses: Final = get_bedrock_stream_event_statuses() + return None if event_type is None or statuses is None else statuses.get(event_type) + + class BedrockEventStreamResponseDict(TypedDict): - status_code: int - headers: Mapping[str, str] - body: bytes + status_code: ReadOnly[int] + headers: ReadOnly[Mapping[str, object]] + body: ReadOnly[bytes] + + +_BEDROCK_EVENT_STREAM_RESPONSE: Final = TypeAdapter(BedrockEventStreamResponseDict) + + +def bedrock_event_stream_response(event: EventStreamMessage) -> BedrockEventStreamResponseDict: + return _BEDROCK_EVENT_STREAM_RESPONSE.validate_python(event.to_response_dict()) + + +def bedrock_event_stream_header(headers: Mapping[str, object], name: str) -> str | None: + value: Final = headers.get(name) + return value if isinstance(value, str) else None + + +def build_bedrock_stream_event_error(event_type: str, status_code: int, body: bytes) -> BedrockError: + return BedrockError(status_code=status_code, message=f"{event_type} {body.decode(errors='replace')}") def build_bedrock_stream_error( @@ -1484,19 +1555,14 @@ def build_bedrock_stream_error( ResponseStream member's httpStatusCode is the real status. Resolve it from the shape and fall back to the raw status when the type is not modeled. """ - exception_type: Final = response_dict["headers"].get(":exception-type") - decoded_body: Final = response_dict["body"].decode() - message: Final = f"{exception_type} {decoded_body}" if exception_type else decoded_body + exception_type: Final = bedrock_event_stream_header(response_dict["headers"], ":exception-type") + if exception_type is None: + return BedrockError(status_code=response_dict["status_code"], message=response_dict["body"].decode()) - status_code = response_dict["status_code"] - if exception_type is not None and response_stream_shape is not None: - member: Final = response_stream_shape.members.get(exception_type) - if member is not None: - modeled_status: Final = (member.metadata or {}).get("error", {}).get("httpStatusCode") - if modeled_status is not None: - status_code = int(modeled_status) - - return BedrockError(status_code=status_code, message=message) + member: Final = _structure_members(response_stream_shape).get(exception_type) + modeled_status: Final = None if member is None else _modeled_error_status(member) + status_code: Final = response_dict["status_code"] if modeled_status is None else modeled_status + return build_bedrock_stream_event_error(exception_type, status_code, response_dict["body"]) class BedrockEventStreamDecoderBase: diff --git a/tests/integration/_support/upstream.py b/tests/integration/_support/upstream.py index e645126032b..a06c6099f0e 100644 --- a/tests/integration/_support/upstream.py +++ b/tests/integration/_support/upstream.py @@ -83,25 +83,39 @@ def _aws_str_header(name: str, value: str) -> bytes: ) +def _aws_int_header(name: str, value: int) -> bytes: + name_bytes: Final = name.encode() + return struct.pack("!B", len(name_bytes)) + name_bytes + struct.pack("!B", 4) + struct.pack("!i", value) + + +def aws_event_stream_frame(headers: Mapping[str, str | int], payload: bytes) -> bytes: + """One AWS event-stream frame: a string header is wire type 7, an int header wire type 4 (int32).""" + headers_bytes: Final = b"".join( + _aws_str_header(name, value) if isinstance(value, str) else _aws_int_header(name, value) + for name, value in headers.items() + ) + total_length: Final = 12 + len(headers_bytes) + len(payload) + 4 + prelude: Final = struct.pack("!II", total_length, len(headers_bytes)) + prelude_crc: Final = struct.pack("!I", zlib.crc32(prelude) & 0xFFFFFFFF) + message: Final = prelude + prelude_crc + headers_bytes + payload + return message + struct.pack("!I", zlib.crc32(message) & 0xFFFFFFFF) + + def _aws_event_frame( event_type: str, payload: Mapping[str, JsonValue], scenario_id: str, unique_id: str, ) -> bytes: - payload_bytes: Final = json.dumps(payload, separators=(",", ":")).replace( - "$REQUEST_ID", scenario_id - ).replace("$UNIQUE_ID", unique_id).encode() - headers_bytes: Final = ( - _aws_str_header(":event-type", event_type) - + _aws_str_header(":content-type", "application/json") - + _aws_str_header(":message-type", "event") + payload_bytes: Final = ( + json.dumps(payload, separators=(",", ":")) + .replace("$REQUEST_ID", scenario_id) + .replace("$UNIQUE_ID", unique_id) + .encode() + ) + return aws_event_stream_frame( + {":event-type": event_type, ":content-type": "application/json", ":message-type": "event"}, payload_bytes ) - total_length: Final = 12 + len(headers_bytes) + len(payload_bytes) + 4 - prelude: Final = struct.pack("!II", total_length, len(headers_bytes)) - prelude_crc: Final = struct.pack("!I", zlib.crc32(prelude) & 0xFFFFFFFF) - message: Final = prelude + prelude_crc + headers_bytes + payload_bytes - return message + struct.pack("!I", zlib.crc32(message) & 0xFFFFFFFF) class ScenarioStore: @@ -147,7 +161,13 @@ class Provider: status: Final = script.popleft() if status != 200: return JSONResponse( - {"error": {"message": "Controlled provider failure", "type": error_type(status), "code": str(status)}}, + { + "error": { + "message": "Controlled provider failure", + "type": error_type(status), + "code": str(status), + } + }, status_code=status, ) return await chat_completions(request) @@ -244,9 +264,7 @@ class Provider: if raw_body: body: Final = JSON_OBJECT.validate_json(raw_body) if isinstance(body, dict): - self.observations.put( - Observation(request.url.path, request.headers.get("authorization", ""), body) - ) + self.observations.put(Observation(request.url.path, request.headers.get("authorization", ""), body)) if isinstance(response, RoutedResponse): route_key: Final = f"{request.method} /{'/'.join(segments[1:])}" route: Final = next( @@ -300,11 +318,10 @@ class Provider: match response: case JsonResponse(): return Response( - content=json.dumps(response.body, separators=(",", ":")).replace( - "$REQUEST_ID", scenario_id - ).replace( - "$UNIQUE_ID", unique_id - ).encode(), + content=json.dumps(response.body, separators=(",", ":")) + .replace("$REQUEST_ID", scenario_id) + .replace("$UNIQUE_ID", unique_id) + .encode(), media_type=response.content_type, status_code=response.status, ) @@ -321,6 +338,7 @@ class Provider: ) case SseResponse(): if response.frame_delay_ms > 0: + async def stream() -> AsyncIterator[bytes]: for frame in response.frames: yield ( @@ -329,9 +347,11 @@ class Provider: await asyncio.sleep(response.frame_delay_ms / 1000) return StreamingResponse(stream(), media_type=response.content_type) - stream_body: Final = ("\n\n".join(response.frames) + "\n\n").replace( - "$REQUEST_ID", scenario_id - ).replace("$UNIQUE_ID", unique_id) + stream_body: Final = ( + ("\n\n".join(response.frames) + "\n\n") + .replace("$REQUEST_ID", scenario_id) + .replace("$UNIQUE_ID", unique_id) + ) return Response(content=stream_body.encode(), media_type=response.content_type) case EventStreamResponse(): events: Final = ( diff --git a/tests/integration/providers/test_bedrock_converse_stream_event_frames_wire.py b/tests/integration/providers/test_bedrock_converse_stream_event_frames_wire.py new file mode 100644 index 00000000000..bf4f611107d --- /dev/null +++ b/tests/integration/providers/test_bedrock_converse_stream_event_frames_wire.py @@ -0,0 +1,935 @@ +import asyncio +import base64 +import json +import os +import re +import signal +import threading +import uuid +from collections.abc import Callable, Iterable, Mapping +from dataclasses import dataclass +from pathlib import Path +from queue import SimpleQueue +from types import MappingProxyType +from typing import Final, Literal +from urllib.parse import unquote, urlsplit + +import anthropic +import httpx +import openai +import psutil +import pytest +import yaml +from integration._support.client import Gateway, Scenario, eventually +from integration._support.database import read_rows +from integration._support.process import owned_proxy_process +from integration._support.upstream import _aws_event_frame, aws_event_stream_frame +from integration._support.wire import Reply, Request, Wire, wire_server +from pydantic import JsonValue, TypeAdapter + +from litellm.proxy.common_utils.encrypt_decrypt_utils import decrypt_if_encrypted_with + +_MODEL_ID: Final = "global.moonshotai.kimi-k3" +_CONVERSE_MODEL: Final = f"bedrock/converse/{_MODEL_ID}" +_STREAM_TARGET: Final = f"/model/{_MODEL_ID}/converse-stream" +_CONVERSE_TARGET: Final = f"/model/{_MODEL_ID}/converse" +_INVOKE_MODEL_ID: Final = "anthropic.claude-3-haiku-20240307-v1:0" +_INVOKE_MODEL: Final = f"bedrock/invoke/{_INVOKE_MODEL_ID}" +_INVOKE_STREAM_TARGET: Final = f"/model/{_INVOKE_MODEL_ID}/invoke-with-response-stream" +_EVENT_STREAM: Final = "application/vnd.amazon.eventstream" +_ANSWER: Final = "bedrock event frame control" +_REJECTION: Final = "structured output schema uses unsupported regex negative look-ahead" +_THROTTLED: Final = "Too many requests, please wait before trying again" +_UNKNOWN_TYPE: Final = "somethingBedrockAddedLater" +_UNKNOWN_ONLY: Final = "none of its 1 events carried a known event type" +_SIGNING_KEY: Final = os.environ.get("LITELLM_SALT_KEY", "sk-integration-salt") +_JSON_HEADERS: Final = MappingProxyType({":content-type": "application/json", ":message-type": "event"}) +_USAGE: Final[dict[str, JsonValue]] = {"usage": {"inputTokens": 11, "outputTokens": 4, "totalTokens": 15}} +_RESPONSE: Final = json.dumps( + { + "output": {"message": {"role": "assistant", "content": [{"text": _ANSWER}]}}, + "stopReason": "end_turn", + **_USAGE, + "metrics": {"latencyMs": 1}, + } +).encode() +_JSON: Final = TypeAdapter(dict[str, JsonValue]) +_AWS: Final[dict[str, JsonValue]] = { + "aws_access_key_id": "AKIASCRIPTEDPROVIDER", + "aws_secret_access_key": "scripted-secret", + "aws_region_name": "us-east-1", +} +_EXTRA: Final[dict[str, JsonValue]] = {"num_retries": 0, "cache": {"no-cache": True}} +_PROMPT: Final = "What does the gateway do with this stream?" +_USER_TURN: Final[dict[str, JsonValue]] = {"role": "user", "content": _PROMPT} +_CONVERSE_USER_TURN: Final[dict[str, JsonValue]] = {"role": "user", "content": [{"text": _PROMPT}]} +_STARTED_WORKER: Final = re.compile(r"Started server process \[(\d+)\]") +_CALL_INDEX: Final = re.compile(r"call-[0-9a-f]{32}-(\d+)") +_PLAIN: Final = "bedrock-event-frames-plain" + +Endpoint = Literal["chat", "messages", "responses"] + + +def _error_body(message: str) -> str: + return json.dumps({"message": message}, separators=(",", ":")) + + +def _frame(event_type: str, payload: Mapping[str, JsonValue]) -> bytes: + return _aws_event_frame(event_type, payload, "sc", "u") + + +def _typed_frame(headers: Mapping[str, str | int], payload: bytes) -> bytes: + return aws_event_stream_frame({**headers, **_JSON_HEADERS}, payload) + + +def _exception_message_frame(exception_type: str, message: str) -> bytes: + return aws_event_stream_frame( + {":exception-type": exception_type, ":content-type": "application/json", ":message-type": "exception"}, + _error_body(message).encode(), + ) + + +def _text_frames(text: str) -> tuple[bytes, ...]: + return ( + _frame("messageStart", {"role": "assistant"}), + _frame("contentBlockDelta", {"delta": {"text": text}, "contentBlockIndex": 0}), + _frame("contentBlockStop", {"contentBlockIndex": 0}), + ) + + +_NORMAL: Final = b"".join( + (*_text_frames(_ANSWER), _frame("messageStop", {"stopReason": "end_turn"}), _frame("metadata", _USAGE)) +) +_VALIDATION_FRAME: Final = _frame("validationException", {"message": _REJECTION}) +_THROTTLING_FRAME: Final = _frame("throttlingException", {"message": _THROTTLED}) +_UNKNOWN_FRAME: Final = _frame(_UNKNOWN_TYPE, {"future": True}) +_UNKNOWN_BESIDE_KNOWN: Final = b"".join( + ( + *_text_frames(_ANSWER), + _UNKNOWN_FRAME, + _frame("messageStop", {"stopReason": "end_turn"}), + _frame("metadata", _USAGE), + ) +) +_THROTTLED_AFTER_TEXT: Final = b"".join((*_text_frames(_ANSWER), _THROTTLING_FRAME)) +_EMPTY_DELTA_BESIDE_KNOWN: Final = b"".join( + ( + _frame("messageStart", {"role": "assistant"}), + _frame("contentBlockDelta", {}), + _frame("contentBlockDelta", {"delta": {"text": _ANSWER}, "contentBlockIndex": 0}), + _frame("messageStop", {"stopReason": "end_turn"}), + _frame("metadata", _USAGE), + ) +) +_EXCEPTION_MID_STREAM: Final = b"".join((*_text_frames(_ANSWER), _VALIDATION_FRAME)) +_EXCEPTION_MESSAGE_MID_STREAM: Final = b"".join( + (*_text_frames(_ANSWER), _exception_message_frame("throttlingException", _THROTTLED)) +) + + +def _invoke_chunk(event: Mapping[str, JsonValue]) -> bytes: + return _frame("chunk", {"bytes": base64.b64encode(json.dumps(event).encode()).decode()}) + + +_INVOKE_STREAM: Final = b"".join( + ( + _invoke_chunk( + { + "type": "message_start", + "message": { + "id": "msg_invoke", + "type": "message", + "role": "assistant", + "content": [], + "model": _INVOKE_MODEL_ID, + "stop_reason": None, + "usage": {"input_tokens": 11, "output_tokens": 1}, + }, + } + ), + _invoke_chunk({"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}), + _invoke_chunk({"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": _ANSWER}}), + _invoke_chunk({"type": "content_block_stop", "index": 0}), + _invoke_chunk({"type": "message_delta", "delta": {"stop_reason": "end_turn"}, "usage": {"output_tokens": 4}}), + _invoke_chunk({"type": "message_stop"}), + ) +) + + +@dataclass(frozen=True, slots=True) +class _Streamed: + status: int + call_id: str + lines: tuple[str, ...] + + @property + def text(self) -> str: + return "\n".join(self.lines) + + +@dataclass(frozen=True, slots=True) +class _Call: + endpoint: Endpoint + user: str + index: int + + +@dataclass(frozen=True, slots=True) +class _Served: + call: _Call + status: int + call_id: str + text: str + + +def _stream_peer(frames: bytes) -> Callable[[Request], Reply]: + def respond(request: Request) -> Reply: + if unquote(request.target) in (_STREAM_TARGET, _INVOKE_STREAM_TARGET): + return Reply(body=frames, content_type=_EVENT_STREAM) + return Reply(body=_RESPONSE) + + return respond + + +def _converse_deployment(scenario: Scenario, wire: Wire, **extra: JsonValue) -> str: + return scenario.model(model=_CONVERSE_MODEL, api_base=wire.url, **_AWS, **extra) + + +def _auth(gateway: Gateway) -> dict[str, str]: + return {"Authorization": f"Bearer {gateway.key}"} + + +def _proxy_url(gateway: Gateway) -> str: + return str(gateway.client.base_url).rstrip("/") + + +def _path(endpoint: Endpoint) -> str: + match endpoint: + case "chat": + return "/v1/chat/completions" + case "messages": + return "/v1/messages" + case "responses": + return "/v1/responses" + + +def _body(endpoint: Endpoint, model: str, *, user: str | None = None) -> dict[str, JsonValue]: + marker: Final[dict[str, JsonValue]] = {} if user is None else {"user": user} + match endpoint: + case "chat": + return {"model": model, "messages": [_USER_TURN], "max_tokens": 16, "stream": True, **_EXTRA, **marker} + case "messages": + return {"model": model, "messages": [_USER_TURN], "max_tokens": 16, "stream": True, **_EXTRA} + case "responses": + return {"model": model, "input": _PROMPT, "stream": True, **_EXTRA, **marker} + + +def _stream( + gateway: Gateway, endpoint: Endpoint, body: Mapping[str, JsonValue], *, key: str | None = None +) -> _Streamed: + headers: Final = _auth(gateway) if key is None else {"Authorization": f"Bearer {key}"} + with gateway.client.stream("POST", _path(endpoint), json=body, headers=headers) as response: + lines: Final = tuple(line for line in response.iter_lines() if line) + return _Streamed(response.status_code, response.headers.get("x-litellm-call-id", ""), lines) + + +def _sse_payloads(lines: Iterable[str]) -> tuple[dict[str, JsonValue], ...]: + return tuple(json.loads(line[6:]) for line in lines if line.startswith("data: ") and line != "data: [DONE]") + + +def _sse_events(lines: Iterable[str]) -> tuple[str, ...]: + return tuple(line[7:] for line in lines if line.startswith("event: ")) + + +def _first_choice(chunk: Mapping[str, JsonValue]) -> dict[str, JsonValue] | None: + choices: Final = chunk.get("choices") + return _JSON.validate_python(choices[0]) if isinstance(choices, list) and choices else None + + +def _chat_text(chunks: Iterable[dict[str, JsonValue]]) -> str: + choices: Final = tuple(choice for choice in map(_first_choice, chunks) if choice is not None) + return "".join(str(_JSON.validate_python(choice["delta"]).get("content") or "") for choice in choices) + + +def _finish_reasons(chunks: Iterable[dict[str, JsonValue]]) -> tuple[JsonValue, ...]: + return tuple(choice.get("finish_reason") for choice in map(_first_choice, chunks) if choice is not None) + + +def _chat_id(chunks: Iterable[dict[str, JsonValue]]) -> str: + (identity,) = {str(chunk["id"]) for chunk in chunks if "id" in chunk} + return identity + + +def _message_id(payloads: Iterable[dict[str, JsonValue]]) -> str: + (started,) = tuple(payload for payload in payloads if payload.get("type") == "message_start") + return str(_JSON.validate_python(started["message"])["id"]) + + +def _messages_text(payloads: Iterable[dict[str, JsonValue]]) -> str: + deltas: Final = tuple(payload for payload in payloads if payload.get("type") == "content_block_delta") + return "".join(str(_JSON.validate_python(delta["delta"]).get("text") or "") for delta in deltas) + + +def _responses_text(events: Iterable[dict[str, JsonValue]]) -> str: + return "".join( + str(event.get("delta") or "") for event in events if event.get("type") == "response.output_text.delta" + ) + + +def _inner_response_id(identity: str) -> str: + managed: Final = decrypt_if_encrypted_with(identity.removeprefix("resp_"), _SIGNING_KEY) + assert managed is not None, identity + issued: Final = managed.split(";", 1)[0].rsplit("response_id:", 1)[1] + decoded: Final = base64.b64decode(issued.removeprefix("resp_")).decode() + return decoded.rsplit("response_id:", 1)[1] + + +def _completed_response_id(events: Iterable[dict[str, JsonValue]]) -> str: + (completed,) = tuple(event for event in events if event.get("type") == "response.completed") + return _inner_response_id(str(_JSON.validate_python(completed["response"])["id"])) + + +def _only_received(wire: Wire) -> tuple[str, dict[str, JsonValue]]: + (request,) = wire.drain() + return unquote(request.target), _JSON.validate_python(json.loads(request.body)) + + +def _assert_stream_request(wire: Wire, target: str = _STREAM_TARGET) -> dict[str, JsonValue]: + received_target, received = _only_received(wire) + assert received_target == target, received_target + return received + + +def _spend_row(request_id: str) -> dict[str, JsonValue]: + assert request_id, "No id to look the spend row up by" + (row,) = eventually( + lambda: read_rows( + 'SELECT request_id, status, call_type, end_user FROM "LiteLLM_SpendLogs" WHERE request_id=%s', + (request_id,), + ), + lambda found: len(found) >= 1, + seconds=70, + ) + return row + + +def _failure_row(call_id: str) -> dict[str, JsonValue]: + row: Final = _spend_row(call_id) + assert row["status"] == "failure", row + return row + + +def _success_row(request_id: str) -> dict[str, JsonValue]: + row: Final = _spend_row(request_id) + assert row["status"] == "success", row + return row + + +def _rows_for(request_ids: frozenset[str], *, expected: int) -> tuple[dict[str, JsonValue], ...]: + rows: Final = eventually( + lambda: read_rows( + 'SELECT request_id, status FROM "LiteLLM_SpendLogs" WHERE request_id = ANY(string_to_array(%s, %s))', + (",".join(sorted(request_ids)), ","), + ), + lambda found: len(found) >= expected, + seconds=90, + ) + return tuple(rows) + + +def _assert_rejected_chat(streamed: _Streamed, status: int, message: str) -> None: + assert streamed.status == status, (streamed.status, streamed.text) + error: Final = _JSON.validate_python(json.loads(streamed.text)["error"]) + assert message in str(error["message"]), streamed.text + assert str(error["code"]) == str(status), streamed.text + + +def _unescaped(text: str) -> str: + return text.replace('\\"', '"') + + +def _assert_rejected_stream_body(streamed: _Streamed, message: str) -> None: + assert streamed.status == 200, (streamed.status, streamed.text) + assert message in _unescaped(streamed.text), streamed.text + assert _ANSWER not in streamed.text, streamed.text + + +def test_r01_chat_stream_with_a_validation_exception_frame_first_is_a_400_and_a_failure_row(gateway: Gateway) -> None: + with wire_server(_stream_peer(_VALIDATION_FRAME)) as wire, gateway.scenario() as scenario: + model: Final = _converse_deployment(scenario, wire) + streamed: Final = _stream(gateway, "chat", _body("chat", model)) + _assert_rejected_chat(streamed, 400, f"validationException {_error_body(_REJECTION)}") + received: Final = _assert_stream_request(wire) + assert received["messages"] == [_CONVERSE_USER_TURN], received + _failure_row(streamed.call_id) + + +async def _consume_openai_chat_stream(client: openai.AsyncOpenAI, model: str) -> None: + stream = await client.chat.completions.create( + model=model, messages=[_USER_TURN], max_tokens=16, stream=True, extra_body=_EXTRA + ) + _ = [chunk async for chunk in stream] + + +async def test_r02_openai_async_sdk_stream_with_a_validation_exception_frame_first_raises_bad_request( + gateway: Gateway, +) -> None: + with wire_server(_stream_peer(_VALIDATION_FRAME)) as wire, gateway.scenario() as scenario: + model: Final = _converse_deployment(scenario, wire) + async with openai.AsyncOpenAI( + base_url=f"{_proxy_url(gateway)}/v1", api_key=gateway.key, max_retries=0 + ) as client: + with pytest.raises(openai.BadRequestError, match=re.escape(_REJECTION)) as raised: + await _consume_openai_chat_stream(client, model) + _assert_stream_request(wire) + _failure_row(raised.value.response.headers.get("x-litellm-call-id", "")) + + +def test_r03_chat_stream_throttled_after_text_delivers_the_text_then_the_error_and_no_stop_chunk( + gateway: Gateway, +) -> None: + with wire_server(_stream_peer(_THROTTLED_AFTER_TEXT)) as wire, gateway.scenario() as scenario: + model: Final = _converse_deployment(scenario, wire) + streamed: Final = _stream(gateway, "chat", _body("chat", model)) + assert streamed.status == 200, streamed.text + chunks: Final = _sse_payloads(streamed.lines) + assert _chat_text(chunks) == _ANSWER, streamed.text + assert "throttlingException" in streamed.text and _THROTTLED in streamed.text, streamed.text + assert "stop" not in _finish_reasons(chunks), streamed.text + _assert_stream_request(wire) + _failure_row(streamed.call_id) + + +def test_r04_messages_stream_with_a_validation_exception_frame_first_emits_an_error_event_and_no_message_stop( + gateway: Gateway, +) -> None: + with wire_server(_stream_peer(_VALIDATION_FRAME)) as wire, gateway.scenario() as scenario: + model: Final = _converse_deployment(scenario, wire) + streamed: Final = _stream(gateway, "messages", _body("messages", model)) + _assert_rejected_stream_body(streamed, f"validationException {_error_body(_REJECTION)}") + events: Final = _sse_events(streamed.lines) + assert "error" in events and "message_stop" not in events, streamed.text + _assert_stream_request(wire) + + +async def test_r05_anthropic_async_sdk_stream_with_a_validation_exception_frame_first_raises( + gateway: Gateway, +) -> None: + with wire_server(_stream_peer(_VALIDATION_FRAME)) as wire, gateway.scenario() as scenario: + model: Final = _converse_deployment(scenario, wire) + async with anthropic.AsyncAnthropic(base_url=_proxy_url(gateway), api_key=gateway.key, max_retries=0) as client: + with pytest.raises(anthropic.APIError, match=re.escape(_REJECTION)): + async with client.messages.stream( + model=model, max_tokens=16, messages=[_USER_TURN], extra_body=_EXTRA + ) as stream: + _ = [event async for event in stream] + _assert_stream_request(wire) + + +def test_r06_responses_stream_with_a_validation_exception_frame_first_fails_the_response(gateway: Gateway) -> None: + with wire_server(_stream_peer(_VALIDATION_FRAME)) as wire, gateway.scenario() as scenario: + model: Final = _converse_deployment(scenario, wire) + streamed: Final = _stream(gateway, "responses", _body("responses", model)) + _assert_rejected_stream_body(streamed, f"validationException {_error_body(_REJECTION)}") + types: Final = tuple(str(event["type"]) for event in _sse_payloads(streamed.lines)) + assert "response.failed" in types and "response.completed" not in types, streamed.text + _assert_stream_request(wire) + _failure_row(streamed.call_id) + + +def test_r07_chat_stream_whose_only_frame_has_an_unknown_event_type_is_a_502_naming_the_type( + gateway: Gateway, +) -> None: + with wire_server(_stream_peer(_UNKNOWN_FRAME)) as wire, gateway.scenario() as scenario: + model: Final = _converse_deployment(scenario, wire) + streamed: Final = _stream(gateway, "chat", _body("chat", model)) + _assert_rejected_chat(streamed, 502, f"{_UNKNOWN_ONLY} (event types=['{_UNKNOWN_TYPE}']") + _assert_stream_request(wire) + _failure_row(streamed.call_id) + + +def _assert_text_stream(gateway: Gateway, endpoint: Endpoint, frames: bytes) -> None: + marker: Final = f"call-{uuid.uuid4().hex}" + with wire_server(_stream_peer(frames)) as wire, gateway.scenario() as scenario: + model: Final = _converse_deployment(scenario, wire) + streamed: Final = _stream(gateway, endpoint, _body(endpoint, model, user=marker)) + assert streamed.status == 200, streamed.text + payloads: Final = _sse_payloads(streamed.lines) + match endpoint: + case "chat": + assert _chat_text(payloads) == _ANSWER, streamed.text + assert _finish_reasons(payloads)[-1] == "stop", streamed.text + _success_row(_chat_id(payloads)) + case "messages": + assert _messages_text(payloads) == _ANSWER, streamed.text + assert _sse_events(streamed.lines)[-1] == "message_stop", streamed.text + _success_row(_message_id(payloads)) + case "responses": + assert _responses_text(payloads) == _ANSWER, streamed.text + assert str(payloads[-1]["type"]) == "response.completed", streamed.text + assert _success_row(_completed_response_id(payloads))["end_user"] == marker + _assert_stream_request(wire) + + +@pytest.mark.parametrize( + "endpoint", ("chat", "messages", "responses"), ids=("r08-chat", "r08-messages", "r08-responses") +) +def test_r08_an_unknown_frame_between_known_frames_leaves_the_text_and_the_success_row_intact( + gateway: Gateway, endpoint: Endpoint +) -> None: + _assert_text_stream(gateway, endpoint, _UNKNOWN_BESIDE_KNOWN) + + +@pytest.mark.parametrize( + "endpoint", ("chat", "messages", "responses"), ids=("r09-chat", "r09-messages", "r09-responses") +) +def test_r09_a_normal_converse_stream_delivers_the_text_and_a_success_row(gateway: Gateway, endpoint: Endpoint) -> None: + _assert_text_stream(gateway, endpoint, _NORMAL) + + +def test_r10_a_non_streaming_converse_call_is_untouched(gateway: Gateway) -> None: + with wire_server(_stream_peer(_NORMAL)) as wire, gateway.scenario() as scenario: + model: Final = _converse_deployment(scenario, wire) + response: Final = gateway.request( + "POST", "/v1/chat/completions", {"model": model, "messages": [_USER_TURN], "max_tokens": 16, **_EXTRA} + ) + assert response.status_code == 200, response.text + assert response.json()["choices"][0]["message"]["content"] == _ANSWER, response.text + _assert_stream_request(wire, _CONVERSE_TARGET) + _success_row(str(response.json()["id"])) + + +def test_r11_an_invoke_framed_anthropic_stream_is_untouched(gateway: Gateway) -> None: + with wire_server(_stream_peer(_INVOKE_STREAM)) as wire, gateway.scenario() as scenario: + model: Final = scenario.model( + model=_INVOKE_MODEL, api_key=None, aws_bedrock_runtime_endpoint=wire.url, api_base=wire.url, **_AWS + ) + streamed: Final = _stream(gateway, "chat", _body("chat", model)) + assert streamed.status == 200, streamed.text + chunks: Final = _sse_payloads(streamed.lines) + assert _chat_text(chunks) == _ANSWER, streamed.text + assert _finish_reasons(chunks)[-1] == "stop", streamed.text + _assert_stream_request(wire, _INVOKE_STREAM_TARGET) + _success_row(_chat_id(chunks)) + + +@pytest.mark.parametrize( + ("exception_type", "expected"), + (("throttlingException", 429), ("somethingNewException", 400)), + ids=("r12", "r13"), +) +def test_r12_r13_an_exception_message_frame_keeps_its_modeled_status( + gateway: Gateway, exception_type: str, expected: int +) -> None: + with ( + wire_server(_stream_peer(_exception_message_frame(exception_type, _THROTTLED))) as wire, + gateway.scenario() as scenario, + ): + model: Final = _converse_deployment(scenario, wire) + streamed: Final = _stream(gateway, "chat", _body("chat", model)) + _assert_rejected_chat(streamed, expected, _THROTTLED) + _assert_stream_request(wire) + _failure_row(streamed.call_id) + + +@pytest.mark.parametrize( + "frames", (_EXCEPTION_MID_STREAM, _EXCEPTION_MESSAGE_MID_STREAM), ids=("r14-event-frame", "r15-exception-message") +) +def test_r14_r15_passthrough_converse_stream_relays_an_exception_frame_byte_for_byte( + gateway: Gateway, frames: bytes +) -> None: + with wire_server(_stream_peer(frames)) as wire, gateway.scenario() as scenario: + deployment: Final = scenario.model( + model=f"bedrock/{_MODEL_ID}", api_base=wire.url, aws_bedrock_runtime_endpoint=wire.url, **_AWS + ) + response: Final = gateway.request( + "POST", f"/bedrock/model/{deployment}/converse-stream", {"messages": [_CONVERSE_USER_TURN]} + ) + assert response.status_code == 200, response.text + assert response.headers.get("content-type") == _EVENT_STREAM, dict(response.headers) + assert response.content == frames, response.content + _assert_stream_request(wire) + + +_HEADERLESS: Final = _typed_frame({}, b'{"future": true}') +_INT_TYPED: Final = _typed_frame({":event-type": 7}, b'{"future": true}') +_EMPTY_TYPED: Final = _typed_frame({":event-type": ""}, b'{"future": true}') +_LONG_TYPE: Final = "x" * 5120 +_LONG_TYPED: Final = _typed_frame({":event-type": _LONG_TYPE}, b'{"future": true}') + + +@pytest.mark.parametrize( + ("frames", "named"), + ( + (_HEADERLESS, f"{_UNKNOWN_ONLY} (event types=['']"), + (_INT_TYPED, f"{_UNKNOWN_ONLY} (event types=['']"), + (_EMPTY_TYPED, f"{_UNKNOWN_ONLY} (event types=['']"), + (_LONG_TYPED, f"{_UNKNOWN_ONLY} (event types=['{_LONG_TYPE}']"), + ( + _UNKNOWN_FRAME + _UNKNOWN_FRAME, + f"none of its 2 events carried a known event type (event types=['{_UNKNOWN_TYPE}']", + ), + ), + ids=( + "s01-no-event-type", + "s02-int-event-type", + "s03-empty-event-type", + "s04-5kb-event-type", + "s05-same-type-twice", + ), +) +def test_s01_to_s05_odd_event_type_headers_alone_are_a_502_that_names_what_arrived( + gateway: Gateway, frames: bytes, named: str +) -> None: + with wire_server(_stream_peer(frames)) as wire, gateway.scenario() as scenario: + model: Final = _converse_deployment(scenario, wire) + streamed: Final = _stream(gateway, "chat", _body("chat", model)) + _assert_rejected_chat(streamed, 502, named) + _assert_stream_request(wire) + _failure_row(streamed.call_id) + + +def test_s06_a_validation_exception_frame_with_a_non_utf8_body_is_a_400_with_the_bytes_replaced( + gateway: Gateway, +) -> None: + frame: Final = _typed_frame({":event-type": "validationException"}, b'{"message": "bad \xff\xfe bytes"}') + with wire_server(_stream_peer(frame)) as wire, gateway.scenario() as scenario: + model: Final = _converse_deployment(scenario, wire) + streamed: Final = _stream(gateway, "chat", _body("chat", model)) + _assert_rejected_chat(streamed, 400, 'validationException {"message": "bad �� bytes"}') + _assert_stream_request(wire) + _failure_row(streamed.call_id) + + +def test_s07_a_validation_exception_frame_with_an_empty_body_is_still_a_400(gateway: Gateway) -> None: + with wire_server(_stream_peer(_typed_frame({":event-type": "validationException"}, b""))) as wire: + with gateway.scenario() as scenario: + model: Final = _converse_deployment(scenario, wire) + streamed: Final = _stream(gateway, "chat", _body("chat", model)) + _assert_rejected_chat(streamed, 400, "validationException") + _assert_stream_request(wire) + _failure_row(streamed.call_id) + + +def test_s08_a_known_frame_with_an_empty_body_beside_normal_frames_keeps_the_text(gateway: Gateway) -> None: + with wire_server(_stream_peer(_EMPTY_DELTA_BESIDE_KNOWN)) as wire, gateway.scenario() as scenario: + model: Final = _converse_deployment(scenario, wire) + streamed: Final = _stream(gateway, "chat", _body("chat", model)) + assert streamed.status == 200, streamed.text + chunks: Final = _sse_payloads(streamed.lines) + assert _chat_text(chunks) == _ANSWER, streamed.text + assert _finish_reasons(chunks)[-1] == "stop", streamed.text + _assert_stream_request(wire) + _success_row(_chat_id(chunks)) + + +def test_s09_an_unauthenticated_stream_never_reaches_the_peer(gateway: Gateway) -> None: + with wire_server(_stream_peer(_VALIDATION_FRAME)) as wire, gateway.scenario() as scenario: + model: Final = _converse_deployment(scenario, wire) + streamed: Final = _stream(gateway, "chat", _body("chat", model), key="sk-integration-bogus") + assert streamed.status == 401, streamed.text + assert wire.drain() == (), streamed.text + + +def test_s10_a_rejected_stream_leaves_a_healthy_deployment_serving(gateway: Gateway) -> None: + with ( + wire_server(_stream_peer(_VALIDATION_FRAME)) as rejecting, + wire_server(_stream_peer(_NORMAL)) as healthy, + gateway.scenario() as scenario, + ): + rejected_model: Final = _converse_deployment(scenario, rejecting) + healthy_model: Final = _converse_deployment(scenario, healthy) + _assert_rejected_chat(_stream(gateway, "chat", _body("chat", rejected_model)), 400, "validationException") + streamed: Final = _stream(gateway, "chat", _body("chat", healthy_model)) + assert streamed.status == 200, streamed.text + assert _chat_text(_sse_payloads(streamed.lines)) == _ANSWER, streamed.text + assert len(rejecting.drain()) == 1 and len(healthy.drain()) == 1 + + +def test_e01_a_rejected_stream_is_never_served_from_the_response_cache(gateway: Gateway) -> None: + with wire_server(_stream_peer(_VALIDATION_FRAME)) as wire, gateway.scenario() as scenario: + model: Final = _converse_deployment(scenario, wire) + cached: Final = {key: value for key, value in _body("chat", model).items() if key != "cache"} + first: Final = _stream(gateway, "chat", cached) + second: Final = _stream(gateway, "chat", cached) + _assert_rejected_chat(first, 400, "validationException") + _assert_rejected_chat(second, 400, "validationException") + assert len(wire.drain()) == 2, (first.text, second.text) + + +def _sibling_deployment(scenario: Scenario, name: str, wire: Wire, **extra: JsonValue) -> None: + created: Final = scenario.gateway.post( + "/model/new", + { + "model_name": name, + "litellm_params": {"model": _CONVERSE_MODEL, "api_base": wire.url, **_AWS, **extra}, + "model_info": {}, + }, + ) + identity: Final = _JSON.validate_python(created["model_info"])["id"] + assert isinstance(identity, str), created + scenario.cleanups.callback(scenario.delete_model, identity) + + +def test_e02_a_throttling_frame_first_is_a_429_after_one_attempt_even_with_retries_and_a_sibling_deployment( + gateway: Gateway, +) -> None: + with wire_server(_stream_peer(_THROTTLING_FRAME)) as wire, gateway.scenario() as scenario: + model: Final = _converse_deployment(scenario, wire, num_retries=2) + _sibling_deployment(scenario, model, wire, num_retries=2) + streamed: Final = _stream(gateway, "chat", {**_body("chat", model), "num_retries": 2}) + _assert_rejected_chat(streamed, 429, f"throttlingException {_error_body(_THROTTLED)}") + attempts: Final = len(wire.drain()) + assert attempts == 1, attempts + _failure_row(streamed.call_id) + + +def test_e03_three_rejected_streams_land_one_failure_row_each(gateway: Gateway) -> None: + with wire_server(_stream_peer(_VALIDATION_FRAME)) as wire, gateway.scenario() as scenario: + model: Final = _converse_deployment(scenario, wire) + streamed: Final = tuple(_stream(gateway, "chat", _body("chat", model)) for _ in range(3)) + for item in streamed: + _assert_rejected_chat(item, 400, "validationException") + call_ids: Final = frozenset(item.call_id for item in streamed) + assert len(call_ids) == 3, streamed + rows: Final = _rows_for(call_ids, expected=3) + assert {str(row["request_id"]) for row in rows} == call_ids, rows + assert all(row["status"] == "failure" for row in rows), rows + assert len(wire.drain()) == 3 + + +def _calls(marker: str, endpoint: Endpoint, indexes: range) -> tuple[_Call, ...]: + return tuple(_Call(endpoint, f"{marker}-{index}", index) for index in indexes) + + +def _burst_body(model: str, call: _Call) -> dict[str, JsonValue]: + prompt: Final = f"{_PROMPT} {call.user}" + match call.endpoint: + case "chat": + return {**_body("chat", model, user=call.user), "messages": [{"role": "user", "content": prompt}]} + case "messages": + return {**_body("messages", model), "messages": [{"role": "user", "content": prompt}]} + case "responses": + return {**_body("responses", model, user=call.user), "input": prompt} + + +def _call_index(request: Request) -> int: + found: Final = _CALL_INDEX.search(request.body.decode()) + assert found is not None, request.body + return int(found.group(1)) + + +async def _send(client: httpx.AsyncClient, key: str, model: str, call: _Call) -> _Served: + async with client.stream( + "POST", _path(call.endpoint), json=_burst_body(model, call), headers={"Authorization": f"Bearer {key}"} + ) as response: + raw: Final = await response.aread() + return _Served(call, response.status_code, response.headers.get("x-litellm-call-id", ""), raw.decode()) + + +async def _burst( + base_url: str, key: str, model: str, calls: tuple[_Call, ...], *, tolerate_transport_errors: bool = False +) -> tuple[_Served, ...]: + async with httpx.AsyncClient(base_url=base_url, timeout=60, trust_env=False) as client: + results: Final = await asyncio.gather( + *(_send(client, key, model, call) for call in calls), return_exceptions=tolerate_transport_errors + ) + for result in results: + assert not isinstance(result, BaseException) or isinstance(result, httpx.TransportError), repr(result) + return tuple(result for result in results if isinstance(result, _Served)) + + +def _carries_text(served: _Served) -> bool: + return _ANSWER in served.text + + +def _is_rejection(served: _Served, message: str) -> bool: + return ( + message in _unescaped(served.text) and not _carries_text(served) and '"finish_reason":"stop"' not in served.text + ) + + +def _served_success_id(served: _Served) -> str: + payloads: Final = _sse_payloads(served.text.splitlines()) + match served.call.endpoint: + case "chat": + return _chat_id(payloads) + case "messages": + return _message_id(payloads) + case "responses": + return _completed_response_id(payloads) + + +async def test_c01_a_mixed_burst_of_normal_rejected_and_unknown_streams_sorts_every_call_and_row( + gateway: Gateway, +) -> None: + marker: Final = f"call-{uuid.uuid4().hex}" + calls: Final = ( + *_calls(marker, "chat", range(0, 10)), + *_calls(marker, "messages", range(10, 20)), + *_calls(marker, "responses", range(20, 30)), + ) + + def respond(request: Request) -> Reply: + match _call_index(request) % 3: + case 1: + return Reply(body=_VALIDATION_FRAME, content_type=_EVENT_STREAM) + case 2: + return Reply(body=_UNKNOWN_FRAME, content_type=_EVENT_STREAM) + case _: + return Reply(body=_NORMAL, content_type=_EVENT_STREAM) + + with wire_server(respond) as wire, gateway.scenario() as scenario: + model: Final = _converse_deployment(scenario, wire) + served: Final = await _burst(_proxy_url(gateway), gateway.key, model, calls) + assert len(served) == 30 + normal: Final = tuple(item for item in served if item.call.index % 3 == 0) + rejected: Final = tuple(item for item in served if item.call.index % 3 == 1) + unknown: Final = tuple(item for item in served if item.call.index % 3 == 2) + for item in normal: + assert item.status == 200 and _carries_text(item), (item.call, item.status, item.text) + for item in rejected: + assert _is_rejection(item, _REJECTION), (item.call, item.status, item.text) + for item in unknown: + assert _is_rejection(item, _UNKNOWN_ONLY), (item.call, item.status, item.text) + failed_ids: Final = frozenset( + item.call_id for item in (*rejected, *unknown) if item.call.endpoint != "messages" + ) + assert len(failed_ids) == 13, failed_ids + failure_rows: Final = _rows_for(failed_ids, expected=13) + assert {str(row["request_id"]) for row in failure_rows} == failed_ids, failure_rows + assert all(row["status"] == "failure" for row in failure_rows), failure_rows + success_ids: Final = frozenset(_served_success_id(item) for item in normal) + assert len(success_ids) == 10, success_ids + success_rows: Final = _rows_for(success_ids, expected=10) + assert {str(row["request_id"]) for row in success_rows} == success_ids, success_rows + assert all(row["status"] == "success" for row in success_rows), success_rows + assert len(wire.drain()) == 30 + + +def _owned_config(wire: Wire, directory: Path) -> Path: + base: Final = _JSON.validate_python(yaml.safe_load(Path("tests/integration/proxy_config.yaml").read_text())) + config: Final[dict[str, JsonValue]] = { + **base, + "model_list": [ + { + "model_name": _PLAIN, + "litellm_params": { + "model": _CONVERSE_MODEL, + "api_base": wire.url, + "api_key": "integration-provider-key", + **_AWS, + }, + } + ], + "router_settings": {**_JSON.validate_python(base["router_settings"]), "num_retries": 0}, + } + path: Final = directory / "bedrock-event-frames.yaml" + path.write_text(yaml.safe_dump(config)) + return path + + +def _open_peer_connections(pid: int, peer_url: str) -> int: + port: Final = urlsplit(peer_url).port + return sum( + 1 + for connection in psutil.Process(pid).net_connections(kind="tcp") + if connection.status == psutil.CONN_ESTABLISHED and connection.raddr and connection.raddr.port == port + ) + + +def _held_rejection(release: threading.Event, held_indexes: SimpleQueue[int]) -> Callable[[Request], Reply]: + first: Final = _frame("messageStart", {"role": "assistant"}) + + def held(request: Request) -> Reply: + held_indexes.put(_call_index(request)) + return Reply(content_type=_EVENT_STREAM, chunks=(first, _VALIDATION_FRAME), gate_after_first=release) + + return held + + +@pytest.mark.timeout(600) +async def test_c02_worker_sigkill_mid_burst_leaves_the_sibling_rejecting_the_held_streams( + gateway: Gateway, tmp_path: Path +) -> None: + marker: Final = f"call-{uuid.uuid4().hex}" + again: Final = f"call-{uuid.uuid4().hex}" + calls: Final = _calls(marker, "chat", range(20)) + release: Final = threading.Event() + held_indexes: Final[SimpleQueue[int]] = SimpleQueue() + with wire_server(_held_rejection(release, held_indexes)) as wire: + config: Final = _owned_config(wire, tmp_path) + with owned_proxy_process(gateway, tmp_path, {}, config=config, workers=2) as owned: + candidate: Final = owned.gateway + workers: Final = eventually( + lambda: tuple(int(pid) for pid in _STARTED_WORKER.findall(owned.log.read_text())), + lambda pids: len(pids) == 2, + seconds=30, + ) + burst: Final = asyncio.create_task( + _burst(_proxy_url(candidate), candidate.key, _PLAIN, calls, tolerate_transport_errors=True) + ) + await asyncio.to_thread(eventually, held_indexes.qsize, lambda size: size == 20, 60) + held_by: Final = MappingProxyType({pid: _open_peer_connections(pid, wire.url) for pid in workers}) + assert sum(held_by.values()) == 20, held_by + victim_pid, survivor_pid = sorted(workers, key=held_by.__getitem__) + psutil.Process(victim_pid).send_signal(signal.SIGKILL) + release.set() + served: Final = await burst + assert held_by[survivor_pid] >= 10, held_by + assert len(served) == held_by[survivor_pid], (held_by, len(served)) + for item in served: + assert item.status in (200, 400) and _is_rejection(item, _REJECTION), ( + item.call, + item.status, + item.text, + ) + eventually( + lambda: len(_STARTED_WORKER.findall(owned.log.read_text())), lambda count: count == 3, seconds=60 + ) + follow_up: Final = await _burst( + _proxy_url(candidate), candidate.key, _PLAIN, _calls(again, "chat", range(6)) + ) + assert len(follow_up) == 6 + for item in follow_up: + assert item.status in (200, 400) and _is_rejection(item, _REJECTION), ( + item.call, + item.status, + item.text, + ) + assert len(wire.drain()) == 26 + served_ids: Final = frozenset(item.call_id for item in (*served, *follow_up)) + assert len(served_ids) == len(served) + 6, served_ids + rows: Final = _rows_for(served_ids, expected=len(served_ids)) + assert {str(row["request_id"]) for row in rows} == served_ids, rows + assert all(row["status"] == "failure" for row in rows), rows + + +@pytest.mark.timeout(600) +async def test_c03_proxy_terminated_mid_burst_lands_every_served_rejection_at_most_once( + gateway: Gateway, tmp_path: Path +) -> None: + marker: Final = f"call-{uuid.uuid4().hex}" + calls: Final = _calls(marker, "chat", range(12)) + release: Final = threading.Event() + held_indexes: Final[SimpleQueue[int]] = SimpleQueue() + with wire_server(_held_rejection(release, held_indexes)) as wire: + config: Final = _owned_config(wire, tmp_path) + with owned_proxy_process(gateway, tmp_path, {}, config=config, workers=1) as owned: + candidate: Final = owned.gateway + burst: Final = asyncio.create_task( + _burst(_proxy_url(candidate), candidate.key, _PLAIN, calls, tolerate_transport_errors=True) + ) + await asyncio.to_thread(eventually, held_indexes.qsize, lambda size: size == 12, 60) + owned.process.terminate() + release.set() + served: Final = await burst + eventually(owned.process.poll, lambda code: code is not None, seconds=60) + assert len(served) <= 12 + for item in served: + assert _is_rejection(item, _REJECTION), (item.call, item.status, item.text) + served_ids: Final = frozenset(item.call_id for item in served if item.call_id) + landed: Final = tuple(str(row["request_id"]) for row in _rows_for(served_ids, expected=0)) + assert len(landed) == len(set(landed)), landed + assert set(landed) <= served_ids, (landed, served_ids) + assert len(wire.drain()) == 12 diff --git a/tests/integration/sdk/test_bedrock_converse_stream_sync_decoder_wire.py b/tests/integration/sdk/test_bedrock_converse_stream_sync_decoder_wire.py new file mode 100644 index 00000000000..90acdd774d0 --- /dev/null +++ b/tests/integration/sdk/test_bedrock_converse_stream_sync_decoder_wire.py @@ -0,0 +1,100 @@ +import re +from collections.abc import Callable, Mapping +from dataclasses import dataclass +from typing import Final +from urllib.parse import unquote + +import pytest +from integration._support.upstream import _aws_event_frame +from integration._support.wire import Reply, Request, Wire, wire_server +from pydantic import JsonValue + +import litellm +from litellm.exceptions import BadGatewayError, BadRequestError, MidStreamFallbackError +from litellm.litellm_core_utils.streaming_handler import CustomStreamWrapper + +_MODEL_ID: Final = "global.moonshotai.kimi-k3" +_CONVERSE_MODEL: Final = f"bedrock/converse/{_MODEL_ID}" +_STREAM_TARGET: Final = f"/model/{_MODEL_ID}/converse-stream" +_EVENT_STREAM: Final = "application/vnd.amazon.eventstream" +_ANSWER: Final = "bedrock sync decoder control" +_REJECTION: Final = "structured output schema uses unsupported regex negative look-ahead" +_UNKNOWN_TYPE: Final = "somethingBedrockAddedLater" +_USAGE: Final[dict[str, JsonValue]] = {"usage": {"inputTokens": 11, "outputTokens": 4, "totalTokens": 15}} + + +def _frame(event_type: str, payload: Mapping[str, JsonValue]) -> bytes: + return _aws_event_frame(event_type, payload, "sc", "u") + + +_NORMAL: Final = b"".join( + ( + _frame("messageStart", {"role": "assistant"}), + _frame("contentBlockDelta", {"delta": {"text": _ANSWER}, "contentBlockIndex": 0}), + _frame("contentBlockStop", {"contentBlockIndex": 0}), + _frame("messageStop", {"stopReason": "end_turn"}), + _frame("metadata", _USAGE), + ) +) +_VALIDATION_FRAME: Final = _frame("validationException", {"message": _REJECTION}) +_UNKNOWN_FRAME: Final = _frame(_UNKNOWN_TYPE, {"future": True}) + + +@dataclass(frozen=True, slots=True) +class _Consumed: + text: str + finish_reasons: tuple[str | None, ...] + + +def _peer(frames: bytes) -> Callable[[Request], Reply]: + def respond(request: Request) -> Reply: + assert unquote(request.target) == _STREAM_TARGET, request.target + return Reply(body=frames, content_type=_EVENT_STREAM) + + return respond + + +def _consume_sync_stream(wire: Wire) -> _Consumed: + response: Final = litellm.completion( + model=_CONVERSE_MODEL, + messages=[{"role": "user", "content": "What does the sync decoder do with this stream?"}], + max_tokens=16, + stream=True, + api_base=wire.url, + aws_access_key_id="AKIASCRIPTEDPROVIDER", + aws_secret_access_key="scripted-secret", + aws_region_name="us-east-1", + num_retries=0, + ) + assert isinstance(response, CustomStreamWrapper), type(response) + chunks: Final = tuple(response) + return _Consumed( + text="".join(str(chunk.choices[0].delta.content or "") for chunk in chunks), + finish_reasons=tuple(chunk.choices[0].finish_reason for chunk in chunks), + ) + + +def test_k01_sync_stream_with_a_validation_exception_frame_first_raises_a_400() -> None: + with wire_server(_peer(_VALIDATION_FRAME)) as wire: + with pytest.raises(BadRequestError, match=re.escape(_REJECTION)) as raised: + _consume_sync_stream(wire) + assert raised.value.status_code == 400, raised.value + assert len(wire.drain()) == 1 + + +def test_k02_sync_stream_whose_only_frame_has_an_unknown_event_type_raises_a_502_naming_it() -> None: + with wire_server(_peer(_UNKNOWN_FRAME)) as wire: + with pytest.raises(MidStreamFallbackError, match=re.escape(_UNKNOWN_TYPE)) as raised: + _consume_sync_stream(wire) + assert raised.value.status_code == 502, raised.value + assert isinstance(raised.value.original_exception, BadGatewayError), raised.value.original_exception + assert "none of its 1 events carried a known event type" in str(raised.value), raised.value + assert len(wire.drain()) == 1 + + +def test_k03_sync_stream_with_normal_frames_delivers_the_text_and_a_stop() -> None: + with wire_server(_peer(_NORMAL)) as wire: + consumed: Final = _consume_sync_stream(wire) + assert consumed.text == _ANSWER, consumed + assert consumed.finish_reasons[-1] == "stop", consumed + assert len(wire.drain()) == 1 diff --git a/tests/unit/llms/bedrock/chat/test_invoke_handler.py b/tests/unit/llms/bedrock/chat/test_invoke_handler.py index ed8b7023977..43b689e499d 100644 --- a/tests/unit/llms/bedrock/chat/test_invoke_handler.py +++ b/tests/unit/llms/bedrock/chat/test_invoke_handler.py @@ -3,6 +3,7 @@ import binascii import itertools import datetime import json +import re import struct from collections.abc import AsyncIterator, Mapping, Sequence from typing import Final @@ -21,7 +22,7 @@ from litellm.llms.bedrock.chat.invoke_handler import ( make_sync_call, ) from litellm.exceptions import MidStreamFallbackError -from litellm.llms.bedrock.common_utils import BedrockError +from litellm.llms.bedrock.common_utils import BedrockError, get_bedrock_stream_event_statuses from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler from litellm.types.utils import ModelResponseStream @@ -717,19 +718,28 @@ async def test_async_invoke_streaming_non_200_forwards_bedrock_response_headers( assert exc_info.value.response.headers["x-amzn-requestid"] == "req-non200-async" -def _bedrock_event_stream_frame(chunk: Mapping[str, object]) -> bytes: +def _event_stream_frame(event_type: str, payload: bytes) -> bytes: def header(name: str, value: str) -> bytes: return bytes([len(name)]) + name.encode() + bytes([7]) + struct.pack(">H", len(value)) + value.encode() - headers: Final = header(":event-type", "chunk") + header(":content-type", "application/json") + header( + headers: Final = header(":event-type", event_type) + header(":content-type", "application/json") + header( ":message-type", "event" ) - payload: Final = json.dumps({"bytes": base64.b64encode(json.dumps(chunk).encode()).decode()}).encode() prelude: Final = struct.pack(">II", 12 + len(headers) + len(payload) + 4, len(headers)) body: Final = prelude + struct.pack(">I", binascii.crc32(prelude)) + headers + payload return body + struct.pack(">I", binascii.crc32(body)) +def _bedrock_event_stream_frame(chunk: Mapping[str, object]) -> bytes: + return _event_stream_frame( + "chunk", json.dumps({"bytes": base64.b64encode(json.dumps(chunk).encode()).decode()}).encode() + ) + + +def _converse_event_frame(event_type: str, body: Mapping[str, object]) -> bytes: + return _event_stream_frame(event_type, json.dumps(body).encode()) + + def _openai_stream_chunk(delta: Mapping[str, str], finish_reason: str | None = None) -> Mapping[str, object]: return { "id": "chatcmpl-1", @@ -925,3 +935,193 @@ async def test_async_converse_stream_with_an_empty_200_body_raises_instead_of_an _ = [chunk async for chunk in stream] _assert_empty_stream_surfaced_as_bad_gateway(exc_info.value) + + +_UPSTREAM_REJECTION: Final = "structured output schema uses unsupported regex negative look-ahead" +_CUSTOMER_REJECTION_EVENT_TYPE: Final = "validationException" + + +def _modeled_exception_event_types() -> tuple[str, ...]: + statuses: Final = get_bedrock_stream_event_statuses() + assert statuses is not None + return tuple(sorted(name for name, status in statuses.items() if status is not None)) + + +def _modeled_status(event_type: str) -> int: + statuses: Final = get_bedrock_stream_event_statuses() + assert statuses is not None + status: Final = statuses[event_type] + assert status is not None + return status + + +_CONVERSE_CONTENT_FRAMES: Final = ( + _converse_event_frame("messageStart", {"role": "assistant"}), + _converse_event_frame("contentBlockDelta", {"contentBlockIndex": 0, "delta": {"text": "hi"}}), + _converse_event_frame("contentBlockStop", {"contentBlockIndex": 0}), + _converse_event_frame("messageStop", {"stopReason": "end_turn"}), +) + + +def _unknown_event_frame() -> bytes: + return _converse_event_frame("somethingBedrockAddedLater", {"message": _UPSTREAM_REJECTION}) + + +@pytest.mark.parametrize("event_type", _modeled_exception_event_types()) +def test_iter_bytes_raises_the_modeled_error_for_an_exception_named_event_frame(event_type: str) -> None: + decoder: Final = AWSEventStreamDecoder(model="us.moonshotai.kimi-k3") + frame: Final = _converse_event_frame(event_type, {"message": _UPSTREAM_REJECTION}) + + with pytest.raises(BedrockError) as exc_info: + list(decoder.iter_bytes(iter([frame]), response_headers=_event_stream_headers())) + + assert exc_info.value.status_code == _modeled_status(event_type) + assert exc_info.value.status_code != 200 + assert exc_info.value.message.startswith(event_type) + assert _UPSTREAM_REJECTION in exc_info.value.message + + +@pytest.mark.asyncio +async def test_aiter_bytes_raises_the_modeled_error_for_an_exception_named_event_frame() -> None: + event_type: Final = _CUSTOMER_REJECTION_EVENT_TYPE + + async def _chunks() -> AsyncIterator[bytes]: + yield _converse_event_frame(event_type, {"message": _UPSTREAM_REJECTION}) + + decoder: Final = AWSEventStreamDecoder(model="us.moonshotai.kimi-k3") + + with pytest.raises(BedrockError) as exc_info: + _ = [chunk async for chunk in decoder.aiter_bytes(_chunks(), response_headers=_event_stream_headers())] + + assert exc_info.value.status_code == _modeled_status(event_type) + assert _UPSTREAM_REJECTION in exc_info.value.message + + +def _assert_unknown_event_stream_error(error: BedrockError, body: bytes) -> None: + assert error.status_code == 502 + assert "HTTP 200" in error.message + assert "none of its 1 events carried a known event type" in error.message + assert "somethingBedrockAddedLater" in error.message + assert _UPSTREAM_REJECTION in error.message + assert f"{len(body)} bytes received" in error.message + assert "req-empty-1" in error.message + + +def test_iter_bytes_raises_when_no_event_carries_a_known_event_type() -> None: + decoder: Final = AWSEventStreamDecoder(model="us.moonshotai.kimi-k3") + body: Final = _unknown_event_frame() + + with pytest.raises(BedrockError) as exc_info: + list(decoder.iter_bytes(iter([body]), response_headers=_event_stream_headers())) + + _assert_unknown_event_stream_error(exc_info.value, body) + + +@pytest.mark.asyncio +async def test_aiter_bytes_raises_when_no_event_carries_a_known_event_type() -> None: + body: Final = _unknown_event_frame() + + async def _chunks() -> AsyncIterator[bytes]: + yield body + + decoder: Final = AWSEventStreamDecoder(model="us.moonshotai.kimi-k3") + + with pytest.raises(BedrockError) as exc_info: + _ = [chunk async for chunk in decoder.aiter_bytes(_chunks(), response_headers=_event_stream_headers())] + + _assert_unknown_event_stream_error(exc_info.value, body) + + +def test_iter_bytes_keeps_a_stream_whose_unknown_event_sits_beside_known_frames() -> None: + decoder: Final = AWSEventStreamDecoder(model="us.moonshotai.kimi-k3") + frames: Final = (_CONVERSE_CONTENT_FRAMES[0], _unknown_event_frame(), *_CONVERSE_CONTENT_FRAMES[1:]) + + chunks: Final = list(decoder.iter_bytes(iter(frames), response_headers=_event_stream_headers())) + + texts: Final = [chunk.choices[0].delta.content for chunk in chunks if isinstance(chunk, ModelResponseStream)] + assert "".join(text or "" for text in texts) == "hi" + finish_reasons: Final = [ + chunk.choices[0].finish_reason for chunk in chunks if isinstance(chunk, ModelResponseStream) + ] + assert "stop" in finish_reasons + + +def _assert_exception_event_surfaced_with_its_modeled_status(error: BaseException, event_type: str) -> None: + assert not isinstance(error, litellm.BadGatewayError) + assert getattr(error, "status_code", None) == _modeled_status(event_type) + assert event_type in str(error) + assert _UPSTREAM_REJECTION in str(error) + + +def test_converse_stream_with_an_exception_event_frame_raises_instead_of_an_empty_turn( + _aws_test_credentials: None, +) -> None: + event_type: Final = _CUSTOMER_REJECTION_EVENT_TYPE + frame: Final = _converse_event_frame(event_type, {"message": _UPSTREAM_REJECTION}) + response: Final = MagicMock(status_code=200, headers=_event_stream_headers()) + response.iter_bytes = lambda chunk_size=None: iter([frame]) + client: Final = HTTPHandler() + client.post = MagicMock(return_value=response) + + with pytest.raises(Exception, match=re.escape(_UPSTREAM_REJECTION)) as exc_info: + list( + litellm.completion( + model="bedrock/us.moonshotai.kimi-k3", + messages=[{"role": "user", "content": "hi"}], + stream=True, + client=client, + ) + ) + + _assert_exception_event_surfaced_with_its_modeled_status(exc_info.value, event_type) + + +@pytest.mark.asyncio +async def test_async_converse_stream_with_an_exception_event_frame_raises_instead_of_an_empty_turn( + _aws_test_credentials: None, +) -> None: + event_type: Final = _CUSTOMER_REJECTION_EVENT_TYPE + + async def _aiter_bytes(chunk_size: int | None = None) -> AsyncIterator[bytes]: + yield _converse_event_frame(event_type, {"message": _UPSTREAM_REJECTION}) + + response: Final = MagicMock(status_code=200, headers=_event_stream_headers()) + response.aiter_bytes = _aiter_bytes + client: Final = AsyncHTTPHandler() + client.post = AsyncMock(return_value=response) + + stream: Final = await litellm.acompletion( + model="bedrock/us.moonshotai.kimi-k3", + messages=[{"role": "user", "content": "hi"}], + stream=True, + client=client, + ) + with pytest.raises(Exception, match=re.escape(_UPSTREAM_REJECTION)) as exc_info: + _ = [chunk async for chunk in stream] + + _assert_exception_event_surfaced_with_its_modeled_status(exc_info.value, event_type) + + +def test_converse_stream_made_only_of_unknown_events_raises_instead_of_an_empty_turn( + _aws_test_credentials: None, +) -> None: + response: Final = MagicMock(status_code=200, headers=_event_stream_headers()) + response.iter_bytes = lambda chunk_size=None: iter([_unknown_event_frame()]) + client: Final = HTTPHandler() + client.post = MagicMock(return_value=response) + + with pytest.raises(MidStreamFallbackError) as exc_info: + list( + litellm.completion( + model="bedrock/us.moonshotai.kimi-k3", + messages=[{"role": "user", "content": "hi"}], + stream=True, + client=client, + ) + ) + + assert exc_info.value.status_code == 502 + assert exc_info.value.is_pre_first_chunk is True + assert isinstance(exc_info.value.original_exception, litellm.BadGatewayError) + assert "somethingBedrockAddedLater" in str(exc_info.value) + assert _UPSTREAM_REJECTION in str(exc_info.value) diff --git a/tests/unit/llms/bedrock/test_bedrock_common_utils.py b/tests/unit/llms/bedrock/test_bedrock_common_utils.py index e5118f90e44..52d539d8ea7 100644 --- a/tests/unit/llms/bedrock/test_bedrock_common_utils.py +++ b/tests/unit/llms/bedrock/test_bedrock_common_utils.py @@ -981,3 +981,72 @@ def test_unmapped_openai_family_model_routes_to_converse(): assert BedrockModelInfo.get_bedrock_route(unmapped) == "converse" imported: Final = "bedrock/openai/arn:aws:bedrock:us-east-1:123456789012:imported-model/abc123" assert BedrockModelInfo.get_bedrock_route(imported) == "openai" + + +def test_bedrock_stream_event_statuses_cover_every_modeled_member_of_both_stream_shapes(): + pytest.importorskip("botocore") + from botocore.loaders import Loader + from botocore.model import ServiceModel + + import litellm.llms.bedrock.common_utils as mod + + mod.get_bedrock_stream_event_statuses.cache_clear() + statuses = mod.get_bedrock_stream_event_statuses() + assert statuses is not None + + service_model = ServiceModel(Loader().load_service_model("bedrock-runtime", "service-2")) + for shape_name in ("ConverseStreamOutput", "ResponseStream"): + for name, member in service_model.shape_for(shape_name).members.items(): + modeled = (member.metadata or {}).get("error", {}).get("httpStatusCode") + assert statuses[name] == (None if modeled is None else int(modeled)) + assert mod.bedrock_stream_event_error_status(name) == statuses[name] + + assert any(status is not None for status in statuses.values()) + assert any(status is None for status in statuses.values()) + assert mod.bedrock_stream_event_error_status("notAModeledEvent") is None + assert mod.bedrock_stream_event_error_status(None) is None + + +def test_bedrock_stream_event_statuses_load_failure_returns_none(): + from unittest.mock import patch + + import litellm.llms.bedrock.common_utils as mod + + pytest.importorskip("botocore") + mod.get_bedrock_stream_event_statuses.cache_clear() + with patch("botocore.loaders.Loader.load_service_model", side_effect=Exception("no data")): + assert mod._load_bedrock_stream_event_statuses() is None + assert mod.get_bedrock_stream_event_statuses() is None + assert mod.bedrock_stream_event_error_status("validationException") is None + mod.get_bedrock_stream_event_statuses.cache_clear() + + +@pytest.mark.parametrize( + ("headers", "expected_status", "expected_message"), + [ + ({":message-type": "error"}, 400, '{"message":"upstream failed"}'), + ( + {":message-type": "exception", ":exception-type": "somethingNotModeled"}, + 400, + 'somethingNotModeled {"message":"upstream failed"}', + ), + ( + {":message-type": "exception", ":exception-type": "throttlingException"}, + 429, + 'throttlingException {"message":"upstream failed"}', + ), + ], +) +def test_build_bedrock_stream_error_resolves_status_from_the_exception_type( + headers: dict[str, str], expected_status: int, expected_message: str +): + pytest.importorskip("botocore") + from litellm.llms.bedrock.common_utils import build_bedrock_stream_error, get_bedrock_response_stream_shape + + error = build_bedrock_stream_error( + {"status_code": 400, "headers": headers, "body": b'{"message":"upstream failed"}'}, + get_bedrock_response_stream_shape(), + ) + + assert error.status_code == expected_status + assert error.message == expected_message From a2068820eeac3cd9936d4fcced2e70af10dbf4d8 Mon Sep 17 00:00:00 2001 From: ishaan-berri <155045088+ishaan-berri@users.noreply.github.com> Date: Fri, 2 Oct 2026 16:07:18 -0700 Subject: [PATCH 05/61] perf(proxy): batch daily model usage writes instead of upserting per request (#44243) * perf(proxy): aggregate daily model usage per flush instead of upserting per request * perf(proxy): drain queued model usage in the spend log flush job * perf(proxy): queue model usage at request time instead of writing to the db * test(proxy): cover batched daily model usage aggregation and retries * test(proxy): read back model insights written by the batched flush * fix(proxy): drain the whole model usage queue each flush so it cannot grow unbounded * test(proxy): give the mock prisma client a model usage queue * test(proxy): cover draining a model usage queue larger than one spend log batch --- litellm/proxy/db/db_spend_update_writer.py | 25 +- litellm/proxy/db/model_usage_rollup.py | 165 +++++++++--- litellm/proxy/utils.py | 20 ++ .../unit/proxy/db/test_model_usage_rollup.py | 248 +++++++++++++----- .../test_model_insights_endpoints.py | 23 +- tests/unit/proxy/test_update_spend.py | 2 + .../proxy/utils/prisma_and_spend/conftest.py | 2 + .../prisma_and_spend/test_spend_functions.py | 27 ++ 8 files changed, 392 insertions(+), 120 deletions(-) diff --git a/litellm/proxy/db/db_spend_update_writer.py b/litellm/proxy/db/db_spend_update_writer.py index ef5dd3f663c..c64ef72ace6 100644 --- a/litellm/proxy/db/db_spend_update_writer.py +++ b/litellm/proxy/db/db_spend_update_writer.py @@ -70,6 +70,7 @@ from litellm.proxy.db.db_transaction_queue.window_spend_update_queue import ( WindowSpendUpdateQueue, ) from litellm.proxy.db.exception_handler import PrismaDBExceptionHandler +from litellm.proxy.db.model_usage_rollup import build_model_usage_transaction from litellm.proxy.route_llm_request import ROUTE_ENDPOINT_MAPPING from litellm.proxy.spend_tracking.compression_savings import ( extract_compression_saved_tokens, @@ -728,6 +729,20 @@ class DBSpendUpdateWriter: except Exception as e: verbose_proxy_logger.debug("_enqueue_tool_usage_transaction error (non-blocking): %s", e) + async def _enqueue_model_usage_transaction( + self, + payload: SpendLogsPayload, + prisma_client: PrismaClient, + ) -> None: + try: + transaction: Final = build_model_usage_transaction(payload) + if transaction is None: + return + async with prisma_client._model_usage_transactions_lock: + prisma_client.model_usage_transactions.append(transaction) + except Exception as e: + verbose_proxy_logger.debug("_enqueue_model_usage_transaction error (non-blocking): %s", e) + async def _enqueue_autorouter_turn_transaction( self, payload: SpendLogsPayload, @@ -1144,15 +1159,7 @@ class DBSpendUpdateWriter: traceback.format_exc(), ) - try: - from litellm.proxy.db.model_usage_rollup import increment_daily_model_usage - - await increment_daily_model_usage(prisma_client=prisma_client, payload=payload_copy) - except Exception: - verbose_proxy_logger.debug( - "_batch_database_updates: increment_daily_model_usage failed: %s", - traceback.format_exc(), - ) + await self._enqueue_model_usage_transaction(payload=payload_copy, prisma_client=prisma_client) async def _update_key_db( self, diff --git a/litellm/proxy/db/model_usage_rollup.py b/litellm/proxy/db/model_usage_rollup.py index acd9130da30..98d53573f30 100644 --- a/litellm/proxy/db/model_usage_rollup.py +++ b/litellm/proxy/db/model_usage_rollup.py @@ -1,5 +1,12 @@ +from __future__ import annotations + +import asyncio +import random +from collections.abc import Mapping, Sequence +from dataclasses import dataclass from datetime import datetime -from typing import Final +from itertools import groupby +from typing import TYPE_CHECKING, Final, Protocol from pydantic import TypeAdapter, ValidationError @@ -8,15 +15,48 @@ from litellm.constants import ( MODEL_INSIGHTS_DEFAULT_TASK, MODEL_INSIGHTS_TASK_TAG_PREFIX, ) -from litellm.proxy._types import SpendLogsPayload +from litellm.proxy._types import DB_RETRY_SAFE_ERROR_TYPES, SpendLogsPayload from litellm.proxy.db.model_insights_tasks import load_model_insight_tasks -from litellm.proxy.utils import PrismaClient -from litellm.repositories.table_repositories import DailyModelUsageRepository + +if TYPE_CHECKING: + from litellm.proxy.utils import PrismaClient _METADATA: Final = TypeAdapter(dict[str, object]) _TAGS: Final = TypeAdapter(list[object]) +class _UpsertTable(Protocol): + def upsert(self, *, where: Mapping[str, object], data: Mapping[str, object]) -> None: ... + + +class _ModelUsageBatch(Protocol): + litellm_dailymodelusage: _UpsertTable + + +class _ModelUsageBatchManager(Protocol): + async def __aenter__(self) -> _ModelUsageBatch: ... + + async def __aexit__(self, exc_type: object, exc_value: object, traceback: object) -> bool | None: ... + + +@dataclass(frozen=True, slots=True) +class ModelUsageKey: + date: str + model_group: str + model: str + custom_llm_provider: str + task_type: str + + +@dataclass(frozen=True, slots=True) +class ModelUsageTransaction: + key: ModelUsageKey + spend: float + prompt_tokens: int + completion_tokens: int + successful: bool + + def model_usage_task_type(request_tags: str) -> str: try: tags: Final = _TAGS.validate_json(request_tags) @@ -48,43 +88,88 @@ def _date_from_start_time(start_time: datetime | str) -> str | None: return start_time[:10] if len(start_time) >= 10 else None -async def increment_daily_model_usage(prisma_client: PrismaClient, payload: SpendLogsPayload) -> None: +def build_model_usage_transaction(payload: SpendLogsPayload) -> ModelUsageTransaction | None: date: Final = _date_from_start_time(payload["startTime"]) if date is None or _is_internal_call(payload["metadata"]): - return - + return None model: Final = payload["model"] or "unknown" - model_group: Final = payload["model_group"] or model - provider: Final = payload["custom_llm_provider"] or "unknown" - task_type: Final = model_usage_task_type(payload["request_tags"]) - successful: Final = 1 if payload["status"] == "success" else 0 - failed: Final = 1 - successful - key: Final = { - "date": date, - "model_group": model_group, - "model": model, - "custom_llm_provider": provider, - "task_type": task_type, - } - await DailyModelUsageRepository(prisma_client).table.upsert( - where={"date_model_group_model_custom_llm_provider_task_type": key}, - data={ - "create": { - **key, - "spend": payload["spend"], - "prompt_tokens": payload["prompt_tokens"], - "completion_tokens": payload["completion_tokens"], - "request_count": 1, - "successful_requests": successful, - "failed_requests": failed, - }, - "update": { - "spend": {"increment": payload["spend"]}, - "prompt_tokens": {"increment": payload["prompt_tokens"]}, - "completion_tokens": {"increment": payload["completion_tokens"]}, - "request_count": {"increment": 1}, - "successful_requests": {"increment": successful}, - "failed_requests": {"increment": failed}, - }, - }, + return ModelUsageTransaction( + key=ModelUsageKey( + date=date, + model_group=payload["model_group"] or model, + model=model, + custom_llm_provider=payload["custom_llm_provider"] or "unknown", + task_type=model_usage_task_type(payload["request_tags"]), + ), + spend=payload["spend"], + prompt_tokens=payload["prompt_tokens"], + completion_tokens=payload["completion_tokens"], + successful=payload["status"] == "success", ) + + +def _model_usage_batch(prisma_client: PrismaClient) -> _ModelUsageBatchManager: + batch: Final[_ModelUsageBatchManager] = prisma_client.db.batch_() + return batch + + +def _sort_key(transaction: ModelUsageTransaction) -> tuple[str, str, str, str, str]: + key: Final = transaction.key + return (key.date, key.model_group, key.model, key.custom_llm_provider, key.task_type) + + +async def flush_model_usage_transactions( + prisma_client: PrismaClient, + transactions: Sequence[ModelUsageTransaction], + n_retry_times: int = 3, +) -> None: + """One upsert per rollup row for the whole drained batch, in a single transaction and in key order so + concurrent pods take row locks in the same order. Only ConnectError is retried: it proves nothing reached + the database, while a retry after an ambiguous post-send failure could double-count the increments.""" + if not transactions: + return + ordered: Final = sorted(transactions, key=_sort_key) + for attempt in range(n_retry_times + 1): + try: + async with _model_usage_batch(prisma_client) as batcher: + for key, grouped in groupby(ordered, key=lambda transaction: transaction.key): + entries = tuple(grouped) + spend = sum(entry.spend for entry in entries) + prompt_tokens = sum(entry.prompt_tokens for entry in entries) + completion_tokens = sum(entry.completion_tokens for entry in entries) + successful = sum(1 for entry in entries if entry.successful) + failed = len(entries) - successful + key_fields = { + "date": key.date, + "model_group": key.model_group, + "model": key.model, + "custom_llm_provider": key.custom_llm_provider, + "task_type": key.task_type, + } + batcher.litellm_dailymodelusage.upsert( + where={"date_model_group_model_custom_llm_provider_task_type": key_fields}, + data={ + "create": { + **key_fields, + "spend": spend, + "prompt_tokens": prompt_tokens, + "completion_tokens": completion_tokens, + "request_count": len(entries), + "successful_requests": successful, + "failed_requests": failed, + }, + "update": { + "spend": {"increment": spend}, + "prompt_tokens": {"increment": prompt_tokens}, + "completion_tokens": {"increment": completion_tokens}, + "request_count": {"increment": len(entries)}, + "successful_requests": {"increment": successful}, + "failed_requests": {"increment": failed}, + }, + }, + ) + return + except DB_RETRY_SAFE_ERROR_TYPES: + if attempt >= n_retry_times: + raise + await asyncio.sleep(2.0**attempt + random.uniform(0, 1)) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 817231bc0b9..5828ed94984 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -266,6 +266,7 @@ if TYPE_CHECKING: from litellm.models.team import LiteLLM_TeamTableCachedObj from litellm.proxy.db.autorouter_session_rollup import AutoRouterTurnTransaction from litellm.proxy.db.baseline_accounting import BaselineAccountingRecord + from litellm.proxy.db.model_usage_rollup import ModelUsageTransaction from litellm.proxy.db.spend_log_tool_index import ToolUsageTransaction from litellm.repositories.prisma_protocols import TableActions from litellm.types.proxy.policy_engine.pipeline_types import GuardrailPipeline @@ -4400,6 +4401,8 @@ class PrismaClient: spend_log_write_lock = asyncio.Lock() tool_usage_transactions: list["ToolUsageTransaction"] = [] _tool_usage_transactions_lock = asyncio.Lock() + model_usage_transactions: ClassVar[list["ModelUsageTransaction"]] = [] + _model_usage_transactions_lock = asyncio.Lock() autorouter_turn_transactions: ClassVar[list["AutoRouterTurnTransaction"]] = [] _autorouter_turn_transactions_lock = asyncio.Lock() @@ -7508,6 +7511,8 @@ async def _total_queued_spend_transactions(prisma_client: PrismaClient) -> int: spend_queue_size: Final = len(prisma_client.spend_log_transactions) async with prisma_client._tool_usage_transactions_lock: tool_queue_size: Final = len(prisma_client.tool_usage_transactions) + async with prisma_client._model_usage_transactions_lock: + model_usage_queue_size: Final = len(prisma_client.model_usage_transactions) async with prisma_client._autorouter_turn_transactions_lock: autorouter_queue_size: Final = len(prisma_client.autorouter_turn_transactions) from litellm.proxy.db.shadow_eval_funnel import pending_shadow_eval_funnel_events @@ -7517,6 +7522,7 @@ async def _total_queued_spend_transactions(prisma_client: PrismaClient) -> int: return ( spend_queue_size + tool_queue_size + + model_usage_queue_size + autorouter_queue_size + baseline_queue_size + pending_shadow_eval_funnel_events() @@ -7652,6 +7658,20 @@ async def _run_spend_logs_job( tool_tracking_err, ) + async with prisma_client._model_usage_transactions_lock: + model_usage_to_process: Final = prisma_client.model_usage_transactions + prisma_client.model_usage_transactions = [] + try: + from litellm.proxy.db.model_usage_rollup import flush_model_usage_transactions + + await flush_model_usage_transactions(prisma_client=prisma_client, transactions=model_usage_to_process) + except Exception as model_usage_err: + verbose_proxy_logger.error( + "Spend tracking - model usage flush failed; %s model usage transactions dropped: %s", + len(model_usage_to_process), + model_usage_err, + ) + await flush_baseline_accounting(prisma_client) async with prisma_client._autorouter_turn_transactions_lock: diff --git a/tests/unit/proxy/db/test_model_usage_rollup.py b/tests/unit/proxy/db/test_model_usage_rollup.py index f54856129dc..b9b806f4c54 100644 --- a/tests/unit/proxy/db/test_model_usage_rollup.py +++ b/tests/unit/proxy/db/test_model_usage_rollup.py @@ -1,9 +1,68 @@ +import asyncio from datetime import datetime, timezone -from unittest.mock import AsyncMock, MagicMock +from typing import Any +from unittest.mock import MagicMock +import httpx import pytest -from litellm.proxy.db.model_usage_rollup import increment_daily_model_usage, model_usage_task_type +from litellm.proxy.db.db_spend_update_writer import DBSpendUpdateWriter +from litellm.proxy.db.model_usage_rollup import ( + ModelUsageKey, + ModelUsageTransaction, + build_model_usage_transaction, + flush_model_usage_transactions, + model_usage_task_type, +) + + +class _FakeBatcher: + def __init__(self) -> None: + self.litellm_dailymodelusage = MagicMock() + + async def __aenter__(self) -> "_FakeBatcher": + return self + + async def __aexit__(self, *args: Any) -> None: + return None + + +def _prisma(batch_: MagicMock) -> MagicMock: + prisma = MagicMock() + prisma.db.batch_ = batch_ + return prisma + + +def _payload(**overrides: Any) -> dict[str, Any]: + return { + "spend": 0.25, + "prompt_tokens": 10, + "completion_tokens": 20, + "startTime": datetime(2026, 9, 28, 13, tzinfo=timezone.utc), + "model": "openai/gpt-5.4-mini", + "model_group": "fast-chat", + "metadata": "{}", + "request_tags": "[]", + "custom_llm_provider": "openai", + "status": "success", + **overrides, + } + + +def _transaction(model: str, spend: float, successful: bool = True) -> ModelUsageTransaction: + return ModelUsageTransaction( + key=ModelUsageKey( + date="2026-09-28", model_group=model, model=model, custom_llm_provider="openai", task_type="debugging" + ), + spend=spend, + prompt_tokens=10, + completion_tokens=5, + successful=successful, + ) + + +async def _no_sleep(seconds: float) -> None: + return None def test_model_usage_task_type_reads_task_tag_or_defaults() -> None: @@ -14,76 +73,131 @@ def test_model_usage_task_type_reads_task_tag_or_defaults() -> None: assert model_usage_task_type("not json") == "uncategorized" -@pytest.mark.asyncio -async def test_increment_daily_model_usage_uses_atomic_prisma_upsert() -> None: - table = MagicMock() - table.upsert = AsyncMock() - prisma_client = MagicMock() - prisma_client.db.litellm_dailymodelusage = table - payload = { - "request_id": "request-1", - "call_type": "acompletion", - "api_key": "key", - "spend": 0.25, - "total_tokens": 30, - "prompt_tokens": 10, - "completion_tokens": 20, - "startTime": datetime(2026, 9, 28, tzinfo=timezone.utc), - "endTime": datetime(2026, 9, 28, tzinfo=timezone.utc), - "completionStartTime": None, - "model": "openai/gpt-5.4-mini", - "model_id": None, - "model_group": "fast-chat", - "mcp_namespaced_tool_name": None, - "agent_id": None, - "api_base": "", - "user": "user", - "metadata": "{}", - "cache_hit": "False", - "cache_key": "", - "request_tags": "[]", - "team_id": None, - "organization_id": None, - "end_user": None, - "requester_ip_address": None, - "custom_llm_provider": "openai", - "messages": None, - "response": None, - "proxy_server_request": None, - "session_id": None, - "request_duration_ms": 20, - "status": "success", - "litellm_call_id": None, - } +def test_build_model_usage_transaction_keys_on_day_model_and_task() -> None: + transaction = build_model_usage_transaction(_payload(request_tags='["task:debugging"]', status="failure")) - await increment_daily_model_usage(prisma_client, payload) + assert transaction == ModelUsageTransaction( + key=ModelUsageKey( + date="2026-09-28", + model_group="fast-chat", + model="openai/gpt-5.4-mini", + custom_llm_provider="openai", + task_type="debugging", + ), + spend=0.25, + prompt_tokens=10, + completion_tokens=20, + successful=False, + ) - call = table.upsert.await_args.kwargs - assert call["data"]["create"]["request_count"] == 1 - assert call["data"]["update"]["completion_tokens"] == {"increment": 20} - assert call["data"]["create"]["task_type"] == "uncategorized" + +def test_build_model_usage_transaction_falls_back_for_missing_model_fields() -> None: + transaction = build_model_usage_transaction( + _payload(model="", model_group=None, custom_llm_provider=None, startTime="2026-09-28T01:02:03Z") + ) + + assert transaction is not None + assert transaction.key == ModelUsageKey( + date="2026-09-28", + model_group="unknown", + model="unknown", + custom_llm_provider="unknown", + task_type="uncategorized", + ) + + +@pytest.mark.parametrize( + "overrides", + [{"metadata": '{"internal_call_origin": "health_check"}'}, {"startTime": "bad"}], +) +def test_build_model_usage_transaction_skips_internal_calls_and_bad_dates(overrides: dict[str, Any]) -> None: + assert build_model_usage_transaction(_payload(**overrides)) is None @pytest.mark.asyncio -async def test_increment_daily_model_usage_records_task_from_request_tags() -> None: - table = MagicMock() - table.upsert = AsyncMock() - prisma_client = MagicMock() - prisma_client.db.litellm_dailymodelusage = table - payload = { - "call_type": "acompletion", - "spend": 0.1, - "prompt_tokens": 1, - "completion_tokens": 2, - "startTime": datetime(2026, 9, 28, tzinfo=timezone.utc), - "model": "gpt-5", - "model_group": "gpt-5", - "metadata": "{}", - "request_tags": '["task:debugging"]', - "custom_llm_provider": "openai", - "status": "success", +async def test_flush_aggregates_each_rollup_row_into_one_upsert() -> None: + batcher = _FakeBatcher() + prisma = _prisma(MagicMock(return_value=batcher)) + + await flush_model_usage_transactions( + prisma_client=prisma, + transactions=[ + _transaction("gpt-5", 0.5), + _transaction("claude", 1.0), + _transaction("gpt-5", 0.25, successful=False), + _transaction("gpt-5", 0.25), + ], + ) + + upserts = { + call.kwargs["where"]["date_model_group_model_custom_llm_provider_task_type"]["model"]: call.kwargs["data"] + for call in batcher.litellm_dailymodelusage.upsert.call_args_list } + assert list(upserts) == ["claude", "gpt-5"] + gpt = upserts["gpt-5"] + assert gpt["create"]["spend"] == 1.0 + assert gpt["create"]["prompt_tokens"] == 30 + assert gpt["create"]["completion_tokens"] == 15 + assert gpt["create"]["request_count"] == 3 + assert gpt["create"]["successful_requests"] == 2 + assert gpt["create"]["failed_requests"] == 1 + assert gpt["update"] == { + "spend": {"increment": 1.0}, + "prompt_tokens": {"increment": 30}, + "completion_tokens": {"increment": 15}, + "request_count": {"increment": 3}, + "successful_requests": {"increment": 2}, + "failed_requests": {"increment": 1}, + } + assert upserts["claude"]["create"]["request_count"] == 1 - await increment_daily_model_usage(prisma_client, payload) - assert table.upsert.await_args.kwargs["data"]["create"]["task_type"] == "debugging" +@pytest.mark.asyncio +async def test_flush_with_no_transactions_touches_nothing() -> None: + prisma = _prisma(MagicMock()) + await flush_model_usage_transactions(prisma_client=prisma, transactions=[]) + prisma.db.batch_.assert_not_called() + + +@pytest.mark.asyncio +async def test_flush_retries_connection_errors(monkeypatch: pytest.MonkeyPatch) -> None: + batcher = _FakeBatcher() + prisma = _prisma(MagicMock(side_effect=[httpx.ConnectError("down"), batcher])) + monkeypatch.setattr("litellm.proxy.db.model_usage_rollup.asyncio.sleep", _no_sleep) + + await flush_model_usage_transactions(prisma_client=prisma, transactions=[_transaction("gpt-5", 0.1)]) + + assert prisma.db.batch_.call_count == 2 + batcher.litellm_dailymodelusage.upsert.assert_called_once() + + +@pytest.mark.asyncio +async def test_flush_does_not_retry_ambiguous_errors() -> None: + prisma = _prisma(MagicMock(side_effect=httpx.ReadTimeout("ambiguous"))) + + with pytest.raises(httpx.ReadTimeout): + await flush_model_usage_transactions(prisma_client=prisma, transactions=[_transaction("gpt-5", 0.1)]) + + prisma.db.batch_.assert_called_once() + + +@pytest.mark.asyncio +async def test_request_time_path_queues_usage_instead_of_writing_to_the_db() -> None: + prisma = MagicMock() + prisma.model_usage_transactions = [] + prisma._model_usage_transactions_lock = asyncio.Lock() + + await DBSpendUpdateWriter()._batch_database_updates( + response_cost=0.25, + user_id="u1", + hashed_token="t1", + team_id=None, + org_id=None, + end_user_id=None, + prisma_client=prisma, + litellm_proxy_budget_name=None, + payload=_payload(request_id="req-1"), + ) + + assert [transaction.key.model for transaction in prisma.model_usage_transactions] == ["openai/gpt-5.4-mini"] + prisma.db.litellm_dailymodelusage.upsert.assert_not_called() diff --git a/tests/unit/proxy/management_endpoints/test_model_insights_endpoints.py b/tests/unit/proxy/management_endpoints/test_model_insights_endpoints.py index 535f32a7f10..7c8d1346944 100644 --- a/tests/unit/proxy/management_endpoints/test_model_insights_endpoints.py +++ b/tests/unit/proxy/management_endpoints/test_model_insights_endpoints.py @@ -7,7 +7,7 @@ from fastapi.testclient import TestClient from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth from litellm.proxy.auth.user_api_key_auth import user_api_key_auth -from litellm.proxy.db.model_usage_rollup import increment_daily_model_usage +from litellm.proxy.db.model_usage_rollup import build_model_usage_transaction, flush_model_usage_transactions from litellm.proxy.management_endpoints.model_insights_endpoints import router @@ -199,7 +199,7 @@ class _InMemoryUsageTable: def __init__(self) -> None: self.rows: dict[tuple[str, ...], dict[str, float]] = {} - async def upsert(self, where: dict, data: dict) -> None: + def upsert(self, where: dict, data: dict) -> None: key_fields = where["date_model_group_model_custom_llm_provider_task_type"] key = tuple(key_fields.values()) if key not in self.rows: @@ -221,11 +221,23 @@ class _InMemoryUsageTable: return list(grouped.values()) +class _InMemoryBatcher: + def __init__(self, table: _InMemoryUsageTable) -> None: + self.litellm_dailymodelusage = table + + async def __aenter__(self) -> "_InMemoryBatcher": + return self + + async def __aexit__(self, *args: object) -> None: + return None + + @pytest.mark.asyncio async def test_model_insights_reads_back_what_the_rollup_wrote() -> None: table = _InMemoryUsageTable() prisma = MagicMock() prisma.db.litellm_dailymodelusage = table + prisma.db.batch_ = MagicMock(return_value=_InMemoryBatcher(table)) payload = { "call_type": "acompletion", "spend": 0.5, @@ -240,8 +252,11 @@ async def test_model_insights_reads_back_what_the_rollup_wrote() -> None: "status": "success", } - await increment_daily_model_usage(prisma, payload) - await increment_daily_model_usage(prisma, {**payload, "request_tags": "[]"}) + transactions = ( + build_model_usage_transaction(payload), + build_model_usage_transaction({**payload, "request_tags": "[]"}), + ) + await flush_model_usage_transactions(prisma, [t for t in transactions if t is not None]) body = _call(table, "metric=requests").json() diff --git a/tests/unit/proxy/test_update_spend.py b/tests/unit/proxy/test_update_spend.py index ebe505b3d60..6b92320762b 100644 --- a/tests/unit/proxy/test_update_spend.py +++ b/tests/unit/proxy/test_update_spend.py @@ -36,6 +36,7 @@ class MockPrismaClient: self.spend_log_transactions = [] self.daily_user_spend_transactions = {} self.tool_usage_transactions = [] + self.model_usage_transactions = [] self.autorouter_turn_transactions = [] self.baseline_accounting_transactions = [] self.baseline_accounting_lock = asyncio.Lock() @@ -49,6 +50,7 @@ class MockPrismaClient: self._spend_log_transactions_lock = asyncio.Lock() self.spend_log_write_lock = asyncio.Lock() self._tool_usage_transactions_lock = asyncio.Lock() + self._model_usage_transactions_lock = asyncio.Lock() self._autorouter_turn_transactions_lock = asyncio.Lock() def jsonify_object(self, obj): diff --git a/tests/unit/proxy/utils/prisma_and_spend/conftest.py b/tests/unit/proxy/utils/prisma_and_spend/conftest.py index 455eb423ddc..e37a82a023b 100644 --- a/tests/unit/proxy/utils/prisma_and_spend/conftest.py +++ b/tests/unit/proxy/utils/prisma_and_spend/conftest.py @@ -133,6 +133,8 @@ def mock_prisma_client() -> MagicMock: client.spend_log_write_lock = asyncio.Lock() client.tool_usage_transactions = [] client._tool_usage_transactions_lock = asyncio.Lock() + client.model_usage_transactions = [] + client._model_usage_transactions_lock = asyncio.Lock() client.jsonify_object = lambda data: dict(data) client.db.is_connected = MagicMock(return_value=False) client.db.connect = AsyncMock() diff --git a/tests/unit/proxy/utils/prisma_and_spend/test_spend_functions.py b/tests/unit/proxy/utils/prisma_and_spend/test_spend_functions.py index d6f41ba55db..7aa06bcafae 100644 --- a/tests/unit/proxy/utils/prisma_and_spend/test_spend_functions.py +++ b/tests/unit/proxy/utils/prisma_and_spend/test_spend_functions.py @@ -223,6 +223,33 @@ async def test_update_spend_logs_job_drains_tool_queue_when_spend_queue_empty( assert mock_prisma_client.tool_usage_transactions == [] +@pytest.mark.asyncio +async def test_update_spend_logs_job_drains_the_whole_model_usage_queue_in_one_run( + mock_prisma_client: Any, monkeypatch: pytest.MonkeyPatch +) -> None: + import litellm.proxy.db.model_usage_rollup as model_usage_mod + import litellm.proxy.db.spend_log_tool_index as tool_mod + import litellm.proxy.guardrails.usage_tracking as guard_mod + + proxy_logging = MagicMock() + proxy_logging.failure_handler = AsyncMock() + queued = [MagicMock() for _ in range(25_000)] + mock_prisma_client.model_usage_transactions = list(queued) + monkeypatch.setattr(guard_mod, "process_spend_logs_guardrail_usage", AsyncMock(), raising=False) + monkeypatch.setattr(tool_mod, "flush_tool_usage_transactions", AsyncMock(), raising=False) + flush_stub = AsyncMock() + monkeypatch.setattr(model_usage_mod, "flush_model_usage_transactions", flush_stub, raising=False) + + await update_spend_logs_job( + prisma_client=mock_prisma_client, + db_writer_client=None, + proxy_logging_obj=proxy_logging, + ) + + assert flush_stub.await_args.kwargs["transactions"] == queued + assert mock_prisma_client.model_usage_transactions == [] + + @pytest.mark.asyncio async def test_update_spend_logs_job_processes_and_clears_queue( mock_prisma_client: Any, make_spend_log_row: Any, monkeypatch: pytest.MonkeyPatch From b94eaa65c3213bd79015f8e3b3f52c7d44a03c53 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Fri, 2 Oct 2026 16:12:15 -0700 Subject: [PATCH 06/61] test(integration): opt the config pass-through spend-log case into auth (#44265) The failure spend-log row from #42695 is written for pass-through routes that run as LLM API routes, which a config route only does with auth: true. The test omitted auth and passed only while config wins (#41779) registered config entries through the typed model, where auth defaults to true. #43962 restored the pre-config-wins registration, so the route lost that status and the row was never written. Set auth: true on the route so the test covers the logging it was written for without depending on that side effect --- .../observability/test_passthrough_upstream_error_visibility.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/observability/test_passthrough_upstream_error_visibility.py b/tests/integration/observability/test_passthrough_upstream_error_visibility.py index bb18add2f2f..c29ce4f1c62 100644 --- a/tests/integration/observability/test_passthrough_upstream_error_visibility.py +++ b/tests/integration/observability/test_passthrough_upstream_error_visibility.py @@ -238,6 +238,7 @@ def test_config_pass_through_route_logs_body_and_strips_query(gateway: Gateway, "target": f"{wire.url}/upstream?trace=secret-q", "include_subpath": True, "headers": {"Authorization": "Bearer scripted"}, + "auth": True, } ] path.write_text(yaml.safe_dump(config)) From a292fd409fb120dd95d10726100ad0c3964dcbbe Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Fri, 2 Oct 2026 16:29:57 -0700 Subject: [PATCH 07/61] test: fix three order-dependent and timing-flaky tests (#44271) * test(integration): answer the model-info refresh GET in the mixed MCP responses wire peer The proxy's periodic model-info refresh sends GET /v1/models to the deployment api_base, which tripped the peer's /responses-only assertion when a tick landed mid-test * test(e2e): wait for the api-keys URL after clicking Virtual Keys in onboarding /ui already renders the Virtual Keys heading, so the helper returned before navigation finished. The late route change moved focus and closed the account menu popover in hideLiteAdmin * test(proxy): stop two unit modules leaking app.openapi_schema and a session-wide Router test_custom_openapi cached a stripped schema on app.openapi_schema and never cleared it, breaking later openapi route tests. test_proxy_reject_logging built a module-level Router that stayed in the live router registry all session and re-added cost-map keys during a reload. Reset the schema via monkeypatch and make the Router a function-scoped fixture --- tests/e2e/ui/helpers/userOnboarding.ts | 1 + .../mcp/test_responses_mcp_mixed_tools.py | 2 ++ tests/unit/proxy/test_proxy_reject_logging.py | 30 ++++++++++--------- tests/unit/proxy/test_proxy_utils.py | 5 ++-- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/tests/e2e/ui/helpers/userOnboarding.ts b/tests/e2e/ui/helpers/userOnboarding.ts index 14e2b0257b2..9db3bb0867b 100644 --- a/tests/e2e/ui/helpers/userOnboarding.ts +++ b/tests/e2e/ui/helpers/userOnboarding.ts @@ -47,6 +47,7 @@ export async function expectUnrestrictedDashboard(page: Page): Promise { const session = await readDashboardSession(page); expect(session.password_reset_required === true, "login must not require a password reset").toBe(false); await virtualKeys.click(); + await expect(page).toHaveURL(/\/ui\/api-keys\/?$/); await expect(page.getByRole("main").getByRole("heading", { name: "Virtual Keys", exact: true })).toBeVisible({ timeout: 30_000, }); diff --git a/tests/integration/mcp/test_responses_mcp_mixed_tools.py b/tests/integration/mcp/test_responses_mcp_mixed_tools.py index 985a8b8a65e..91119f1c258 100644 --- a/tests/integration/mcp/test_responses_mcp_mixed_tools.py +++ b/tests/integration/mcp/test_responses_mcp_mixed_tools.py @@ -21,6 +21,8 @@ def test_responses_with_gateway_mcp_and_caller_function_tool_hands_both_to_model upstream_tools: list[tuple[str, ...]] = [] def respond(request: Request) -> Reply: + if request.method == "GET" and request.target.endswith("/models"): + return Reply(body=b'{"object":"list","data":[]}') assert request.target.endswith("/responses"), request.target body: Final = json.loads(request.body) upstream_tools.append(tuple(str(tool.get("name")) for tool in body.get("tools", ()))) diff --git a/tests/unit/proxy/test_proxy_reject_logging.py b/tests/unit/proxy/test_proxy_reject_logging.py index d5a3acb2cd7..4e250ed3c52 100644 --- a/tests/unit/proxy/test_proxy_reject_logging.py +++ b/tests/unit/proxy/test_proxy_reject_logging.py @@ -74,18 +74,20 @@ class testLogger(CustomLogger): self.reaches_sync_failure_event = True -router = Router( - model_list=[ - { - "model_name": "fake-model", - "litellm_params": { - "model": "openai/fake", - "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", - "api_key": "sk-12345", - }, - } - ] -) +@pytest.fixture +def router() -> Router: + return Router( + model_list=[ + { + "model_name": "fake-model", + "litellm_params": { + "model": "openai/fake", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "api_key": "sk-12345", + }, + } + ] + ) def _register_proxy_test_logger(callback_logger: testLogger) -> None: @@ -130,7 +132,7 @@ def _register_proxy_test_logger(callback_logger: testLogger) -> None: ], ) @pytest.mark.asyncio -async def test_chat_completion_request_with_redaction(route, body): +async def test_chat_completion_request_with_redaction(route, body, router, monkeypatch): """ IMPORTANT Enterprise Test - Do not delete it: Makes a /chat/completions request on LiteLLM Proxy @@ -139,7 +141,7 @@ async def test_chat_completion_request_with_redaction(route, body): """ from litellm.proxy import proxy_server - setattr(proxy_server, "llm_router", router) + monkeypatch.setattr(proxy_server, "llm_router", router) _test_logger = testLogger() _register_proxy_test_logger(_test_logger) litellm.set_verbose = True diff --git a/tests/unit/proxy/test_proxy_utils.py b/tests/unit/proxy/test_proxy_utils.py index ab787bbbe27..44ab2155734 100644 --- a/tests/unit/proxy/test_proxy_utils.py +++ b/tests/unit/proxy/test_proxy_utils.py @@ -1881,9 +1881,10 @@ async def test_health_check_not_called_when_disabled(monkeypatch): } }, ) -def test_custom_openapi(mock_get_openapi_schema): - from litellm.proxy.proxy_server import custom_openapi +def test_custom_openapi(mock_get_openapi_schema, monkeypatch): + from litellm.proxy.proxy_server import app, custom_openapi + monkeypatch.setattr(app, "openapi_schema", None) openapi_schema = custom_openapi() assert openapi_schema is not None From 22bd49e2311ec0314da6ed4ed25e685baf1f1358 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2026 16:54:13 -0700 Subject: [PATCH 08/61] fix(ui): make the Lens traces refresh button always clickable (#44252) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com> --- .../TraceView/AgentTracesSection.test.tsx | 37 +++++++++++++++++-- .../TraceView/AgentTracesSection.tsx | 7 +++- .../view_logs/TraceView/TimeRangeControls.tsx | 21 ++++++----- 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.test.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.test.tsx index 29acaec7f74..e90544ca0a6 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.test.tsx @@ -7,7 +7,7 @@ import { ApiError } from "@/lib/http/client"; import { chooseSelectOption, renderWithProviders, testQueryClient } from "../../../../tests/test-utils"; import traceList from "./__fixtures__/trace_list.json"; import AgentTracesPage from "./AgentTracesPage"; -import { AgentTracesSection, filterRuns } from "./AgentTracesSection"; +import { AgentTracesSection, filterRuns, type TimeControls } from "./AgentTracesSection"; import type { TracePage, TraceSummary } from "./traceTypes"; vi.mock("../../networking", () => ({ @@ -45,7 +45,7 @@ const renderSection = () => ); // A UTC-pinned day around the fixture runs (2026-09-30 ~06:43 UTC), so they land in the same bucket in any timezone. -const renderWindowed = () => +const renderWindowed = (timeControls?: TimeControls) => renderWithProviders( endTime="2026-10-01T00:00Z" isCustomDate isLiveTail={false} + timeControls={timeControls} />, ); @@ -425,6 +426,25 @@ describe("AgentTracesSection", () => { expect(screen.queryByTestId("timeline-selection")).not.toBeInTheDocument(); expect(rowCount()).toBe(runs.length); }); + + it("clears timeline zoom when refreshed", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); + renderWindowed({ rangeHours: 24, onRangeHoursChange: () => {}, onLiveChange: () => {} }); + await screen.findAllByTestId("agent-trace-row"); + const area = screen.getByTestId("timeline-area"); + const x = (bucket: number) => bucket * 10 + 5; + + fireEvent.pointerDown(area, { clientX: x(0), pointerId: 1 }); + fireEvent.pointerMove(area, { clientX: x(1), pointerId: 1 }); + fireEvent.pointerUp(area, { clientX: x(1), pointerId: 1 }); + expect(screen.getByTestId("timeline-selection")).toBeInTheDocument(); + expect(screen.queryAllByTestId("agent-trace-row")).toHaveLength(0); + + fireEvent.click(screen.getByRole("button", { name: "Refresh" })); + + expect(screen.queryByTestId("timeline-selection")).not.toBeInTheDocument(); + expect(screen.getAllByTestId("agent-trace-row")).toHaveLength(runs.length); + }); }); describe("AgentTracesPage", () => { @@ -453,7 +473,18 @@ describe("AgentTracesPage", () => { expect(live).toHaveAttribute("aria-pressed", "true"); fireEvent.click(live); expect(live).toHaveAttribute("aria-pressed", "false"); - expect(screen.getByRole("button", { name: "Reset zoom" })).toBeDisabled(); + expect(screen.getByRole("button", { name: "Refresh" })).toBeEnabled(); + }); + + it("refreshes the trace list", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); + renderWithProviders(); + await screen.findByTestId("runs-table"); + + const callsBeforeRefresh = vi.mocked(agentTraceListCall).mock.calls.length; + fireEvent.click(screen.getByRole("button", { name: "Refresh" })); + + await waitFor(() => expect(vi.mocked(agentTraceListCall).mock.calls.length).toBeGreaterThan(callsBeforeRefresh)); }); it("keeps the time controls on an empty range the user picked, instead of showing onboarding", async () => { diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.tsx index 4ec8fe7e0ec..1b25ca19051 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.tsx @@ -211,8 +211,11 @@ export function AgentTracesSection({ onRangeHoursChange={(hours) => changeRange(hours, timeControls.onRangeHoursChange)} live={isLiveTail} onLiveChange={timeControls.onLiveChange} - zoomed={zoom !== null} - onResetZoom={() => setZoom(null)} + onRefresh={() => { + setZoom(null); + checkTraces(); + }} + refreshing={traces.isFetching} /> )} diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/TimeRangeControls.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/TimeRangeControls.tsx index 0d3cf77a1e5..03d950a7725 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/TimeRangeControls.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/TimeRangeControls.tsx @@ -35,30 +35,31 @@ interface TimeRangeControlsProps { onRangeHoursChange: (hours: number) => void; live: boolean; onLiveChange: (live: boolean) => void; - zoomed: boolean; - onResetZoom: () => void; + onRefresh: () => void; + refreshing: boolean; } -/** Joined control group: reset zoom, the actual time range (opens presets), and Live. */ +/** Joined control group: refresh, the actual time range (opens presets), and Live. */ export function TimeRangeControls({ range, rangeHours, onRangeHoursChange, live, onLiveChange, - zoomed, - onResetZoom, + onRefresh, + refreshing, }: TimeRangeControlsProps) { return (
From f498176a276b1f16b9c8ae6a265efe8c56ffd188 Mon Sep 17 00:00:00 2001 From: moe-berri Date: Fri, 2 Oct 2026 17:27:13 -0700 Subject: [PATCH 09/61] feat(lens): add sample previews and improve setup and worker feedback (#44268) * feat(lens): add interactive traces and investigations demo * fix(lens): limit sample previews to setup screens * fix(lens): simplify tracing setup and align sample previews * fix(ui): unify Lens and ROI demo notices * fix(lens): distinguish preparation from zero selected runs * fix(lens): report incompatible workers and address setup review --- deploy/lens/README.md | 6 +- deploy/lens/compose.yaml | 2 +- litellm/proxy/lens/worker.py | 31 +- tests/unit/proxy/lens/test_worker.py | 40 ++ .../public/assets/logos/google-adk.png | Bin 0 -> 11976 bytes .../public/assets/logos/hermes.png | Bin 0 -> 1100 bytes .../public/assets/logos/openclaw.png | Bin 0 -> 1015 bytes .../public/assets/logos/strands.svg | 4 + .../hooks/models/useModelCostMap.ts | 3 +- .../lens/_components/LensOverview.tsx | 58 -- .../lens/_components/LensProgress.tsx | 2 +- .../_components/LensView.integration.test.tsx | 35 +- .../(dashboard)/lens/_components/LensView.tsx | 37 +- .../lens/_components/LensWelcome.tsx | 13 +- .../LensWorkspace.integration.test.tsx | 137 +++++ .../lens/_components/LensWorkspace.tsx | 117 ++++ .../lens/_components/WorkerSetup.tsx | 2 +- .../lens/_components/lensData.test.ts | 22 + .../(dashboard)/lens/_components/lensData.ts | 9 + .../lens/_components/lensDemoData.test.ts | 55 ++ .../lens/_components/lensDemoData.ts | 483 +++++++++++++++++ .../src/app/(dashboard)/lens/page.tsx | 54 +- .../ROICalculatorView.integration.test.tsx | 26 + .../_components/ROICalculatorView.tsx | 25 +- .../src/components/lens/LensDemoContext.tsx | 12 + .../src/components/lens/LensPreviewButton.tsx | 18 + .../src/components/shared/DemoNotice.tsx | 16 + .../src/components/shared/PageHeader.tsx | 22 +- .../view_logs/TraceView/AgentTracesPage.tsx | 9 +- .../AgentTracesSection.integration.test.tsx | 509 ++++++++++++++++++ .../TraceView/AgentTracesSection.test.tsx | 502 +---------------- .../TraceView/AgentTracesSection.tsx | 36 +- .../view_logs/TraceView/DetailContent.tsx | 17 +- .../view_logs/TraceView/DetailPane.tsx | 17 +- .../view_logs/TraceView/TimeRangeControls.tsx | 28 +- .../view_logs/TraceView/TraceDrawer.tsx | 17 +- .../view_logs/TraceView/TracePreview.tsx | 70 --- ... => TracingSetupCard.integration.test.tsx} | 111 ++-- .../view_logs/TraceView/TracingSetupCard.tsx | 498 +++++++---------- .../view_logs/TraceView/spanProvider.ts | 4 +- .../view_logs/TraceView/tracingSetupGuides.ts | 501 +++++++++++++++++ .../view_logs/TraceView/useAgentTraces.ts | 12 +- 42 files changed, 2427 insertions(+), 1133 deletions(-) create mode 100644 ui/litellm-dashboard/public/assets/logos/google-adk.png create mode 100644 ui/litellm-dashboard/public/assets/logos/hermes.png create mode 100644 ui/litellm-dashboard/public/assets/logos/openclaw.png create mode 100644 ui/litellm-dashboard/public/assets/logos/strands.svg create mode 100644 ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensWorkspace.integration.test.tsx create mode 100644 ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensWorkspace.tsx create mode 100644 ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensDemoData.test.ts create mode 100644 ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensDemoData.ts create mode 100644 ui/litellm-dashboard/src/components/lens/LensDemoContext.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/LensPreviewButton.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/DemoNotice.tsx create mode 100644 ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.integration.test.tsx delete mode 100644 ui/litellm-dashboard/src/components/view_logs/TraceView/TracePreview.tsx rename ui/litellm-dashboard/src/components/view_logs/TraceView/{TracingSetupCard.test.tsx => TracingSetupCard.integration.test.tsx} (63%) create mode 100644 ui/litellm-dashboard/src/components/view_logs/TraceView/tracingSetupGuides.ts diff --git a/deploy/lens/README.md b/deploy/lens/README.md index 315d072b501..4b65c88afcd 100644 --- a/deploy/lens/README.md +++ b/deploy/lens/README.md @@ -19,11 +19,13 @@ The URL, database, and retention settings can also come from `CLICKHOUSE_URL`, ` Retention changes require a proxy restart. ClickHouse removes expired rows during background merges, not immediately at startup. Enable request/response logging to analyze LLM requests. Lens can only inspect content you actually retain -In Lens, click **Set up analysis**, choose an existing virtual key or **Create worker key**, then **Generate setup command**. The LiteLLM address is filled in for you; change it only if the server running Docker needs a different network address. Copy the command and run it on your server. The dialog changes to **Analyzer connected** when the container checks in +In **Lens > Investigations**, click **Connect worker**, choose an analysis model and monthly limit, then **Get install command**. Use **Advanced options** to select an existing virtual key or change the proxy URL if the server running Docker needs a different network address. Copy the command and run it on your server. The dashboard shows **Worker connected** when the container checks in The command already contains the compatible worker image and one worker token. The selected virtual key stays on the proxy; its secret is never sent to the worker. No source checkout, environment file, or second LiteLLM deployment is needed. Keep the command private because it includes the token. The LiteLLM release provides the dashboard and APIs; the container only runs background analysis -The dashboard and Compose file pin a verified worker image by digest. The image uses Linux amd64, and the generated command selects that platform. Worker image releases are independent of proxy releases: update the pinned image when changing their API contract. CI also publishes immutable commit tags for reproducible builds +The dashboard and Compose file pin a verified worker image by digest. The image uses Linux amd64, and the generated command selects that platform. CI also publishes immutable `:sha-` tags for successful worker builds on `main`. Keep the worker image compatible with your gateway version + +After upgrading the gateway, update the worker image and redeploy it while keeping its proxy URL and token. Existing containers do not update automatically. If an investigation reports a worker compatibility error, update the image before retrying For deployments managed with Compose, download `compose.yaml` and provide `LITELLM_URL` and `LENS_WORKER_TOKEN` in an environment file. Its default image is already selected: diff --git a/deploy/lens/compose.yaml b/deploy/lens/compose.yaml index 4d1224fd41e..fc9850fcb04 100644 --- a/deploy/lens/compose.yaml +++ b/deploy/lens/compose.yaml @@ -1,6 +1,6 @@ services: lens-worker: - image: ${LENS_WORKER_IMAGE:-ghcr.io/berriai/litellm-lens-worker@sha256:67eba741c1b97c749975c5c38e2370a603e1105babc908d613c1b79d7b995393} + image: ${LENS_WORKER_IMAGE:-ghcr.io/berriai/litellm-lens-worker@sha256:44f0597c7583dcfef999ece9a8bc02cfeb9f0f5167a1221cee3bd10b1b79271b} environment: LITELLM_URL: ${LITELLM_URL:?Set the URL reachable from this container} LENS_WORKER_TOKEN: ${LENS_WORKER_TOKEN:?Create a worker credential in the Lens UI} diff --git a/litellm/proxy/lens/worker.py b/litellm/proxy/lens/worker.py index 62f8295e7d3..051b4a09392 100644 --- a/litellm/proxy/lens/worker.py +++ b/litellm/proxy/lens/worker.py @@ -8,6 +8,7 @@ from types import MappingProxyType from typing import Final import httpx +from pydantic import BaseModel, ConfigDict, ValidationError from .analysis import analyze_sample from .models import Claim, Coverage, ExecutionContent, ModelRequest, ModelResult, Progress, Result, Sample @@ -15,6 +16,17 @@ from .models import Claim, Coverage, ExecutionContent, ModelRequest, ModelResult logger: Final = logging.getLogger("litellm.lens.worker") +class ClaimedJobIdentity(BaseModel): + model_config = ConfigDict(frozen=True, extra="ignore") + id: str + + +class ClaimIdentity(BaseModel): + model_config = ConfigDict(frozen=True, extra="ignore") + lens_id: str + job: ClaimedJobIdentity + + def failure_message(error: Exception) -> str: if isinstance(error, (OSError, sqlite3.Error)): return "Worker temporary storage failed. Increase its capacity or reduce analysis parallelism." @@ -74,9 +86,24 @@ class LensWorker: async def run_once(self) -> bool: response: Final = await self.client.post("/lens/worker/claim", params=MappingProxyType({"protocol_version": 2})) response.raise_for_status() - if response.json() is None: + payload: Final = response.json() + if payload is None: return False - claim: Final = Claim.model_validate(response.json()) + try: + claim: Final = Claim.model_validate(payload) + except ValidationError: + identity: Final = ClaimIdentity.model_validate(payload) + failure: Final = await self.client.post( + f"/lens/worker/{identity.lens_id}/{identity.job.id}/result", + json=Result( + coverage=Coverage(), + error="The worker could not read this investigation. Update the worker to match the gateway, then retry.", + ).model_dump(), + ) + if failure.status_code != 409: + failure.raise_for_status() + logger.warning("Worker could not read a claimed investigation; reported a version compatibility failure") + return True prefix: Final = f"/lens/worker/{claim.lens_id}/{claim.job.id}" async def model(body: ModelRequest) -> ModelResult: diff --git a/tests/unit/proxy/lens/test_worker.py b/tests/unit/proxy/lens/test_worker.py index dce3fc04d45..0e64b0f10c8 100644 --- a/tests/unit/proxy/lens/test_worker.py +++ b/tests/unit/proxy/lens/test_worker.py @@ -3,6 +3,7 @@ from typing import Final import httpx import pytest +from pydantic import ValidationError from litellm.proxy.lens.models import ( Claim, @@ -81,6 +82,45 @@ async def test_idle_worker_does_not_start_an_analysis() -> None: assert await LensWorker(client).run_once() is False +@pytest.mark.asyncio +@pytest.mark.parametrize("result_status", (200, 409)) +async def test_incompatible_claim_reports_failure_instead_of_leaving_the_investigation_running( + result_status: int, +) -> None: + claim: Final = Claim(lens_id="lens", job=queue_job(lens(), NOW, "job").jobs[0], findings=()) + payload: Final = claim.model_dump(mode="json") | { + "job": claim.job.model_dump(mode="json") | { + "settings": claim.job.settings.model_dump() | {"future_setting": "private content"}, + }, + } + saved: Final = SimpleQueue[Result]() + + def handle(request: httpx.Request) -> httpx.Response: + if request.url.path == "/lens/worker/claim": + return httpx.Response(200, json=payload) + assert request.url.path == "/lens/worker/lens/job/result" + saved.put(Result.model_validate_json(request.content)) + return httpx.Response(result_status, json=True) + + async with httpx.AsyncClient(base_url="https://proxy.test", transport=httpx.MockTransport(handle)) as client: + assert await LensWorker(client).run_once() is True + assert saved.get_nowait().error == ( + "The worker could not read this investigation. Update the worker to match the gateway, then retry." + ) + assert saved.empty() + + +@pytest.mark.asyncio +async def test_claim_without_an_identity_does_not_report_failure_for_another_investigation() -> None: + def handle(request: httpx.Request) -> httpx.Response: + assert request.url.path == "/lens/worker/claim" + return httpx.Response(200, json={"job": {"settings": {"future_setting": True}}}) + + async with httpx.AsyncClient(base_url="https://proxy.test", transport=httpx.MockTransport(handle)) as client: + with pytest.raises(ValidationError): + await LensWorker(client).run_once() + + @pytest.mark.asyncio @pytest.mark.parametrize("model_status", (200, 402, 503)) async def test_worker_reads_claimed_activity_and_reports_analysis_or_failure(model_status: int) -> None: diff --git a/ui/litellm-dashboard/public/assets/logos/google-adk.png b/ui/litellm-dashboard/public/assets/logos/google-adk.png new file mode 100644 index 0000000000000000000000000000000000000000..9f967caa3008bdb13b446e1545cb16eef4e29149 GIT binary patch literal 11976 zcmdUV`9IWO^#7e1%h<-)q7aE}p^_!*5ZRJRcFI;LYh_K$*g`7VQKYs#;_MksZKKj=80bt?!`wIgvGx(rF*nLwyZBW)Lun7G_=c;9_1wchS%Yh?3 z0J7H&bhOL@VJnlyPck|}=GSrRCfnE}2Cnb!@|AK}8UIM`y61Ay#cwR?fxgPR@kQI( zBChC;4EVrSsP7S`@`*Bi#Ak zrR2HA`8Q??u5L{|(hXU~53*Jn39dR5oWmw2qs`Q?J2TH-l})ro32|1 z2{pQd^C?FcdP+Pl!<`xTzGCmjyM;BUKdwu4Q&wGVX>om_miWp6 z9=tSE$viQqJFj)Wl(50bVYE!S`Q?hMm*m4`>tS6Fict5FBl>1TacEM$h8dHfHv zjY82p`Gxx@KVD8-iLlZWAss_C*Pjn^zq$B``$2tKMDs^-{DPapaylML4C9pH2WWwYl zqyqRHMw%71@kMBV7V>cw@Vu>j`a*9B&a-o4l&&bozpOH-5=LAALt|f})Lk#fKOZ5h zC?k>PdC3}SPq|&kC9(G0KtbVMwch-u%bGXS$gKd_SR8(g4PRU#&Qwr;p(PQcUO|Vw z_*MqTKh$M-L7E|Hc9bK zL6@-7r(r{p*H@d>D}7@L)07ZuiqPi`p%2oSTaxL&;(v&LrXNDkW7vpOngWUlhdR0= zJ=08B^B?e~2r>NQ{XO?MXI5&PV-+x=?FA>^wC5lcd&rW!cpm;Pb(Yb}zY5G=e5hCA z?ICOG7|^6Q{&<_1(h&wN+QM+Vx!%UiQgLuXj>llL#E_a}^Ndl`&aq4|)KWBfZ?Qb| zkBn?vQIQ$;0!0mXottJv?sjk|uh3kI!!Bc6IGkuwI3Z$G4DGeqI9XeNd4!?D&&{Pu?w`xr@h8M9OboS<#NK)bE=hG7z) z-z<>9^W4Q=@L{q}Ookn_3$?qKa*vIcYr>?q_q~m4D@6s3 znA6X%4$rSBs=QGtJyq6x;ID_4VaCU>UD>Hh+cWchn{7Hc@V?1L^iJ94;q~hwDU*UQsU?!FTy4sNWNF^H=wD+Z!V?Mc z_rtIcA(_5ATN7#37Cg#1_ixo^uc~ys)L6RN&-2{(8#i1|U%{Wpw@j!(`9useJ?3U{ z#b|bz5YgjAf7+*H)4jmuF2vmQ%zrg$p$}}(Y|ygWoBuWl%ox_jBx{`Ha1M5`gNEP; zp0($BaH=oBSbh!R-r#*(c;pF5`=U|@6aA+!xG?-|G!?lGv#qPV@1XJN7EC*AovcDe zzG$Icei+ewd)xiO;S_FKp@q!u(X%htw^b(C@UD-wuy6%0#ZjSabpJx^1Ib+31wi+^ z-#zZyTvt$ROy<_B@h&FHI>JPDR&rCG zQqUfuh9O(QkyZtpn;bRNVBJAJ*EPof-i0o&+-J*NjdL?_s4lN8s!#K~2tyaOq-x4| z#^Q~4c*lG1+EDSfiu?`;f0xXVOSAnNYi-_HoOH2bc%WPJ)1>RR%OPeL)wnh5D>~0W zWeZ`&+H(=VqYW4c)}Wy4*lF#RYUdY2H86l-@r=61bQf+8Ge5q zwO~l_xI4Jhs3m+x*cZU>D($4I|x%AqddUr^=ATj80<9yjVHO%N_G(mOd4E-;r6Yx>EFo49H_V@7U$@+#wb$# zk_kx}A}ROE6{)F!EWyyjk-U*kF{WoXhM98I-5It`((>Au{xjZHzWf`BUzp6i_4X@g7A36i zh6hXIYp^D1e8tdV`J;yAjd67uAk{V{%J+j_+ZWcD1J-_g=C7N8O;JX>Hm`q$6YgyY z?)u_iW9q?iNOlg3pB-FY|DbAAs7N&U66wqBFvr6uU+LE#!AXdQ)#>W*1)`!hSGe{N z#hz$pZa<6G!O(xkb5R1QA=ieQt;@FPms;jv-D%J0^5hOBwV(>ak*uZ*#pYOd8iknM z??MLz?kpTcMpknZ{ul(uhY`%Uftb*Q;FujA3Lnky4x7Ch!>WC)Ro03c-p^$f2 z3@^g4InP^hbTnW^!3#5qp^JQd3Ue)v(qjykJ8>LTX^2{>oLLT%a4|O?xGj7eEITrSWG7)(iv$nU5ZS(H`e{ z_jKe6E@+VaNfW#pc}^|aQ8Gl4WSBwp257|@<{9mD7{03JW0Ngc;LG%KEKZ${T*EssD0!Io*OepD|@ zOW+V!7BVk>ee>uDSNyW;!dQOi)i5q@@Qk&Wwq1S_5JRQ{&#&+>l@SMP{`V*@R;!2@8n$!S}(%VL&8J#ikMXo_GTML29KC5Q<}G% zn%(Vu`d*J7DNIiMZW|&?sX6bSAa4m3TcFvpwtWtMvNvxf1R2}R_Nk7>U;lv>W?TU8 zaz48Ie0+#MyKnEf$>(A)*QlP{$;x0`%Q>g!&5$fV_sg2$5plgST6*A~xipR*FR7u- zImW>Z^cq(IcH{NXe?O{^e^99OEs*$;%&OOQpo06c_3^MFwW{j=*WkwNv}c;+poZ5e z=aQI0?9>KA8`x7WA^XExI3O?@1ZD)|mle9}UKwr9eRZ~qXi<;a4W7EA=skveB#cz^ z4lv76nLVlpZ_t8~@mH$1L*6!QgnF`oeXS2An?W7dUyR&K=(|R@)3NC*DUi>3J5*QoeoUA<8pf-(dQYOy_Hj*MwwXC6J)S=_0Jse&9jlyZvMYgk zx0i7U1u4WN3AsnjUq5cn-OL*gfe+?K&)_7OJYxI6MJ80tFRy)~yF@>Ew9WKR?ELj( zGFusDW`gv1t}mMp%nw7YEL|dG)(aJO{yIP5XHK!i0-0;lA4HhM3f1EQDL3 zMVVon2Nn#To}P%i0Z*X&lOnSxbLrm;s{%Kz|^e<9&#|@M)9bQ3M>AqX(nU zAi{&yyd97dP?TGAf*F`;=`-CL9Q?iB7qgeMbUzTY^!URAKR6s!56 zB^#Dc4oZ_w$P>G<{HUCkfT{arm%WMc~Cw@ciO}|H-FSIgf3T2kJB`NxB z*F$dw=rLb^U~7?%J5@#7Sa0mgO$p32d(mg+4^xH=WaRHyJ;SA7-P;MrQSF`Q`g?<^$M{*9yT;$eDL6GSWVosxJwUW>`s>GT!^sdH269=8bYl@nnA`SBUyhJEqY0y&IjSCnxzQR%?f~Wd^$II;*3zCY?v)ZC(SP z_Je-knxfRe+==k8ly*wVQNDui=uU$VA2CVle!a)lE)Hs<)K+KCWji}ZJPo8$cdKcF zSK;EQ5B~cZK|v;u_3V2q)$a$m>b{HrAaizLH!nz|PrB6h0kOlnG&18)_)*xA%Z&La z4RLOL4+@8clpr%XZVUFM4)xV4#bv1*V;`@Dzs^OFpZwk{EB8`(HFwd(g`VAN7q3~j z!NSwAIn{L9pd-1D+DxSH7t_W+%Y)fj-$^~u7##rGr3W^`cM3YeJ8c+QKlPUSdUr|Y zE^US$T~!52Hp)i#sI)MP+9bxUfiY4t|zA7%I$41E^1KE^jrvAzp$fPoV6$SRsl1C92rBJMl9Z~-K1{Q~y@#S72Cdk0uu zD;pG6iHbr_&>-iJ4bOXy8L1rVgidZQ9I#hZ;i@P??JbUm(jzpZzx98So<5j>J-u2F z!12OtQU8_ceYzGe+V=z6Ur=$${<&~JJy;V~-B+smEy;9I?;li&tYXFR0#d~CvSwr{ zE_Foed8g@C1q(qZ`i5SuNtB%HxQ0I+K#J{pvt6O7`rpkaaQgzod{|}bhdsuO=S;tae@2(nfA8^ESl9zw1FDIEu<4|lv;-SHCU)5iJX_&D#+&?-<>ox?_ySYq5c*j+^?GwEzK~&ZqD1D&>Bjt zdR@ex$isdQ-kalxxHBgFH)1JHQuM{_^dnkjAv2053E{R*m53i9*p%OHP%r`~D0-~P z6^%FVx-B2ibhb2wK%kmL;-?b2$b3N1u&|+B<3zXOLVZwp@o0Pdj59$0GQU<|?L^J#L|e~^XxBmf+JhnM7bl@6?13m&ww98M1R zv(+Bb<5#%TjMAt zDt%N09?rcqgJHMUXAVhj3kNt0)NaN8K}7Go&y2|nC`^_3fgy)niqq*4df*(}fB@{v zHBHju2T{j+uM6++o>Bo}Dt|POn4}kO7U&3Hz2`N0&)>iIv?CG}dJInASmv4?r^Xjv zvDc(1y3=4eP+%)+1bh47q=CwMRf`t`kkzT0!o@GWXu)ZtMN$wIgQB-w1p3{@a+xdXa`R#$ifdml4_5gi@Zzqnhc4v0Y?xs0M_kPCr(C^STCh%>7c>SS2F`~Q@ba+jxqdO*LtOp$S^&eB4UtodGT>sm;zI3fW zdF`dHR6!R>I&8r3(Vbg91AS10nsr16+GOp7Om-sb13{;!;yk|o%@E03GQEL1_%S!U z2ofN%fh9YSKfxRRiRzmE?gcZm?4D_=LGQ)!(qr-%USl%*EZ-ol28_Bra$XM}t%nzd z&rhk0EQxn@NXt(&#|s`@o6HdY!z&K)9yRv10!BJc?^tizcE756n_{QWKe)R)y|ToN zj*ZZyQeKSgvbgkn8%4S7v!7TOy1NS=g z5uV8qTu!TWG2*Q+)mfG6_8xX5H4AkdSU`-SZo6*?QT2hA(jA&AanY42|>K=&TL+L0u!ASy+e|oC{cfx-8y=asqE75?J zngYeCTBjMot17jm05%#ydWamiPYr zCCgxTJgG>j9Tm_`zE7jKI*|@5@4-cph+Ct z4hSqsn5I1)`W$Y&C%-ewE{K0AB7iLE$>M&uvm79ea}pli9pQ&c>(f{y{yYJJcs96V zDz(Zi-E+5U%HG=-_-ywJi@7c^0NnExs_Xlz?d-g8k1pEz=zTm2{P?mR(DSV{9Kx@F zHLt7BaI5GFM4cTya2$Y<5Ej2Vhz)eg>gXDmQJVMazU`YB`pvtz)XjstntvJbl;&x_ zGX253rEx6qAuB%=UA^ir*R_te1>j(O6M+q=qaM6#|8nY}r*dkM{YL#~m_y)c6=3yM znBOO$kYo+Jb81!wPvOk2Qk>}{$8Yt+NCji}5oTG&(kbn#V(?nR*m}ij>bNu~@5FEK z3qtpz+sMinu`XN~+^-`DfnYC*Y#3kOT32AQY46WKB5ljHmG z1<0MK$fsdK6M8VbxDyM^ASub04y)kL;_&VQUO zfp-F76%jP(aJK3F!nHg^+xOe_oL$I;23h-b1feV9l8#aDI|qAM89~c*brGFjuSjlb zoDPn?y(+)ZCe94#T4cFIb*GNx-#8-_@9fKnO-*#6`lo6Z>V}N%9l%&Y_{%EG z``H`qz7D>%V9tOzyoa4JvVjhes&uf$)Sq*AQ-1OL@gK-|)(aly1YH7PY5*oN;4tUS zNPFp?_cY`*J;2*T_|s|hfYB4DueoZe@&^gdLN*2diqiKupWlz^z_V5RMq`mvcN1exQOiaW=LbT{Qu@!LvR%KL-JhBFgXdJ#xS|7(Bbn$iOqJWm zye&fBPFqOkUpbF*t%8etg?@i;G16!966SKp4?mta?jo<=Fz>p$O?RJ8W6ivdskHT1fP^@+w!!HO73tHPljRV`%h)d%USPb<@?1+o0!i~2iwr{U5-*( z$JNQX1P{A2=Oe6LQYSJDj2`8>x@2>8Yt%*0hxK#7eLu5Oud!0yXZ(&9n8d4%{&o#% zXv-68ljkF8?C!OkZAn$;J1A|L{;J^)dxVFRPRQ?MbC=}%&kMiXps@?7L5Z^Fk2ODg z?iid%TgIOncD9?LA+>4E&`c;xM zx92Oe&swvdw7M||UR)Q7)=ZIAv@H&o2XI9@O6N?(rpYnciEs;C!ZiVxG{V=FcpYNzxOp1C1-vseA<9G?*<2B8n3_)Iye(76=P}}9&tE!Ro%~jPp?A%W z$OiEQtj`X_4(ep{Bj1MAwOk$Z{@U$uk=KFkxIKSs*w_(fz`NH?RdP}hwWPP5RbH;M zPPz#@?pLKAXk5@P1hqH^Mcv6xl;#AN#2zNa(mdj{%M%x)9)lJ{rRbBe!U8dVR{`OC!Pd0<@x}YnvkY*i z{nBVOIafQx7|G~l%nc->1P_dV;k1cm_}0)ObZb~(#v|}-yro(Hhg-x5?WM<9U&*%p z;9UV>UHs-%iUL!;d?hE!L+RBblNWcSE+e>7+IygP>Kuw{eUa$cTqCi_beu371WBJg z&J5n2PdcUA!E{(Evp}4-IbW!(QPRJ3F&3IQE0kV-mwejos{GUHiarMFP&<5ORzU|% z30CWoe8zq)_P4$rk`c-`o9MHRXfzlOl~_Bg4e0x?@Lx#GbQ)=HdeIizBX|Gt_qLbg zWQEwWF);xvZ^JXb<3ArAiSO=yHGiyVn3&xjZ2Mz*YP&2Gtu~v~#^|ZV`%8f=*c!HV zkr7B3_&PRse7=9>u3cP^r_)rEfim%t1i*9Z9Hj*7pE$QEq}q)hy|-N>=^}a~65;*` z4t!33BTggCRitK=Mp2}tCWg7 z{9UiQ>AE(p@`|xYTO5OAw1A!poY65y?z2fOp8jbP&$Tzv6s(I6k`eQYL`Xljs6aru zGi~0B{kT;{K2&-$Z_g zeLDL_lSXOEvrw|AfR#))w;*Jygv^=tC4--o4(ict~9opJ255QEw%*o~G7aXYeGyO2k3e(ST){$nylG*SfyM+el z$@oAQDkU%ZfmTjiXQb`4wb35PnT^mk0=v-Cmw}rBwt)gvfjEVa^LV;qL zQN6#HyF#qu(cy9b?LDV=&|4kDiHY|kGSwO~cTWn$7{^{%4$v$ewl4eR9}n&7c6Z9J z`Pus2HO7yU$i$O}VsSr97(55>u9d<>TI9JR`snU!)R1!bkg0O|V1IKi9kR@vfsie(xulFf`^I!EzGYhTpP`6hpTR^vfeZv>D3H)6 zbnWe41L(@5blf0=Aof3(OS3g>4Z7cUxd@=fFoW6A8DqcYP_O_5@k@~FJI6t}m<}1d zU(k0Ybl1lrL&JtfMY=~Gk`ez5MUiZ47@uNm=vovC5a}ZkXD>rlT>lJ>na~VIjj1&h z34@FnKcvzI$i#4S1Ck=N*%pvw{r|BPhmeoH1XZ2)1^5%tKwHp2F)t)lFpPvI$Onc- z9-_~w01_JNB?U=?j`x|H<}8e%8(R_1;W9^AsdF`4P&wbf`H+eqM^iy3&MmuJyM@Ne+=^OM5o0 zpUF0O32U^BdcN7{!{>IMr&Q-5UmnrM06kCQhH*M_oOvmuAl@@--J))NF3>nf9=TsJ zWp8^LM(%X0=v+Hnx|KY*_fXTmaU543bfZsmn6pIsn~y_+nq1)X#G)IJa)?ko=v< zFn<_dx&6|6Dai5Hm`aY?1uTyM>z^|`*EX_~4t;p(!G8mLW;f1z_p@2q`_5nlcNhI- z-C}bkM3$rXv4AA`mCfy^u152LSCaL2m1V|_sjkseA{Wlbl=bF@6Kj(*q9W zZw#ev@_sGMhm43QNq+Vrq`nPjEYb-OMMDl1dQFFNWAac$XMMj3t38$H}b;I1^*W{-8df4{vZwj8|zWg#4Hic|>#7lU8@oSmJwVH-TCYWJ?lFC+ec zkxw3UBi87kJmLBX>PdtbQ*G&i4`FGtGvEr#$Z+@OVr9KN)Bt+FWH+FVy%Q1%q6ry3 zgk!z}d~S*^Rp8d?0Z0*Npp@(2!GmV!CSN+AZcpX@1NHkM$wxNlCT(->uBrV+2#|n5 zZHGIa-KU~I8!NB8^3JsvWXN>9XK4NaHd zEq9KMmZi(Zv!CSqyoqNqB%4t-FHJOl*$iit$amq7uHu4=|2zG8yVbie&8Bf$yrVti>={CfT>Ts#Zvp{84nBcklqSqq4(x(Q|a5% zV*4qnsW+G$Pz-#*u}`nmCvsb+Rc79&JN$1pNLUnl2tw?ba?#ymabG*DnLU| zD`CV*JV%vc6DPW4@cl3O+m-h-TA=n98HF*5qHcy&*X9=0gsxj+I>SenwB?Qa&aNRa z?;w&~NrJ)jc3f&I=LdV~NmH8|QEnHK60SCjb6CdgE)>*fK|#I1#{Zo`_&-w+|5pK6 aa~f)e-P@me1|joTmVxdSoic5Q@c#!yYc4(j literal 0 HcmV?d00001 diff --git a/ui/litellm-dashboard/public/assets/logos/hermes.png b/ui/litellm-dashboard/public/assets/logos/hermes.png new file mode 100644 index 0000000000000000000000000000000000000000..de47b728d12a648a0d3affd06e7d3ed75eacd562 GIT binary patch literal 1100 zcmV-S1he~zP)q7y%f8W&ke&%s_tx@FGChvw#-?7=i6_R^alj?rfw76x79^ zx##o|X}Y_ps(Y&d?m#jmnUXy7&*6OA)5jD@3X%o?dlV$a*x?obKUAF&&dq=Vz_fdM zXxkQT+oG;(e0_bPZCfa%pp=5v8lLCjO_Yw~EW4P$_FGXfj$_7g%-Cx>x!dgqb5cq+P4ki+n40OkR!aGBxYn9gRWVId=6Oz%lu}YkNze0G6a~{XeQCt6 zvFtF$004p@u!-mUK0MDuS(Z>rVY}U8zu)6ola=m7MIHfQcBcyZRe)&F(ot|T-UYNv)K$%%F&HZr_&(*LWqHJQ+P$LB91ix`n-_L=fC~Um7)|O&T(=Z4EDy6vHZYSy5 zr)>Yas;c*L!1sNmX^PEe17i%bEVE3=vJ52Qx~`3HAG6sE^Z9)AMaP~W^*|q5YerG@ z;Gw?OYpo}l(f2^7nD=;UKoA5k3rk0}u_1)8)b%{?y%0>5;3-VH_HLaq#wzSn8uoEK z9$}0@UDu;VJUe`hRbzCVG@!G#TQqi9|6W%&#=OvJNk`wON!V4dj*e5Y>r+rl`Ouhj zvF}T{5aM|bWLaii*1erE2K)W~sh8&SxvhL1uGi}z4;G7sZRI9IV2rUY?oS zC6>!2=JPpLtJPq0DdmF!V^ZBWdNc%46j2Casr8KGNs=%OLjqtJhW7f^NRs412ikrq zUa!~GT64GCQ7JXr4RtZj^ZaRvzSo2i1I(Z)ZJLH!Yx=%#7p&Lohs}Z@u-aZ#)ufSc zcp&=)AOo=OI9rw_4u=D>EJGNE`2G9$Xgrltmgm0j+xt>VD5Y#CWQ-Y9!!Qg7dEezh zIYlw8HG?1+ZONl3Vv;0mnubvnjke-xnp!Vvt@~5u{ij(y#rHm!`@BD&&#bD7d7eKc zS3j(2nsTvN^gHCb`!xHwH688k)9ib!)w-8q&+`z5Aw17RUDv4V`rfqzDej)XY0NhO z|90lTI^Z}?>o|@NUj|3GLC)Vz$bm0<)8omzPHZ9 z8x6UBeY5QB{F&G$0Twt9U@AxDsv5I5uwla|ROBz&(Q$~_Pxo;Up0>Bgo~>Hd`n0XB z>oM^`YisMh<;!Cr(02o#39@a4f7rT$o>B(kYD?F9t-88M#Rejr z?~qciC@nZ>a@;4<`$vj1W5NNprZjC$U;~u=FD0^41J{#jh=zKIcnmjE8Tg0gR@x}W zZ)HqRyin`^KnCg=`&{I^-qVVoVl7zuXcV9AIfftKZH7Po-8=KJ_`wL4>^_Q(%}s9H z&d!|}$(KGGR_|Jt>i03=K1 zm*D~KI9p810=I2siL~ipUN}4}6MiELqftbuma5B%X8yVlz@HieV)WuS5-$b~;tRu2 z$yLx@DUEe?#oDH8;`dA{HD5~tVR~S`AQ)qwVzOKySR6otbB_UPtRhR3wifT}dqlyz zrqAL0NF;zru=r>?4I`HW&kIjz40H?(JkbD13Tz_^6;xOHp&%RzspOiFI}XN7Q;Ca0 zBZcWW+myD3JWd-zd_{rHAGEZbh`N)nX1$<&j;CxEpoqCmj3~(8wy|#fLss*(4x_ulyuiu zq#6^0H2yu6{Eo9Wu$dCANlgHim{2|X3jki}k?e{CbkDHM1!1?UHQO`cULnLEQc5wU z31F&Tki>sfjj9f%g#CiCZ>763%2{q=)B!pH^i+2$cF~{BLN^6TRUG z(QWJchX5{Ado1@OP25$|>Z<@26PKv_i3zE`DSV3>8gL;?cE|!eyQL4cH$Y;WNNAHS lL~(o_$LW!U_jrp6@fsk=FmPoZ + + + diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModelCostMap.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModelCostMap.ts index 2d82eedf25c..d9824b4753e 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModelCostMap.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModelCostMap.ts @@ -4,8 +4,9 @@ import { createQueryKeys } from "../common/queryKeysFactory"; const modelCostMapKeys = createQueryKeys("modelCostMap"); -export const useModelCostMap = () => { +export const useModelCostMap = (enabled = true) => { return useQuery>({ + enabled, queryKey: modelCostMapKeys.list({}), queryFn: async () => await modelCostMap(), staleTime: 60 * 1000, // 1 minute diff --git a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensOverview.tsx b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensOverview.tsx index 2e2a0318d85..c6cd8485cbe 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensOverview.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensOverview.tsx @@ -67,64 +67,6 @@ export function InvestigationList({ ); } -export function InvestigationExample({ onClose }: { onClose: () => void }) { - return ( - { - if (!open) onClose(); - }} - > - - - Example investigation - -
-
- - - 3 of 20 conversations -
-

- Failed lookups leave customers without answers -

- - The agent retries the same failed order lookup, then ends the conversation without an answer or a handoff. - -
-
-
-

After three failed lookups, the agent replies:

-
“I will check that for you.”
-
-
- - See the trace - -
    -
  1. - Customer · Where is my order? -
  2. -
  3. - Order lookup · Service unavailable -
  4. -
  5. - Two retries · Same error, no new information -
  6. -
  7. - Agent · I will check that for you. Conversation - ends. -
  8. -
-
-
-
-
- ); -} - export function MonitoringSetup({ settings, ready, diff --git a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensProgress.tsx b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensProgress.tsx index c2b9d227276..8249f284ada 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensProgress.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensProgress.tsx @@ -53,7 +53,7 @@ export function LensProgress({ job, onCancel }: { job: Job; onCancel?: () => voi >
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensView.integration.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensView.integration.test.tsx index a3ec7877223..b3d7366bbab 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensView.integration.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensView.integration.test.tsx @@ -231,39 +231,18 @@ it("runs saved settings immediately without opening setup", async () => { expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); }); -it("lets a new user inspect example evidence and return to setup without starting an investigation", async () => { +it("offers the interactive demo without starting an investigation", async () => { window.history.replaceState({}, "", "/lens/"); testQueryClient.clear(); vi.mocked(apiClient.get).mockImplementation(async (path) => { if (path === "/lens") return { lenses: [], workers: [], tracing_enabled: false }; return { traces: false, requests: false }; }); + const onDemo = vi.fn(); const user = userEvent.setup(); - renderWithProviders(); - - await user.click(await screen.findByRole("button", { name: "View an example" })); - const example = within(screen.getByRole("dialog", { name: "Example investigation" })); - expect(example.getByRole("heading", { name: "Failed lookups leave customers without answers" })).toBeVisible(); - const evidence = example.getByText(/Where is my order\?/); - expect(evidence).not.toBeVisible(); - await user.click(example.getByText("See the trace")); - expect(evidence).toBeVisible(); - expect(example.getByText(/Service unavailable/)).toBeVisible(); - await user.click(example.getByText("See the trace")); - expect(evidence).not.toBeVisible(); - - await user.click(example.getByRole("button", { name: "Close" })); - await waitFor(() => expect(screen.queryByRole("dialog")).not.toBeInTheDocument()); - expect(screen.getByRole("link", { name: "Set up traces" })).toBeVisible(); - expect(screen.getByRole("button", { name: "Connect worker" })).toBeDisabled(); - expect(screen.getByRole("button", { name: "New investigation" })).toBeDisabled(); - - await user.click(screen.getByRole("button", { name: "View an example" })); - expect(screen.getByRole("dialog", { name: "Example investigation" })).toBeVisible(); - expect(screen.getByText(/Where is my order\?/)).not.toBeVisible(); - await user.keyboard("{Escape}"); - await waitFor(() => expect(screen.queryByRole("dialog")).not.toBeInTheDocument()); - expect(screen.getByRole("button", { name: "View an example" })).toBeVisible(); + renderWithProviders(); + await user.click(await screen.findByRole("button", { name: "Preview sample" })); + expect(onDemo).toHaveBeenCalledOnce(); expect(apiClient.post).not.toHaveBeenCalled(); }); @@ -276,13 +255,14 @@ it("guides a first-time administrator into worker connection and lens setup", as return { traces: true, requests: false, data: [] }; }); const user = userEvent.setup(); - renderWithProviders(); + renderWithProviders(); const guide = within(await screen.findByRole("region", { name: "Find what needs attention" })); expect(apiClient.get).toHaveBeenCalledWith("/lens/activity/available", { accessToken: "test" }); expect(await guide.findByRole("link", { name: "View traces" })).toHaveAttribute( "href", expect.stringMatching(/^\/ui\/lens\/?\?tab=traces$/), ); + expect(await guide.findByRole("button", { name: "Preview sample" })).toBeVisible(); await user.click(guide.getByRole("button", { name: "Connect worker" })); const connection = within(await screen.findByRole("dialog", { name: "Connect a worker" })); expect(connection.getByRole("button", { name: "Get install command" })).toBeVisible(); @@ -305,6 +285,7 @@ it("guides a first-time administrator into worker connection and lens setup", as }); }); await waitFor(() => expect(guide.getByRole("button", { name: "New investigation" })).toBeEnabled()); + expect(guide.queryByRole("button", { name: "Preview sample" })).not.toBeInTheDocument(); await user.click(guide.getByRole("button", { name: "New investigation" })); expect(await screen.findByRole("dialog", { name: "Which activity should we investigate?" })).toBeVisible(); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensView.tsx index 09a8607813d..cd78effe0f8 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensView.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensView.tsx @@ -1,4 +1,5 @@ "use client"; +import { useLensDemo } from "@/components/lens/LensDemoContext"; import type { components } from "@/lib/http/schema"; import { useEffect, useState } from "react"; @@ -28,7 +29,7 @@ import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription } from "@/components/ui/sheet"; import { Popover, PopoverContent, PopoverTitle, PopoverTrigger } from "@/components/ui/popover"; import { LensFinding } from "./LensFinding"; -import { apiClient } from "@/components/networking"; +import { apiClient as liveApiClient } from "@/components/networking"; import { TracePanel } from "./TracePanel"; import { LensSetup } from "./LensSetup"; import { LensRuns } from "./LensRuns"; @@ -37,13 +38,7 @@ import { WorkerSetup } from "./WorkerSetup"; import { useAnalysisKeyInfo } from "./AnalysisKeyDetails"; import { uiHref } from "@/utils/uiHref"; import { ApiError } from "@/lib/http/client"; -import { - InvestigationList, - InvestigationExample, - MonitoringSetup, - InvestigationSummary, - InvestigationFailure, -} from "./LensOverview"; +import { InvestigationList, MonitoringSetup, InvestigationSummary, InvestigationFailure } from "./LensOverview"; import { LensWelcome } from "./LensWelcome"; import { workerConnected, @@ -70,10 +65,19 @@ function emptyFindingTitle(active: boolean, scanned: boolean, status?: string) { return scanned ? "No matching findings" : "Ready for the first analysis"; } -export function LensView({ accessToken, readOnly = false }: { accessToken: string; readOnly?: boolean }) { +export function LensView({ + accessToken, + readOnly = false, + onDemo, +}: { + accessToken: string; + readOnly?: boolean; + onDemo?: () => void; +}) { + const demo = useLensDemo(); + const apiClient = demo?.client ?? liveApiClient; const client = useQueryClient(); const [workerSetup, setWorkerSetup] = useState(false); - const [showExample, setShowExample] = useState(false); const [monitoring, setMonitoring] = useState(false); const [now, setNow] = useState(Date.now); useEffect(() => { @@ -85,6 +89,7 @@ export function LensView({ accessToken, readOnly = false }: { accessToken: strin queryKey: key, queryFn: () => apiClient.get("/lens", { accessToken }), refetchInterval: (current) => { + if (demo) return false; const running = current.state.data?.lenses.some((item) => item.jobs.some((job) => ["queued", "running"].includes(job.status)), ); @@ -100,7 +105,10 @@ export function LensView({ accessToken, readOnly = false }: { accessToken: strin queryFn: () => apiClient.get<{ data: import("./lensData").AnalysisModelInfo[] }>("/model_group/info", { accessToken }), }); - const [selected, setSelected] = useQueryState("lens", parseAsString.withOptions({ history: "push" })); + const [liveSelected, setLiveSelected] = useQueryState("lens", parseAsString.withOptions({ history: "push" })); + const [demoSelected, setDemoSelected] = useState(null); + const selected = demo ? demoSelected : liveSelected; + const setSelected = demo ? setDemoSelected : setLiveSelected; const [editing, setEditing] = useState<"new" | "edit" | "duplicate" | null>(null); const [batchId, setBatchId] = useState("latest"); const [historyOffset, setHistoryOffset] = useState(0); @@ -146,7 +154,7 @@ export function LensView({ accessToken, readOnly = false }: { accessToken: strin queryKey: ["lens-activity-available", accessToken], queryFn: () => apiClient.get<{ traces: boolean; requests: boolean }>("/lens/activity/available", { accessToken }), enabled: loaded, - refetchInterval: 5000, + refetchInterval: demo ? (false as const) : 5000, }; const activity = useQuery(activityOptions); const tracesReady = activity.data?.traces === true && !activity.error; @@ -157,7 +165,7 @@ export function LensView({ accessToken, readOnly = false }: { accessToken: strin queryKey: ["lens-history", lens?.id, historyOffset, accessToken], enabled: !!lens, queryFn: () => apiClient.get(`/lens/${lens?.id}/runs`, { accessToken, query: { offset: historyOffset } }), - refetchInterval: 10000, + refetchInterval: demo ? (false as const) : 10000, }; const history = useQuery(historyQuery); const historical = useQuery({ @@ -297,7 +305,7 @@ export function LensView({ accessToken, readOnly = false }: { accessToken: strin }} onConnect={() => setWorkerSetup(true)} onCreate={() => setEditing("new")} - onExample={() => setShowExample(true)} + onDemo={activity.isSuccess && !ready ? onDemo : undefined} /> )} {showReadiness && !ready && ( @@ -687,7 +695,6 @@ export function LensView({ accessToken, readOnly = false }: { accessToken: strin } /> )} - {showExample && setShowExample(false)} />} {monitoring && lens && ( void; onCreate: () => void; onRetry: () => void; - onExample: () => void; + onDemo?: () => void; }) { const activityReady = tracesReady || requestsReady; const workerReady = activityReady && connected; @@ -39,16 +40,14 @@ export function LensWelcome({ className: "col-start-2 w-fit sm:col-start-auto", }); return ( -
+
+ {onDemo && }

Find what needs attention

Check how your agents behave. Get findings you can trace back to what happened.

-
  1. diff --git a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensWorkspace.integration.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensWorkspace.integration.test.tsx new file mode 100644 index 00000000000..8b7531c9bc2 --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensWorkspace.integration.test.tsx @@ -0,0 +1,137 @@ +import { fireEvent, screen, within } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { chooseSelectOption, renderWithProviders, testQueryClient } from "@/../tests/test-utils"; +import { LensWorkspace } from "./LensWorkspace"; +import { createLensDemoData } from "./lensDemoData"; + +const network = vi.fn(); +beforeEach(() => { + testQueryClient.clear(); + vi.stubGlobal("fetch", network); + network.mockReset(); + network.mockImplementation(async (input) => { + const path = new URL(String(input), "http://localhost").pathname; + if (path === "/v1/traces") return Response.json({ detail: "Tracing is not enabled" }, { status: 501 }); + if (path === "/lens") return Response.json({ lenses: [], workers: [], tracing_enabled: false }); + return Response.json({ data: [], traces: false, requests: false }); + }); +}); + +describe("Lens interactive demo", () => { + it("opens without tracing, filters sample runs, and restores the live view without mixing data", async () => { + const user = userEvent.setup(); + const onUrlUpdate = vi.fn(); + renderWithProviders(, { + onUrlUpdate, + }); + expect(await screen.findByText("Enable tracing")).toBeVisible(); + await user.click(screen.getByRole("button", { name: "Preview sample" })); + expect(await screen.findByText("Where is order #1042?")).toBeVisible(); + expect(screen.getByText("You’re viewing demo data")).toBeVisible(); + expect(screen.queryByRole("button", { name: "Set up tracing" })).not.toBeInTheDocument(); + network.mockClear(); + await user.click(screen.getByRole("button", { name: "Refresh", exact: true })); + expect(screen.getByText("Where is order #1042?")).toBeVisible(); + fireEvent.change(screen.getByPlaceholderText("Search input or trace ID"), { target: { value: "headphones" } }); + expect(screen.getByText("Can I return my headphones?")).toBeVisible(); + expect(screen.queryByText("Where is order #1042?")).not.toBeInTheDocument(); + fireEvent.change(screen.getByRole("textbox", { name: "Search runs" }), { target: { value: "" } }); + await chooseSelectOption(user, screen.getByRole("combobox", { name: "Filter by agent" }), "support_agent"); + await chooseSelectOption(user, screen.getByRole("combobox", { name: "Filter by status" }), "Failed"); + expect(within(screen.getByRole("table", { name: "Agent runs" })).getAllByRole("row")).toHaveLength(4); + await user.click(screen.getByRole("tab", { name: "Investigations", exact: true })); + expect(await screen.findByRole("button", { name: /Support quality/ })).toBeVisible(); + expect(screen.queryByRole("button", { name: "New investigation" })).not.toBeInTheDocument(); + expect(network).not.toHaveBeenCalled(); + expect(onUrlUpdate).not.toHaveBeenCalled(); + await user.click(screen.getByRole("button", { name: "Exit demo" })); + expect(await screen.findByText("Enable tracing")).toBeVisible(); + expect(screen.queryByText("You’re viewing demo data")).not.toBeInTheDocument(); + expect(screen.queryByText("Can I return my headphones?")).not.toBeInTheDocument(); + }); + + it("connects findings and history to their original trace without live requests or URL changes", async () => { + const user = userEvent.setup(); + const onUrlUpdate = vi.fn(); + renderWithProviders(, { + searchParams: "?tab=investigations", + onUrlUpdate, + }); + await screen.findByRole("button", { name: "Preview sample" }); + await user.click(screen.getByRole("button", { name: "Preview sample" })); + await user.click(await screen.findByRole("button", { name: /Support quality/ })); + network.mockClear(); + await user.click(screen.getByRole("button", { name: /Repeated lookups leave customers without an answer/ })); + const finding = screen.getByRole("dialog"); + expect(within(finding).getByText(/The support agent retries/)).toBeVisible(); + const summaries = within(finding).getAllByText("support_agent", { exact: true }); + await user.click(summaries[0]); + await user.click(within(finding).getAllByRole("button", { name: /Open original step/ })[0]); + expect(await screen.findByRole("complementary", { name: "Span details" })).toHaveTextContent( + "I will check that for you.", + ); + await user.click(screen.getByRole("button", { name: "Copy for agent" })); + expect(await navigator.clipboard.readText()).toContain("I will check that for you."); + expect(await navigator.clipboard.readText()).not.toContain("Authorization"); + await user.click(screen.getByRole("tab", { name: "Attributes" })); + expect(await screen.findByText("gen_ai.agent.name")).toBeVisible(); + await user.click( + within(screen.getByRole("dialog", { name: "Original run" })).getByRole("button", { name: "Close", exact: true }), + ); + await user.click(within(screen.getByRole("dialog")).getByRole("button", { name: "Close", exact: true })); + await user.click(screen.getByRole("tab", { name: "History" })); + await user.click(screen.getAllByRole("button", { name: /runs reviewed/ })[1]); + expect(await screen.findByText(/1 linked run · high priority/)).toBeVisible(); + expect(network).not.toHaveBeenCalled(); + expect(onUrlUpdate).not.toHaveBeenCalled(); + await user.click(screen.getByRole("button", { name: "Exit demo" })); + expect(await screen.findByRole("heading", { name: "Find what needs attention" })).toBeVisible(); + }); + + it("has no demo entry for existing investigations, populated traces, or connecting another agent", async () => { + const user = userEvent.setup(); + const data = createLensDemoData(); + const saved = data.lenses[0]; + network.mockImplementation(async (input) => { + const path = new URL(String(input), "http://localhost").pathname; + if (path === "/lens") return Response.json({ lenses: [saved], workers: [], tracing_enabled: true }); + if (path.endsWith("/runs")) return Response.json(saved.jobs); + if (path === "/v1/traces") return Response.json({ data: data.runs.map((run) => run.trace.summary) }); + return Response.json({ data: [], traces: true, requests: false }); + }); + renderWithProviders(, { + searchParams: `?tab=investigations&lens=${saved.id}`, + }); + expect(await screen.findByRole("heading", { name: saved.settings.name })).toBeVisible(); + expect(screen.queryByRole("button", { name: "Preview sample" })).not.toBeInTheDocument(); + await user.click(within(screen.getByRole("tablist", { name: "Lens" })).getByRole("tab", { name: "Traces" })); + expect(await screen.findByText("Where is order #1042?")).toBeVisible(); + expect(screen.queryByRole("button", { name: "Preview sample" })).not.toBeInTheDocument(); + await user.click(screen.getByRole("button", { name: "Set up tracing" })); + expect(await screen.findByRole("heading", { name: "Connect another agent" })).toBeVisible(); + expect(screen.queryByRole("button", { name: "Preview sample" })).not.toBeInTheDocument(); + }); + + it("shows the header preview only for the active tab that still needs setup", async () => { + const user = userEvent.setup(); + const saved = createLensDemoData().lenses[0]; + network.mockImplementation(async (input) => { + const path = new URL(String(input), "http://localhost").pathname; + if (path === "/lens") return Response.json({ lenses: [saved], workers: [], tracing_enabled: false }); + if (path.endsWith("/runs")) return Response.json(saved.jobs); + if (path === "/v1/traces") return Response.json({ detail: "Tracing is not enabled" }, { status: 501 }); + return Response.json({ data: [], traces: false, requests: false }); + }); + renderWithProviders(); + expect(await screen.findByRole("button", { name: "Preview sample" })).toBeVisible(); + const tabs = within(screen.getByRole("tablist", { name: "Lens" })); + await user.click(tabs.getByRole("tab", { name: "Investigations" })); + expect(await screen.findByRole("button", { name: new RegExp(saved.settings.name) })).toBeVisible(); + expect(screen.queryByRole("button", { name: "Preview sample" })).not.toBeInTheDocument(); + await user.click(tabs.getByRole("tab", { name: "Traces" })); + await user.click(await screen.findByRole("button", { name: "Preview sample" })); + expect(await screen.findByRole("table", { name: "Agent runs" })).toBeVisible(); + expect(screen.queryByRole("button", { name: "Preview sample" })).not.toBeInTheDocument(); + }); +}); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensWorkspace.tsx b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensWorkspace.tsx new file mode 100644 index 00000000000..9c7d2ca3db8 --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensWorkspace.tsx @@ -0,0 +1,117 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { Aperture } from "lucide-react"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { parseAsString, parseAsStringLiteral, useQueryState } from "nuqs"; +import AgentTracesPage from "@/components/view_logs/TraceView/AgentTracesPage"; +import { DemoNotice } from "@/components/shared/DemoNotice"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { LensDemoContext, useLensDemo } from "@/components/lens/LensDemoContext"; +import { LensPreviewTarget } from "@/components/lens/LensPreviewButton"; +import { isProxyAdminRole, isProxyAdminTierRole } from "@/utils/roles"; +import { LensView } from "./LensView"; +import { createLensDemo } from "./lensDemoData"; + +type Tab = "traces" | "investigations"; +type WorkspaceProps = { accessToken: string; userRole: string; readOnly: boolean }; + +export function LensWorkspace(props: WorkspaceProps) { + const [demoTab, setDemoTab] = useState(null); + return demoTab ? ( + setDemoTab(null)} /> + ) : ( + + ); +} + +function DemoSession({ initialTab, onExit }: { initialTab: Tab; onExit: () => void }) { + const [demo] = useState(createLensDemo); + const [client] = useState( + () => new QueryClient({ defaultOptions: { queries: { retry: false, staleTime: Infinity } } }), + ); + useEffect( + () => () => { + client.clear(); + }, + [client], + ); + return ( + + + + + + ); +} + +function LensContent({ + accessToken, + userRole, + readOnly, + initialTab = "traces", + onDemo, + onExit, +}: WorkspaceProps & { initialTab?: Tab; onDemo?: (tab: Tab) => void; onExit?: () => void }) { + const demo = useLensDemo(); + const [tab, setTab] = useQueryState( + "tab", + parseAsStringLiteral(["traces", "investigations"]).withOptions({ history: "push" }), + ); + const [lensId] = useQueryState("lens", parseAsString); + const [demoTab, setDemoTab] = useState(initialTab); + const [previewTarget, setPreviewTarget] = useState(null); + const defaultTab = lensId ? "investigations" : "traces"; + const activeTab = demo ? demoTab : tab ?? defaultTab; + const openDemo = onDemo ? () => onDemo(activeTab) : undefined; + return ( + +
    +
    +

    +

    +
    +
    + {demo && } + (demo ? setDemoTab(value as Tab) : void setTab(value as Tab))} + className="min-h-0 flex-1 gap-4" + > + + + Traces + + + Investigations + + + + + + + {demo || isProxyAdminTierRole(userRole) ? ( + + ) : ( +

    + Investigations require proxy administrator access. You can still view your traces. +

    + )} +
    +
    +
    +
    + ); +} diff --git a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/WorkerSetup.tsx b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/WorkerSetup.tsx index 4554174f37b..931dd7c17d2 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/WorkerSetup.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/WorkerSetup.tsx @@ -21,7 +21,7 @@ import { AnalysisKey, AnalysisAccessFields, createAnalysisKey, type AnalysisAcce import type { LensList, WorkerCreated } from "./lensData"; export const LENS_WORKER_IMAGE = - "ghcr.io/berriai/litellm-lens-worker@sha256:67eba741c1b97c749975c5c38e2370a603e1105babc908d613c1b79d7b995393"; + "ghcr.io/berriai/litellm-lens-worker@sha256:44f0597c7583dcfef999ece9a8bc02cfeb9f0f5167a1221cee3bd10b1b79271b"; function initialProxyAddress(): string { const url = new URL(proxyBaseUrl || serverRootPath, window.location.origin); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensData.test.ts b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensData.test.ts index 84e0fe7e6e6..3be6164fce2 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensData.test.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensData.test.ts @@ -58,6 +58,28 @@ const job: Job = { }; describe("Analysis progress", () => { + it.each(["Collecting executions", "Reading executions"])( + "does not turn an unreported run count into zero runs during %s", + (stage) => { + expect(analysisProgress({ ...job, stage })).toEqual({ + step: -1, + title: "Preparing activity", + done: 0, + total: 0, + detail: "Loading the runs selected for this investigation.", + }); + }, + ); + + it("shows the selected count as soon as the worker reports it", () => { + expect(analysisProgress({ ...job, coverage: { ...coverage, selected: 7 } })).toMatchObject({ + step: 0, + done: 0, + total: 7, + detail: "0 of 7 selected runs reviewed", + }); + }); + it("measures review progress against the sample, not all eligible runs", () => { const expected = { step: 0, done: 7, total: 20, detail: "7 of 20 selected runs reviewed" }; expect( diff --git a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensData.ts b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensData.ts index c066577817b..1e30dc75a38 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensData.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensData.ts @@ -108,6 +108,15 @@ export function analysisProgress(job: Job) { : `${investigated} patterns checked against the original activity`, }; } + if (!selected) { + return { + step: -1, + title: "Preparing activity", + done: 0, + total: 0, + detail: "Loading the runs selected for this investigation.", + }; + } return { step: 0, title: "Reviewing activity", diff --git a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensDemoData.test.ts b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensDemoData.test.ts new file mode 100644 index 00000000000..eabae2928bd --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensDemoData.test.ts @@ -0,0 +1,55 @@ +import { describe, expect, it, vi } from "vitest"; +import { createLensDemo, createLensDemoData } from "./lensDemoData"; +import type { TracePage } from "@/components/view_logs/TraceView/traceTypes"; +import { evidenceTarget } from "./lensData"; + +describe("Lens demo data", () => { + it("links every finding to the quoted original step and assessed run", () => { + const data = createLensDemoData(); + for (const lens of data.lenses) { + for (const job of lens.jobs) { + for (const finding of job.findings ?? []) { + for (const evidence of finding.evidence) { + const target = evidenceTarget(evidence.execution_id); + const run = data.runs.find(({ trace }) => trace.summary.trace_id === target?.id); + const step = run?.details.find((span) => span.span_id === evidence.span_id); + expect(step?.output).toContain(evidence.quote); + expect(job.sample?.executions.map((execution) => execution.id)).toContain(evidence.execution_id); + expect( + job.assessments.find((assessment) => assessment.execution_id === evidence.execution_id)?.[ + finding.kind === "issue" ? "issue_checks" : "pattern_checks" + ], + ).toContain(finding.check_id); + } + } + } + } + }); + + it("keeps trace totals, timestamps, and agent names consistent", () => { + const data = createLensDemoData(); + for (const { trace } of data.runs) { + expect(trace.summary.span_count).toBe(trace.spans.length); + expect(trace.summary.agent_names).toContain(trace.agents[0].name); + expect(trace.summary.error_count).toBe(trace.spans.filter((span) => span.status === "error").length); + for (const span of trace.spans) { + expect(span.start_offset_ms + span.duration_ms).toBeLessThanOrEqual(trace.summary.duration_ms); + } + } + }); + + it("filters time windows locally and rejects writes or unknown reads without network access", async () => { + const network = vi.spyOn(globalThis, "fetch"); + const now = Date.now(); + const { client } = createLensDemo(now); + const all = await client.get("/v1/traces"); + const recent = await client.get("/v1/traces", { query: { start_ms: now - 3600_000, end_ms: now } }); + expect(recent.data.length).toBeGreaterThan(0); + expect(recent.data.length).toBeLessThan(all.data.length); + expect(recent.data.every((trace) => Date.parse(trace.start_time) >= now - 3600_000)).toBe(true); + await expect(client.post("/lens", { body: {} })).rejects.toMatchObject({ status: 403 }); + await expect(client.get("/lens/real-investigation")).rejects.toMatchObject({ status: 404 }); + expect(network).not.toHaveBeenCalled(); + network.mockRestore(); + }); +}); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensDemoData.ts b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensDemoData.ts new file mode 100644 index 00000000000..8b4e1645c9e --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensDemoData.ts @@ -0,0 +1,483 @@ +import { createApiClient } from "@/lib/http/client"; +import type { LensDemo } from "@/components/lens/LensDemoContext"; +import type { Trace, Span, SpanDetail } from "@/components/view_logs/TraceView/traceTypes"; +import type { Lens, Finding, Job, Settings } from "./lensData"; + +type Scenario = { + agent: string; + question: string; + tool: string; + result: string; + answer: string; + failed?: boolean; +}; + +const scenarios: Scenario[] = [ + { + agent: "support_agent", + question: "Where is order #1042?", + tool: "lookup_order", + result: "Order service timed out", + answer: "I will check that for you.", + failed: true, + }, + { + agent: "research_agent", + question: "Which database should we use for analytics?", + tool: "search_docs", + result: "Column stores are designed for analytical queries. No comparative benchmark is available.", + answer: "The column store is 40% faster than the row store for your workload.", + }, + { + agent: "support_agent", + question: "Can I return my headphones?", + tool: "lookup_return_policy", + result: "Returns are accepted within 30 days. This order was delivered 10 days ago.", + answer: "Yes. Your order is eligible for a return. I can help you start it.", + }, + { + agent: "release_agent", + question: "Is the checkout release ready to ship?", + tool: "read_test_results", + result: "Checkout: 120 passed, 0 failed. Rollback plan approved.", + answer: "All 120 checkout tests passed and the rollback plan is approved. The release is ready.", + }, + { + agent: "support_agent", + question: "My order #1088 has not arrived", + tool: "lookup_order", + result: "Order service timed out", + answer: "I will check that for you.", + failed: true, + }, + { + agent: "research_agent", + question: "How long do we retain audit logs?", + tool: "search_docs", + result: "Audit log retention is 90 days on the current plan.", + answer: "Audit logs are retained for 90 days, according to the plan documentation.", + }, + { + agent: "release_agent", + question: "Review the search release", + tool: "read_test_results", + result: "Search: 86 passed, 1 failed. Unicode query regression remains open.", + answer: "Hold the release. The Unicode query regression is still failing.", + }, + { + agent: "support_agent", + question: "Update the address on order #1091", + tool: "lookup_order", + result: "Order #1091 has not shipped. Address changes are allowed.", + answer: "Your order has not shipped. Please confirm the new delivery address.", + }, + { + agent: "research_agent", + question: "Can this plan store data in Europe?", + tool: "search_docs", + result: "Regional availability is not documented for this plan.", + answer: "I could not verify European data residency for this plan. Please confirm with support before choosing it.", + }, + { + agent: "support_agent", + question: "Check the delivery date for order #1107", + tool: "lookup_order", + result: "Order service timed out", + answer: "The order service is unavailable. I can connect you to support instead.", + failed: true, + }, + { + agent: "release_agent", + question: "Summarize the authentication release checks", + tool: "read_test_results", + result: "Authentication: 214 passed, 0 failed. Security review approved.", + answer: "All 214 tests passed and the security review is approved.", + }, + { + agent: "research_agent", + question: "Does the API support request retries?", + tool: "search_docs", + result: "Retry requests with exponential backoff after a 429 or 503 response.", + answer: "Yes. Use exponential backoff for 429 and 503 responses, as documented.", + }, +]; + +const executionId = (traceId: string) => btoa(JSON.stringify(["traces", "", traceId])); +const iso = (time: number) => new Date(time).toISOString(); + +function makeTrace(scene: Scenario, index: number, now: number) { + const traceId = (index + 1).toString(16).padStart(32, "0"); + const spanId = (step: number) => `${index + 1}${step}`.padStart(16, "0"); + const toolCount = scene.failed && index !== 9 ? 3 : 1; + const duration = toolCount === 3 ? 6340 : 3180 + index * 137; + const base: Span = { + agent: scene.agent, + duration_ms: duration, + error: null, + error_truncated: false, + framework: "", + input_preview: scene.question, + input_tokens: 0, + output_tokens: 0, + litellm_request_id: null, + model: null, + name: scene.agent, + parent_span_id: null, + span_id: spanId(0), + spend: null, + start_offset_ms: 0, + status: "ok", + type: "agent", + }; + const tools: Span[] = Array.from({ length: toolCount }, (_, i) => ({ + ...base, + span_id: spanId(i + 1), + parent_span_id: base.span_id, + name: scene.tool, + type: "tool", + start_offset_ms: 150 + i * 1600, + duration_ms: scene.failed ? 1500 : 340, + status: scene.failed ? "error" : "ok", + error: scene.failed ? scene.result : null, + })); + const model: Span = { + ...base, + span_id: spanId(5), + parent_span_id: base.span_id, + name: "Generate response", + type: "llm", + model: "demo-chat-model", + start_offset_ms: toolCount * 1600, + duration_ms: 1200, + input_tokens: 520 + index * 41, + output_tokens: 48 + index * 7, + spend: 0.003 + index * 0.0002, + }; + const trace: Trace = { + summary: { + agent_count: 1, + agent_invocations: 1, + agent_names: [scene.agent], + duration_ms: duration, + error_count: scene.failed ? toolCount : 0, + frameworks: [], + input_preview: scene.question, + input_tokens: model.input_tokens, + output_tokens: model.output_tokens, + llm_calls: 1, + models: [model.model!], + name: scene.agent, + service: "demo-agents", + span_count: toolCount + 2, + spend: model.spend, + start_time: iso(now - (index + 1) * 35 * 60_000), + status: scene.failed ? "error" : "ok", + tool_calls: toolCount, + trace_id: traceId, + }, + agents: [ + { + name: scene.agent, + parent_agent: null, + duration_ms: duration, + invocations: 1, + llm_calls: 1, + tool_calls: toolCount, + spend: model.spend, + }, + ], + spans: [base, ...tools, model], + }; + const details: SpanDetail[] = trace.spans.map((span) => ({ + span_id: span.span_id, + input: + span.type === "tool" + ? JSON.stringify({ query: scene.question }) + : JSON.stringify([{ role: "user", content: scene.question }]), + output: span.type === "tool" ? scene.result : JSON.stringify([{ role: "assistant", content: scene.answer }]), + attributes: { + "gen_ai.agent.name": scene.agent, + "service.name": "demo-agents", + demo: "true", + ...(span.model ? { "gen_ai.request.model": span.model } : {}), + }, + })); + return { trace, details }; +} + +export function createLensDemoData(now = Date.now()) { + const runs = scenarios.map((scene, index) => makeTrace(scene, index, now)); + const finding = ({ + id, + check, + title, + description, + suggestion, + indices, + kind = "issue", + }: { + id: string; + check: string; + title: string; + description: string; + suggestion: string; + indices: number[]; + kind?: Finding["kind"]; + }): Finding => ({ + id, + check_id: check, + title, + description, + suggestion, + kind, + priority: kind === "issue" ? "high" : "low", + status: "open", + reason: "", + revision: 1, + first_seen: iso(now - 86_400_000), + last_seen: iso(now - 300_000), + limitation: "", + occurrences: indices.map((i) => executionId(runs[i].trace.summary.trace_id)), + evidence: indices.map((i) => ({ + execution_id: executionId(runs[i].trace.summary.trace_id), + span_id: runs[i].trace.spans.at(-1)!.span_id, + quote: scenarios[i].answer, + role: "support", + })), + }); + const findingInputs: Parameters[0][] = [ + { + id: "failed-lookups", + check: "recover", + title: "Repeated lookups leave customers without an answer", + description: + "The support agent retries the same unavailable order service three times, then promises to check without answering or offering a handoff.", + suggestion: "After repeated failures, explain the problem and offer a handoff.", + indices: [0, 4], + }, + { + id: "safe-handoff", + check: "recover", + title: "A clear handoff helps when the order service is unavailable", + description: + "The agent explains the service outage and offers a support handoff instead of promising an answer it cannot provide.", + suggestion: "Keep this fallback for unavailable services.", + indices: [9], + kind: "pattern", + }, + { + id: "unsupported-claim", + check: "grounding", + title: "Performance claim has no supporting benchmark", + description: + "The answer claims a 40% performance improvement, but the retrieved documentation contains no comparative benchmark.", + suggestion: "Require a benchmark source for numeric performance claims, or remove the comparison.", + indices: [1], + }, + { + id: "uncertainty", + check: "grounding", + title: "Missing information is acknowledged", + description: + "When documentation does not establish regional availability, the agent says so and asks the user to verify it.", + suggestion: "Keep stating when a source does not answer the question.", + indices: [8], + kind: "pattern", + }, + { + id: "release-blocked", + check: "release", + title: "Failing checks correctly block the release", + description: "The release agent identifies an unresolved regression and recommends holding the release.", + suggestion: "Continue requiring passing checks before recommending a release.", + indices: [6], + kind: "pattern", + }, + ]; + const findings = findingInputs.map(finding); + const definitions = [ + { + id: "support", + name: "Support quality", + agent: "support_agent", + check: "recover", + context: + "Answer the customer's question using order information. If a tool fails, explain the problem and offer a handoff.", + instruction: "Look for repeated failed calls and conversations that end without an answer or a handoff.", + }, + { + id: "research", + name: "Research accuracy", + agent: "research_agent", + check: "grounding", + context: "Answer questions using verified documentation. Acknowledge missing information.", + instruction: "Find claims that are not supported by the retrieved sources.", + }, + { + id: "release", + name: "Release readiness", + agent: "release_agent", + check: "release", + context: "Review test results and recommend a release only when all required checks pass.", + instruction: "Check whether failed tests are acknowledged before a release recommendation.", + }, + ]; + const lenses: Lens[] = definitions.map((definition) => { + const settings: Settings = { + name: definition.name, + agent_name: definition.agent, + context: definition.context, + checks: [{ id: definition.check, instruction: definition.instruction, enabled: true }], + source: "traces", + service: "", + filters: [], + team_id: "", + execution_ids: [], + lookback_hours: 24, + sample_size: 0, + sample_percent: 100, + concurrency: 8, + monthly_budget: 100, + interval_minutes: 30, + enabled: false, + model: "demo-analysis-model", + }; + const executions = runs + .filter(({ trace }) => trace.summary.name === definition.agent) + .map(({ trace }) => ({ + id: executionId(trace.summary.trace_id), + trace_ref: "", + metadata: [], + root_seen: true, + service: trace.summary.service, + source: "traces" as const, + trace_id: trace.summary.trace_id, + team_id: "", + name: trace.summary.name, + start_time: trace.summary.start_time, + span_count: trace.summary.span_count, + })); + const relevant = findings.filter((f) => f.check_id === definition.check); + const jobs: Job[] = [0, 1].map((day) => { + const sample = day === 0 ? executions : executions.slice(1); + const selectedIds = new Set(sample.map((item) => item.id)); + const snapshot = relevant + .map((f) => ({ + ...f, + occurrences: f.occurrences.filter((id) => selectedIds.has(id)), + evidence: f.evidence.filter((e) => selectedIds.has(e.execution_id)), + })) + .filter((f) => f.occurrences.length > 0); + return { + id: `${definition.id}-scan-${day}`, + findings: snapshot, + settings, + revision: 1, + assessments: sample.map((e) => ({ + execution_id: e.id, + cannot_assess: false, + issue_checks: snapshot.some((f) => f.kind === "issue" && f.occurrences.includes(e.id)) + ? [definition.check] + : [], + pattern_checks: snapshot.some((f) => f.kind === "pattern" && f.occurrences.includes(e.id)) + ? [definition.check] + : [], + })), + attempts: 1, + error: "", + cost: sample.length * 0.012, + coverage: { + eligible: sample.length, + selected: sample.length, + screened: sample.length, + investigated: sample.length, + inconclusive: 0, + grouping_batches: 1, + grouped_batches: 1, + candidates: snapshot.length, + partial: 0, + unassessable: 0, + }, + status: "completed", + stage: "Complete", + created_at: iso(now - 330_000 - day * 60_000), + finished_at: iso(now - 300_000 - day * 60_000), + start: iso(now - 86_400_000), + end: iso(now - 300_000 - day * 60_000), + sample: { eligible: sample.length, selected: sample.length, executions: sample }, + }; + }); + return { + id: definition.id, + version: 1, + revision: 1, + spent: jobs.reduce((sum, job) => sum + job.cost, 0), + scope: { all_teams: true, api_key_hash: "", team_id: "" }, + settings, + created_at: iso(now - 2 * 86_400_000), + next_run_at: iso(now), + budget_month: iso(now).slice(0, 7), + findings: relevant, + jobs, + }; + }); + return { runs, lenses }; +} + +function readDemoTrace(data: ReturnType, url: URL): unknown { + const parts = url.pathname.split("/").filter(Boolean); + if (parts[0] !== "v1" || parts[1] !== "traces") return undefined; + if (!parts[2]) { + const start = Number(url.searchParams.get("start_ms") ?? 0); + const end = Number(url.searchParams.get("end_ms") ?? Infinity); + return { + data: data.runs + .map((run) => run.trace.summary) + .filter((trace) => Date.parse(trace.start_time) >= start && Date.parse(trace.start_time) <= end), + next_cursor: null, + }; + } + const run = data.runs.find(({ trace }) => trace.summary.trace_id === parts[2]); + if (!parts[3]) return run?.trace; + if (parts[3] === "spans" && !parts[5]) return run?.details.find((span) => span.span_id === parts[4]); + if (parts[3] === "spans" && parts[5] === "error") { + const span = run?.trace.spans.find((item) => item.span_id === parts[4]); + return span + ? { span_id: span.span_id, message: span.error ?? "", total_chars: span.error?.length ?? 0, next_cursor: null } + : undefined; + } + return undefined; +} + +export function createLensDemo(now = Date.now()): LensDemo { + const data = createLensDemoData(now); + const read = (url: URL): unknown => { + const parts = url.pathname.split("/").filter(Boolean); + if (url.pathname === "/lens") return { lenses: data.lenses, workers: [], tracing_enabled: true }; + if (url.pathname === "/lens/activity/available") return { traces: true, requests: false }; + if (url.pathname === "/models" || url.pathname === "/model_group/info") return { data: [] }; + if (parts[0] === "lens" && parts[2] === "runs") { + const jobs = data.lenses.find((lens) => lens.id === parts[1])?.jobs; + return parts[3] + ? jobs?.find((job) => job.id === parts[3]) + : jobs?.slice(Number(url.searchParams.get("offset") ?? 0)); + } + return readDemoTrace(data, url); + }; + return { + client: createApiClient({ + getBaseUrl: () => "https://lens-demo.invalid", + fetchImpl: async (input, init) => { + if (init?.method !== "GET") return Response.json({ detail: "Demo data is read-only" }, { status: 403 }); + const result = read(new URL(String(input))); + return result === undefined + ? Response.json({ detail: "This item is not in the demo" }, { status: 404 }) + : Response.json(result); + }, + }), + copyTrace: (traceId, spanId) => { + const run = data.runs.find(({ trace }) => trace.summary.trace_id === traceId); + return JSON.stringify(spanId ? run?.details.find((span) => span.span_id === spanId) : run, null, 2); + }, + }; +} diff --git a/ui/litellm-dashboard/src/app/(dashboard)/lens/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/lens/page.tsx index b29e498600f..da13f48165b 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/lens/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/lens/page.tsx @@ -1,60 +1,10 @@ "use client"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; -import { Aperture } from "lucide-react"; -import { parseAsString, parseAsStringLiteral, useQueryState } from "nuqs"; -import AgentTracesPage from "@/components/view_logs/TraceView/AgentTracesPage"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { isProxyAdminRole, isProxyAdminTierRole } from "@/utils/roles"; -import { LensView } from "./_components/LensView"; +import { LensWorkspace } from "./_components/LensWorkspace"; export default function LensPage() { const { accessToken, userRole, isViewOnly } = useAuthorized(); - const [tab, setTab] = useQueryState( - "tab", - parseAsStringLiteral(["traces", "investigations"]).withOptions({ history: "push" }), - ); - const [lensId] = useQueryState("lens", parseAsString); - const defaultTab = lensId ? "investigations" : "traces"; - const activeTab = tab ?? defaultTab; if (!accessToken) return null; - return ( -
    -

    -

    - void setTab(value as "traces" | "investigations")} - className="min-h-0 flex-1 gap-4" - > - - - Traces - - - Investigations - - - - - - - {isProxyAdminTierRole(userRole ?? "") ? ( - - ) : ( -

    - Investigations require proxy administrator access. You can still view your traces. -

    - )} -
    -
    -
    - ); + return ; } diff --git a/ui/litellm-dashboard/src/app/(dashboard)/roi-calculator/_components/ROICalculatorView.integration.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/roi-calculator/_components/ROICalculatorView.integration.test.tsx index 3e71e9fb860..1501938e9c5 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/roi-calculator/_components/ROICalculatorView.integration.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/roi-calculator/_components/ROICalculatorView.integration.test.tsx @@ -249,6 +249,32 @@ describe("ROICalculatorView", () => { expect(screen.getAllByText("Connect GitHub to get started")).toHaveLength(1); }); + it("clearly identifies the sample report and returns to setup when exiting", async () => { + const emptySettings = { ...settings, has_github_token: false, ready: false, repos: [], estimator_model: "" }; + vi.mocked(apiClient.get).mockImplementation((path: string, options) => { + if (path === "/roi-calculator/settings") return Promise.resolve(emptySettings); + if (path === "/roi-calculator/report") { + return Promise.resolve({ report: options?.query?.mode === "demo" ? { ...summary, mode: "demo" } : null }); + } + return Promise.resolve(idleStatus); + }); + + render(); + fireEvent.click(await screen.findByRole("button", { name: "Preview sample report" })); + + expect(await screen.findByText("You’re viewing demo data")).toBeVisible(); + expect(screen.getByText("Spend per estimated engineering hour")).toBeVisible(); + expect(screen.queryByRole("button", { name: "Run analysis" })).not.toBeInTheDocument(); + expect(screen.queryByRole("tab", { name: "Settings" })).not.toBeInTheDocument(); + + fireEvent.click(screen.getByRole("button", { name: "Exit demo" })); + + expect(screen.getByRole("heading", { name: "Connect GitHub to get started" })).toBeVisible(); + expect(screen.queryByText("You’re viewing demo data")).not.toBeInTheDocument(); + expect(apiClient.post).not.toHaveBeenCalled(); + expect(apiClient.put).not.toHaveBeenCalled(); + }); + it("returns to Overview and shows the last sync time when completion is polled from Settings", async () => { const runningStatus = { ...idleStatus, diff --git a/ui/litellm-dashboard/src/app/(dashboard)/roi-calculator/_components/ROICalculatorView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/roi-calculator/_components/ROICalculatorView.tsx index 1f5b136dbb7..40c1104354d 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/roi-calculator/_components/ROICalculatorView.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/roi-calculator/_components/ROICalculatorView.tsx @@ -5,6 +5,7 @@ import { Calculator, RefreshCw } from "lucide-react"; import { apiClient } from "@/components/networking"; import { PageHeader } from "@/components/shared/PageHeader"; +import { DemoNotice } from "@/components/shared/DemoNotice"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; @@ -222,6 +223,13 @@ export default function ROICalculatorView({ } title="ROI Calculator" + titleAction={ + !liveSummary && showLiveStatus ? ( + + ) : undefined + } subtitle={ <> {summary @@ -238,22 +246,7 @@ export default function ROICalculatorView({ } /> - {!liveSummary && showLiveStatus && ( - - )} - {sampleSummary && ( - - Sample report - - Example data only. No GitHub or model requests were made. - - - - )} + {sampleSummary && setSampleSummary(null)} />} {liveSummary && showLiveStatus && (

    {status.next_update ? `Next update ${formatSyncedAt(status.next_update)}` : scheduleLabel} diff --git a/ui/litellm-dashboard/src/components/lens/LensDemoContext.tsx b/ui/litellm-dashboard/src/components/lens/LensDemoContext.tsx new file mode 100644 index 00000000000..dd34511bc09 --- /dev/null +++ b/ui/litellm-dashboard/src/components/lens/LensDemoContext.tsx @@ -0,0 +1,12 @@ +"use client"; + +import { createContext, useContext } from "react"; +import type { ApiClient } from "@/lib/http/client"; + +export interface LensDemo { + client: ApiClient; + copyTrace: (traceId: string, spanId?: string) => string; +} + +export const LensDemoContext = createContext(null); +export const useLensDemo = () => useContext(LensDemoContext); diff --git a/ui/litellm-dashboard/src/components/lens/LensPreviewButton.tsx b/ui/litellm-dashboard/src/components/lens/LensPreviewButton.tsx new file mode 100644 index 00000000000..845a6b8d279 --- /dev/null +++ b/ui/litellm-dashboard/src/components/lens/LensPreviewButton.tsx @@ -0,0 +1,18 @@ +"use client"; + +import { createContext, useContext } from "react"; +import { createPortal } from "react-dom"; +import { Button } from "@/components/ui/button"; + +export const LensPreviewTarget = createContext(undefined); + +export function LensPreviewButton({ onClick }: { onClick: () => void }) { + const target = useContext(LensPreviewTarget); + const button = ( + + ); + if (target === null) return null; + return target ? createPortal(button, target) : button; +} diff --git a/ui/litellm-dashboard/src/components/shared/DemoNotice.tsx b/ui/litellm-dashboard/src/components/shared/DemoNotice.tsx new file mode 100644 index 00000000000..e435d725db5 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/DemoNotice.tsx @@ -0,0 +1,16 @@ +import { Info } from "lucide-react"; +import { Button } from "@/components/ui/button"; + +export function DemoNotice({ onExit }: { onExit?: () => void }) { + return ( +

    +

    +

    + +
    + ); +} diff --git a/ui/litellm-dashboard/src/components/shared/PageHeader.tsx b/ui/litellm-dashboard/src/components/shared/PageHeader.tsx index 81092821efc..6fc5b0f3238 100644 --- a/ui/litellm-dashboard/src/components/shared/PageHeader.tsx +++ b/ui/litellm-dashboard/src/components/shared/PageHeader.tsx @@ -14,11 +14,12 @@ interface PageHeaderProps { subtitle: React.ReactNode; icon: React.ReactNode; primaryAction?: React.ReactNode; + titleAction?: React.ReactNode; tabs?: React.ReactNode | ((slots: EmbeddedTabsSlots) => React.ReactNode); utilities?: React.ReactNode; } -export function PageHeader({ title, subtitle, icon, primaryAction, tabs, utilities }: PageHeaderProps) { +export function PageHeader({ title, subtitle, icon, primaryAction, titleAction, tabs, utilities }: PageHeaderProps) { const leadingControls = primaryAction == null ? null : (
    @@ -31,14 +32,17 @@ export function PageHeader({ title, subtitle, icon, primaryAction, tabs, utiliti return (
    -
    - -

    {title}

    +
    +
    + +

    {title}

    +
    + {titleAction}

    {subtitle}

    diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesPage.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesPage.tsx index d0c5964a4c3..d46fc372e78 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesPage.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesPage.tsx @@ -1,4 +1,5 @@ "use client"; +import { useLensDemo } from "@/components/lens/LensDemoContext"; import moment from "moment"; import { useMemo, useState } from "react"; @@ -13,12 +14,15 @@ export default function AgentTracesPage({ isActive = true, readOnly = false, canMintTracingKey = false, + onDemo, }: { accessToken: string; isActive?: boolean; readOnly?: boolean; canMintTracingKey?: boolean; + onDemo?: () => void; }) { + const demo = useLensDemo(); const [rangeHours, setRangeHours] = useState(DEFAULT_RANGE_HOURS); const [live, setLive] = useState(true); const [anchor, setAnchor] = useState(() => moment()); @@ -36,16 +40,17 @@ export default function AgentTracesPage({ }; return ( -
    +
    diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.integration.test.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.integration.test.tsx new file mode 100644 index 00000000000..816bf5f9267 --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.integration.test.tsx @@ -0,0 +1,509 @@ +import { act, fireEvent, screen, waitFor, within } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +import { ApiError } from "@/lib/http/client"; + +import { chooseSelectOption, renderWithProviders, testQueryClient } from "../../../../tests/test-utils"; +import traceList from "./__fixtures__/trace_list.json"; +import AgentTracesPage from "./AgentTracesPage"; +import { AgentTracesSection, type TimeControls } from "./AgentTracesSection"; +import type { TracePage, TraceSummary } from "./traceTypes"; + +vi.mock("../../networking", () => ({ + apiClient: { get: vi.fn(), post: vi.fn() }, + agentTraceListCall: vi.fn(), + sendOtlpTraceCall: vi.fn(), + agentTraceCall: vi.fn(), + agentTraceSpanCall: vi.fn(), + getProxyBaseUrl: () => "http://localhost:4000", +})); + +vi.mock("./TraceDrawer", () => ({ + RunView: ({ traceId, onBack }: { traceId: string; onBack: () => void }) => ( +
    + run {traceId} + +
    + ), +})); + +import { agentTraceListCall, apiClient } from "../../networking"; + +const runs = (traceList as TracePage).data as TraceSummary[]; + +const renderSection = () => + renderWithProviders( + , + ); + +// A UTC-pinned day around the fixture runs (2026-09-30 ~06:43 UTC), so they land in the same bucket in any timezone. +const renderWindowed = (timeControls?: TimeControls) => + renderWithProviders( + , + ); + +const bucketRunCounts = () => + screen.getAllByTestId("timeline-bucket").map((bucket) => Number(bucket.getAttribute("data-runs"))); + +describe("AgentTracesSection", () => { + afterEach(() => { + vi.useRealTimers(); + vi.restoreAllMocks(); + }); + + beforeEach(() => { + vi.spyOn(HTMLElement.prototype, "getBoundingClientRect").mockReturnValue({ + left: 0, + width: 600, + top: 0, + height: 56, + right: 600, + bottom: 56, + x: 0, + y: 0, + toJSON: () => ({}), + } as DOMRect); + testQueryClient.clear(); + vi.mocked(agentTraceListCall).mockReset(); + vi.mocked(apiClient.get).mockResolvedValue({ data: [] }); + }); + + it.each([ + [401, "Your session is no longer valid. Sign out and sign in again."], + [403, "Your account does not have access to these traces."], + ])("stops live polling after HTTP %s and explains how to recover", async (status, message) => { + vi.useFakeTimers(); + vi.mocked(agentTraceListCall).mockRejectedValue(new ApiError("Private token details", Number(status), {})); + renderWithProviders( + , + ); + await act(async () => { + await vi.advanceTimersByTimeAsync(60_000); + }); + expect(screen.getByText(`Could not load runs: ${message}`)).toBeVisible(); + expect(screen.queryByText(/Private token details/)).not.toBeInTheDocument(); + expect(agentTraceListCall).toHaveBeenCalledOnce(); + expect(screen.getByTestId("runs-footer")).toHaveTextContent("Update failed"); + }); + + it("renders the setup snippet when the proxy answers 501", async () => { + vi.mocked(agentTraceListCall).mockRejectedValue( + new ApiError("Agent tracing is not enabled", 501, { detail: "Agent tracing is not enabled" }), + ); + renderSection(); + + const card = await screen.findByTestId("tracing-setup-card"); + expect(card).toHaveTextContent("Tracing is not enabled"); + expect(screen.getByRole("button", { name: "Preview sample" })).toBeVisible(); + expect(card).toHaveTextContent("type: clickhouse"); + expect(card).toHaveTextContent("url: os.environ/CLICKHOUSE_URL"); + expect(screen.getByRole("button", { name: "Check setup" })).toBeEnabled(); + expect(card).not.toHaveTextContent(/langsmith/i); + expect(card).toHaveTextContent("ClickHouse and proxy setup"); + expect(card).toHaveTextContent("Ask your proxy administrator"); + }); + + it("shows the waiting guide when tracing is on but no runs have arrived", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue({ ...(traceList as TracePage), data: [] }); + renderSection(); + + const card = await screen.findByTestId("tracing-setup-card"); + expect(card).toHaveTextContent("Connect your agent"); + expect(card).toHaveTextContent("Waiting for your first trace"); + expect(screen.getByRole("button", { name: "Preview sample" })).toBeVisible(); + expect(card).not.toHaveTextContent("store: clickhouse"); + }); + + it("keeps the trace list available when traces exist outside the current time window", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue({ ...(traceList as TracePage), data: [] }); + vi.mocked(apiClient.get).mockResolvedValue(traceList); + renderSection(); + expect(await screen.findByText("No runs match these filters.")).toBeVisible(); + expect(screen.queryByTestId("tracing-setup-card")).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: "Preview sample" })).not.toBeInTheDocument(); + expect(apiClient.get).toHaveBeenCalledWith("/v1/traces", { accessToken: "sk-test", query: { start_ms: 0 } }); + }); + + it("checks proxy readiness, waits for an agent, and confirms receipt using actual query results", async () => { + vi.mocked(agentTraceListCall).mockRejectedValue(new ApiError("Tracing is not enabled", 501, {})); + renderSection(); + const checkSetup = await screen.findByRole("button", { name: "Check setup" }); + vi.mocked(agentTraceListCall).mockResolvedValue({ ...(traceList as TracePage), data: [] }); + fireEvent.click(checkSetup); + expect(await screen.findByRole("heading", { name: "Connect your agent" })).toBeVisible(); + expect(screen.getByText("Waiting for your first trace")).toBeVisible(); + vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); + fireEvent.click(screen.getByRole("button", { name: "Check for traces" })); + expect(await screen.findAllByTestId("agent-trace-row")).toHaveLength(runs.length); + expect(screen.getByText("Traces received. Select a run to inspect it.")).toBeVisible(); + expect(screen.queryByRole("button", { name: "Preview sample" })).not.toBeInTheDocument(); + expect(screen.queryByTestId("tracing-setup-card")).not.toBeInTheDocument(); + }); + + it("keeps setup visible while checking and explains when tracing is still disabled", async () => { + const failure = new ApiError("Tracing is not enabled", 501, {}); + vi.mocked(agentTraceListCall).mockRejectedValue(failure); + renderSection(); + const checkSetup = await screen.findByRole("button", { name: "Check setup" }); + let rejectCheck: (error: Error) => void = () => {}; + vi.mocked(agentTraceListCall).mockImplementationOnce( + () => + new Promise((_, reject) => { + rejectCheck = reject; + }), + ); + fireEvent.click(checkSetup); + expect(await screen.findByRole("button", { name: "Checking…" })).toBeDisabled(); + expect(screen.getByRole("heading", { name: "Enable tracing" })).toBeVisible(); + await act(async () => rejectCheck(failure)); + expect(await screen.findByText(/Tracing is still unavailable/)).toBeVisible(); + expect(screen.getByRole("button", { name: "Check setup" })).toBeEnabled(); + }); + + it("keeps received traces and the open drawer visible during subsequent fetches", async () => { + vi.mocked(agentTraceListCall).mockRejectedValue(new ApiError("Tracing is not enabled", 501, {})); + renderSection(); + const checkSetup = await screen.findByRole("button", { name: "Check setup" }); + vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); + fireEvent.click(checkSetup); + const rows = await screen.findAllByTestId("agent-trace-row"); + fireEvent.click(rows[0]); + const drawer = screen.getByRole("complementary", { name: "Trace details" }); + + let finishRefresh: (page: TracePage) => void = () => {}; + vi.mocked(agentTraceListCall).mockImplementationOnce( + () => + new Promise((resolve) => { + finishRefresh = resolve; + }), + ); + await act(async () => { + void testQueryClient.invalidateQueries({ queryKey: ["agentTraces"] }); + }); + expect(await screen.findByText("Updating…")).toBeVisible(); + expect(screen.getAllByTestId("agent-trace-row")).toHaveLength(runs.length); + expect(screen.getByRole("complementary", { name: "Trace details" })).toBe(drawer); + expect(screen.queryByTestId("tracing-setup-card")).not.toBeInTheDocument(); + await act(async () => finishRefresh(traceList as TracePage)); + }); + + it("separates a failed history check from the empty list and retries that check", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue({ ...(traceList as TracePage), data: [] }); + vi.mocked(apiClient.get).mockRejectedValue(new ApiError("History unavailable", 503, {})); + renderSection(); + expect(await screen.findByRole("alert")).toHaveTextContent("Could not check earlier traces. History unavailable"); + expect(screen.getByText("No runs match these filters.")).toBeVisible(); + expect(screen.queryByText(/Could not load runs/)).not.toBeInTheDocument(); + + vi.mocked(apiClient.get).mockResolvedValue(traceList); + fireEvent.click(screen.getByRole("button", { name: "Retry trace check" })); + await waitFor(() => expect(screen.queryByRole("alert")).not.toBeInTheDocument()); + expect(screen.getByText("No runs match these filters.")).toBeVisible(); + expect(agentTraceListCall).toHaveBeenCalledTimes(1); + }); + + it("treats a proxy without the trace routes (404) like tracing being off", async () => { + vi.mocked(agentTraceListCall).mockRejectedValue(new ApiError("Not Found", 404, { detail: "Not Found" })); + renderSection(); + + const card = await screen.findByTestId("tracing-setup-card"); + expect(card).toHaveTextContent("Tracing is not enabled"); + expect(card).toHaveTextContent("url: os.environ/CLICKHOUSE_URL"); + }); + + it("lists every run with its input, counts and failed column", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); + renderSection(); + + const rows = await screen.findAllByTestId("agent-trace-row"); + expect(rows).toHaveLength(runs.length); + const lead = rows.find((row) => row.textContent?.includes("Should we store OTEL agent spans")); + expect(lead).toBeDefined(); + const failed = rows.find((row) => row.textContent?.includes("acme-404")) as HTMLElement; + expect(within(failed).getByLabelText("2 errors")).toBeInTheDocument(); + expect(screen.getByText(`${runs.length} runs`)).toBeInTheDocument(); + expect(screen.getByRole("columnheader", { name: "Cost" })).toBeInTheDocument(); + expect(within(failed).getByText("—")).toBeInTheDocument(); + }); + + it("shows the spend returned for a run", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue({ + ...(traceList as TracePage), + data: [{ ...runs[0], spend: 0.025 }], + }); + renderSection(); + + const row = await screen.findByTestId("agent-trace-row"); + expect(within(row).getByText("$0.03")).toBeInTheDocument(); + }); + + it("filters by input text and by trace id", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); + renderSection(); + await screen.findAllByTestId("agent-trace-row"); + + const search = screen.getByLabelText("Search runs"); + fireEvent.change(search, { target: { value: "acme-404" } }); + expect(screen.getAllByTestId("agent-trace-row")).toHaveLength(1); + + const lead = runs.find((r) => r.name === "research_lead") as TraceSummary; + fireEvent.change(search, { target: { value: lead.trace_id.slice(0, 10) } }); + const rows = screen.getAllByTestId("agent-trace-row"); + expect(rows).toHaveLength(1); + expect(rows[0]).toHaveTextContent("Should we store OTEL agent spans"); + }); + + it("uses recorded agent names for the column and filter even when services are shared", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue({ + ...(traceList as TracePage), + data: [ + ...runs.slice(1).map((run) => ({ ...run, service: "shared-app", agent_names: ["research-agent"] })), + { ...runs[0], service: "shared-app", agent_names: ["billing-agent", "review-agent"] }, + ], + }); + const user = userEvent.setup(); + renderSection(); + await screen.findAllByTestId("agent-trace-row"); + + expect(screen.getByRole("columnheader", { name: "Agent" })).toBeInTheDocument(); + const agentFilter = screen.getByRole("combobox", { name: "Filter by agent" }); + expect(agentFilter).toHaveTextContent("All agents"); + + await chooseSelectOption(user, agentFilter, "billing-agent"); + const rows = screen.getAllByTestId("agent-trace-row"); + expect(rows).toHaveLength(1); + expect(rows[0]).toHaveTextContent("billing-agent"); + expect(rows[0]).not.toHaveTextContent("shared-app"); + + await chooseSelectOption(user, agentFilter, "review-agent"); + expect(screen.getAllByTestId("agent-trace-row")).toHaveLength(1); + + await chooseSelectOption(user, agentFilter, "All agents"); + expect(screen.getAllByTestId("agent-trace-row")).toHaveLength(runs.length); + }); + + it("shows each run's agent name with the logo of the SDK that produced it", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue({ + ...(traceList as TracePage), + data: [ + { ...runs[0], agent_names: ["research-bot"], frameworks: ["claude-agent-sdk", "claude-code"] }, + { ...runs[1], agent_names: [], frameworks: ["claude-code"] }, + { ...runs[2], frameworks: [] }, + ], + }); + renderSection(); + const [sdkRun, cliRun, plainRun] = await screen.findAllByTestId("agent-trace-row"); + const agentCell = (row: HTMLElement) => within(row).getAllByRole("cell")[1]; + + expect(agentCell(sdkRun)).toHaveTextContent(/^research-bot$/); + expect(agentCell(sdkRun)).toHaveAttribute("title", "research-bot · Claude Agent SDK"); + expect(within(sdkRun).getByRole("img", { name: "Claude Agent SDK logo", hidden: true })).toHaveAttribute( + "src", + expect.stringContaining("anthropic.svg"), + ); + expect(agentCell(cliRun)).toHaveTextContent(/^Claude Code$/); + expect(within(plainRun).queryByRole("img", { hidden: true })).not.toBeInTheDocument(); + expect(agentCell(plainRun)).toHaveTextContent((runs[2].agent_names ?? [runs[2].service]).join(", ")); + }); + + it("opens a run in a side drawer over the list and swaps runs without closing it", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); + renderSection(); + const rows = await screen.findAllByTestId("agent-trace-row"); + + fireEvent.click(rows[0]); + const drawer = screen.getByRole("complementary", { name: "Trace details" }); + expect(within(drawer).getByTestId("run-view")).toHaveTextContent(`run ${runs[0].trace_id}`); + expect(screen.getByTestId("runs-table")).toBeInTheDocument(); + expect(rows[0]).toHaveAttribute("aria-selected", "true"); + + fireEvent.click(rows[1]); + expect(screen.getByRole("complementary", { name: "Trace details" })).toBe(drawer); + expect(within(drawer).getByTestId("run-view")).toHaveTextContent(`run ${runs[1].trace_id}`); + expect(rows[1]).toHaveAttribute("aria-selected", "true"); + expect(rows[0]).toHaveAttribute("aria-selected", "false"); + }); + + it("closes the drawer when the open row is clicked again or Escape is pressed", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); + renderSection(); + const rows = await screen.findAllByTestId("agent-trace-row"); + + fireEvent.click(rows[0]); + fireEvent.click(rows[0]); + expect(rows[0]).toHaveAttribute("aria-selected", "false"); + + fireEvent.click(rows[1]); + fireEvent.keyDown(window, { key: "Escape" }); + expect(rows[1]).toHaveAttribute("aria-selected", "false"); + }); + + it("moves to the next and previous run with j / k and the header arrows", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); + renderSection(); + const rows = await screen.findAllByTestId("agent-trace-row"); + + fireEvent.click(rows[0]); + fireEvent.keyDown(window, { key: "j" }); + expect(screen.getByTestId("run-view")).toHaveTextContent(`run ${runs[1].trace_id}`); + fireEvent.keyDown(window, { key: "k" }); + expect(screen.getByTestId("run-view")).toHaveTextContent(`run ${runs[0].trace_id}`); + expect(screen.getByRole("button", { name: "Previous trace (K)" })).toBeDisabled(); + fireEvent.click(screen.getByRole("button", { name: "Next trace (J)" })); + expect(screen.getByTestId("run-view")).toHaveTextContent(`run ${runs[1].trace_id}`); + }); + + it("plots every loaded run on the timeline", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); + renderWindowed(); + await screen.findAllByTestId("agent-trace-row"); + + expect(screen.getByTestId("traces-timeline")).toBeInTheDocument(); + const counts = bucketRunCounts(); + expect(counts).toHaveLength(60); + expect(counts.reduce((a, b) => a + b, 0)).toBe(runs.length); + }); + + it("zooms by dragging, resizes and pans the bracket, and clears with Esc", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); + renderWindowed(); + await screen.findAllByTestId("agent-trace-row"); + const area = screen.getByTestId("timeline-area"); + const x = (bucket: number) => bucket * 10 + 5; + const drag = (target: HTMLElement, from: number, to: number) => { + fireEvent.pointerDown(target, { clientX: x(from), pointerId: 1 }); + fireEvent.pointerMove(area, { clientX: x(to), pointerId: 1 }); + fireEvent.pointerUp(area, { clientX: x(to), pointerId: 1 }); + }; + const rowCount = () => screen.queryAllByTestId("agent-trace-row").length; + const withRuns = bucketRunCounts().flatMap((count, i) => (count > 0 ? [i] : [])); + const first = withRuns[0]; + // The pan below moves a [0, first] bracket to the far right; it must end up clear of every run. + expect(first).toBeGreaterThan(1); + expect(first).toBeLessThan(30); + + drag(area, 0, 1); + expect(screen.getByTestId("timeline-selection")).toBeInTheDocument(); + expect(rowCount()).toBe(0); + + drag(screen.getByTestId("timeline-handle-hi"), 1, first); + expect(rowCount()).toBeGreaterThan(0); + + drag(screen.getByTestId("timeline-selection"), 1, 1 - first); + expect(rowCount()).toBeGreaterThan(0); + drag(screen.getByTestId("timeline-selection"), 0, 59); + expect(rowCount()).toBe(0); + + fireEvent.keyDown(screen.getByTestId("traces-timeline"), { key: "Escape" }); + expect(screen.queryByTestId("timeline-selection")).not.toBeInTheDocument(); + expect(rowCount()).toBe(runs.length); + }); + + it("clears timeline zoom when refreshed", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); + renderWindowed({ rangeHours: 24, onRangeHoursChange: () => {}, onLiveChange: () => {} }); + await screen.findAllByTestId("agent-trace-row"); + const area = screen.getByTestId("timeline-area"); + const x = (bucket: number) => bucket * 10 + 5; + + fireEvent.pointerDown(area, { clientX: x(0), pointerId: 1 }); + fireEvent.pointerMove(area, { clientX: x(1), pointerId: 1 }); + fireEvent.pointerUp(area, { clientX: x(1), pointerId: 1 }); + expect(screen.getByTestId("timeline-selection")).toBeInTheDocument(); + expect(screen.queryAllByTestId("agent-trace-row")).toHaveLength(0); + + fireEvent.click(screen.getByRole("button", { name: "Refresh" })); + + expect(screen.queryByTestId("timeline-selection")).not.toBeInTheDocument(); + expect(screen.getAllByTestId("agent-trace-row")).toHaveLength(runs.length); + }); +}); + +describe("AgentTracesPage", () => { + beforeEach(() => { + testQueryClient.clear(); + vi.mocked(agentTraceListCall).mockReset(); + vi.mocked(apiClient.get).mockResolvedValue({ data: [] }); + }); + + it("shows the actual range, switches presets from the popover, and toggles Live", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); + renderWithProviders(); + await screen.findByTestId("runs-table"); + + const trigger = screen.getByRole("button", { name: "Time range" }); + expect(trigger).toHaveTextContent(/ to /); + expect(screen.getByTestId("traces-timeline")).toHaveTextContent("Total 1d"); + + fireEvent.click(trigger); + fireEvent.click(await screen.findByRole("menuitemradio", { name: "Last 7 days" })); + expect(await screen.findByText("Total 7d")).toBeInTheDocument(); + const last = vi.mocked(agentTraceListCall).mock.calls.at(-1)?.[0]; + expect((last?.endMs ?? 0) - (last?.startMs ?? 0)).toBeGreaterThanOrEqual(7 * 24 * 3600 * 1000 - 60_000); + + const live = screen.getByRole("button", { name: "Live" }); + expect(live).toHaveAttribute("aria-pressed", "true"); + fireEvent.click(live); + expect(live).toHaveAttribute("aria-pressed", "false"); + expect(screen.getByRole("button", { name: "Refresh" })).toBeEnabled(); + }); + + it("refreshes the trace list", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); + renderWithProviders(); + await screen.findByTestId("runs-table"); + + const callsBeforeRefresh = vi.mocked(agentTraceListCall).mock.calls.length; + fireEvent.click(screen.getByRole("button", { name: "Refresh" })); + + await waitFor(() => expect(vi.mocked(agentTraceListCall).mock.calls.length).toBeGreaterThan(callsBeforeRefresh)); + }); + + it("keeps the time controls on an empty range the user picked, instead of showing onboarding", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); + renderWithProviders(); + await screen.findByTestId("runs-table"); + + vi.mocked(agentTraceListCall).mockResolvedValue({ ...(traceList as TracePage), data: [] }); + fireEvent.click(screen.getByRole("button", { name: "Time range" })); + fireEvent.click(await screen.findByRole("menuitemradio", { name: "Last hour" })); + + expect(await screen.findByText("No runs match these filters.")).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Time range" })).toBeInTheDocument(); + expect(screen.queryByTestId("tracing-setup-card")).not.toBeInTheDocument(); + }); + + it("asks the proxy for the last 24 hours by default", async () => { + vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); + renderWithProviders(); + await screen.findByTestId("runs-table"); + + const { startMs, endMs } = vi.mocked(agentTraceListCall).mock.calls[0][0]; + expect(endMs - startMs).toBeGreaterThanOrEqual(24 * 3600 * 1000 - 60_000); + expect(endMs - startMs).toBeLessThan(24 * 3600 * 1000 + 120_000); + }); +}); diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.test.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.test.tsx index e90544ca0a6..062c6d30da6 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.test.tsx @@ -1,330 +1,11 @@ -import { act, fireEvent, screen, waitFor, within } from "@testing-library/react"; -import userEvent from "@testing-library/user-event"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; - -import { ApiError } from "@/lib/http/client"; - -import { chooseSelectOption, renderWithProviders, testQueryClient } from "../../../../tests/test-utils"; +import { describe, expect, it } from "vitest"; +import { filterRuns } from "./AgentTracesSection"; import traceList from "./__fixtures__/trace_list.json"; -import AgentTracesPage from "./AgentTracesPage"; -import { AgentTracesSection, filterRuns, type TimeControls } from "./AgentTracesSection"; import type { TracePage, TraceSummary } from "./traceTypes"; -vi.mock("../../networking", () => ({ - apiClient: { get: vi.fn(), post: vi.fn() }, - agentTraceListCall: vi.fn(), - sendOtlpTraceCall: vi.fn(), - agentTraceCall: vi.fn(), - agentTraceSpanCall: vi.fn(), - getProxyBaseUrl: () => "http://localhost:4000", -})); - -vi.mock("./TraceDrawer", () => ({ - RunView: ({ traceId, onBack }: { traceId: string; onBack: () => void }) => ( -
    - run {traceId} - -
    - ), -})); - -import { agentTraceListCall, apiClient } from "../../networking"; - const runs = (traceList as TracePage).data as TraceSummary[]; -const renderSection = () => - renderWithProviders( - , - ); - -// A UTC-pinned day around the fixture runs (2026-09-30 ~06:43 UTC), so they land in the same bucket in any timezone. -const renderWindowed = (timeControls?: TimeControls) => - renderWithProviders( - , - ); - -const bucketRunCounts = () => - screen.getAllByTestId("timeline-bucket").map((bucket) => Number(bucket.getAttribute("data-runs"))); - -describe("AgentTracesSection", () => { - afterEach(() => { - vi.useRealTimers(); - vi.restoreAllMocks(); - }); - - beforeEach(() => { - vi.spyOn(HTMLElement.prototype, "getBoundingClientRect").mockReturnValue({ - left: 0, - width: 600, - top: 0, - height: 56, - right: 600, - bottom: 56, - x: 0, - y: 0, - toJSON: () => ({}), - } as DOMRect); - testQueryClient.clear(); - vi.mocked(agentTraceListCall).mockReset(); - vi.mocked(apiClient.get).mockResolvedValue({ data: [] }); - }); - - it.each([ - [401, "Your session is no longer valid. Sign out and sign in again."], - [403, "Your account does not have access to these traces."], - ])("stops live polling after HTTP %s and explains how to recover", async (status, message) => { - vi.useFakeTimers(); - vi.mocked(agentTraceListCall).mockRejectedValue(new ApiError("Private token details", Number(status), {})); - renderWithProviders( - , - ); - await act(async () => { - await vi.advanceTimersByTimeAsync(60_000); - }); - expect(screen.getByText(`Could not load runs: ${message}`)).toBeVisible(); - expect(screen.queryByText(/Private token details/)).not.toBeInTheDocument(); - expect(agentTraceListCall).toHaveBeenCalledOnce(); - expect(screen.getByTestId("runs-footer")).toHaveTextContent("Update failed"); - }); - - it("renders the setup snippet when the proxy answers 501", async () => { - vi.mocked(agentTraceListCall).mockRejectedValue( - new ApiError("Agent tracing is not enabled", 501, { detail: "Agent tracing is not enabled" }), - ); - renderSection(); - - const card = await screen.findByTestId("tracing-setup-card"); - expect(card).toHaveTextContent("Tracing is not enabled"); - expect(card).toHaveTextContent("type: clickhouse"); - expect(card).toHaveTextContent("url: os.environ/CLICKHOUSE_URL"); - expect(screen.getByRole("button", { name: "Check setup" })).toBeEnabled(); - expect(card).not.toHaveTextContent(/langsmith/i); - expect(card).toHaveTextContent("ClickHouse and proxy setup"); - expect(card).toHaveTextContent("Ask your proxy administrator"); - }); - - it("shows the waiting guide when tracing is on but no runs have arrived", async () => { - vi.mocked(agentTraceListCall).mockResolvedValue({ ...(traceList as TracePage), data: [] }); - renderSection(); - - const card = await screen.findByTestId("tracing-setup-card"); - expect(card).toHaveTextContent("Connect your agent"); - expect(card).toHaveTextContent("Waiting for your first trace"); - expect(card).not.toHaveTextContent("store: clickhouse"); - }); - - it("keeps the trace list available when traces exist outside the current time window", async () => { - vi.mocked(agentTraceListCall).mockResolvedValue({ ...(traceList as TracePage), data: [] }); - vi.mocked(apiClient.get).mockResolvedValue(traceList); - renderSection(); - expect(await screen.findByText("No runs match these filters.")).toBeVisible(); - expect(screen.queryByTestId("tracing-setup-card")).not.toBeInTheDocument(); - expect(apiClient.get).toHaveBeenCalledWith("/v1/traces", { accessToken: "sk-test", query: { start_ms: 0 } }); - }); - - it("checks proxy readiness, waits for an agent, and confirms receipt using actual query results", async () => { - vi.mocked(agentTraceListCall).mockRejectedValue(new ApiError("Tracing is not enabled", 501, {})); - renderSection(); - const checkSetup = await screen.findByRole("button", { name: "Check setup" }); - vi.mocked(agentTraceListCall).mockResolvedValue({ ...(traceList as TracePage), data: [] }); - fireEvent.click(checkSetup); - expect(await screen.findByRole("heading", { name: "Connect your agent" })).toBeVisible(); - expect(screen.getByText("Waiting for your first trace")).toBeVisible(); - vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); - fireEvent.click(screen.getByRole("button", { name: "Check for traces" })); - expect(await screen.findAllByTestId("agent-trace-row")).toHaveLength(runs.length); - expect(screen.getByText("Traces received. Select a run to inspect it.")).toBeVisible(); - expect(screen.queryByTestId("tracing-setup-card")).not.toBeInTheDocument(); - }); - - it("keeps setup visible while checking and explains when tracing is still disabled", async () => { - const failure = new ApiError("Tracing is not enabled", 501, {}); - vi.mocked(agentTraceListCall).mockRejectedValue(failure); - renderSection(); - const checkSetup = await screen.findByRole("button", { name: "Check setup" }); - let rejectCheck: (error: Error) => void = () => {}; - vi.mocked(agentTraceListCall).mockImplementationOnce( - () => - new Promise((_, reject) => { - rejectCheck = reject; - }), - ); - fireEvent.click(checkSetup); - expect(await screen.findByRole("button", { name: "Checking…" })).toBeDisabled(); - expect(screen.getByRole("heading", { name: "Enable tracing" })).toBeVisible(); - await act(async () => rejectCheck(failure)); - expect(await screen.findByText(/Tracing is still unavailable/)).toBeVisible(); - expect(screen.getByRole("button", { name: "Check setup" })).toBeEnabled(); - }); - - it("keeps received traces and the open drawer visible during subsequent fetches", async () => { - vi.mocked(agentTraceListCall).mockRejectedValue(new ApiError("Tracing is not enabled", 501, {})); - renderSection(); - const checkSetup = await screen.findByRole("button", { name: "Check setup" }); - vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); - fireEvent.click(checkSetup); - const rows = await screen.findAllByTestId("agent-trace-row"); - fireEvent.click(rows[0]); - const drawer = screen.getByRole("complementary", { name: "Trace details" }); - - let finishRefresh: (page: TracePage) => void = () => {}; - vi.mocked(agentTraceListCall).mockImplementationOnce( - () => - new Promise((resolve) => { - finishRefresh = resolve; - }), - ); - await act(async () => { - void testQueryClient.invalidateQueries({ queryKey: ["agentTraces"] }); - }); - expect(await screen.findByText("Updating…")).toBeVisible(); - expect(screen.getAllByTestId("agent-trace-row")).toHaveLength(runs.length); - expect(screen.getByRole("complementary", { name: "Trace details" })).toBe(drawer); - expect(screen.queryByTestId("tracing-setup-card")).not.toBeInTheDocument(); - await act(async () => finishRefresh(traceList as TracePage)); - }); - - it("separates a failed history check from the empty list and retries that check", async () => { - vi.mocked(agentTraceListCall).mockResolvedValue({ ...(traceList as TracePage), data: [] }); - vi.mocked(apiClient.get).mockRejectedValue(new ApiError("History unavailable", 503, {})); - renderSection(); - expect(await screen.findByRole("alert")).toHaveTextContent("Could not check earlier traces. History unavailable"); - expect(screen.getByText("No runs match these filters.")).toBeVisible(); - expect(screen.queryByText(/Could not load runs/)).not.toBeInTheDocument(); - - vi.mocked(apiClient.get).mockResolvedValue(traceList); - fireEvent.click(screen.getByRole("button", { name: "Retry trace check" })); - await waitFor(() => expect(screen.queryByRole("alert")).not.toBeInTheDocument()); - expect(screen.getByText("No runs match these filters.")).toBeVisible(); - expect(agentTraceListCall).toHaveBeenCalledTimes(1); - }); - - it("treats a proxy without the trace routes (404) like tracing being off", async () => { - vi.mocked(agentTraceListCall).mockRejectedValue(new ApiError("Not Found", 404, { detail: "Not Found" })); - renderSection(); - - const card = await screen.findByTestId("tracing-setup-card"); - expect(card).toHaveTextContent("Tracing is not enabled"); - expect(card).toHaveTextContent("url: os.environ/CLICKHOUSE_URL"); - }); - - it("lists every run with its input, counts and failed column", async () => { - vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); - renderSection(); - - const rows = await screen.findAllByTestId("agent-trace-row"); - expect(rows).toHaveLength(runs.length); - const lead = rows.find((row) => row.textContent?.includes("Should we store OTEL agent spans")); - expect(lead).toBeDefined(); - const failed = rows.find((row) => row.textContent?.includes("acme-404")) as HTMLElement; - expect(within(failed).getByLabelText("2 errors")).toBeInTheDocument(); - expect(screen.getByText(`${runs.length} runs`)).toBeInTheDocument(); - expect(screen.getByRole("columnheader", { name: "Cost" })).toBeInTheDocument(); - expect(within(failed).getByText("—")).toBeInTheDocument(); - }); - - it("shows the spend returned for a run", async () => { - vi.mocked(agentTraceListCall).mockResolvedValue({ - ...(traceList as TracePage), - data: [{ ...runs[0], spend: 0.025 }], - }); - renderSection(); - - const row = await screen.findByTestId("agent-trace-row"); - expect(within(row).getByText("$0.03")).toBeInTheDocument(); - }); - - it("filters by input text and by trace id", async () => { - vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); - renderSection(); - await screen.findAllByTestId("agent-trace-row"); - - const search = screen.getByLabelText("Search runs"); - fireEvent.change(search, { target: { value: "acme-404" } }); - expect(screen.getAllByTestId("agent-trace-row")).toHaveLength(1); - - const lead = runs.find((r) => r.name === "research_lead") as TraceSummary; - fireEvent.change(search, { target: { value: lead.trace_id.slice(0, 10) } }); - const rows = screen.getAllByTestId("agent-trace-row"); - expect(rows).toHaveLength(1); - expect(rows[0]).toHaveTextContent("Should we store OTEL agent spans"); - }); - - it("uses recorded agent names for the column and filter even when services are shared", async () => { - vi.mocked(agentTraceListCall).mockResolvedValue({ - ...(traceList as TracePage), - data: [ - ...runs.slice(1).map((run) => ({ ...run, service: "shared-app", agent_names: ["research-agent"] })), - { ...runs[0], service: "shared-app", agent_names: ["billing-agent", "review-agent"] }, - ], - }); - const user = userEvent.setup(); - renderSection(); - await screen.findAllByTestId("agent-trace-row"); - - expect(screen.getByRole("columnheader", { name: "Agent" })).toBeInTheDocument(); - const agentFilter = screen.getByRole("combobox", { name: "Filter by agent" }); - expect(agentFilter).toHaveTextContent("All agents"); - - await chooseSelectOption(user, agentFilter, "billing-agent"); - const rows = screen.getAllByTestId("agent-trace-row"); - expect(rows).toHaveLength(1); - expect(rows[0]).toHaveTextContent("billing-agent"); - expect(rows[0]).not.toHaveTextContent("shared-app"); - - await chooseSelectOption(user, agentFilter, "review-agent"); - expect(screen.getAllByTestId("agent-trace-row")).toHaveLength(1); - - await chooseSelectOption(user, agentFilter, "All agents"); - expect(screen.getAllByTestId("agent-trace-row")).toHaveLength(runs.length); - }); - - it("shows each run's agent name with the logo of the SDK that produced it", async () => { - vi.mocked(agentTraceListCall).mockResolvedValue({ - ...(traceList as TracePage), - data: [ - { ...runs[0], agent_names: ["research-bot"], frameworks: ["claude-agent-sdk", "claude-code"] }, - { ...runs[1], agent_names: [], frameworks: ["claude-code"] }, - { ...runs[2], frameworks: [] }, - ], - }); - renderSection(); - const [sdkRun, cliRun, plainRun] = await screen.findAllByTestId("agent-trace-row"); - const agentCell = (row: HTMLElement) => within(row).getAllByRole("cell")[1]; - - expect(agentCell(sdkRun)).toHaveTextContent(/^research-bot$/); - expect(agentCell(sdkRun)).toHaveAttribute("title", "research-bot · Claude Agent SDK"); - expect(within(sdkRun).getByRole("img", { name: "Claude Agent SDK logo", hidden: true })).toHaveAttribute( - "src", - expect.stringContaining("anthropic.svg"), - ); - expect(agentCell(cliRun)).toHaveTextContent(/^Claude Code$/); - expect(within(plainRun).queryByRole("img", { hidden: true })).not.toBeInTheDocument(); - expect(agentCell(plainRun)).toHaveTextContent((runs[2].agent_names ?? [runs[2].service]).join(", ")); - }); - +describe("filterRuns", () => { it("status filter 'Failed' keeps only runs with errors", () => { const failed = filterRuns(runs, "", "all", "error"); expect(failed.length).toBeGreaterThan(0); @@ -333,181 +14,4 @@ describe("AgentTracesSection", () => { expect(ok.every((r) => r.error_count === 0)).toBe(true); expect(failed.length + ok.length).toBe(runs.length); }); - - it("opens a run in a side drawer over the list and swaps runs without closing it", async () => { - vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); - renderSection(); - const rows = await screen.findAllByTestId("agent-trace-row"); - - fireEvent.click(rows[0]); - const drawer = screen.getByRole("complementary", { name: "Trace details" }); - expect(within(drawer).getByTestId("run-view")).toHaveTextContent(`run ${runs[0].trace_id}`); - expect(screen.getByTestId("runs-table")).toBeInTheDocument(); - expect(rows[0]).toHaveAttribute("aria-selected", "true"); - - fireEvent.click(rows[1]); - expect(screen.getByRole("complementary", { name: "Trace details" })).toBe(drawer); - expect(within(drawer).getByTestId("run-view")).toHaveTextContent(`run ${runs[1].trace_id}`); - expect(rows[1]).toHaveAttribute("aria-selected", "true"); - expect(rows[0]).toHaveAttribute("aria-selected", "false"); - }); - - it("closes the drawer when the open row is clicked again or Escape is pressed", async () => { - vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); - renderSection(); - const rows = await screen.findAllByTestId("agent-trace-row"); - - fireEvent.click(rows[0]); - fireEvent.click(rows[0]); - expect(rows[0]).toHaveAttribute("aria-selected", "false"); - - fireEvent.click(rows[1]); - fireEvent.keyDown(window, { key: "Escape" }); - expect(rows[1]).toHaveAttribute("aria-selected", "false"); - }); - - it("moves to the next and previous run with j / k and the header arrows", async () => { - vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); - renderSection(); - const rows = await screen.findAllByTestId("agent-trace-row"); - - fireEvent.click(rows[0]); - fireEvent.keyDown(window, { key: "j" }); - expect(screen.getByTestId("run-view")).toHaveTextContent(`run ${runs[1].trace_id}`); - fireEvent.keyDown(window, { key: "k" }); - expect(screen.getByTestId("run-view")).toHaveTextContent(`run ${runs[0].trace_id}`); - expect(screen.getByRole("button", { name: "Previous trace (K)" })).toBeDisabled(); - fireEvent.click(screen.getByRole("button", { name: "Next trace (J)" })); - expect(screen.getByTestId("run-view")).toHaveTextContent(`run ${runs[1].trace_id}`); - }); - - it("plots every loaded run on the timeline", async () => { - vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); - renderWindowed(); - await screen.findAllByTestId("agent-trace-row"); - - expect(screen.getByTestId("traces-timeline")).toBeInTheDocument(); - const counts = bucketRunCounts(); - expect(counts).toHaveLength(60); - expect(counts.reduce((a, b) => a + b, 0)).toBe(runs.length); - }); - - it("zooms by dragging, resizes and pans the bracket, and clears with Esc", async () => { - vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); - renderWindowed(); - await screen.findAllByTestId("agent-trace-row"); - const area = screen.getByTestId("timeline-area"); - const x = (bucket: number) => bucket * 10 + 5; - const drag = (target: HTMLElement, from: number, to: number) => { - fireEvent.pointerDown(target, { clientX: x(from), pointerId: 1 }); - fireEvent.pointerMove(area, { clientX: x(to), pointerId: 1 }); - fireEvent.pointerUp(area, { clientX: x(to), pointerId: 1 }); - }; - const rowCount = () => screen.queryAllByTestId("agent-trace-row").length; - const withRuns = bucketRunCounts().flatMap((count, i) => (count > 0 ? [i] : [])); - const first = withRuns[0]; - // The pan below moves a [0, first] bracket to the far right; it must end up clear of every run. - expect(first).toBeGreaterThan(1); - expect(first).toBeLessThan(30); - - drag(area, 0, 1); - expect(screen.getByTestId("timeline-selection")).toBeInTheDocument(); - expect(rowCount()).toBe(0); - - drag(screen.getByTestId("timeline-handle-hi"), 1, first); - expect(rowCount()).toBeGreaterThan(0); - - drag(screen.getByTestId("timeline-selection"), 1, 1 - first); - expect(rowCount()).toBeGreaterThan(0); - drag(screen.getByTestId("timeline-selection"), 0, 59); - expect(rowCount()).toBe(0); - - fireEvent.keyDown(screen.getByTestId("traces-timeline"), { key: "Escape" }); - expect(screen.queryByTestId("timeline-selection")).not.toBeInTheDocument(); - expect(rowCount()).toBe(runs.length); - }); - - it("clears timeline zoom when refreshed", async () => { - vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); - renderWindowed({ rangeHours: 24, onRangeHoursChange: () => {}, onLiveChange: () => {} }); - await screen.findAllByTestId("agent-trace-row"); - const area = screen.getByTestId("timeline-area"); - const x = (bucket: number) => bucket * 10 + 5; - - fireEvent.pointerDown(area, { clientX: x(0), pointerId: 1 }); - fireEvent.pointerMove(area, { clientX: x(1), pointerId: 1 }); - fireEvent.pointerUp(area, { clientX: x(1), pointerId: 1 }); - expect(screen.getByTestId("timeline-selection")).toBeInTheDocument(); - expect(screen.queryAllByTestId("agent-trace-row")).toHaveLength(0); - - fireEvent.click(screen.getByRole("button", { name: "Refresh" })); - - expect(screen.queryByTestId("timeline-selection")).not.toBeInTheDocument(); - expect(screen.getAllByTestId("agent-trace-row")).toHaveLength(runs.length); - }); -}); - -describe("AgentTracesPage", () => { - beforeEach(() => { - testQueryClient.clear(); - vi.mocked(agentTraceListCall).mockReset(); - vi.mocked(apiClient.get).mockResolvedValue({ data: [] }); - }); - - it("shows the actual range, switches presets from the popover, and toggles Live", async () => { - vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); - renderWithProviders(); - await screen.findByTestId("runs-table"); - - const trigger = screen.getByRole("button", { name: "Time range" }); - expect(trigger).toHaveTextContent(/ to /); - expect(screen.getByTestId("traces-timeline")).toHaveTextContent("Total 1d"); - - fireEvent.click(trigger); - fireEvent.click(await screen.findByRole("menuitemradio", { name: "Last 7 days" })); - expect(await screen.findByText("Total 7d")).toBeInTheDocument(); - const last = vi.mocked(agentTraceListCall).mock.calls.at(-1)?.[0]; - expect((last?.endMs ?? 0) - (last?.startMs ?? 0)).toBeGreaterThanOrEqual(7 * 24 * 3600 * 1000 - 60_000); - - const live = screen.getByRole("button", { name: "Live" }); - expect(live).toHaveAttribute("aria-pressed", "true"); - fireEvent.click(live); - expect(live).toHaveAttribute("aria-pressed", "false"); - expect(screen.getByRole("button", { name: "Refresh" })).toBeEnabled(); - }); - - it("refreshes the trace list", async () => { - vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); - renderWithProviders(); - await screen.findByTestId("runs-table"); - - const callsBeforeRefresh = vi.mocked(agentTraceListCall).mock.calls.length; - fireEvent.click(screen.getByRole("button", { name: "Refresh" })); - - await waitFor(() => expect(vi.mocked(agentTraceListCall).mock.calls.length).toBeGreaterThan(callsBeforeRefresh)); - }); - - it("keeps the time controls on an empty range the user picked, instead of showing onboarding", async () => { - vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); - renderWithProviders(); - await screen.findByTestId("runs-table"); - - vi.mocked(agentTraceListCall).mockResolvedValue({ ...(traceList as TracePage), data: [] }); - fireEvent.click(screen.getByRole("button", { name: "Time range" })); - fireEvent.click(await screen.findByRole("menuitemradio", { name: "Last hour" })); - - expect(await screen.findByText("No runs match these filters.")).toBeInTheDocument(); - expect(screen.getByRole("button", { name: "Time range" })).toBeInTheDocument(); - expect(screen.queryByTestId("tracing-setup-card")).not.toBeInTheDocument(); - }); - - it("asks the proxy for the last 24 hours by default", async () => { - vi.mocked(agentTraceListCall).mockResolvedValue(traceList as TracePage); - renderWithProviders(); - await screen.findByTestId("runs-table"); - - const { startMs, endMs } = vi.mocked(agentTraceListCall).mock.calls[0][0]; - expect(endMs - startMs).toBeGreaterThanOrEqual(24 * 3600 * 1000 - 60_000); - expect(endMs - startMs).toBeLessThan(24 * 3600 * 1000 + 120_000); - }); }); diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.tsx index 1b25ca19051..45f1b223088 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/AgentTracesSection.tsx @@ -1,4 +1,5 @@ "use client"; +import { useLensDemo } from "@/components/lens/LensDemoContext"; import moment from "moment"; import { useMemo, useState } from "react"; @@ -61,6 +62,7 @@ interface AgentTracesSectionProps { onRunOpenChange?: (open: boolean) => void; readOnly?: boolean; canMintTracingKey?: boolean; + onDemo?: () => void; } function useTracingSetup(traces: AgentTracesResult, isActive: boolean, rangeChanged: boolean) { @@ -102,7 +104,9 @@ export function AgentTracesSection({ onRunOpenChange, readOnly = false, canMintTracingKey = false, + onDemo, }: AgentTracesSectionProps) { + const demo = useLensDemo(); const [openTrace, setOpenTrace] = useState(null); const [query, setQuery] = useState(""); const [agent, setAgent] = useState(ALL_AGENTS); @@ -160,10 +164,11 @@ export function AgentTracesSection({ checking: traces.isFetching, }; - if (setup.disabledDetail != null) return ; + if (setup.disabledDetail != null) + return ; // Onboarding only on the first, default view; an empty range the user picked keeps its controls. if (checkHistory && !history.error && history.data === false) - return ; + return ; if (showSetup) { return (
    @@ -185,11 +190,7 @@ export function AgentTracesSection({ return (
    {checkHistory && } - {setup.received && ( -

    - Traces received. Select a run to inspect it. -

    - )} + - + {!demo && ( + + )} {timeControls && ( changeRange(hours, timeControls.onRangeHoursChange)} live={isLiveTail} + showLive={!demo} onLiveChange={timeControls.onLiveChange} onRefresh={() => { setZoom(null); @@ -240,6 +244,16 @@ export function AgentTracesSection({ ); } +function TracesReceived({ received }: { received: boolean }) { + const demo = useLensDemo(); + if (!received || demo) return null; + return ( +

    + Traces received. Select a run to inspect it. +

    + ); +} + function RunsFooter({ count, zoom, diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/DetailContent.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/DetailContent.tsx index 86ec3007f72..da2175cf0ff 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/DetailContent.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/DetailContent.tsx @@ -1,4 +1,5 @@ "use client"; +import { useLensDemo } from "@/components/lens/LensDemoContext"; import { useQuery, type UseQueryOptions } from "@tanstack/react-query"; import { useState } from "react"; @@ -29,9 +30,15 @@ const errorReason = (headline: string): string => /^([A-Za-z_][\w.]*)\(/.exec(he /** Shared lazy fetch of one span's full input / output / attributes. */ export function useSpanDetail(accessToken: string, traceId: string, spanId: string | null, traceRef?: string) { + const demo = useLensDemo(); const queryOptions: UseQueryOptions = { queryKey: ["agentTraceSpan", traceId, traceRef, spanId, accessToken], - queryFn: () => agentTraceSpanCall(accessToken, traceId, spanId as string, traceRef), + queryFn: () => + demo + ? demo.client.get( + `/v1/traces/${encodeURIComponent(traceId)}/spans/${encodeURIComponent(spanId as string)}`, + ) + : agentTraceSpanCall(accessToken, traceId, spanId as string, traceRef), enabled: spanId !== null, staleTime: Infinity, }; @@ -146,11 +153,17 @@ interface DetailContentProps { } function DiagnosticContent({ accessToken, traceId, traceRef, span }: DetailContentProps) { + const demo = useLensDemo(); const [opened, setOpened] = useState(false); const [cursor, setCursor] = useState(null); const queryOptions: UseQueryOptions = { queryKey: ["agentTraceSpanError", traceId, traceRef, span.span_id, accessToken, cursor], - queryFn: () => agentTraceSpanErrorCall(accessToken, traceId, span.span_id, { traceRef, cursor }), + queryFn: () => + demo + ? demo.client.get( + `/v1/traces/${encodeURIComponent(traceId)}/spans/${encodeURIComponent(span.span_id)}/error`, + ) + : agentTraceSpanErrorCall(accessToken, traceId, span.span_id, { traceRef, cursor }), enabled: opened, staleTime: Infinity, gcTime: 0, diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/DetailPane.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/DetailPane.tsx index 985de97cdc9..aa777b9861e 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/DetailPane.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/DetailPane.tsx @@ -1,4 +1,5 @@ "use client"; +import { useLensDemo } from "@/components/lens/LensDemoContext"; import { PanelRightClose } from "lucide-react"; import { useState } from "react"; @@ -104,6 +105,7 @@ function SpanPane({ onClose: () => void; }) { const [tab, setTab] = useState("content"); + const demo = useLensDemo(); const traceId = trace.summary.trace_id; const detailQuery = useSpanDetail( accessToken, @@ -162,9 +164,13 @@ function SpanPane({
    @@ -186,6 +192,7 @@ function GroupMetric({ label, value }: { label: string; value: string }) { /** ×N group: rollup of every invocation plus the first failure's message. */ function GroupPane({ trace, row, onClose }: { trace: Trace; row: GroupRowData; onClose: () => void }) { + const demo = useLensDemo(); const tokens = row.members.reduce((sum, m) => sum + m.input_tokens + m.output_tokens, 0); const firstFailure = row.members.find((m) => m.status === "error" && m.error); return ( @@ -222,7 +229,11 @@ function GroupPane({ trace, row, onClose }: { trace: Trace; row: GroupRowData; o
    diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/TimeRangeControls.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/TimeRangeControls.tsx index 03d950a7725..c9e25c2e033 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/TimeRangeControls.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/TimeRangeControls.tsx @@ -34,6 +34,7 @@ interface TimeRangeControlsProps { rangeHours: number; onRangeHoursChange: (hours: number) => void; live: boolean; + showLive?: boolean; onLiveChange: (live: boolean) => void; onRefresh: () => void; refreshing: boolean; @@ -45,6 +46,7 @@ export function TimeRangeControls({ rangeHours, onRangeHoursChange, live, + showLive = true, onLiveChange, onRefresh, refreshing, @@ -84,18 +86,20 @@ export function TimeRangeControls({ - + {showLive && ( + + )}
    ); diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/TraceDrawer.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/TraceDrawer.tsx index a991c771a56..435ea51e8d2 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/TraceDrawer.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/TraceDrawer.tsx @@ -1,4 +1,5 @@ "use client"; +import { useLensDemo } from "@/components/lens/LensDemoContext"; import { useQuery } from "@tanstack/react-query"; import { ArrowLeft, Check, Copy } from "lucide-react"; @@ -74,6 +75,7 @@ const toggle = (set: ReadonlySet, id: string): Set => { }; function CopyForAgent({ traceId, traceRef }: { traceId: string; traceRef?: string }) { + const demo = useLensDemo(); const [copied, setCopied] = useState(false); useEffect(() => { if (!copied) return; @@ -86,11 +88,16 @@ function CopyForAgent({ traceId, traceRef }: { traceId: string; traceRef?: strin size="xs" className="h-7 shrink-0 gap-1.5 rounded-md text-[12px] shadow-none" onClick={async () => - setCopied(await copyToClipboard(agentHandoffText(traceId, null, traceRef), "Command copied")) + setCopied( + await copyToClipboard( + demo ? demo.copyTrace(traceId) : agentHandoffText(traceId, null, traceRef), + demo ? "Trace copied" : "Command copied", + ), + ) } > {copied ? : } - {copied ? "Command copied" : "Copy for agent"} + {copied ? "Copied" : "Copy for agent"} ); } @@ -277,9 +284,13 @@ interface RunViewProps { /** One agent run: header with totals and "Copy for agent", span tree on the left, span details on the right. */ export function RunView({ traceId, traceRef, initialSpanId, accessToken, onBack, embedded = false }: RunViewProps) { + const demo = useLensDemo(); const traceQuery = useQuery({ queryKey: ["agentTrace", traceId, traceRef, accessToken], - queryFn: () => agentTraceCall(accessToken, traceId, traceRef), + queryFn: () => + demo + ? demo.client.get(`/v1/traces/${encodeURIComponent(traceId)}`) + : agentTraceCall(accessToken, traceId, traceRef), staleTime: 30_000, }); const trace = traceQuery.data; diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/TracePreview.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/TracePreview.tsx deleted file mode 100644 index c4fd04602ac..00000000000 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/TracePreview.tsx +++ /dev/null @@ -1,70 +0,0 @@ -"use client"; - -import { cn } from "@/lib/cva.config"; - -import previewTrace from "./previewTrace.json"; -import { SpanIcon } from "./SpanIcon"; -import type { SpanType } from "./traceTypes"; -import { fmtMs } from "./traceUtils"; - -interface PreviewRow { - id: string; - name: string; - type: SpanType; - model: string | null; - depth: number; - start_offset_ms: number; - duration_ms: number; - error: boolean; -} - -const preview = previewTrace as { - name: string; - input_preview: string; - span_count: number; - duration_ms: number; - rows: PreviewRow[]; -}; - -export function TracePreview() { - const total = preview.duration_ms; - return ( -
    -
    - - {preview.name} - {preview.input_preview} - - {preview.span_count} steps · {fmtMs(total)} - -
    -
    - {preview.rows.map((row) => ( -
    -
    - - {row.name} -
    -
    -
    -
    - - {fmtMs(row.duration_ms)} - -
    - ))} -
    -
    -
    - ); -} diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/TracingSetupCard.test.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/TracingSetupCard.integration.test.tsx similarity index 63% rename from ui/litellm-dashboard/src/components/view_logs/TraceView/TracingSetupCard.test.tsx rename to ui/litellm-dashboard/src/components/view_logs/TraceView/TracingSetupCard.integration.test.tsx index 65e5e9229ff..e1d128d59e9 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/TracingSetupCard.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/TracingSetupCard.integration.test.tsx @@ -12,6 +12,7 @@ import { tracingEnvSnippet, TracingSetupCard, } from "./TracingSetupCard"; +import { FRAMEWORKS } from "./tracingSetupGuides"; import type { Trace } from "./traceTypes"; vi.mock("../../networking", () => ({ @@ -31,6 +32,7 @@ const renderCard = ( onCheck?: () => void; readOnly?: boolean; canMintTracingKey?: boolean; + onDemo?: () => void; } = {}, ) => { const onOpenTrace = vi.fn(); @@ -38,6 +40,7 @@ const renderCard = ( vi.clearAllMocks()); describe("TracingSetupCard", () => { - it("shows agent connection guidance and labels the example run as sample data", async () => { + it("offers the interactive demo while waiting for the first trace", async () => { const user = userEvent.setup(); - const { card } = renderCard(); + const onDemo = vi.fn(); + const { card } = renderCard({ onDemo }); expect(screen.getByRole("heading", { name: "Connect your agent" })).toBeVisible(); expect(screen.getByText("Tracing enabled")).toBeVisible(); expect(screen.getByText("Waiting for your first trace")).toBeVisible(); - expect(screen.getByTestId("trace-preview")).toBeVisible(); - expect(card).toHaveTextContent("sample data, not your runs"); + await user.click(screen.getByRole("button", { name: "Preview sample" })); + expect(onDemo).toHaveBeenCalledOnce(); + expect(sendOtlpTraceCall).not.toHaveBeenCalled(); expect(card).not.toHaveTextContent("store: clickhouse"); await user.click(screen.getByText("Set up manually")); - expect(screen.getByText(/^export OTEL_EXPORTER_OTLP_ENDPOINT=http:\/\/proxy.test/)).toBeVisible(); + expect(screen.getByText(/^export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=/)).toBeVisible(); expect(card).not.toHaveTextContent(/langsmith/i); }); - it("lists the OTEL endpoints before the framework picker, each copyable", async () => { + it("keeps connection details visible and copies the full trace endpoint", async () => { const user = userEvent.setup(); - const { card } = renderCard(); - const text = card.textContent ?? ""; - expect(text.indexOf("OpenTelemetry (OTEL) endpoints")).toBeLessThan(text.indexOf("Your agent framework")); + renderCard(); + expect(screen.getByRole("combobox", { name: "Your agent framework" })).toBeVisible(); + expect(screen.getByRole("button", { name: "Copy http://proxy.test/v1/traces" })).toBeVisible(); await user.click(screen.getByRole("button", { name: "Copy http://proxy.test/v1/traces" })); expect(copyToClipboard).toHaveBeenLastCalledWith("http://proxy.test/v1/traces"); }); - it("hides the sample preview once traces are arriving", () => { + it("shows connection guidance for another agent without a demo", () => { renderCard({ connected: true }); expect(screen.getByRole("heading", { name: "Connect another agent" })).toBeVisible(); - expect(screen.queryByTestId("trace-preview")).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: "Preview sample" })).not.toBeInTheDocument(); }); it("builds the coding agent command for the selected framework and keeps both manual installers", async () => { const user = userEvent.setup(); renderCard(); await chooseSelectOption(user, screen.getByRole("combobox", { name: "Your agent framework" }), "CrewAI"); - const prompt = codingAgentPrompt("http://proxy.test", { - label: "CrewAI", - packages: "crewai openinference-instrumentation-crewai", - }); - const commandText = () => screen.getByText(/^claude |^codex /).textContent; - expect(commandText()).toBe(codingAgentCommand("Claude Code", prompt)); + const prompt = codingAgentPrompt( + "http://proxy.test", + FRAMEWORKS.find((guide) => guide.id === "crewai")!, + "openai/gpt-6-sol", + ); + expect(screen.getByText(/^claude /)).not.toBeVisible(); + await user.click(screen.getByRole("button", { name: "Copy setup command" })); + expect(copyToClipboard).toHaveBeenLastCalledWith(codingAgentCommand("Claude Code", prompt)); await user.click(screen.getByRole("tab", { name: "Codex" })); - expect(commandText()).toBe(codingAgentCommand("Codex", prompt)); + await user.click(screen.getByRole("button", { name: "Copy setup command" })); + expect(copyToClipboard).toHaveBeenLastCalledWith(codingAgentCommand("Codex", prompt)); + await user.click(screen.getByText("View command")); + expect(screen.getByText(/^codex /)).toBeVisible(); + expect(screen.getByText(/^codex /)).toHaveTextContent(codingAgentCommand("Codex", prompt), { + normalizeWhitespace: false, + }); await user.click(screen.getByText("Set up manually")); - expect(screen.getByText(/pip install -U opentelemetry-distro/)).toHaveTextContent( + expect(screen.getByText(/^pip install opentelemetry-distro/)).toHaveTextContent( "crewai openinference-instrumentation-crewai", ); await user.click(screen.getByRole("tab", { name: "uv" })); - expect(screen.getByText(/uv add opentelemetry-distro/)).toHaveTextContent( + expect(screen.getByText(/^uv add opentelemetry-distro/)).toHaveTextContent( "crewai openinference-instrumentation-crewai", ); }); - it("uses npm and a CommonJS-safe TypeScript entrypoint for the Vercel AI SDK", async () => { + it("uses the selected framework's tracing and agent name without asking for a model", async () => { const user = userEvent.setup(); const { card } = renderCard(); await chooseSelectOption(user, screen.getByRole("combobox", { name: "Your agent framework" }), "Vercel AI SDK"); await user.click(screen.getByText("Set up manually")); - expect(card).toHaveTextContent("npm install ai @ai-sdk/openai-compatible @vercel/otel"); - expect(card).toHaveTextContent("my_agent.ts"); - expect(card).not.toHaveTextContent("opentelemetry-instrument python"); - const quickstart = screen.getByText(/registerOTel\(\{/).textContent ?? ""; - expect(quickstart).toContain("async function main()"); - expect(quickstart.split("\n").filter((line) => /^(const|let) .*= await /.test(line))).toEqual([]); + expect(screen.queryByRole("combobox", { name: "Model" })).not.toBeInTheDocument(); + expect(card).toHaveTextContent("npm install ai @ai-sdk/otel"); + expect(card).toHaveTextContent('const AGENT_NAME = "research_agent"'); + expect(card).toHaveTextContent("functionId: AGENT_NAME"); + expect(card).toHaveTextContent("Use a model configured on this proxy."); + expect(screen.getByText(/^import \{ createOpenAICompatible/)).toHaveTextContent( + 'const model = litellm("openai/gpt-6-sol")', + ); + expect(card).toHaveTextContent('baseURL: "http://proxy.test/v1"'); + }); + + it("keeps plugin model settings and uses a generated tracing key only for tracing", async () => { + const user = userEvent.setup(); + vi.mocked(apiClient.post).mockResolvedValue({ key: SECRET }); + const { card } = renderCard(); + await chooseSelectOption(user, screen.getByRole("combobox", { name: "Your agent framework" }), "Hermes"); + await user.click(screen.getByText("Set up manually")); + expect(screen.queryByRole("combobox", { name: "Model" })).not.toBeInTheDocument(); + expect(card).toHaveTextContent("Keep your existing model settings"); + await user.click(screen.getByRole("button", { name: "Generate tracing key" })); + await screen.findByText("Your tracing key"); + expect(card).toHaveTextContent("gen_ai.agent.name: research_agent"); + expect(card).toHaveTextContent("endpoint: http://proxy.test/v1/traces"); + expect(card).toHaveTextContent('Authorization: "Bearer ${LITELLM_TRACING_KEY}"'); + expect(card).not.toHaveTextContent(SECRET); }); it("hides the actions a read-only viewer cannot perform", () => { @@ -121,7 +153,7 @@ describe("TracingSetupCard", () => { expect(screen.queryByRole("button", { name: "Send a test trace" })).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Generate tracing key" })).not.toBeInTheDocument(); expect(card).toHaveTextContent("ask a proxy admin for one"); - expect(card).toHaveTextContent("OpenTelemetry (OTEL) endpoints"); + expect(card).toHaveTextContent("Connection details"); }); it("offers a scoped tracing key only to callers allowed to set key routes", () => { @@ -135,7 +167,7 @@ describe("TracingSetupCard", () => { const user = userEvent.setup(); vi.mocked(apiClient.post).mockResolvedValue({ key: SECRET }); const { card } = renderCard(); - + await user.click(screen.getByText("Set up manually")); await user.click(screen.getByRole("button", { name: "Generate tracing key" })); expect(await screen.findByText("Your tracing key")).toBeVisible(); @@ -193,24 +225,23 @@ describe("TracingSetupCard", () => { }); describe("setup snippets", () => { - it("uses OTLP HTTP with the proxy base URL and reads the key from the environment", () => { + it("uses the instance trace endpoint and keeps tracing and inference keys separate", () => { const env = tracingEnvSnippet("http://proxy.test"); - expect(env).toContain("OTEL_EXPORTER_OTLP_ENDPOINT=http://proxy.test\n"); - expect(env).toContain("OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf"); - expect(env).not.toContain("/v1/traces"); + expect(env).toContain('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="http://proxy.test/v1/traces"'); + expect(env).toContain('OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"'); expect(env).not.toContain("export LITELLM_API_KEY="); expect(env).toContain("Bearer $LITELLM_API_KEY"); const withKey = tracingEnvSnippet("http://proxy.test", SECRET); expect(withKey).toContain(`export LITELLM_TRACING_KEY=${SECRET}\n`); - expect(withKey).toContain('OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer $LITELLM_TRACING_KEY"'); - expect(withKey).not.toContain("LITELLM_API_KEY"); + expect(withKey).toContain('OTEL_EXPORTER_OTLP_TRACES_HEADERS="Authorization=Bearer $LITELLM_TRACING_KEY"'); + expect(withKey).not.toContain("export LITELLM_API_KEY="); - const prompt = codingAgentPrompt("http://proxy.test", { label: "LangChain", packages: "langchain" }); - expect(prompt).toContain("base_url=http://proxy.test/v1"); - expect(prompt).toContain("opentelemetry-distro opentelemetry-exporter-otlp-proto-http langchain"); - expect(prompt).toContain("OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf"); + const prompt = codingAgentPrompt("http://proxy.test", FRAMEWORKS[0], "openai/gpt-6-sol"); + expect(prompt).toContain('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="http://proxy.test/v1/traces"'); + expect(prompt).toContain("Keep the existing model configuration"); + expect(prompt).toContain('AGENT_NAME = "research_agent"'); + expect(prompt).toContain("name=AGENT_NAME"); expect(prompt).toContain("Lens > Traces"); - expect(prompt).not.toMatch(/langsmith/i); }); it("builds a shell-safe command for each coding agent", () => { diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/TracingSetupCard.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/TracingSetupCard.tsx index de776da0617..7c6155446ea 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/TracingSetupCard.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/TracingSetupCard.tsx @@ -4,31 +4,23 @@ import { ArrowRight, ArrowUpRight, Check, Copy, KeyRound, Loader2, Send } from " import { useState } from "react"; import { cn } from "@/lib/cva.config"; +import { LensPreviewButton } from "@/components/lens/LensPreviewButton"; import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { copyToClipboard } from "@/utils/dataUtils"; import anthropicLogo from "../../../../public/assets/logos/anthropic.svg"; -import crewaiLogo from "../../../../public/assets/logos/crewai-color.svg"; -import langchainLogo from "../../../../public/assets/logos/langchain.svg"; -import langgraphLogo from "../../../../public/assets/logos/langgraph-color.svg"; -import llamaindexLogo from "../../../../public/assets/logos/llamaindex-color.svg"; -import openaiAgentsLogo from "../../../../public/assets/logos/openai-agents.svg"; import openaiLogo from "../../../../public/assets/logos/openai_small.svg"; import otelLogo from "../../../../public/assets/logos/opentelemetry.svg"; -import pydanticAiLogo from "../../../../public/assets/logos/pydantic-ai-color.svg"; -import vercelLogo from "../../../../public/assets/logos/vercel.svg"; import { agentTraceCall, apiClient, getProxyBaseUrl, sendOtlpTraceCall } from "../../networking"; import { ActiveDot } from "./ActiveDot"; import { sampleTraceExport } from "./sampleTrace"; -import { TracePreview } from "./TracePreview"; +import { FRAMEWORKS, frameworkSnippet, type FrameworkGuide } from "./tracingSetupGuides"; import type { TraceSummary } from "./traceTypes"; const COPIED_RESET_MS = 1500; const DOCS_URL = "https://docs.litellm.ai/docs/proxy/lens"; -const OTEL_BASE_PACKAGES = "opentelemetry-distro opentelemetry-exporter-otlp-proto-http"; -const PY_RUN_SNIPPET = "opentelemetry-instrument python my_agent.py"; -const TS_RUN_SNIPPET = "npx tsx my_agent.ts"; +const EXAMPLE_MODEL = "openai/gpt-6-sol"; const SAMPLE_TRACE_POLL_MS = 1000; export const TRACING_KEY_REQUEST = { key_alias: "Agent tracing", @@ -41,177 +33,36 @@ type Installer = "pip" | "uv"; type CodingAgent = "Claude Code" | "Codex"; const PY_INSTALL: Record string> = { - pip: (packages) => `pip install -U ${packages}`, + pip: (packages) => `pip install ${packages}`, uv: (packages) => `uv add ${packages}`, }; -interface FrameworkGuide { - id: string; - label: string; - logo: string; - packages: string; - quickstart: string; - typescript?: boolean; -} - -const FRAMEWORKS: readonly FrameworkGuide[] = [ - { - id: "deep-agents", - label: "Deep Agents", - logo: langgraphLogo.src, - packages: "deepagents langchain-openai openinference-instrumentation-langchain", - quickstart: `from deepagents import create_deep_agent -from langchain_openai import ChatOpenAI - -llm = ChatOpenAI(model="claude-sonnet-4-5", base_url="{PROXY}/v1", api_key=os.environ["LITELLM_API_KEY"]) -agent = create_deep_agent(model=llm, tools=[], system_prompt="You are a careful researcher.") -agent.invoke({"messages": [{"role": "user", "content": "What is LiteLLM?"}]})`, - }, - { - id: "vercel-ai-sdk", - label: "Vercel AI SDK", - logo: vercelLogo.src, - typescript: true, - packages: "ai @ai-sdk/openai-compatible @vercel/otel @opentelemetry/api", - quickstart: `import { createOpenAICompatible } from "@ai-sdk/openai-compatible"; -import { registerOTel } from "@vercel/otel"; -import { generateText } from "ai"; - -registerOTel({ serviceName: process.env.OTEL_SERVICE_NAME ?? "my-agent" }); - -const litellm = createOpenAICompatible({ name: "litellm", baseURL: "{PROXY}/v1", apiKey: process.env.LITELLM_API_KEY }); - -async function main() { - const { text } = await generateText({ - model: litellm("claude-sonnet-4-5"), - prompt: "What is LiteLLM?", - experimental_telemetry: { isEnabled: true, functionId: "my_agent" }, - }); - console.log(text); -} - -main();`, - }, - { - id: "langgraph", - label: "LangGraph", - logo: langgraphLogo.src, - packages: "langgraph langchain-openai openinference-instrumentation-langchain", - quickstart: `from langchain.agents import create_agent -from langchain_openai import ChatOpenAI - -llm = ChatOpenAI(model="claude-sonnet-4-5", base_url="{PROXY}/v1", api_key=os.environ["LITELLM_API_KEY"]) -agent = create_agent(model=llm, tools=[], name="my_agent") -agent.invoke({"messages": [{"role": "user", "content": "What is LiteLLM?"}]})`, - }, - { - id: "langchain", - label: "LangChain", - logo: langchainLogo.src, - packages: "langchain langchain-openai openinference-instrumentation-langchain", - quickstart: `from langchain_openai import ChatOpenAI - -llm = ChatOpenAI(model="claude-sonnet-4-5", base_url="{PROXY}/v1", api_key=os.environ["LITELLM_API_KEY"]) -llm.invoke("What is LiteLLM?")`, - }, - { - id: "openai-agents", - label: "OpenAI Agents SDK", - logo: openaiAgentsLogo.src, - packages: "openai-agents openinference-instrumentation-openai-agents", - quickstart: `from agents import Agent, OpenAIChatCompletionsModel, Runner -from openai import AsyncOpenAI - -client = AsyncOpenAI(base_url="{PROXY}/v1", api_key=os.environ["LITELLM_API_KEY"]) -agent = Agent(name="my_agent", model=OpenAIChatCompletionsModel(model="claude-sonnet-4-5", openai_client=client)) -print(Runner.run_sync(agent, "What is LiteLLM?").final_output)`, - }, - { - id: "crewai", - label: "CrewAI", - logo: crewaiLogo.src, - packages: "crewai openinference-instrumentation-crewai", - quickstart: `from crewai import LLM, Agent, Crew, Task - -llm = LLM(model="openai/claude-sonnet-4-5", base_url="{PROXY}/v1", api_key=os.environ["LITELLM_API_KEY"]) -agent = Agent(role="Researcher", goal="Answer questions", backstory="", llm=llm) -task = Task(description="What is LiteLLM?", expected_output="A short answer", agent=agent) -Crew(agents=[agent], tasks=[task]).kickoff()`, - }, - { - id: "pydantic-ai", - label: "Pydantic AI", - logo: pydanticAiLogo.src, - packages: "pydantic-ai openinference-instrumentation-pydantic-ai", - quickstart: `from pydantic_ai import Agent -from pydantic_ai.models.openai import OpenAIModel -from pydantic_ai.providers.openai import OpenAIProvider - -provider = OpenAIProvider(base_url="{PROXY}/v1", api_key=os.environ["LITELLM_API_KEY"]) -agent = Agent(OpenAIModel("claude-sonnet-4-5", provider=provider), name="my_agent", instrument=True) -print(agent.run_sync("What is LiteLLM?").output)`, - }, - { - id: "llamaindex", - label: "LlamaIndex", - logo: llamaindexLogo.src, - packages: "llama-index llama-index-llms-openai-like openinference-instrumentation-llama-index", - quickstart: `from llama_index.llms.openai_like import OpenAILike - -llm = OpenAILike(model="claude-sonnet-4-5", api_base="{PROXY}/v1", api_key=os.environ["LITELLM_API_KEY"], is_chat_model=True) -print(llm.complete("What is LiteLLM?"))`, - }, - { - id: "otel", - label: "OpenTelemetry", - logo: otelLogo.src, - packages: "", - quickstart: `# Any OTEL SDK works. Use the gen_ai.* semantic conventions: -# gen_ai.operation.name, gen_ai.agent.name, gen_ai.response.id, gen_ai.usage.* -from opentelemetry import trace - -tracer = trace.get_tracer("my-agent") -attrs = {"gen_ai.operation.name": "invoke_agent", "gen_ai.agent.name": "my_agent"} -with tracer.start_as_current_span("my_agent", attributes=attrs): - ...`, - }, -]; - -const installPackages = (guide: Pick): string => - guide.typescript ? guide.packages : [OTEL_BASE_PACKAGES, guide.packages].filter(Boolean).join(" "); - -/** The endpoint is the proxy base URL: OTLP exporters append /v1/traces themselves. */ export const tracingEnvSnippet = (proxyUrl: string, tracingKey: string | null = null): string => [ ...(tracingKey ? [`export LITELLM_TRACING_KEY=${tracingKey}`] : []), - `export OTEL_EXPORTER_OTLP_ENDPOINT=${proxyUrl}`, - "export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf", - `export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer $${tracingKey ? "LITELLM_TRACING_KEY" : "LITELLM_API_KEY"}"`, - "export OTEL_SERVICE_NAME=my-agent", + `export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="${proxyUrl}/v1/traces"`, + `export OTEL_EXPORTER_OTLP_TRACES_HEADERS="Authorization=Bearer $${tracingKey ? "LITELLM_TRACING_KEY" : "LITELLM_API_KEY"}"`, + 'export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"', + 'export OTEL_METRICS_EXPORTER="none"', + 'export OTEL_LOGS_EXPORTER="none"', ].join("\n"); -export const codingAgentPrompt = ( - proxyUrl: string, - guide: Pick, -): string => +export const codingAgentPrompt = (proxyUrl: string, guide: FrameworkGuide, model: string): string => [ `Send this ${guide.label} project's OpenTelemetry traces to LiteLLM.`, - "", - `1. Add these dependencies: ${installPackages(guide)}`, - "2. Set these env vars wherever the project loads config (.env, settings, deployment manifests):", - ` OTEL_EXPORTER_OTLP_ENDPOINT=${proxyUrl}`, - " OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf", - ' OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer $LITELLM_API_KEY"', - " OTEL_SERVICE_NAME=", - guide.typescript - ? "3. Call registerOTel() from @vercel/otel at startup and pass experimental_telemetry: { isEnabled: true } to every AI SDK call." - : "3. Start the app through OTEL auto-instrumentation: opentelemetry-instrument .", - `4. Point every LLM client at LiteLLM: base_url=${proxyUrl}/v1, api key from LITELLM_API_KEY.`, - "5. Give each agent and subagent a name so runs are easy to read.", - "6. Run the agent once and confirm the run shows up in the LiteLLM UI under Lens > Traces.", - "", - "Never hardcode the key. Read it from LITELLM_API_KEY.", - ].join("\n"); + "Keep the existing model configuration, authentication, and application behavior. Never hardcode a key; read it from LITELLM_API_KEY.", + "Set the trace destination wherever this project loads environment variables:", + tracingEnvSnippet(proxyUrl), + guide.install ?? `Install and enable the ${guide.plugin?.label}: ${guide.plugin?.url}`, + guide.plugin?.instruction ?? + "Initialize OpenTelemetry before creating the agent. If the app already configures a tracer provider, keep it and point its exporter at the destination above instead.", + "Adapt this example to the existing application, replacing research_agent with the agent's name:", + frameworkSnippet(guide, proxyUrl, model), + guide.note ?? "", + "Run the agent once and confirm its named run appears in Lens > Traces.", + ] + .filter(Boolean) + .join("\n\n"); const shellQuote = (value: string): string => `'${value.replaceAll("'", "'\\''")}'`; @@ -222,10 +73,9 @@ export const maskSecret = (secret: string): string => secret.length > 10 ? `${secret.slice(0, 5)}${"•".repeat(16)}${secret.slice(-4)}` : "•".repeat(secret.length); export const otlpEndpoints = (proxyUrl: string): readonly (readonly [string, string, boolean])[] => [ - ["Traces endpoint (POST)", `${proxyUrl}/v1/traces`, true], - ["OTEL_EXPORTER_OTLP_ENDPOINT", proxyUrl, true], + ["Traces endpoint", `${proxyUrl}/v1/traces`, true], ["Auth header", "Authorization: Bearer ", true], - ["Protocol", "OTLP/HTTP, protobuf or JSON (gRPC not supported)", false], + ["Protocol", "OTLP/HTTP (protobuf or JSON)", false], ]; export const PROXY_CONFIG_SNIPPET = [ @@ -270,8 +120,8 @@ function CodeBlock({
             {display}
    @@ -322,7 +172,7 @@ function LineTabs({
     function Step({ title, children }: { title: React.ReactNode; children: React.ReactNode }) {
       return (
         
    -

    {title}

    +

    {title}

    {children}
    ); @@ -395,7 +245,7 @@ function SendTestTrace({ const busy = state.kind === "sending" || state.kind === "waiting"; return (
    - - {!connected && ( -

    - Waiting for your first trace -

    - )}
    {missingAfterCheck && (

    @@ -442,7 +290,7 @@ function TraceReceipt({ logs for export errors.

    )} - +
); } @@ -526,19 +374,19 @@ function EndpointValue({ value }: { value: string }) { ); } -function Endpoints({ proxyUrl }: { proxyUrl: string }) { +function Endpoints({ proxyUrl, children }: { proxyUrl: string; children?: React.ReactNode }) { return (
-

- - OpenTelemetry (OTEL) endpoints -

-

- Point any OpenTelemetry exporter here. The framework guides below set these for you. -

-
+
+

+ + Connection details +

+ {children} +
+
{otlpEndpoints(proxyUrl).map(([label, value, copyable]) => ( -
+
{label}
{copyable ? : {value}} @@ -574,7 +422,7 @@ interface ConnectAgentProps { function EnableTracing({ checked, checking, onCheck }: { checked: boolean; checking: boolean; onCheck: () => void }) { return ( <> - +

Set your ClickHouse URL, add this to config.yaml, then restart the proxy. Ask your proxy administrator if you don’t manage this deployment. @@ -594,14 +442,68 @@ function EnableTracing({ checked, checking, onCheck }: { checked: boolean; check Tracing is still unavailable. Check that the configuration was applied to this proxy and it has restarted.

)} - +
+ +
); } +function CodingAgentSetup({ proxyUrl, guide, model }: { proxyUrl: string; guide: FrameworkGuide; model: string }) { + const [codingAgent, setCodingAgent] = useState("Claude Code"); + const [copied, setCopied] = useState(null); + const command = codingAgentCommand(codingAgent, codingAgentPrompt(proxyUrl, guide, model)); + const copy = async () => { + if (await copyToClipboard(command)) { + setCopied(command); + window.setTimeout(() => setCopied(null), COPIED_RESET_MS); + } + }; + return ( +
+

+ Connect your project +

+
+
+ { + setCodingAgent(value); + setCopied(null); + }} + logos={CODING_AGENT_LOGOS} + /> +
+
+

+ Run the setup command in your agent’s project. It uses your LITELLM_API_KEY + . +

+ +
+ View command +
+              {command}
+            
+
+
+
+
+ ); +} + function ConnectAgent({ accessToken, onOpenTrace, @@ -615,27 +517,20 @@ function ConnectAgent({ const proxyUrl = getProxyBaseUrl().replace(/\/$/, ""); const [framework, setFramework] = useState(FRAMEWORKS[0].id); const [installer, setInstaller] = useState("pip"); - const [codingAgent, setCodingAgent] = useState("Claude Code"); const [tracingKey, setTracingKey] = useState(null); const guide = FRAMEWORKS.find((f) => f.id === framework) ?? FRAMEWORKS[0]; - const packages = installPackages(guide); - const install = guide.typescript ? `npm install ${packages}` : PY_INSTALL[installer](packages); - const quickstart = guide.quickstart.replace("{PROXY}", proxyUrl); + const install = guide.install?.startsWith("pip install ") + ? PY_INSTALL[installer](guide.install.slice("pip install ".length)) + : guide.install; + const model = EXAMPLE_MODEL; + const quickstart = frameworkSnippet(guide, proxyUrl, model, !!tracingKey); return ( <> - {!readOnly && ( - -

- Send a small sample run (an agent, an LLM call and a tool call) to confirm tracing works end to end. -

- -
- )} - - - + + {!readOnly && } +
-
- - {canMintTracingKey && !readOnly ? ( - - ) : ( -

- Use any LiteLLM virtual key you already have, or ask a proxy admin for one. -

- )} -
- - - Let Claude Code or - Codex connect it - - } - > -

- Run this in your agent’s project. It starts your coding agent with the setup task and reads the key from - LITELLM_API_KEY. -

- - } - /> -
- -
- Set up manually - - npm - ) : ( - - ) - } - /> + +
+ Set up manually + + {canMintTracingKey && !readOnly ? ( + + ) : ( +

+ Use any LiteLLM virtual key you already have, or ask a proxy admin for one. +

+ )}
+ {install && ( + + npm + ) : ( + + ) + } + /> + + )} + {guide.plugin && ( + +

+ Install and enable the{" "} + + {guide.plugin.label} + + . {guide.plugin.instruction} +

+
+ )} +

+ {tracingKey ? ( + <> + Tracing uses the key above. Set LITELLM_API_KEY to a key with model access. + + ) : ( + <> + Set LITELLM_API_KEY to your LiteLLM key. + + )} +

Shell} + wrap />
-

- Replace the example model with a model configured on your proxy. +

+ {guide.existingModel ? "Keep your existing model settings. " : "Save and run this example. "} + Replace research_agent with your agent’s name. + {!guide.existingModel && " Use a model configured on this proxy."}

- {guide.typescript ? "my_agent.ts" : "my_agent.py"}} - /> -
- Shell} /> -
+ {guide.fileName}} wrap /> + {guide.note &&

{guide.note}

} + {!guide.existingModel && ( +
+ Shell} + /> +
+ )} +
+ View in docs
- - {!connected && ( -
-

- Example run (sample data, not your runs) -

- -
- )} ); } @@ -755,6 +654,7 @@ export function TracingSetupCard({ checking = false, readOnly = false, canMintTracingKey = false, + onDemo, }: { detail: string | null; accessToken: string; @@ -764,6 +664,7 @@ export function TracingSetupCard({ checking?: boolean; readOnly?: boolean; canMintTracingKey?: boolean; + onDemo?: () => void; }) { const [checked, setChecked] = useState(false); const enabled = detail === null; @@ -773,9 +674,18 @@ export function TracingSetupCard({ }; return ( -
-
+
+ {onDemo && } +

{setupTitle(enabled, connected)}

+ + {enabled ? ( + + ) : ( + -

- {enabled && } - {enabled ? "Tracing enabled" : "Tracing is not enabled"} -

{enabled ? ( = { + MODEL: JSON.stringify(model), + OPENAI_MODEL: JSON.stringify(`openai/${model}`), + BASE_URL: JSON.stringify(`${proxyUrl}/v1`), + PROXY_URL: JSON.stringify(proxyUrl), + TRACE_URL: `${proxyUrl}/v1/traces`, + }; + const code = guide.quickstart.replace( + /\{(MODEL|OPENAI_MODEL|BASE_URL|PROXY_URL|TRACE_URL)\}/g, + (_, name: string) => values[name], + ); + return tracingKey && guide.existingModel ? code.replaceAll("${LITELLM_API_KEY}", "${LITELLM_TRACING_KEY}") : code; +} diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/useAgentTraces.ts b/ui/litellm-dashboard/src/components/view_logs/TraceView/useAgentTraces.ts index a50d57409de..46f4b4bed71 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/useAgentTraces.ts +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/useAgentTraces.ts @@ -1,3 +1,4 @@ +import { useLensDemo } from "@/components/lens/LensDemoContext"; import { useInfiniteQuery, useQuery, type UseQueryOptions } from "@tanstack/react-query"; import moment from "moment"; import { useMemo } from "react"; @@ -65,6 +66,7 @@ export function useAgentTraces({ isLiveTail, enabled, }: UseAgentTracesOptions): AgentTracesResult { + const demo = useLensDemo(); const fetchPage = (pageParam: unknown): Promise => { const nowMs = Date.now(); const listOptions: Parameters[0] = { @@ -73,7 +75,11 @@ export function useAgentTraces({ endMs: isCustomDate ? moment(endTime).valueOf() : nowMs, cursor: pageParam as string | null, }; - return agentTraceListCall(listOptions); + return demo + ? demo.client.get("/v1/traces", { + query: { start_ms: listOptions.startMs, end_ms: listOptions.endMs, cursor: listOptions.cursor }, + }) + : agentTraceListCall(listOptions); }; const queryOptions: Parameters>[0] = { queryKey: ["agentTraces", accessToken, startTime, endTime, isCustomDate], @@ -106,9 +112,11 @@ export function useAgentTraces({ } export function useTraceAvailability(accessToken: string, enabled: boolean) { + const demo = useLensDemo(); + const client = demo?.client ?? apiClient; const options: UseQueryOptions = { queryKey: ["trace-availability", accessToken], - queryFn: () => apiClient.get("/v1/traces", { accessToken, query: { start_ms: 0 } }), + queryFn: () => client.get("/v1/traces", { accessToken, query: { start_ms: 0 } }), select: (page: TracePage) => page.data.length > 0, enabled, retry: false, From 9e31afbc6db72dca0d024c99ed3845949cad6a9a Mon Sep 17 00:00:00 2001 From: "berriai-litellm-provider-info-sync[bot]" <328147090+berriai-litellm-provider-info-sync[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2026 17:31:44 -0700 Subject: [PATCH 10/61] chore(openrouter): sync prices, limits and deprecation dates from the models API (#44287) Price-Sync: litellm-providers Co-authored-by: berriai-litellm-provider-info-sync[bot] <328147090+berriai-litellm-provider-info-sync[bot]@users.noreply.github.com> --- ...odel_prices_and_context_window_backup.json | 128 +++++++++++------- model_prices_and_context_window.json | 128 +++++++++++------- 2 files changed, 152 insertions(+), 104 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 12e4760ed3a..2b613ff1aea 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -42258,14 +42258,14 @@ "supports_web_search": false }, "openrouter/deepseek/deepseek-v4.1-flash": { - "cache_read_input_token_cost": 1e-08, - "input_cost_per_token": 3e-08, + "cache_read_input_token_cost": 6e-09, + "input_cost_per_token": 3e-07, "litellm_provider": "openrouter", "max_input_tokens": 1048576, "max_output_tokens": 943718, "max_tokens": 943718, "mode": "chat", - "output_cost_per_token": 5e-07, + "output_cost_per_token": 1.2e-06, "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, "supports_function_calling": true, @@ -43604,19 +43604,19 @@ "supports_web_search": false }, "openrouter/qwen/qwen3.5-35b-a3b": { - "input_cost_per_token": 1.625e-07, + "input_cost_per_token": 1.5e-07, "litellm_provider": "openrouter", "max_input_tokens": 262144, - "max_output_tokens": 16384, - "max_tokens": 16384, + "max_output_tokens": 235929, + "max_tokens": 235929, "mode": "chat", - "output_cost_per_token": 1.3e-06, + "output_cost_per_token": 1e-06, "source": "https://openrouter.ai/api/v1/models", "supports_function_calling": true, "supports_reasoning": true, "supports_tool_choice": true, "supports_vision": true, - "cache_read_input_token_cost": 1.5625e-07, + "cache_read_input_token_cost": 5e-08, "supports_audio_input": false, "supports_pdf_input": false, "supports_prompt_caching": true, @@ -43924,14 +43924,14 @@ }, "openrouter/z-ai/glm-5.1": { "cache_creation_input_token_cost": 0.0, - "cache_read_input_token_cost": 1.7914e-07, - "input_cost_per_token": 9.646e-07, + "cache_read_input_token_cost": 2.6e-07, + "input_cost_per_token": 1.4e-06, "litellm_provider": "openrouter", "max_input_tokens": 204800, "max_output_tokens": 131072, "max_tokens": 131072, "mode": "chat", - "output_cost_per_token": 3.0316e-06, + "output_cost_per_token": 4.4e-06, "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, "supports_function_calling": true, @@ -67419,13 +67419,13 @@ "supports_web_search": false }, "openrouter/z-ai/glm-5.3": { - "input_cost_per_token": 2.219e-07, - "output_cost_per_token": 3.39e-06, - "cache_read_input_token_cost": 1.775e-07, + "input_cost_per_token": 1.4e-06, + "output_cost_per_token": 4.4e-06, + "cache_read_input_token_cost": 1.4e-07, "litellm_provider": "openrouter", "max_input_tokens": 1048576, - "max_output_tokens": 943718, - "max_tokens": 943718, + "max_output_tokens": 131072, + "max_tokens": 131072, "mode": "chat", "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, @@ -67556,8 +67556,8 @@ "supports_prompt_caching": true }, "openrouter/deepseek/deepseek-v4-flash-0731": { - "cache_read_input_token_cost": 1.08e-08, - "input_cost_per_token": 1.08e-08, + "cache_read_input_token_cost": 5.1e-09, + "input_cost_per_token": 5.1e-09, "litellm_provider": "openrouter", "max_input_tokens": 1048576, "max_output_tokens": 943718, @@ -67609,6 +67609,7 @@ "input_cost_per_token": 9e-08, "output_cost_per_token": 1.8e-07, "cache_read_input_token_cost": 9e-09, + "deprecation_date": "2026-10-31", "litellm_provider": "openrouter", "max_input_tokens": 1048576, "max_output_tokens": 131072, @@ -67626,6 +67627,7 @@ "supports_web_search": false }, "openrouter/poolside/laguna-s-2.1:free": { + "deprecation_date": "2026-10-31", "input_cost_per_token": 0.0, "output_cost_per_token": 0.0, "litellm_provider": "openrouter", @@ -67645,14 +67647,14 @@ "supports_web_search": false }, "openrouter/moonshotai/kimi-k3": { - "cache_read_input_token_cost": 4.357e-07, - "input_cost_per_token": 4.357e-07, + "cache_read_input_token_cost": 2.7e-07, + "input_cost_per_token": 2.7e-06, "litellm_provider": "openrouter", "max_input_tokens": 1048576, "max_output_tokens": 943718, "max_tokens": 943718, "mode": "chat", - "output_cost_per_token": 1e-05, + "output_cost_per_token": 1.35e-05, "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, "supports_function_calling": true, @@ -67668,6 +67670,7 @@ "input_cost_per_token": 6e-08, "output_cost_per_token": 1.2e-07, "cache_read_input_token_cost": 3e-08, + "deprecation_date": "2026-10-31", "litellm_provider": "openrouter", "max_input_tokens": 262144, "max_output_tokens": 32768, @@ -67685,6 +67688,7 @@ "supports_web_search": false }, "openrouter/poolside/laguna-xs-2.1:free": { + "deprecation_date": "2026-10-31", "input_cost_per_token": 0.0, "output_cost_per_token": 0.0, "litellm_provider": "openrouter", @@ -67867,13 +67871,13 @@ "supports_web_search": false }, "openrouter/nvidia/nemotron-3-ultra-550b-a55b": { - "input_cost_per_token": 6e-07, - "output_cost_per_token": 2.4e-06, - "cache_read_input_token_cost": 1.2e-07, + "input_cost_per_token": 5e-07, + "output_cost_per_token": 2.2e-06, + "cache_read_input_token_cost": 1e-07, "litellm_provider": "openrouter", "max_input_tokens": 262144, - "max_output_tokens": 182520, - "max_tokens": 182520, + "max_output_tokens": 16384, + "max_tokens": 16384, "mode": "chat", "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, @@ -68131,14 +68135,14 @@ "supports_web_search": true }, "openrouter/deepseek/deepseek-v4-flash": { - "cache_read_input_token_cost": 8.372e-09, - "input_cost_per_token": 4.186e-08, + "cache_read_input_token_cost": 5.6e-09, + "input_cost_per_token": 2.8e-08, "litellm_provider": "openrouter", "max_input_tokens": 1048576, - "max_output_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 384000, + "max_tokens": 384000, "mode": "chat", - "output_cost_per_token": 8.372e-08, + "output_cost_per_token": 5.6e-08, "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, "supports_function_calling": true, @@ -68172,14 +68176,14 @@ "supports_web_search": false }, "openrouter/google/gemma-4-26b-a4b-it": { - "cache_read_input_token_cost": 4.25e-08, - "input_cost_per_token": 7.65e-08, + "cache_read_input_token_cost": 3.75e-08, + "input_cost_per_token": 6.75e-08, "litellm_provider": "openrouter", "max_input_tokens": 262144, "max_output_tokens": 235929, "max_tokens": 235929, "mode": "chat", - "output_cost_per_token": 2.55e-07, + "output_cost_per_token": 2.25e-07, "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, "supports_function_calling": true, @@ -68958,11 +68962,11 @@ "openrouter/deepseek/deepseek-v3.1-terminus": { "cache_read_input_token_cost": 1.35e-07, "deprecation_date": "2026-09-28", - "input_cost_per_token": 3e-07, + "input_cost_per_token": 2.7e-07, "litellm_provider": "openrouter", "max_input_tokens": 163840, - "max_output_tokens": 65536, - "max_tokens": 65536, + "max_output_tokens": 147456, + "max_tokens": 147456, "mode": "chat", "output_cost_per_token": 1e-06, "source": "https://openrouter.ai/api/v1/models", @@ -69006,12 +69010,13 @@ "supports_web_search": false }, "openrouter/qwen/qwen3-next-80b-a3b-thinking": { + "deprecation_date": "2026-10-09", "input_cost_per_token": 1.5e-07, "output_cost_per_token": 1.2e-06, "litellm_provider": "openrouter", "max_input_tokens": 262144, - "max_output_tokens": 235929, - "max_tokens": 235929, + "max_output_tokens": 32768, + "max_tokens": 32768, "mode": "chat", "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, @@ -69187,13 +69192,13 @@ "supports_web_search": false }, "openrouter/qwen/qwen3-30b-a3b-instruct-2507": { - "input_cost_per_token": 4.815e-08, + "input_cost_per_token": 1e-07, "litellm_provider": "openrouter", "max_input_tokens": 262144, - "max_output_tokens": 32000, - "max_tokens": 32000, + "max_output_tokens": 235929, + "max_tokens": 235929, "mode": "chat", - "output_cost_per_token": 1.9305e-07, + "output_cost_per_token": 3e-07, "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, "supports_function_calling": true, @@ -74319,14 +74324,14 @@ "supports_web_search": false }, "openrouter/inclusionai/ling-3.0-flash-fin": { - "cache_read_input_token_cost": 1.2e-08, - "input_cost_per_token": 6e-08, + "cache_read_input_token_cost": 8.4e-09, + "input_cost_per_token": 4.2e-08, "litellm_provider": "openrouter", "max_input_tokens": 262144, - "max_output_tokens": 235929, - "max_tokens": 235929, + "max_output_tokens": 32768, + "max_tokens": 32768, "mode": "chat", - "output_cost_per_token": 1.8e-07, + "output_cost_per_token": 1.232e-07, "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, "supports_function_calling": true, @@ -76406,12 +76411,12 @@ "supports_web_search": false }, "openrouter/thinkingmachines/inkling": { - "cache_read_input_token_cost": 1.7e-07, - "input_cost_per_token": 1e-06, + "cache_read_input_token_cost": 1.6e-07, + "input_cost_per_token": 9.5e-07, "litellm_provider": "openrouter", "max_input_tokens": 524288, - "max_output_tokens": 471859, - "max_tokens": 471859, + "max_output_tokens": 262144, + "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4.05e-06, "source": "https://openrouter.ai/api/v1/models", @@ -79675,5 +79680,24 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true + }, + "openrouter/inclusionai/ling-3.1-flash": { + "input_cost_per_token": 0.0, + "litellm_provider": "openrouter", + "max_input_tokens": 262144, + "max_output_tokens": 32768, + "max_tokens": 32768, + "mode": "chat", + "output_cost_per_token": 0.0, + "source": "https://openrouter.ai/api/v1/models", + "supports_audio_input": false, + "supports_function_calling": true, + "supports_pdf_input": false, + "supports_prompt_caching": false, + "supports_reasoning": true, + "supports_response_schema": false, + "supports_tool_choice": true, + "supports_vision": false, + "supports_web_search": false } } diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 12e4760ed3a..2b613ff1aea 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -42258,14 +42258,14 @@ "supports_web_search": false }, "openrouter/deepseek/deepseek-v4.1-flash": { - "cache_read_input_token_cost": 1e-08, - "input_cost_per_token": 3e-08, + "cache_read_input_token_cost": 6e-09, + "input_cost_per_token": 3e-07, "litellm_provider": "openrouter", "max_input_tokens": 1048576, "max_output_tokens": 943718, "max_tokens": 943718, "mode": "chat", - "output_cost_per_token": 5e-07, + "output_cost_per_token": 1.2e-06, "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, "supports_function_calling": true, @@ -43604,19 +43604,19 @@ "supports_web_search": false }, "openrouter/qwen/qwen3.5-35b-a3b": { - "input_cost_per_token": 1.625e-07, + "input_cost_per_token": 1.5e-07, "litellm_provider": "openrouter", "max_input_tokens": 262144, - "max_output_tokens": 16384, - "max_tokens": 16384, + "max_output_tokens": 235929, + "max_tokens": 235929, "mode": "chat", - "output_cost_per_token": 1.3e-06, + "output_cost_per_token": 1e-06, "source": "https://openrouter.ai/api/v1/models", "supports_function_calling": true, "supports_reasoning": true, "supports_tool_choice": true, "supports_vision": true, - "cache_read_input_token_cost": 1.5625e-07, + "cache_read_input_token_cost": 5e-08, "supports_audio_input": false, "supports_pdf_input": false, "supports_prompt_caching": true, @@ -43924,14 +43924,14 @@ }, "openrouter/z-ai/glm-5.1": { "cache_creation_input_token_cost": 0.0, - "cache_read_input_token_cost": 1.7914e-07, - "input_cost_per_token": 9.646e-07, + "cache_read_input_token_cost": 2.6e-07, + "input_cost_per_token": 1.4e-06, "litellm_provider": "openrouter", "max_input_tokens": 204800, "max_output_tokens": 131072, "max_tokens": 131072, "mode": "chat", - "output_cost_per_token": 3.0316e-06, + "output_cost_per_token": 4.4e-06, "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, "supports_function_calling": true, @@ -67419,13 +67419,13 @@ "supports_web_search": false }, "openrouter/z-ai/glm-5.3": { - "input_cost_per_token": 2.219e-07, - "output_cost_per_token": 3.39e-06, - "cache_read_input_token_cost": 1.775e-07, + "input_cost_per_token": 1.4e-06, + "output_cost_per_token": 4.4e-06, + "cache_read_input_token_cost": 1.4e-07, "litellm_provider": "openrouter", "max_input_tokens": 1048576, - "max_output_tokens": 943718, - "max_tokens": 943718, + "max_output_tokens": 131072, + "max_tokens": 131072, "mode": "chat", "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, @@ -67556,8 +67556,8 @@ "supports_prompt_caching": true }, "openrouter/deepseek/deepseek-v4-flash-0731": { - "cache_read_input_token_cost": 1.08e-08, - "input_cost_per_token": 1.08e-08, + "cache_read_input_token_cost": 5.1e-09, + "input_cost_per_token": 5.1e-09, "litellm_provider": "openrouter", "max_input_tokens": 1048576, "max_output_tokens": 943718, @@ -67609,6 +67609,7 @@ "input_cost_per_token": 9e-08, "output_cost_per_token": 1.8e-07, "cache_read_input_token_cost": 9e-09, + "deprecation_date": "2026-10-31", "litellm_provider": "openrouter", "max_input_tokens": 1048576, "max_output_tokens": 131072, @@ -67626,6 +67627,7 @@ "supports_web_search": false }, "openrouter/poolside/laguna-s-2.1:free": { + "deprecation_date": "2026-10-31", "input_cost_per_token": 0.0, "output_cost_per_token": 0.0, "litellm_provider": "openrouter", @@ -67645,14 +67647,14 @@ "supports_web_search": false }, "openrouter/moonshotai/kimi-k3": { - "cache_read_input_token_cost": 4.357e-07, - "input_cost_per_token": 4.357e-07, + "cache_read_input_token_cost": 2.7e-07, + "input_cost_per_token": 2.7e-06, "litellm_provider": "openrouter", "max_input_tokens": 1048576, "max_output_tokens": 943718, "max_tokens": 943718, "mode": "chat", - "output_cost_per_token": 1e-05, + "output_cost_per_token": 1.35e-05, "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, "supports_function_calling": true, @@ -67668,6 +67670,7 @@ "input_cost_per_token": 6e-08, "output_cost_per_token": 1.2e-07, "cache_read_input_token_cost": 3e-08, + "deprecation_date": "2026-10-31", "litellm_provider": "openrouter", "max_input_tokens": 262144, "max_output_tokens": 32768, @@ -67685,6 +67688,7 @@ "supports_web_search": false }, "openrouter/poolside/laguna-xs-2.1:free": { + "deprecation_date": "2026-10-31", "input_cost_per_token": 0.0, "output_cost_per_token": 0.0, "litellm_provider": "openrouter", @@ -67867,13 +67871,13 @@ "supports_web_search": false }, "openrouter/nvidia/nemotron-3-ultra-550b-a55b": { - "input_cost_per_token": 6e-07, - "output_cost_per_token": 2.4e-06, - "cache_read_input_token_cost": 1.2e-07, + "input_cost_per_token": 5e-07, + "output_cost_per_token": 2.2e-06, + "cache_read_input_token_cost": 1e-07, "litellm_provider": "openrouter", "max_input_tokens": 262144, - "max_output_tokens": 182520, - "max_tokens": 182520, + "max_output_tokens": 16384, + "max_tokens": 16384, "mode": "chat", "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, @@ -68131,14 +68135,14 @@ "supports_web_search": true }, "openrouter/deepseek/deepseek-v4-flash": { - "cache_read_input_token_cost": 8.372e-09, - "input_cost_per_token": 4.186e-08, + "cache_read_input_token_cost": 5.6e-09, + "input_cost_per_token": 2.8e-08, "litellm_provider": "openrouter", "max_input_tokens": 1048576, - "max_output_tokens": 131072, - "max_tokens": 131072, + "max_output_tokens": 384000, + "max_tokens": 384000, "mode": "chat", - "output_cost_per_token": 8.372e-08, + "output_cost_per_token": 5.6e-08, "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, "supports_function_calling": true, @@ -68172,14 +68176,14 @@ "supports_web_search": false }, "openrouter/google/gemma-4-26b-a4b-it": { - "cache_read_input_token_cost": 4.25e-08, - "input_cost_per_token": 7.65e-08, + "cache_read_input_token_cost": 3.75e-08, + "input_cost_per_token": 6.75e-08, "litellm_provider": "openrouter", "max_input_tokens": 262144, "max_output_tokens": 235929, "max_tokens": 235929, "mode": "chat", - "output_cost_per_token": 2.55e-07, + "output_cost_per_token": 2.25e-07, "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, "supports_function_calling": true, @@ -68958,11 +68962,11 @@ "openrouter/deepseek/deepseek-v3.1-terminus": { "cache_read_input_token_cost": 1.35e-07, "deprecation_date": "2026-09-28", - "input_cost_per_token": 3e-07, + "input_cost_per_token": 2.7e-07, "litellm_provider": "openrouter", "max_input_tokens": 163840, - "max_output_tokens": 65536, - "max_tokens": 65536, + "max_output_tokens": 147456, + "max_tokens": 147456, "mode": "chat", "output_cost_per_token": 1e-06, "source": "https://openrouter.ai/api/v1/models", @@ -69006,12 +69010,13 @@ "supports_web_search": false }, "openrouter/qwen/qwen3-next-80b-a3b-thinking": { + "deprecation_date": "2026-10-09", "input_cost_per_token": 1.5e-07, "output_cost_per_token": 1.2e-06, "litellm_provider": "openrouter", "max_input_tokens": 262144, - "max_output_tokens": 235929, - "max_tokens": 235929, + "max_output_tokens": 32768, + "max_tokens": 32768, "mode": "chat", "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, @@ -69187,13 +69192,13 @@ "supports_web_search": false }, "openrouter/qwen/qwen3-30b-a3b-instruct-2507": { - "input_cost_per_token": 4.815e-08, + "input_cost_per_token": 1e-07, "litellm_provider": "openrouter", "max_input_tokens": 262144, - "max_output_tokens": 32000, - "max_tokens": 32000, + "max_output_tokens": 235929, + "max_tokens": 235929, "mode": "chat", - "output_cost_per_token": 1.9305e-07, + "output_cost_per_token": 3e-07, "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, "supports_function_calling": true, @@ -74319,14 +74324,14 @@ "supports_web_search": false }, "openrouter/inclusionai/ling-3.0-flash-fin": { - "cache_read_input_token_cost": 1.2e-08, - "input_cost_per_token": 6e-08, + "cache_read_input_token_cost": 8.4e-09, + "input_cost_per_token": 4.2e-08, "litellm_provider": "openrouter", "max_input_tokens": 262144, - "max_output_tokens": 235929, - "max_tokens": 235929, + "max_output_tokens": 32768, + "max_tokens": 32768, "mode": "chat", - "output_cost_per_token": 1.8e-07, + "output_cost_per_token": 1.232e-07, "source": "https://openrouter.ai/api/v1/models", "supports_audio_input": false, "supports_function_calling": true, @@ -76406,12 +76411,12 @@ "supports_web_search": false }, "openrouter/thinkingmachines/inkling": { - "cache_read_input_token_cost": 1.7e-07, - "input_cost_per_token": 1e-06, + "cache_read_input_token_cost": 1.6e-07, + "input_cost_per_token": 9.5e-07, "litellm_provider": "openrouter", "max_input_tokens": 524288, - "max_output_tokens": 471859, - "max_tokens": 471859, + "max_output_tokens": 262144, + "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 4.05e-06, "source": "https://openrouter.ai/api/v1/models", @@ -79675,5 +79680,24 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true + }, + "openrouter/inclusionai/ling-3.1-flash": { + "input_cost_per_token": 0.0, + "litellm_provider": "openrouter", + "max_input_tokens": 262144, + "max_output_tokens": 32768, + "max_tokens": 32768, + "mode": "chat", + "output_cost_per_token": 0.0, + "source": "https://openrouter.ai/api/v1/models", + "supports_audio_input": false, + "supports_function_calling": true, + "supports_pdf_input": false, + "supports_prompt_caching": false, + "supports_reasoning": true, + "supports_response_schema": false, + "supports_tool_choice": true, + "supports_vision": false, + "supports_web_search": false } } From dd86ca517584a6662859436aa3ecb0b13c276a0f Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2026 18:01:27 -0700 Subject: [PATCH 11/61] refactor(traces): type the ClickHouse query help response (#44285) * refactor(traces): type the ClickHouse query help response Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test(traces): include agent names and frameworks in named contract round trips Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test(traces): cover native query help validation in the storage adapter Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Yujong Lee Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../crates/storage-clickhouse/src/lib.rs | 2 +- .../crates/storage-clickhouse/src/read.rs | 22 +- .../crates/traces-clickhouse/src/lib.rs | 2 +- .../crates/traces-clickhouse/src/query.rs | 292 +++++++++++++----- .../traces-clickhouse/src/query/guide.rs | 5 +- .../traces-clickhouse/src/query_access.rs | 40 ++- .../crates/traces-clickhouse/src/table.rs | 10 +- .../templates/query_help.jinja | 20 +- .../traces-clickhouse/tests/migrations.rs | 8 +- .../traces-clickhouse/tests/query_access.rs | 2 +- .../crates/traces/tests/query/named.rs | 4 +- litellm/rust_bridge/_native.pyi | 2 +- litellm/rust_bridge/trace_query_responses.py | 35 ++- litellm/rust_bridge/traces.py | 13 +- tests/unit/proxy/test_tracing_endpoints.py | 69 ++++- 15 files changed, 392 insertions(+), 134 deletions(-) diff --git a/litellm-rust/crates/storage-clickhouse/src/lib.rs b/litellm-rust/crates/storage-clickhouse/src/lib.rs index 7cba0a11653..7ab2aa9bc0a 100644 --- a/litellm-rust/crates/storage-clickhouse/src/lib.rs +++ b/litellm-rust/crates/storage-clickhouse/src/lib.rs @@ -4,7 +4,7 @@ mod read; pub use error::Error; pub use insert::{insert_compressed_rows, insert_encoded_rows}; -pub use read::{Parameter, Query, execute_read, fetch, fetch_json}; +pub use read::{Parameter, Query, READ_LIMITS, ReadLimits, execute_read, fetch, fetch_json}; use url::Url; #[derive(Clone)] diff --git a/litellm-rust/crates/storage-clickhouse/src/read.rs b/litellm-rust/crates/storage-clickhouse/src/read.rs index 0dae94109bc..59d5b0de558 100644 --- a/litellm-rust/crates/storage-clickhouse/src/read.rs +++ b/litellm-rust/crates/storage-clickhouse/src/read.rs @@ -5,7 +5,18 @@ use serde::{Deserialize, Serialize, de::DeserializeOwned}; use crate::{Connection, Error}; -const MAX_RESPONSE_BYTES: usize = 4 * 1024 * 1024; +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct ReadLimits { + pub result_rows: u64, + pub response_bytes: usize, + pub execution_seconds: u64, +} + +pub const READ_LIMITS: ReadLimits = ReadLimits { + result_rows: 1000, + response_bytes: 4 * 1024 * 1024, + execution_seconds: 10, +}; #[derive(Debug, Deserialize, Serialize)] #[serde(untagged)] @@ -78,9 +89,12 @@ pub async fn execute_read( .clear() .extend_pairs(existing_pairs) .append_pair("readonly", "1") - .append_pair("max_result_rows", "1000") + .append_pair("max_result_rows", &READ_LIMITS.result_rows.to_string()) .append_pair("result_overflow_mode", "throw") - .append_pair("max_execution_time", "10") + .append_pair( + "max_execution_time", + &READ_LIMITS.execution_seconds.to_string(), + ) .append_pair("wait_end_of_query", "1") .append_pair("default_format", "JSON"); @@ -101,7 +115,7 @@ pub async fn execute_read( let mut body = Vec::new(); while let Some(chunk) = response.chunk().await.map_err(|_| Error::Transport)? { - if body.len() + chunk.len() > MAX_RESPONSE_BYTES { + if body.len() + chunk.len() > READ_LIMITS.response_bytes { return Err(Error::ResponseTooLarge); } body.extend_from_slice(&chunk); diff --git a/litellm-rust/crates/traces-clickhouse/src/lib.rs b/litellm-rust/crates/traces-clickhouse/src/lib.rs index cd5877e4993..ba2f776b51a 100644 --- a/litellm-rust/crates/traces-clickhouse/src/lib.rs +++ b/litellm-rust/crates/traces-clickhouse/src/lib.rs @@ -12,7 +12,7 @@ pub use error::Error; pub use insert::{InsertRow, InsertTable, encode_rows, insert_rows, insert_shared_rows}; pub use litellm_storage_clickhouse::{Connection, Parameter}; pub use litellm_traces::{QueryScope, ReadQuery}; -pub use query::{execute_read, query_help, query_sql}; +pub use query::{QueryHelp, execute_read, query_help, query_sql}; pub use query_access::QueryReaders; pub use schema::{ NORMALIZED_FIELD_DEFINITIONS, NormalizedFieldDefinition, ensure_schema, schema_statements, diff --git a/litellm-rust/crates/traces-clickhouse/src/query.rs b/litellm-rust/crates/traces-clickhouse/src/query.rs index c862cebf556..459f0f95fe7 100644 --- a/litellm-rust/crates/traces-clickhouse/src/query.rs +++ b/litellm-rust/crates/traces-clickhouse/src/query.rs @@ -6,11 +6,14 @@ use futures_util::{ stream::{self, TryStreamExt}, }; use litellm_http::Client; -use serde::{Deserialize, Serialize}; -use serde_json::{Value, json}; +use serde::{Deserialize, Serialize, Serializer}; +use serde_json::Value; use strum::IntoEnumIterator; -use super::{Connection, Error, NORMALIZED_FIELD_DEFINITIONS, Parameter}; +use super::{ + Connection, Error, NORMALIZED_FIELD_DEFINITIONS, NormalizedFieldDefinition, Parameter, + query_access::READER_LIMITS, +}; mod guide; pub mod lens; @@ -48,10 +51,42 @@ enum PathPart { Index(usize), } +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, strum::Display)] +#[serde(rename_all = "lowercase")] +#[strum(serialize_all = "lowercase")] +enum JsonKind { + Array, + Boolean, + Integer, + Null, + Number, + Object, + String, +} + +impl JsonKind { + fn of(value: &Value) -> Self { + match value { + Value::Null => Self::Null, + Value::Bool(_) => Self::Boolean, + Value::Number(number) if number.is_i64() || number.is_u64() => Self::Integer, + Value::Number(_) => Self::Number, + Value::String(_) => Self::String, + Value::Array(_) => Self::Array, + Value::Object(_) => Self::Object, + } + } +} + +#[derive(Clone, Copy, Debug, Serialize, strum::Display)] +enum MapValueType { + String, +} + #[derive(Serialize)] struct MetadataField { path: Vec, - types: BTreeSet<&'static str>, + types: BTreeSet, expression: String, } @@ -66,42 +101,150 @@ struct ColumnSchema { #[derive(Serialize)] struct TableSchema { - name: &'static str, + name: TraceTable, columns: Vec, } +trait Unobserved { + fn unobserved() -> Self; +} + +enum Discovery { + Observed(T), + Unavailable(String), +} + +impl Serialize for Discovery { + fn serialize(&self, serializer: S) -> Result { + #[derive(Serialize)] + struct Unavailable<'a, T> { + #[serde(flatten)] + sample: T, + error: &'a str, + } + match self { + Self::Observed(sample) => sample.serialize(serializer), + Self::Unavailable(error) => Unavailable { + sample: T::unobserved(), + error, + } + .serialize(serializer), + } + } +} + #[derive(Serialize)] -struct MetadataCatalog { - table: &'static str, - column: &'static str, +struct MetadataSample { fields: Vec, sampled_rows: usize, invalid_json_rows: usize, truncated: bool, +} + +impl Unobserved for MetadataSample { + fn unobserved() -> Self { + Self { + fields: Vec::new(), + sampled_rows: 0, + invalid_json_rows: 0, + truncated: true, + } + } +} + +#[derive(Serialize)] +struct MetadataCatalog { + table: TraceTable, + column: &'static str, + #[serde(flatten)] + discovery: Discovery, sample_sql: &'static str, scope: &'static str, - #[serde(skip_serializing_if = "Option::is_none")] - error: Option, } #[derive(Serialize)] struct AttributeField { key: String, #[serde(rename = "type")] - kind: &'static str, + kind: MapValueType, expression: String, } #[derive(Serialize)] -struct AttributeCatalog { - table: &'static str, - column: &'static str, +struct AttributeSample { fields: Vec, truncated: bool, +} + +impl Unobserved for AttributeSample { + fn unobserved() -> Self { + Self { + fields: Vec::new(), + truncated: true, + } + } +} + +#[derive(Serialize)] +struct AttributeCatalog { + table: TraceTable, + column: &'static str, + #[serde(flatten)] + discovery: Discovery, discovery_sql: String, scope: &'static str, - #[serde(skip_serializing_if = "Option::is_none")] - error: Option, +} + +#[derive(Serialize)] +struct NormalizedField { + table: TraceTable, + name: &'static str, + column: &'static str, + #[serde(rename = "type")] + kind: &'static str, + meaning: &'static str, +} + +impl From<&NormalizedFieldDefinition> for NormalizedField { + fn from(field: &NormalizedFieldDefinition) -> Self { + Self { + table: TraceTable::OtelTraces, + name: field.name, + column: field.clickhouse_column, + kind: field.clickhouse_type, + meaning: field.meaning, + } + } +} + +#[derive(Serialize)] +struct Relationship { + left: &'static str, + right: &'static str, + additional_predicates: &'static str, + meaning: &'static str, +} + +const RELATIONSHIPS: [Relationship; 1] = [Relationship { + left: "otel_traces.LiteLLMRequestId", + right: "spend_logs.response_id", + additional_predicates: "otel_traces.TeamId = spend_logs.team_id AND (otel_traces.TeamId != '' OR (otel_traces.UserId != '' AND otel_traces.UserId = spend_logs.user) OR (otel_traces.ApiKeyHash != '' AND otel_traces.ApiKeyHash = spend_logs.api_key))", + meaning: "The normalized ID is the response ID, not request_id. Cached requests can share response_id; joins may return multiple spend rows", +}]; + +#[derive(Serialize)] +pub struct QueryHelp { + dialect: &'static str, + access: &'static str, + response: &'static str, + tables: Vec, + normalized_fields: Vec, + metadata: MetadataCatalog, + attributes: Vec, + relationships: &'static [Relationship], + examples: [guide::Example; 5], + gotchas: [String; 11], + guide: String, } pub async fn execute_read( @@ -153,22 +296,16 @@ fn metadata_expression(path: &[PathPart]) -> String { fn discover( value: &Value, path: Vec, - fields: &mut BTreeMap, BTreeSet<&'static str>>, + fields: &mut BTreeMap, BTreeSet>, ) -> bool { if path.len() > MAX_DEPTH || (fields.len() >= MAX_FIELDS && !fields.contains_key(&path)) { return true; } if !path.is_empty() { - let kind = match value { - Value::Null => "null", - Value::Bool(_) => "boolean", - Value::Number(number) if number.is_i64() || number.is_u64() => "integer", - Value::Number(_) => "number", - Value::String(_) => "string", - Value::Array(_) => "array", - Value::Object(_) => "object", - }; - fields.entry(path.clone()).or_default().insert(kind); + fields + .entry(path.clone()) + .or_default() + .insert(JsonKind::of(value)); } match value { Value::Object(object) => object.iter().fold(false, |limited, (key, value)| { @@ -194,7 +331,7 @@ fn discover( } } -fn metadata_catalog(sample: &[MetadataRow]) -> MetadataCatalog { +fn metadata_sample(sample: &[MetadataRow]) -> MetadataSample { let (fields, limited, invalid_rows) = sample.iter().take(SAMPLE_ROWS).fold( (BTreeMap::new(), sample.len() > SAMPLE_ROWS, 0), |(fields, limited, invalid_rows), row| match serde_json::from_str::(&row.metadata) { @@ -214,24 +351,19 @@ fn metadata_catalog(sample: &[MetadataRow]) -> MetadataCatalog { types, }) .collect(); - MetadataCatalog { - table: "spend_logs", - column: "metadata", + MetadataSample { fields, sampled_rows: sample.len().min(SAMPLE_ROWS), invalid_json_rows: invalid_rows, truncated: limited, - sample_sql: METADATA_SQL, - error: None, - scope: METADATA_SCOPE, } } -pub async fn query_help(client: &Client, connection: &Connection) -> Result { +pub async fn query_help(client: &Client, connection: &Connection) -> Result { let tables = stream::iter(TraceTable::iter()) .then(|table| async move { Ok::<_, Error>(TableSchema { - name: table.into(), + name: table, columns: rows::( client, connection, @@ -242,13 +374,15 @@ pub async fn query_help(client: &Client, connection: &Connection) -> Result>() .await?; - let metadata = match rows::(client, connection, METADATA_SQL).await { - Ok(sample) => metadata_catalog(&sample), - Err(error) => MetadataCatalog { - error: Some(error.to_string()), - truncated: true, - ..metadata_catalog(&[]) + let metadata = MetadataCatalog { + table: TraceTable::SpendLogs, + column: "metadata", + discovery: match rows::(client, connection, METADATA_SQL).await { + Ok(sample) => Discovery::Observed(metadata_sample(&sample)), + Err(error) => Discovery::Unavailable(error.to_string()), }, + sample_sql: METADATA_SQL, + scope: METADATA_SCOPE, }; let attributes = stream::iter(["SpanAttributes", "ResourceAttributes"]) .then(|column| async move { @@ -257,27 +391,27 @@ pub async fn query_help(client: &Client, connection: &Connection) -> Result= now() - INTERVAL 7 DAY \ LIMIT 200) ORDER BY key LIMIT 201" ); - let (keys, error) = match rows::(client, connection, &sql).await { - Ok(keys) => (keys, None), - Err(error) => (Vec::new(), Some(error.to_string())), + let discovery = match rows::(client, connection, &sql).await { + Ok(keys) => Discovery::Observed(AttributeSample { + truncated: keys.len() > MAX_FIELDS, + fields: keys + .into_iter() + .take(MAX_FIELDS) + .map(|row| AttributeField { + expression: format!("{column}[{}]", literal(&row.key)), + key: row.key, + kind: MapValueType::String, + }) + .collect(), + }), + Err(error) => Discovery::Unavailable(error.to_string()), }; - let fields = keys - .iter() - .take(MAX_FIELDS) - .map(|row| AttributeField { - key: row.key.clone(), - kind: "String", - expression: format!("{column}[{}]", literal(&row.key)), - }) - .collect(); AttributeCatalog { - table: "otel_traces", + table: TraceTable::OtelTraces, column, - fields, - truncated: error.is_some() || keys.len() > MAX_FIELDS, + discovery, discovery_sql: sql, scope: ATTRIBUTE_SCOPE, - error, } }) .collect::>() @@ -287,33 +421,31 @@ pub async fn query_help(client: &Client, connection: &Connection) -> Result>(), - "metadata": metadata, - "attributes": attributes, - "relationships": [{ - "left": "otel_traces.LiteLLMRequestId", "right": "spend_logs.response_id", - "additional_predicates": "otel_traces.TeamId = spend_logs.team_id AND (otel_traces.TeamId != '' OR (otel_traces.UserId != '' AND otel_traces.UserId = spend_logs.user) OR (otel_traces.ApiKeyHash != '' AND otel_traces.ApiKeyHash = spend_logs.api_key))", - "meaning": "The normalized ID is the response ID, not request_id. Cached requests can share response_id; joins may return multiple spend rows" - }], - "examples": guide.examples()?, - "gotchas": guide.gotchas()?, - "guide": guide::render(&guide)?, - }).to_string()) + Ok(QueryHelp { + dialect: "ClickHouse SQL", + access: "Request-log visibility enforced by ClickHouse row policies; proxy admins see all rows, users see their own rows and permitted teams, and callers without user identity see their own key rows", + response: "ClickHouse JSON envelope: meta, data, rows, statistics; 64-bit integers may be strings", + examples: guide.examples()?, + gotchas: guide.gotchas()?, + guide: guide::render(&guide)?, + normalized_fields: NORMALIZED_FIELD_DEFINITIONS + .iter() + .map(NormalizedField::from) + .collect(), + relationships: &RELATIONSHIPS, + tables, + metadata, + attributes, + }) } #[cfg(test)] mod tests { use super::*; use rstest::rstest; + use serde_json::json; #[rstest] fn metadata_discovery_preserves_mixed_types_and_reports_invalid_rows() { @@ -328,7 +460,7 @@ mod tests { metadata: "invalid".into(), }, ]; - let catalog = json!(metadata_catalog(&sample)); + let catalog = json!(metadata_sample(&sample)); assert_eq!( catalog["fields"], json!([{ @@ -351,7 +483,7 @@ mod tests { metadata: json!(metadata).to_string(), }) .collect(); - let catalog = json!(metadata_catalog(&sample)); + let catalog = json!(metadata_sample(&sample)); assert_eq!(catalog["truncated"], true); assert_eq!(catalog["sampled_rows"], row_count.min(SAMPLE_ROWS)); assert_eq!( diff --git a/litellm-rust/crates/traces-clickhouse/src/query/guide.rs b/litellm-rust/crates/traces-clickhouse/src/query/guide.rs index 3bf7336648d..a0b60f9666f 100644 --- a/litellm-rust/crates/traces-clickhouse/src/query/guide.rs +++ b/litellm-rust/crates/traces-clickhouse/src/query/guide.rs @@ -1,8 +1,8 @@ use askama::Template; use serde::Serialize; -use super::{AttributeCatalog, MetadataCatalog, TableSchema}; -use crate::{Error, NormalizedFieldDefinition}; +use super::{AttributeCatalog, Discovery, MetadataCatalog, TableSchema}; +use crate::{Error, NormalizedFieldDefinition, query_access::ReaderLimits}; #[derive(Template)] #[template(path = "query_help.jinja", escape = "none", blocks = [ @@ -33,6 +33,7 @@ pub(super) struct QueryGuide<'a> { pub normalized_fields: &'a [NormalizedFieldDefinition], pub metadata: &'a MetadataCatalog, pub attributes: &'a [AttributeCatalog], + pub limits: &'a ReaderLimits, } #[derive(Serialize)] diff --git a/litellm-rust/crates/traces-clickhouse/src/query_access.rs b/litellm-rust/crates/traces-clickhouse/src/query_access.rs index 94ead18dd94..5d82a5e2e93 100644 --- a/litellm-rust/crates/traces-clickhouse/src/query_access.rs +++ b/litellm-rust/crates/traces-clickhouse/src/query_access.rs @@ -2,6 +2,7 @@ use std::{sync::Arc, time::Duration}; use hmac::{Hmac, Mac}; use litellm_http::Client; +use litellm_storage_clickhouse::READ_LIMITS; use litellm_traces::QueryScope; use moka::future::Cache; use strum::IntoEnumIterator; @@ -11,6 +12,33 @@ use tokio::sync::{OwnedSemaphorePermit, Semaphore}; use super::{Connection, Error, TraceTable}; +const MIB: u64 = 1024 * 1024; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) struct ReaderLimits { + pub result_rows: u64, + pub result_bytes: u64, + pub memory_bytes: u64, + pub execution_seconds: u64, +} + +impl ReaderLimits { + pub fn result_mib(&self) -> u64 { + self.result_bytes / MIB + } + + pub fn memory_mib(&self) -> u64 { + self.memory_bytes / MIB + } +} + +pub(crate) const READER_LIMITS: ReaderLimits = ReaderLimits { + result_rows: READ_LIMITS.result_rows, + result_bytes: READ_LIMITS.response_bytes as u64, + memory_bytes: 256 * MIB, + execution_seconds: READ_LIMITS.execution_seconds, +}; + #[derive(Clone)] pub struct QueryReaders { writer: Connection, @@ -75,13 +103,19 @@ impl QueryReaders { return Err(Error::InvalidScope); } let password_hash = format!("{:x}", Sha256::digest(password)); + let ReaderLimits { + result_rows, + result_bytes, + memory_bytes, + execution_seconds, + } = READER_LIMITS; self.execute( client, format!( "CREATE USER IF NOT EXISTS {user} IDENTIFIED WITH sha256_hash BY '{password_hash}' \ - SETTINGS readonly = 1 CONST, max_execution_time = 10 CONST, \ - max_result_rows = 1000 CONST, max_result_bytes = 4194304 CONST, \ - result_overflow_mode = 'throw' CONST, max_memory_usage = 268435456 CONST, \ + SETTINGS readonly = 1 CONST, max_execution_time = {execution_seconds} CONST, \ + max_result_rows = {result_rows} CONST, max_result_bytes = {result_bytes} CONST, \ + result_overflow_mode = 'throw' CONST, max_memory_usage = {memory_bytes} CONST, \ max_threads = 2 CONST, max_concurrent_queries_for_user = 8 CONST" ), ) diff --git a/litellm-rust/crates/traces-clickhouse/src/table.rs b/litellm-rust/crates/traces-clickhouse/src/table.rs index 5e3fe83fa82..4cbbf3cee89 100644 --- a/litellm-rust/crates/traces-clickhouse/src/table.rs +++ b/litellm-rust/crates/traces-clickhouse/src/table.rs @@ -1,6 +1,14 @@ #[derive( - Clone, Copy, Debug, strum::Display, strum::AsRefStr, strum::EnumIter, strum::IntoStaticStr, + Clone, + Copy, + Debug, + serde::Serialize, + strum::Display, + strum::AsRefStr, + strum::EnumIter, + strum::IntoStaticStr, )] +#[serde(rename_all = "snake_case")] #[strum(serialize_all = "snake_case")] pub enum TraceTable { OtelTraces, diff --git a/litellm-rust/crates/traces-clickhouse/templates/query_help.jinja b/litellm-rust/crates/traces-clickhouse/templates/query_help.jinja index a79342c8343..6e746221022 100644 --- a/litellm-rust/crates/traces-clickhouse/templates/query_help.jinja +++ b/litellm-rust/crates/traces-clickhouse/templates/query_help.jinja @@ -11,18 +11,18 @@ Normalized span fields {% endfor %} Observed LLM call metadata {{ metadata.scope }} -Sampled rows: {{ metadata.sampled_rows }}; invalid JSON rows: {{ metadata.invalid_json_rows }}; truncated: {{ metadata.truncated }} -{% if let Some(error) = metadata.error %}Metadata discovery unavailable: {{ error }} -{% else if metadata.fields.is_empty() %}No metadata paths found in the sampled rows -{% else %}{% for field in metadata.fields %}{{ field.expression }}: {% for kind in field.types %}{{ kind }} {% endfor %} -{% endfor %}{% endif %} +{% match metadata.discovery %}{% when Discovery::Unavailable(error) %}Metadata discovery unavailable: {{ error }} +{% when Discovery::Observed(sample) %}Sampled rows: {{ sample.sampled_rows }}; invalid JSON rows: {{ sample.invalid_json_rows }}; truncated: {{ sample.truncated }} +{% if sample.fields.is_empty() %}No metadata paths found in the sampled rows +{% else %}{% for field in sample.fields %}{{ field.expression }}: {% for kind in field.types %}{{ kind }} {% endfor %} +{% endfor %}{% endif %}{% endmatch %} Observed span and resource attributes {% for catalog in attributes %}{{ catalog.table }}.{{ catalog.column }} {{ catalog.scope }} -{% if let Some(error) = catalog.error %}Attribute discovery unavailable: {{ error }} -{% else if catalog.fields.is_empty() %}No attribute keys found in the sampled spans -{% else %}{% for field in catalog.fields %}{{ field.expression }}: {{ field.kind }} -{% endfor %}{% endif %}{% endfor %} +{% match catalog.discovery %}{% when Discovery::Unavailable(error) %}Attribute discovery unavailable: {{ error }} +{% when Discovery::Observed(sample) %}{% if sample.fields.is_empty() %}No attribute keys found in the sampled spans +{% else %}{% for field in sample.fields %}{{ field.expression }}: {{ field.kind }} +{% endfor %}{% endif %}{% endmatch %}{% endfor %} Examples {% block recent_spans_name %}Recent normalized LLM spans{% endblock %} @@ -44,7 +44,7 @@ Gotchas {% block time_window %}Always bound Timestamp or start_time and use LIMIT; add TeamId/ApiKeyHash or team_id/api_key filters when investigating one tenant{% endblock %} -{% block reader_limits %}The reader enforces 1000 result rows, 4 MiB response bytes, 256 MiB memory and a 10 second query limit; exceeding limits fails instead of returning partial results{% endblock %} +{% block reader_limits %}The reader enforces {{ limits.result_rows }} result rows, {{ limits.result_mib() }} MiB response bytes, {{ limits.memory_mib() }} MiB memory and a {{ limits.execution_seconds }} second query limit; exceeding limits fails instead of returning partial results{% endblock %} {% block reader_profile %}LiteLLM provisions SELECT-only readers from the configured ClickHouse connection and enforces request-log visibility through row policies. Callers see their own user rows and permitted teams, or their own key rows when no user identity is available. Provisioning requires CREATE USER, ALTER USER, CREATE ROW POLICY, and GRANT SELECT permissions{% endblock %} diff --git a/litellm-rust/crates/traces-clickhouse/tests/migrations.rs b/litellm-rust/crates/traces-clickhouse/tests/migrations.rs index dff7d8d5940..9fdeb450c70 100644 --- a/litellm-rust/crates/traces-clickhouse/tests/migrations.rs +++ b/litellm-rust/crates/traces-clickhouse/tests/migrations.rs @@ -1467,8 +1467,8 @@ async fn query_help_discovers_live_schema_and_runs_its_examples( ) .await?; } - let help: serde_json::Value = serde_json::from_str( - &litellm_traces_clickhouse::query_help(&database.client, &reader).await?, + let help = serde_json::to_value( + litellm_traces_clickhouse::query_help(&database.client, &reader).await?, )?; let keys: std::collections::BTreeSet<_> = help .as_object() @@ -1654,8 +1654,8 @@ async fn query_help_preserves_schema_and_guide_when_discovery_hits_reader_limits }))).collect::, _>>()?; insert_rows(&database, "otel_traces", spans).await?; let reader = Connection::configured(&database.url, "trace_test", "help_reader", "")?; - let help: serde_json::Value = serde_json::from_str( - &litellm_traces_clickhouse::query_help(&database.client, &reader).await?, + let help = serde_json::to_value( + litellm_traces_clickhouse::query_help(&database.client, &reader).await?, )?; assert_eq!(help["tables"].as_array().ok_or("tables")?.len(), 3); assert!(!help["examples"].as_array().ok_or("examples")?.is_empty()); diff --git a/litellm-rust/crates/traces-clickhouse/tests/query_access.rs b/litellm-rust/crates/traces-clickhouse/tests/query_access.rs index d4c7c886bd0..be749351a72 100644 --- a/litellm-rust/crates/traces-clickhouse/tests/query_access.rs +++ b/litellm-rust/crates/traces-clickhouse/tests/query_access.rs @@ -94,7 +94,7 @@ async fn queries_and_help_are_scoped_by_the_database( .await?, )?; assert_eq!(summary["data"][0]["count"], json!(expected.len())); - let help = query_help(&database.client, &reader).await?; + let help = serde_json::to_string(&query_help(&database.client, &reader).await?)?; assert_eq!(help.contains("secret_b"), expected.contains(&"b")); assert_eq!(help.contains("secret-b"), expected.contains(&"b")); let recreated = QueryReaders::new(database.writer.clone(), "trace_test".to_owned()); diff --git a/litellm-rust/crates/traces/tests/query/named.rs b/litellm-rust/crates/traces/tests/query/named.rs index b20da7bd800..7db77df8a29 100644 --- a/litellm-rust/crates/traces/tests/query/named.rs +++ b/litellm-rust/crates/traces/tests/query/named.rs @@ -46,10 +46,10 @@ fn named_requests_preserve_all_access_cases(#[case] teams: Vec<&str>, #[case] ke #[rstest] fn result_contracts_preserve_public_field_names() { round_trip::( - json!({"trace_id": "trace", "trace_ref": "ref", "team_id": "team", "api_key_hash": "key", "user_id": "user", "name": "agent", "service": "service", "input_preview": "input", "status": "ok", "start_ms": -1, "duration_ms": 20, "span_count": u64::MAX, "agent_count": 1, "agent_invocations": 2, "llm_calls": 3, "tool_calls": 4, "input_tokens": 5, "output_tokens": 6, "models": ["model"], "error_count": 0, "request_ids": ["request"]}), + json!({"trace_id": "trace", "trace_ref": "ref", "team_id": "team", "api_key_hash": "key", "user_id": "user", "name": "agent", "service": "service", "input_preview": "input", "status": "ok", "start_ms": -1, "duration_ms": 20, "span_count": u64::MAX, "agent_count": 1, "agent_invocations": 2, "agent_names": ["agent"], "frameworks": ["framework"], "llm_calls": 3, "tool_calls": 4, "input_tokens": 5, "output_tokens": 6, "models": ["model"], "error_count": 0, "request_ids": ["request"]}), ); round_trip::( - json!({"span_id": "span", "parent_span_id": "parent", "name": "agent", "type": "agent", "agent": "agent", "status": "error", "status_message": "error", "error_truncated": 1, "start_ns": -1, "duration_ns": u64::MAX, "service": "service", "input_preview": "input", "model": "model", "input_tokens": u32::MAX, "output_tokens": 6, "litellm_request_id": "request", "team_id": "team", "api_key_hash": "key", "user_id": "user"}), + json!({"span_id": "span", "parent_span_id": "parent", "name": "agent", "type": "agent", "agent": "agent", "framework": "framework", "status": "error", "status_message": "error", "error_truncated": 1, "start_ns": -1, "duration_ns": u64::MAX, "service": "service", "input_preview": "input", "model": "model", "input_tokens": u32::MAX, "output_tokens": 6, "litellm_request_id": "request", "team_id": "team", "api_key_hash": "key", "user_id": "user"}), ); round_trip::( json!({"span_id": "span", "input": "input", "output": "output", "attributes": {"count": "42"}}), diff --git a/litellm/rust_bridge/_native.pyi b/litellm/rust_bridge/_native.pyi index a86daaa35ad..45bccad6179 100644 --- a/litellm/rust_bridge/_native.pyi +++ b/litellm/rust_bridge/_native.pyi @@ -41,7 +41,7 @@ class NativeTraceStorage: def ensure_schema(self) -> Future[None]: ... def insert_rows(self, table: str, rows: Sequence[Mapping[str, object]]) -> Future[None]: ... def query_sql(self, sql: str, scope: QueryScope, secret: str) -> Future[str]: ... - def query_help(self, scope: QueryScope, secret: str) -> Future[str]: ... + def query_help(self, scope: QueryScope, secret: str) -> Future[JsonValue]: ... def query(self, query: ReadQueryName, parameters: Mapping[str, str | int | float | Sequence[str]]) -> Future[str]: ... @final diff --git a/litellm/rust_bridge/trace_query_responses.py b/litellm/rust_bridge/trace_query_responses.py index 914d9b6c8d4..ad7678fcd9b 100644 --- a/litellm/rust_bridge/trace_query_responses.py +++ b/litellm/rust_bridge/trace_query_responses.py @@ -1,9 +1,12 @@ from collections.abc import Mapping -from typing import Final +from typing import Final, Literal from pydantic import BaseModel, ConfigDict, JsonValue _RESPONSE_CONFIG: Final = ConfigDict(frozen=True, extra="allow") +_HELP_CONFIG: Final = ConfigDict(frozen=True, extra="forbid") +TraceTableName = Literal["otel_traces", "agent_traces_by_key", "spend_logs"] +MetadataValueType = Literal["array", "boolean", "integer", "null", "number", "object", "string"] class TraceQueryColumn(BaseModel): @@ -28,14 +31,14 @@ class TraceSQLResponse(BaseModel): class TraceQueryTable(BaseModel): - model_config = ConfigDict(frozen=True) - name: str + model_config = _HELP_CONFIG + name: TraceTableName columns: tuple[TraceQueryColumn, ...] class TraceQueryNormalizedField(BaseModel): - model_config = ConfigDict(frozen=True) - table: str + model_config = _HELP_CONFIG + table: TraceTableName name: str column: str type: str @@ -43,15 +46,15 @@ class TraceQueryNormalizedField(BaseModel): class TraceQueryMetadataField(BaseModel): - model_config = ConfigDict(frozen=True) + model_config = _HELP_CONFIG path: tuple[str | int, ...] - types: tuple[str, ...] + types: tuple[MetadataValueType, ...] expression: str class TraceQueryMetadata(BaseModel): - model_config = ConfigDict(frozen=True) - table: str + model_config = _HELP_CONFIG + table: TraceTableName column: str fields: tuple[TraceQueryMetadataField, ...] sampled_rows: int @@ -63,15 +66,15 @@ class TraceQueryMetadata(BaseModel): class TraceQueryAttributeField(BaseModel): - model_config = ConfigDict(frozen=True) + model_config = _HELP_CONFIG key: str - type: str + type: Literal["String"] expression: str class TraceQueryAttributes(BaseModel): - model_config = ConfigDict(frozen=True) - table: str + model_config = _HELP_CONFIG + table: TraceTableName column: str fields: tuple[TraceQueryAttributeField, ...] truncated: bool @@ -81,7 +84,7 @@ class TraceQueryAttributes(BaseModel): class TraceQueryRelationship(BaseModel): - model_config = ConfigDict(frozen=True) + model_config = _HELP_CONFIG left: str right: str additional_predicates: str @@ -89,13 +92,13 @@ class TraceQueryRelationship(BaseModel): class TraceQueryExample(BaseModel): - model_config = ConfigDict(frozen=True) + model_config = _HELP_CONFIG name: str sql: str class TraceQueryHelp(BaseModel): - model_config = ConfigDict(frozen=True) + model_config = _HELP_CONFIG dialect: str access: str response: str diff --git a/litellm/rust_bridge/traces.py b/litellm/rust_bridge/traces.py index 2ed2df4e55e..23778983d75 100644 --- a/litellm/rust_bridge/traces.py +++ b/litellm/rust_bridge/traces.py @@ -2,7 +2,7 @@ from collections.abc import Awaitable, Mapping, Sequence from dataclasses import dataclass from typing import Final, Literal, Protocol, TypedDict, TypeVar, runtime_checkable -from pydantic import BaseModel, ConfigDict, Field, TypeAdapter, ValidationError +from pydantic import BaseModel, ConfigDict, Field, JsonValue, TypeAdapter, ValidationError from typing_extensions import ReadOnly from litellm.rust_bridge.loader import get_native_bridge @@ -111,7 +111,7 @@ class NativeStore(Protocol): def query_sql(self, sql: str, scope: QueryScope, secret: str) -> Awaitable[str]: ... - def query_help(self, scope: QueryScope, secret: str) -> Awaitable[str]: ... + def query_help(self, scope: QueryScope, secret: str) -> Awaitable[JsonValue]: ... def query( self, name: ReadQueryName, parameters: Mapping[str, str | int | float | Sequence[str]] @@ -189,6 +189,13 @@ def _decode_query_response(adapter: TypeAdapter[_ResponseT], body: str) -> _Resp raise RuntimeError("Native trace query returned an invalid response") from error +def _validate_query_response(adapter: TypeAdapter[_ResponseT], value: JsonValue) -> _ResponseT: + try: + return adapter.validate_python(value) + except ValidationError as error: + raise RuntimeError("Native trace query returned an invalid response") from error + + class ClickHouseStorage: def __init__(self, config: TraceStorageConfig) -> None: native: Final = _native() @@ -216,7 +223,7 @@ class ClickHouseStorage: async def query_help(self, scope: QueryScope, secret: str) -> TraceQueryHelp: result: Final = await self._native.query_help(scope, secret) - return _decode_query_response(_HELP_RESPONSE, result) + return _validate_query_response(_HELP_RESPONSE, result) async def lens_sample(self, parameters: LensSampleParams) -> tuple[ExecutionRow, ...]: return await self.query(LENS_SAMPLE, parameters) diff --git a/tests/unit/proxy/test_tracing_endpoints.py b/tests/unit/proxy/test_tracing_endpoints.py index 7c69796ddfa..fd05d54dcb6 100644 --- a/tests/unit/proxy/test_tracing_endpoints.py +++ b/tests/unit/proxy/test_tracing_endpoints.py @@ -2,8 +2,9 @@ Tests for the agent tracing endpoints (litellm/proxy/tracing_endpoints.py). """ -from collections.abc import AsyncGenerator +from collections.abc import AsyncGenerator, Mapping from contextlib import asynccontextmanager +from types import ModuleType from typing import Final from unittest.mock import AsyncMock, MagicMock @@ -15,9 +16,10 @@ from litellm.proxy import tracing_endpoints from litellm.proxy._types import LitellmUserRoles, ProxyLifespanState, UserAPIKeyAuth from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.tracing_runtime import manage_tracing, provide_storage +from litellm.rust_bridge import loader from litellm.rust_bridge.trace_queries import SPAN_DETAIL, SpanDetailParams from litellm.rust_bridge.trace_query_responses import TraceQueryHelp, TraceSQLResponse -from litellm.rust_bridge.traces import ClickHouseStorage +from litellm.rust_bridge.traces import AdminQueryScope, ClickHouseStorage, TraceStorageConfig from litellm.tracing import TraceReceiver, TracingPayloadTooLargeError from litellm.tracing.store import TraceStore from litellm.tracing.types import TraceScope @@ -29,14 +31,14 @@ SQL_ENVELOPE: Final = { "statistics": {"elapsed": 0.01, "rows_read": 1, "bytes_read": 8}, "rows_before_limit_at_least": 1, } -QUERY_HELP: Final = { +QUERY_HELP: Final[Mapping[str, object]] = { "dialect": "test SQL", "access": "authenticated scope", "response": "JSON envelope", - "tables": [{"name": "traces", "columns": [{"name": "value", "type": "String", "comment": "label"}]}], + "tables": [{"name": "otel_traces", "columns": [{"name": "value", "type": "String", "comment": "label"}]}], "normalized_fields": [], "metadata": { - "table": "traces", + "table": "spend_logs", "column": "metadata", "fields": [], "sampled_rows": 0, @@ -668,3 +670,60 @@ def test_queries_require_a_proxy_secret( receiver.store.storage.query_sql.assert_awaited_once_with( "SELECT 1", {"kind": "logs", "user_id": "", "team_ids": (), "api_key_hash": "hashed-key"}, secret ) + + +class _NativeConfig: + def __init__(self, database: str, url: str, retention_days: int) -> None: + pass + + +class _NativeReturningHelp(ModuleType): + def __init__(self, help_payload: Mapping[str, object]) -> None: + super().__init__("native_traces") + + class Storage: + def __init__(self, config: _NativeConfig) -> None: + pass + + async def query_help(self, scope: AdminQueryScope, secret: str) -> Mapping[str, object]: + return help_payload + + self.NativeTraceConfig: Final = _NativeConfig + self.NativeTraceStorage: Final = Storage + self.trace_decode_otlp: Final = list + self.trace_encode_error: Final = bytes + self.trace_normalized_field_definitions: Final = list + + +async def test_storage_validates_the_native_query_help_value(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(loader, "_cached_bridge", _NativeReturningHelp(QUERY_HELP)) + storage: Final = ClickHouseStorage(TraceStorageConfig("http://clickhouse:8123")) + assert await storage.query_help({"kind": "admin"}, "secret") == TraceQueryHelp.model_validate(QUERY_HELP) + + +@pytest.mark.parametrize( + "drift", + ( + { + "metadata": { + "table": "spend_logs", + "column": "metadata", + "fields": [{"path": ["a"], "types": ["boolen"], "expression": "a"}], + "sampled_rows": 1, + "invalid_json_rows": 0, + "truncated": False, + "sample_sql": "SELECT metadata FROM spend_logs", + "scope": "bounded sample", + } + }, + {"tables": [{"name": "traces", "columns": [{"name": "value", "type": "String"}]}]}, + {"unexpected": True}, + ), +) +async def test_storage_rejects_native_query_help_that_drifts_from_the_contract( + monkeypatch: pytest.MonkeyPatch, drift: Mapping[str, object] +) -> None: + monkeypatch.setattr(loader, "_cached_bridge", _NativeReturningHelp({**QUERY_HELP, **drift})) + storage: Final = ClickHouseStorage(TraceStorageConfig("http://clickhouse:8123")) + with pytest.raises(RuntimeError, match="invalid response"): + await storage.query_help({"kind": "admin"}, "secret") From b877a38e5fc41d797f67fd973d8ccad53b23fbac Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Fri, 2 Oct 2026 18:12:56 -0700 Subject: [PATCH 12/61] fix(proxy): stop queued registry read-throughs spending the resync budget (#44277) * fix(proxy): stop queued registry read-throughs spending the resync budget RegistryReadThrough.attempt serializes misses behind one lock, but every request that was queued behind the first one still spent a unit of the 20-per-5s resync budget and re-ran the DB resync, even though the first request had already loaded the object. A burst of more than 20 requests for a model created on another worker therefore exhausted the budget and the rest got 400 "Invalid model name". attempt now checks whether the key is already loaded once it holds the lock and returns early without touching the budget. Models check the router's model names and deployment ids, guardrails and agents reuse their existing registry lookups. * test(proxy): gate the queued read-through test on events and cover each registry's loaded check The queued-requests test now holds the first resync on an asyncio.Event instead of a timed sleep and records calls in a recorder with tuple and frozenset state. New tests show the model, guardrail and agent read-throughs each answer an object that is already loaded without reading the database, so rewiring any registry's loaded check now fails a test * test(proxy): keep the queued read-through recorder inside its test and type the agent registry fixture --- .../common_utils/registry_read_through.py | 28 ++- .../test_registry_read_through.py | 166 ++++++++++++++++-- tests/unit/proxy/conftest.py | 4 +- 3 files changed, 184 insertions(+), 14 deletions(-) diff --git a/litellm/proxy/common_utils/registry_read_through.py b/litellm/proxy/common_utils/registry_read_through.py index 8a82e253c5c..32beb0b7447 100644 --- a/litellm/proxy/common_utils/registry_read_through.py +++ b/litellm/proxy/common_utils/registry_read_through.py @@ -36,6 +36,7 @@ READ_THROUGH_MAX_RESYNCS_PER_WINDOW: Final = 20 class RegistryReadThrough: __slots__ = ( + "_is_loaded", "_lock", "_max_resyncs_per_window", "_miss_ttl_seconds", @@ -49,11 +50,13 @@ class RegistryReadThrough: def __init__( self, resync: Callable[[str], Awaitable[bool]], + is_loaded: Callable[[str], bool], miss_ttl_seconds: float = READ_THROUGH_MISS_TTL_SECONDS, max_resyncs_per_window: int = READ_THROUGH_MAX_RESYNCS_PER_WINDOW, resync_window_seconds: float = READ_THROUGH_RESYNC_WINDOW_SECONDS, ) -> None: self._resync = resync + self._is_loaded = is_loaded self._miss_ttl_seconds = miss_ttl_seconds self._max_resyncs_per_window = max_resyncs_per_window self._resync_window_seconds = resync_window_seconds @@ -78,6 +81,8 @@ class RegistryReadThrough: async with self._lock: if self._recent_misses.get_cache(key) is not None: return False + if self._is_loaded(key): + return True if not self._consume_resync_budget(): verbose_proxy_logger.warning( "registry read-through for %r skipped: resync budget of %s per %ss exhausted", @@ -190,9 +195,26 @@ async def _resync_agents(agent_id_or_name: str) -> bool: return True -model_registry_read_through: Final = RegistryReadThrough(resync=_resync_model_deployments) -guardrail_registry_read_through: Final = RegistryReadThrough(resync=_resync_guardrails) -agent_registry_read_through: Final = RegistryReadThrough(resync=_resync_agents) +def _model_is_loaded(model_name_or_id: str) -> bool: + from litellm.proxy import proxy_server + + router: Final = proxy_server.llm_router + if router is None: + return False + return model_name_or_id in router.model_names or router.has_model_id(model_name_or_id) + + +def _guardrail_is_loaded(guardrail_name: str) -> bool: + return _initialized_guardrail(guardrail_name) is not None + + +def _agent_is_loaded(agent_id_or_name: str) -> bool: + return _agent_from_registry(agent_id_or_name) is not None + + +model_registry_read_through: Final = RegistryReadThrough(resync=_resync_model_deployments, is_loaded=_model_is_loaded) +guardrail_registry_read_through: Final = RegistryReadThrough(resync=_resync_guardrails, is_loaded=_guardrail_is_loaded) +agent_registry_read_through: Final = RegistryReadThrough(resync=_resync_agents, is_loaded=_agent_is_loaded) def _agent_from_registry(agent_id_or_name: str) -> "AgentResponse | None": diff --git a/tests/unit/proxy/common_utils/test_registry_read_through.py b/tests/unit/proxy/common_utils/test_registry_read_through.py index 9e20386bf3d..5713f7dfaa7 100644 --- a/tests/unit/proxy/common_utils/test_registry_read_through.py +++ b/tests/unit/proxy/common_utils/test_registry_read_through.py @@ -1,10 +1,17 @@ import asyncio -from typing import Final +from typing import TYPE_CHECKING, Final import pytest from litellm.proxy.common_utils.registry_read_through import RegistryReadThrough +if TYPE_CHECKING: + from litellm.proxy.agent_endpoints.agent_registry import AgentRegistry + + +def nothing_loaded(_key: str) -> bool: + return False + class ResyncSpy: def __init__(self, found: bool = True, error: Exception | None = None) -> None: @@ -22,7 +29,7 @@ class ResyncSpy: @pytest.mark.asyncio async def test_attempt_returns_true_when_resync_finds_object(): spy: Final = ResyncSpy(found=True) - read_through: Final = RegistryReadThrough(resync=spy) + read_through: Final = RegistryReadThrough(resync=spy, is_loaded=nothing_loaded) assert await read_through.attempt("new-model") is True assert spy.calls == ["new-model"] @@ -31,7 +38,7 @@ async def test_attempt_returns_true_when_resync_finds_object(): @pytest.mark.asyncio async def test_attempt_found_key_is_not_negative_cached(): spy: Final = ResyncSpy(found=True) - read_through: Final = RegistryReadThrough(resync=spy) + read_through: Final = RegistryReadThrough(resync=spy, is_loaded=nothing_loaded) assert await read_through.attempt("new-model") is True assert await read_through.attempt("new-model") is True @@ -41,7 +48,7 @@ async def test_attempt_found_key_is_not_negative_cached(): @pytest.mark.asyncio async def test_missing_key_is_negative_cached_within_ttl(): spy: Final = ResyncSpy(found=False) - read_through: Final = RegistryReadThrough(resync=spy, miss_ttl_seconds=60.0) + read_through: Final = RegistryReadThrough(resync=spy, is_loaded=nothing_loaded, miss_ttl_seconds=60.0) assert await read_through.attempt("ghost-model") is False assert await read_through.attempt("ghost-model") is False @@ -51,7 +58,7 @@ async def test_missing_key_is_negative_cached_within_ttl(): @pytest.mark.asyncio async def test_negative_cache_expires_and_resync_runs_again(): spy: Final = ResyncSpy(found=False) - read_through: Final = RegistryReadThrough(resync=spy, miss_ttl_seconds=0.05) + read_through: Final = RegistryReadThrough(resync=spy, is_loaded=nothing_loaded, miss_ttl_seconds=0.05) assert await read_through.attempt("ghost-model") is False await asyncio.sleep(0.1) @@ -62,7 +69,7 @@ async def test_negative_cache_expires_and_resync_runs_again(): @pytest.mark.asyncio async def test_resync_exception_returns_false_without_negative_caching(): spy: Final = ResyncSpy(error=RuntimeError("db down")) - read_through: Final = RegistryReadThrough(resync=spy) + read_through: Final = RegistryReadThrough(resync=spy, is_loaded=nothing_loaded) assert await read_through.attempt("new-model") is False assert await read_through.attempt("new-model") is False @@ -77,7 +84,7 @@ async def test_concurrent_attempts_for_missing_key_resync_once(): return await super().__call__(key) spy: Final = SlowResyncSpy(found=False) - read_through: Final = RegistryReadThrough(resync=spy, miss_ttl_seconds=60.0) + read_through: Final = RegistryReadThrough(resync=spy, is_loaded=nothing_loaded, miss_ttl_seconds=60.0) results: Final = await asyncio.gather(*(read_through.attempt("ghost-model") for _ in range(5))) assert results == [False] * 5 @@ -87,7 +94,7 @@ async def test_concurrent_attempts_for_missing_key_resync_once(): @pytest.mark.asyncio async def test_distinct_keys_do_not_share_negative_cache(): spy: Final = ResyncSpy(found=False) - read_through: Final = RegistryReadThrough(resync=spy, miss_ttl_seconds=60.0) + read_through: Final = RegistryReadThrough(resync=spy, is_loaded=nothing_loaded, miss_ttl_seconds=60.0) assert await read_through.attempt("ghost-a") is False assert await read_through.attempt("ghost-b") is False @@ -98,7 +105,11 @@ async def test_distinct_keys_do_not_share_negative_cache(): async def test_resync_budget_exhausted_blocks_resync_without_negative_caching(): spy: Final = ResyncSpy(found=False) read_through: Final = RegistryReadThrough( - resync=spy, miss_ttl_seconds=60.0, max_resyncs_per_window=2, resync_window_seconds=60.0 + resync=spy, + is_loaded=nothing_loaded, + miss_ttl_seconds=60.0, + max_resyncs_per_window=2, + resync_window_seconds=60.0, ) assert await read_through.attempt("ghost-a") is False @@ -108,10 +119,48 @@ async def test_resync_budget_exhausted_blocks_resync_without_negative_caching(): assert read_through._recent_misses.get_cache("ghost-c") is None +@pytest.mark.asyncio +async def test_requests_queued_behind_a_successful_resync_spend_no_budget(): + from unittest.mock import AsyncMock, call + + entered: Final = asyncio.Event() + release: Final = asyncio.Event() + new_model_loaded: Final = asyncio.Event() + + async def gated_load(key: str) -> bool: + entered.set() + await release.wait() + if key == "new-model": + new_model_loaded.set() + return True + + def is_loaded(key: str) -> bool: + return key == "new-model" and new_model_loaded.is_set() + + resync: Final = AsyncMock(side_effect=gated_load) + read_through: Final = RegistryReadThrough( + resync=resync, + is_loaded=is_loaded, + max_resyncs_per_window=2, + resync_window_seconds=60.0, + ) + + burst: Final = asyncio.gather(*(read_through.attempt("new-model") for _ in range(25))) + await entered.wait() + release.set() + + assert await burst == [True] * 25 + assert resync.await_args_list == [call("new-model")] + assert await read_through.attempt("other-model") is True + assert resync.await_args_list == [call("new-model"), call("other-model")] + + @pytest.mark.asyncio async def test_resync_budget_replenishes_after_window(): spy: Final = ResyncSpy(found=True) - read_through: Final = RegistryReadThrough(resync=spy, max_resyncs_per_window=1, resync_window_seconds=0.05) + read_through: Final = RegistryReadThrough( + resync=spy, is_loaded=nothing_loaded, max_resyncs_per_window=1, resync_window_seconds=0.05 + ) assert await read_through.attempt("model-a") is True assert await read_through.attempt("model-b") is False @@ -551,3 +600,100 @@ async def test_agent_read_through_hydrates_identity_binding(lookup, clean_agent_ assert agent.identity is not None assert agent.identity.model_dump(include=set(binding)) == binding assert clean_agent_registry.get_agent_by_id(agent_id="agent-id").identity == agent.identity + + +def test_model_is_loaded_matches_router_model_names_and_deployment_ids(monkeypatch: pytest.MonkeyPatch): + import litellm.proxy.proxy_server as proxy_server + from litellm import Router + from litellm.proxy.common_utils.registry_read_through import _model_is_loaded + + router: Final = Router( + model_list=[ + { + "model_name": "loaded-model", + "litellm_params": {"model": "openai/gpt-4o-mini", "api_key": "sk-test"}, + "model_info": {"id": "loaded-deployment-id"}, + } + ] + ) + monkeypatch.setattr(proxy_server, "llm_router", router) + + assert _model_is_loaded("loaded-model") is True + assert _model_is_loaded("loaded-deployment-id") is True + assert _model_is_loaded("model-created-on-a-sibling") is False + + monkeypatch.setattr(proxy_server, "llm_router", None) + assert _model_is_loaded("loaded-model") is False + + +@pytest.mark.asyncio +async def test_model_read_through_answers_a_loaded_model_without_reading_the_db(monkeypatch: pytest.MonkeyPatch): + from unittest.mock import AsyncMock, MagicMock + + import litellm.proxy.proxy_server as proxy_server + from litellm import Router + from litellm.proxy.common_utils.registry_read_through import model_registry_read_through + + prisma_client: Final = MagicMock() + prisma_client.db.litellm_proxymodeltable.find_many = AsyncMock(side_effect=AssertionError("db read")) + router: Final = Router( + model_list=[ + { + "model_name": "wired-loaded-model", + "litellm_params": {"model": "openai/gpt-4o-mini", "api_key": "sk-test"}, + } + ] + ) + monkeypatch.setattr(proxy_server, "prisma_client", prisma_client) + monkeypatch.setattr(proxy_server, "store_model_in_db", True) + monkeypatch.setattr(proxy_server, "llm_router", router) + + assert await model_registry_read_through.attempt("wired-loaded-model") is True + prisma_client.db.litellm_proxymodeltable.find_many.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_guardrail_read_through_answers_a_loaded_guardrail_without_reading_the_db( + monkeypatch: pytest.MonkeyPatch, +): + from unittest.mock import AsyncMock, MagicMock + + import litellm.proxy.proxy_server as proxy_server + from litellm.proxy.common_utils.registry_read_through import guardrail_registry_read_through + from litellm.proxy.guardrails.guardrail_registry import IN_MEMORY_GUARDRAIL_HANDLER + from litellm.types.guardrails import Guardrail + + guardrail_id: Final = "wired-loaded-guardrail-id" + guardrail_name: Final = "wired-loaded-guardrail" + prisma_client: Final = MagicMock() + prisma_client.db.litellm_guardrailstable.find_first = AsyncMock(side_effect=AssertionError("db read")) + monkeypatch.setattr(proxy_server, "prisma_client", prisma_client) + monkeypatch.setattr(proxy_server, "store_model_in_db", True) + + IN_MEMORY_GUARDRAIL_HANDLER.sync_guardrail_from_db( + guardrail=Guardrail(**dict(FakeGuardrailRow(guardrail_id, guardrail_name))) + ) + try: + assert await guardrail_registry_read_through.attempt(guardrail_name) is True + prisma_client.db.litellm_guardrailstable.find_first.assert_not_awaited() + finally: + IN_MEMORY_GUARDRAIL_HANDLER.delete_in_memory_guardrail(guardrail_id) + + +@pytest.mark.asyncio +async def test_agent_read_through_answers_a_loaded_agent_without_reading_the_db( + clean_agent_registry: "AgentRegistry", monkeypatch: pytest.MonkeyPatch +): + import litellm.proxy.proxy_server as proxy_server + from litellm.proxy.common_utils.registry_read_through import agent_registry_read_through + from litellm.types.agents import AgentResponse + + monkeypatch.setattr(proxy_server, "store_model_in_db", False) + clean_agent_registry.register_agent( + agent_config=AgentResponse.model_validate( + FakeAgentRow("wired-loaded-agent-id", "wired-loaded-agent").model_dump() + ) + ) + + assert await agent_registry_read_through.attempt("wired-loaded-agent-id") is True + assert await agent_registry_read_through.attempt("wired-loaded-agent") is True diff --git a/tests/unit/proxy/conftest.py b/tests/unit/proxy/conftest.py index 50c89387d80..6dec588b763 100644 --- a/tests/unit/proxy/conftest.py +++ b/tests/unit/proxy/conftest.py @@ -411,6 +411,8 @@ def create_proxy_test_client( def fresh_agent_read_through(monkeypatch): from litellm.proxy.common_utils import registry_read_through - read_through = registry_read_through.RegistryReadThrough(resync=registry_read_through._resync_agents) + read_through = registry_read_through.RegistryReadThrough( + resync=registry_read_through._resync_agents, is_loaded=registry_read_through._agent_is_loaded + ) monkeypatch.setattr(registry_read_through, "agent_registry_read_through", read_through) return read_through From b28ce93d2bdc8467b404234f40fe3d17aa7d0d80 Mon Sep 17 00:00:00 2001 From: ishaan-berri <155045088+ishaan-berri@users.noreply.github.com> Date: Fri, 2 Oct 2026 18:28:22 -0700 Subject: [PATCH 13/61] feat(lens): show investigation progress as one staged bar with time left (#44301) * feat(lens): compute overall investigation progress, speed and time left * test(lens): cover overall progress, speed and time left estimates * feat(lens): show investigation progress as one staged bar with time left * feat(lens): track per-stage counts and durations for the progress readout * test(lens): cover stage durations and short time left labels * feat(lens): restyle investigation progress as a terminal-style readout --- .../lens/_components/LensProgress.tsx | 155 ++++++++++++------ .../lens/_components/lensData.test.ts | 94 +++++++++++ .../(dashboard)/lens/_components/lensData.ts | 84 +++++++++- 3 files changed, 285 insertions(+), 48 deletions(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensProgress.tsx b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensProgress.tsx index 8249f284ada..3e01671a832 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensProgress.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/LensProgress.tsx @@ -1,11 +1,31 @@ "use client"; import { useEffect, useState } from "react"; -import { Check, Loader2 } from "lucide-react"; import { Button } from "@/components/ui/button"; -import { analysisElapsed, analysisProgress, nextCheckStatus, type Lens, type Job } from "./lensData"; +import { + analysisElapsed, + analysisFraction, + analysisPace, + analysisProgress, + analysisStages, + durationText, + nextCheckStatus, + remainingLabel, + stageDurations, + type Lens, + type Job, + type ProgressSample, +} from "./lensData"; -const steps = ["Review runs", "Find patterns", "Check evidence"]; +const steps = ["review runs", "find patterns", "check evidence"]; +const markers = { done: "✓", active: "▸", todo: "·" }; +const rowText = { done: "text-foreground", active: "font-medium text-foreground", todo: "text-muted-foreground" }; +const blocks = 32; + +function stageState(index: number, current: number): keyof typeof markers { + if (index < current) return "done"; + return index === current ? "active" : "todo"; +} export function LensProgress({ job, onCancel }: { job: Job; onCancel?: () => void }) { const [now, setNow] = useState(Date.now); @@ -14,57 +34,98 @@ export function LensProgress({ job, onCancel }: { job: Job; onCancel?: () => voi return () => window.clearInterval(timer); }, []); const progress = analysisProgress(job); - const percent = progress.total ? Math.min(100, (progress.done / progress.total) * 100) : undefined; + const fraction = analysisFraction(progress); + const [samples, setSamples] = useState([]); + const latest = samples.at(-1); + if (!latest || latest.step !== progress.step || latest.done !== progress.done) { + setSamples([...samples, { at: now, step: progress.step, done: progress.done, fraction }].slice(-120)); + } + const pace = analysisPace(samples, now); + const percent = Math.round(fraction * 100); + const queued = progress.step < 0; + const counts = analysisStages(job); + const durations = stageDurations(samples, job.created_at, now); + const filled = Math.round(fraction * blocks); + const stats = [ + ["eta", remainingLabel(pace.secondsLeft)], + ["rate", pace.perMinute === null ? "–" : `${Math.round(pace.perMinute)}/min`], + ["elapsed", analysisElapsed(job.created_at, now)], + ]; return ( -
-
-
-
- - {analysisElapsed(job.created_at, now)} elapsed +
+
+ + {progress.title} + {!queued && · {progress.detail}} -
-
    - {steps.map((label, index) => ( -
  1. -
    - - {index < progress.step && } - {label} - -
  2. - ))} -
-
-

{progress.detail}

-
-
-
-
-
- {job.status === "running" && You can leave this page while the investigation runs.} {onCancel && ( - )}
+
+
+ +
+ {Array.from({ length: blocks }, (_, index) => ( + + ))} +
+ + {percent}% +
+
    + {steps.map((label, index) => { + const state = stageState(index, progress.step); + const { done, total } = counts[index]; + const seconds = durations[index]; + return ( +
  1. + + {label} + + {state === "todo" || !total ? "–" : `${Math.min(done, total)}/${total}`} + + {seconds === null ? "" : durationText(seconds)} +
  2. + ); + })} +
+
+ {queued ? ( + {progress.detail} + ) : ( + stats.map(([key, value]) => ( + + {key} {value} + + )) + )} +
+
); } diff --git a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensData.test.ts b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensData.test.ts index 3be6164fce2..25276fe5a44 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensData.test.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensData.test.ts @@ -4,6 +4,10 @@ import { workerConnected, type LensList, analysisProgress, + analysisFraction, + analysisPace, + remainingLabel, + stageDurations, normalizeFilters, sortedFindings, type Finding, @@ -136,6 +140,96 @@ describe("Analysis progress", () => { }); }); +describe("Analysis pace", () => { + it("fills the bar left to right across stages without jumping backwards at a stage boundary", () => { + const endOfReview = analysisFraction({ step: 0, done: 20, total: 20 }); + const startOfGrouping = analysisFraction({ step: 1, done: 0, total: 4 }); + expect(analysisFraction({ step: -1, done: 0, total: 0 })).toBe(0); + expect(analysisFraction({ step: 0, done: 10, total: 20 })).toBeLessThan(endOfReview); + expect(startOfGrouping).toBeCloseTo(endOfReview); + expect(analysisFraction({ step: 2, done: 5, total: 5 })).toBeCloseTo(1); + }); + + it("measures speed within the current stage and projects time left from overall progress", () => { + const start = Date.parse("2026-09-30T12:00:00Z"); + const pace = analysisPace( + [ + { at: start, step: 0, done: 0, fraction: 0 }, + { at: start + 30000, step: 0, done: 30, fraction: 0.25 }, + ], + start + 30000, + ); + expect(pace.perMinute).toBe(60); + expect(pace.secondsLeft).toBe(90); + }); + + it("waits for enough samples instead of showing a wild first estimate", () => { + const start = Date.parse("2026-09-30T12:00:00Z"); + expect(analysisPace([{ at: start, step: 0, done: 1, fraction: 0.01 }], start + 2000)).toEqual({ + perMinute: null, + secondsLeft: null, + }); + expect(remainingLabel(null)).toBe("estimating"); + }); + + it("lengthens the estimate while progress stalls", () => { + const start = Date.parse("2026-09-30T12:00:00Z"); + const samples = [ + { at: start, step: 0, done: 0, fraction: 0 }, + { at: start + 10000, step: 0, done: 10, fraction: 0.1 }, + ]; + const moving = analysisPace(samples, start + 10000).secondsLeft ?? 0; + const stalled = analysisPace(samples, start + 40000).secondsLeft ?? 0; + expect(stalled).toBeGreaterThan(moving); + }); + + it("still estimates when the worker reports progress less than once a minute", () => { + const start = Date.parse("2026-09-30T12:00:00Z"); + const pace = analysisPace( + [ + { at: start, step: 2, done: 0, fraction: 0.8 }, + { at: start + 90000, step: 2, done: 1, fraction: 0.85 }, + ], + start + 90000, + ); + expect(pace.secondsLeft).toBeCloseTo(270); + expect(pace.perMinute).toBeCloseTo(2 / 3); + }); + + it("measures from the last minute rather than the whole run once updates are frequent", () => { + const start = Date.parse("2026-09-30T12:00:00Z"); + const samples = [ + { at: start, step: 0, done: 0, fraction: 0 }, + { at: start + 120000, step: 0, done: 12, fraction: 0.06 }, + { at: start + 180000, step: 0, done: 72, fraction: 0.36 }, + ]; + expect(analysisPace(samples, start + 180000).perMinute).toBe(60); + }); + + it("rounds remaining time up so the label never promises less than the estimate", () => { + expect(remainingLabel(61)).toBe("~2m"); + expect(remainingLabel(30)).toBe("<1m"); + }); +}); + +describe("Stage durations", () => { + const start = Date.parse("2026-09-30T12:00:00Z"); + const createdAt = "2026-09-30T12:00:00Z"; + + it("times finished stages from the transitions it saw and the active stage up to now", () => { + const samples = [ + { at: start + 5000, step: 0, done: 10, fraction: 0.1 }, + { at: start + 124000, step: 1, done: 0, fraction: 0.6 }, + ]; + expect(stageDurations(samples, createdAt, start + 145000)).toEqual([124, 21, null]); + }); + + it("does not guess when a stage started before the page was opened", () => { + const samples = [{ at: start + 90000, step: 1, done: 2, fraction: 0.7 }]; + expect(stageDurations(samples, createdAt, start + 100000)).toEqual([null, null, null]); + }); +}); + describe("Lens selection and findings", () => { it("preserves literal equals signs in a metadata value", () => { expect(normalizeFilters([{ key: " swarm ", value: " research=v2 " }])).toEqual([ diff --git a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensData.ts b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensData.ts index 1e30dc75a38..7aa2df9edbe 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensData.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/lens/_components/lensData.ts @@ -126,8 +126,90 @@ export function analysisProgress(job: Job) { }; } +export function analysisStages(job: Job): { done: number; total: number }[] { + const { + screened = 0, + selected = 0, + grouped_batches = 0, + grouping_batches = 0, + investigated = 0, + candidates = 0, + } = job.coverage ?? {}; + return [ + { done: screened, total: selected }, + { done: grouped_batches, total: grouping_batches }, + { done: investigated, total: candidates }, + ]; +} + +export interface ProgressSample { + at: number; + step: number; + done: number; + fraction: number; +} + +export const stageWeights = [0.6, 0.2, 0.2]; + +export function analysisFraction({ + step, + done, + total, +}: Pick, "step" | "done" | "total">): number { + if (step < 0) return 0; + const before = stageWeights.slice(0, step).reduce((sum, weight) => sum + weight, 0); + return before + stageWeights[step] * (total ? Math.min(1, done / total) : 0); +} + +function windowStart(samples: readonly ProgressSample[], now: number): ProgressSample | undefined { + return samples.findLast((sample) => now - sample.at >= 60000) ?? samples[0]; +} + +export function analysisPace(samples: readonly ProgressSample[], now: number) { + const latest = samples.at(-1); + if (!latest) return { perMinute: null, secondsLeft: null }; + const first = windowStart( + samples.filter((sample) => sample.step === latest.step), + now, + ); + const anchor = windowStart(samples, now); + if (!first || !anchor) return { perMinute: null, secondsLeft: null }; + const stepMinutes = (now - first.at) / 60000; + const perMinute = stepMinutes >= 1 / 6 ? (latest.done - first.done) / stepMinutes : null; + const spanSeconds = (now - anchor.at) / 1000; + const gained = latest.fraction - anchor.fraction; + const secondsLeft = spanSeconds >= 10 && gained > 0 ? ((1 - latest.fraction) * spanSeconds) / gained : null; + return { perMinute, secondsLeft }; +} + +export function stageDurations(samples: readonly ProgressSample[], createdAt: string, now: number): (number | null)[] { + const current = samples.at(-1)?.step ?? -1; + const starts = [0, 1, 2].map((stage) => { + if (stage === 0) return Date.parse(createdAt); + const entered = samples.findIndex( + (sample, index) => index > 0 && sample.step >= stage && samples[index - 1].step < stage, + ); + return entered < 0 ? null : samples[entered].at; + }); + return starts.map((start, stage) => { + if (start === null || stage > current) return null; + const end = stage === current ? now : starts[stage + 1]; + return end === null ? null : Math.max(0, Math.floor((end - start) / 1000)); + }); +} + +export function remainingLabel(seconds: number | null): string { + if (seconds === null) return "estimating"; + if (seconds < 60) return "<1m"; + if (seconds < 3600) return `~${Math.ceil(seconds / 60)}m`; + return `~${Math.floor(seconds / 3600)}h ${Math.ceil((seconds % 3600) / 60)}m`; +} + export function analysisElapsed(createdAt: string, now: number): string { - const seconds = Math.max(0, Math.floor((now - Date.parse(createdAt)) / 1000)); + return durationText(Math.max(0, Math.floor((now - Date.parse(createdAt)) / 1000))); +} + +export function durationText(seconds: number): string { if (!Number.isFinite(seconds)) return "0s"; if (seconds < 60) return `${seconds}s`; if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${seconds % 60}s`; From 4b9f9903f33310a416a5ae6ff79a4cdbd66a06e7 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sat, 3 Oct 2026 01:35:34 +0000 Subject: [PATCH 14/61] refactor(ui): compose dashboard pages with shared layouts (#44306) * refactor(ui): compose logs tabs directly in the route * feat(ui): share composable dashboard page layouts * refactor(ui): compose page header and logs toolbar from parts PageHeader drops its icon/title/subtitle/primaryAction/tabs/utilities props and the leadingControls render prop in favor of PageHeaderTitle, PageHeaderDescription and PageHeaderControls that each wrap one element and forward native props. LogsTableToolbar's 15 props collapse into one LogsTimeRange value plus composable LogsToolbar, LogsTimeRangePicker and LogsToolbarSwitch parts assembled in the panel. Co-Authored-By: Claude Opus 5.5 * refactor(ui): express DataTable layout classes as cva variants Replaces the hand-rolled class-pair constants with boolean cva variants, which also brings DataTable back under the complexity budget. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Yujong Lee Co-authored-by: Claude Opus 5.5 --- tests/e2e/ui/tests/logs/logs.spec.ts | 69 +++++++++ .../_components/AccessGroupsPage.tsx | 81 ++++++----- .../(dashboard)/api-keys/ApiKeysDashboard.tsx | 5 +- .../budgets/_components/budget_panel.tsx | 61 ++++---- .../_components/CostOptimizationView.tsx | 56 ++++---- .../_components/GuardrailsMonitorView.tsx | 5 +- .../_components/GuardrailsOverview.tsx | 30 ++-- .../src/app/(dashboard)/layout.tsx | 2 +- .../logs/page.integration.test.tsx} | 18 +-- .../(dashboard)/logs/page.test.tsx} | 28 ++-- .../src/app/(dashboard)/logs/page.tsx | 75 ++++++++-- .../_components/ModelInsightsView.tsx | 21 +-- .../projects/_components/ProjectsPage.tsx | 77 +++++----- .../_components/ROICalculatorView.tsx | 53 +++---- .../DeletedKeysPage/DeletedKeysPage.tsx | 7 +- .../DeletedKeysTable/DeletedKeysTable.tsx | 1 + .../DeletedTeamsPage/DeletedTeamsPage.tsx | 7 +- .../DeletedTeamsTable/DeletedTeamsTable.tsx | 1 + .../src/components/Teams.test.tsx | 10 -- ui/litellm-dashboard/src/components/Teams.tsx | 63 ++++---- .../VirtualKeysPage/VirtualKeysTable.tsx | 21 +-- .../shared/DataTable/DataTable.test.tsx | 22 --- .../components/shared/DataTable/DataTable.tsx | 94 +++++++----- .../src/components/shared/Page.tsx | 40 ++++++ .../src/components/shared/PageHeader.test.tsx | 99 +++++-------- .../src/components/shared/PageHeader.tsx | 78 ++++------ .../components/view_logs/AuditLogsPanel.tsx | 6 +- .../components/view_logs/AuditLogsTable.tsx | 1 + .../components/view_logs/LogsTableToolbar.tsx | 135 +++++++----------- .../view_logs/RequestLogsPanel.test.tsx | 8 +- .../components/view_logs/RequestLogsPanel.tsx | 68 ++++----- .../src/components/view_logs/index.tsx | 105 -------------- 32 files changed, 661 insertions(+), 686 deletions(-) rename ui/litellm-dashboard/src/{components/view_logs/index.integration.test.tsx => app/(dashboard)/logs/page.integration.test.tsx} (90%) rename ui/litellm-dashboard/src/{components/view_logs/index.test.tsx => app/(dashboard)/logs/page.test.tsx} (88%) create mode 100644 ui/litellm-dashboard/src/components/shared/Page.tsx delete mode 100644 ui/litellm-dashboard/src/components/view_logs/index.tsx diff --git a/tests/e2e/ui/tests/logs/logs.spec.ts b/tests/e2e/ui/tests/logs/logs.spec.ts index 60b547ccda0..688bfe8e3b4 100644 --- a/tests/e2e/ui/tests/logs/logs.spec.ts +++ b/tests/e2e/ui/tests/logs/logs.spec.ts @@ -54,6 +54,75 @@ test.describe("Logs page", () => { permissions: ["clipboard-read", "clipboard-write"], }); + test("log tables fill the available height and empty requests stay centered after resizing", async ({ + page, + }) => { + await navigateToPage(page, Page.Logs); + await dismissFeedbackPopup(page); + await visibleTestId(page, "datatable-search").fill( + `missing-request-${uniqueSuffix()}`, + ); + const emptyTitle = page.getByText("No matching requests", { exact: true }); + await expect(emptyTitle).toBeVisible(); + + for (const viewport of [ + { width: 1440, height: 900 }, + { width: 1024, height: 720 }, + ]) { + await page.setViewportSize(viewport); + await expect + .poll(async () => { + const frame = await visibleTestId( + page, + "data-table-frame", + ).boundingBox(); + return frame + ? Math.abs(viewport.height - frame.y - frame.height - 24) + : Infinity; + }) + .toBeLessThanOrEqual(2); + await expect + .poll(async () => { + const body = await page + .locator("table") + .filter({ visible: true }) + .first() + .locator("tbody") + .boundingBox(); + const scroller = await visibleTestId( + page, + "data-table-scroller", + ).boundingBox(); + const message = await emptyTitle.locator("..").boundingBox(); + if (!body || !message || !scroller) return Infinity; + return Math.max( + Math.abs( + message.x + message.width / 2 - scroller.x - scroller.width / 2, + ), + Math.abs(message.y + message.height / 2 - body.y - body.height / 2), + ); + }) + .toBeLessThanOrEqual(4); + for (const tab of ["Deleted Keys", "Deleted Teams"]) { + await page.getByRole("tab", { name: tab, exact: true }).click(); + await expect + .poll(async () => { + const frame = await visibleTestId( + page, + "data-table-frame", + ).boundingBox(); + return frame + ? Math.abs(viewport.height - frame.y - frame.height - 24) + : Infinity; + }) + .toBeLessThanOrEqual(2); + } + await page + .getByRole("tab", { name: "Request Logs", exact: true }) + .click(); + } + }); + test("a chat sent from the Playground lands in Logs with its content", async ({ page, request }) => { const prompt = `logs-playground-prompt-${uniqueSuffix()}`; await openPlayground(page); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsPage.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsPage.tsx index 2e82fe3c418..ac40a28a258 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsPage.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsPage.tsx @@ -1,9 +1,10 @@ +import { Page, PageContent } from "@/components/shared/Page"; import { AccessGroupResponse, useAccessGroups } from "@/app/(dashboard)/hooks/accessGroups/useAccessGroups"; import { useDeleteAccessGroup } from "@/app/(dashboard)/hooks/accessGroups/useDeleteAccessGroup"; import { Boxes, Plus, SearchIcon, X } from "lucide-react"; import { useMemo, useState } from "react"; import DeleteResourceModal from "@/components/common_components/DeleteResourceModal"; -import { PageHeader } from "@/components/shared/PageHeader"; +import { PageHeader, PageHeaderControls, PageHeaderDescription, PageHeaderTitle } from "@/components/shared/PageHeader"; import { Button } from "@/components/ui/button"; import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from "@/components/ui/input-group"; import { AccessGroupDetail } from "./AccessGroupsDetailsPage"; @@ -59,49 +60,53 @@ export function AccessGroupsPage() { } return ( -
- } - title="Access Groups" - subtitle="Manage resource permissions for your organization" - primaryAction={ - canModify ? ( + + + + + Access Groups + + Manage resource permissions for your organization + {canModify && ( + - ) : undefined - } - /> + + )} + -
- - - - - setSearchText(e.target.value)} - /> - {searchText && ( - - setSearchText("")}> - - + +
+ + + - )} - -
+ setSearchText(e.target.value)} + /> + {searchText && ( + + setSearchText("")}> + + + + )} +
+
- 0} - canModify={canModify} - onGroupClick={setSelectedGroupId} - onDeleteClick={setGroupToDelete} - /> + 0} + canModify={canModify} + onGroupClick={setSelectedGroupId} + onDeleteClick={setGroupToDelete} + /> + @@ -126,6 +131,6 @@ export function AccessGroupsPage() { }} confirmLoading={deleteMutation.isPending} /> -
+ ); } diff --git a/ui/litellm-dashboard/src/app/(dashboard)/api-keys/ApiKeysDashboard.tsx b/ui/litellm-dashboard/src/app/(dashboard)/api-keys/ApiKeysDashboard.tsx index 376fee72b88..915df2f5ded 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/api-keys/ApiKeysDashboard.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/api-keys/ApiKeysDashboard.tsx @@ -1,5 +1,6 @@ "use client"; +import { Page } from "@/components/shared/Page"; import { teamListCall as v2TeamListCall } from "@/app/(dashboard)/hooks/teams/useTeams"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; import { KeyResponse, Team } from "@/components/key_team_helpers/key_list"; @@ -71,7 +72,7 @@ export default function ApiKeysDashboard() { }, [accessToken, userID, userRole]); return ( -
+ -
+ ); } diff --git a/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_panel.tsx b/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_panel.tsx index 7455c252e26..630d90e91b9 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_panel.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_panel.tsx @@ -3,13 +3,15 @@ * */ +import { Page, PageTabs, PageTabsList, PageTabsTrigger } from "@/components/shared/Page"; import { Plus, Wallet } from "lucide-react"; import React, { useCallback, useState } from "react"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { prism } from "react-syntax-highlighter/dist/esm/styles/prism"; import { useSyntaxTheme } from "@/hooks/useSyntaxTheme"; -import { PageHeader } from "@/components/shared/PageHeader"; +import { PageHeader, PageHeaderControls, PageHeaderDescription, PageHeaderTitle } from "@/components/shared/PageHeader"; +import { ToolbarSeparator } from "@/components/shared/ToolbarSeparator"; import { Button } from "@/components/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import DeleteResourceModal from "@/components/common_components/DeleteResourceModal"; @@ -78,35 +80,30 @@ const BudgetPanel: React.FC = ({ accessToken }) => { }; return ( -
- - } - title="Budgets" - subtitle="Spend, TPM and RPM limits you can assign to customers." - primaryAction={ - canModify ? ( - - ) : undefined - } - tabs={({ leadingControls }) => ( - - {leadingControls} - - Budgets - - - Examples - - - )} - /> + + + + + + Budgets + + Spend, TPM and RPM limits you can assign to customers. + + + {canModify && ( + <> + + + + )} + Budgets + Examples + + +
@@ -174,8 +171,8 @@ const BudgetPanel: React.FC = ({ accessToken }) => {
-
-
+ + ); }; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.tsx index 0aa4f88495a..6a4c49963df 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.tsx @@ -1,12 +1,13 @@ "use client"; +import { Page, PageTabs, PageTabsList, PageTabsTrigger } from "@/components/shared/Page"; import React from "react"; import { Info, PiggyBank } from "lucide-react"; import useCan from "@/app/(dashboard)/hooks/useCan"; import { Alert, AlertDescription } from "@/components/shared/Alert"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { PageHeader } from "@/components/shared/PageHeader"; +import { TabsContent } from "@/components/ui/tabs"; +import { PageHeader, PageHeaderControls, PageHeaderDescription, PageHeaderTitle } from "@/components/shared/PageHeader"; import UsageTab from "./UsageTab"; import PromptCompressionTab from "./PromptCompressionTab"; import PromptCachingTab from "./PromptCachingTab"; @@ -33,37 +34,30 @@ const CostOptimizationView: React.FC = ({ accessToken }; return ( -
- - } - title="Cost Optimization" - subtitle="Track and configure the mechanisms that save you money: prompt compression and prompt caching. Auto routers live under Models + Endpoints, on the Auto-Routers tab" - tabs={({ leadingControls }) => ( - - {leadingControls} - - Overall - + + + + + + Cost Optimization + + + Track and configure the mechanisms that save you money: prompt compression and prompt caching. Auto routers + live under Models + Endpoints, on the Auto-Routers tab + + + + Overall {canViewProxyWideCostData && ( <> - - Prompt Compression - - - Prompt Caching - - - Auto-Router - + Prompt Compression + Prompt Caching + Auto-Router )} - - )} - /> + + +
= ({ accessToken )} - -
+ + ); }; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailsMonitorView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailsMonitorView.tsx index f90a46e19e4..1dd6686d7fe 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailsMonitorView.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailsMonitorView.tsx @@ -1,3 +1,4 @@ +import { Page } from "@/components/shared/Page"; import type { DateRangePickerValue } from "@/components/shared/date_picker_types"; import { parseAsString, useQueryState } from "nuqs"; import React, { useCallback, useMemo, useState } from "react"; @@ -48,7 +49,7 @@ export default function GuardrailsMonitorView({ accessToken = null }: Guardrails ); return ( -
+ {!selectedGuardrailId ? ( )} -
+ ); } diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailsOverview.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailsOverview.tsx index 468e6967d81..5627e7fc3cb 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailsOverview.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailsOverview.tsx @@ -18,7 +18,7 @@ import { type UsageUnits, } from "@/components/GuardrailsMonitor/usageUnits"; import { Button } from "@/components/ui/button"; -import { PageHeader } from "@/components/shared/PageHeader"; +import { PageHeader, PageHeaderControls, PageHeaderDescription, PageHeaderTitle } from "@/components/shared/PageHeader"; import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner"; import { EvaluationSettingsModal } from "./EvaluationSettingsModal"; import { MetricCard } from "@/components/GuardrailsMonitor/MetricCard"; @@ -282,20 +282,20 @@ export function GuardrailsOverview({ return (
- } - title="Guardrails Monitor" - subtitle="Monitor guardrail performance across all requests" - utilities={ - <> - {dateRangeControl} - - - } - /> + + + + Guardrails Monitor + + Monitor guardrail performance across all requests + + {dateRangeControl} + + +
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx b/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx index 72f26919060..9d8718cb84a 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx @@ -149,7 +149,7 @@ function DashboardShell({ children }: { children: React.ReactNode }) { -
{children}
+
{children}
{!isPlayground && }
diff --git a/ui/litellm-dashboard/src/components/view_logs/index.integration.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/logs/page.integration.test.tsx similarity index 90% rename from ui/litellm-dashboard/src/components/view_logs/index.integration.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/logs/page.integration.test.tsx index f2d70b74c96..dc8a57e50ba 100644 --- a/ui/litellm-dashboard/src/components/view_logs/index.integration.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/logs/page.integration.test.tsx @@ -1,8 +1,8 @@ import { screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import SpendLogsTable from "./index"; -import { renderWithProviders, testQueryClient } from "../../../tests/test-utils"; +import LogsPage from "./page"; +import { renderWithProviders, testQueryClient } from "../../../../tests/test-utils"; const { useAuthorizedMock, useOrganizationsMock } = vi.hoisted(() => ({ useAuthorizedMock: vi.fn(), @@ -17,7 +17,7 @@ vi.mock("@/app/(dashboard)/hooks/organizations/useOrganizations", () => ({ useOrganizations: useOrganizationsMock, })); -vi.mock("./RequestLogsPanel", () => ({ +vi.mock("@/components/view_logs/RequestLogsPanel", () => ({ default: function RequestLogsPanelMock() { return
; }, @@ -47,17 +47,13 @@ const defaultProps = { const ORG_ADMIN_MEMBERSHIPS = [{ organization_id: "org-1", members: [{ user_id: "user-1", user_role: "org_admin" }] }]; const renderAs = (sessionRole: string, organizations: unknown[] = []) => { - useAuthorizedMock.mockReturnValue({ - accessToken: "sk-test", - userId: "user-1", - userRole: sessionRole, - premiumUser: true, - }); + const session = { ...defaultProps, userId: defaultProps.userID, userRole: sessionRole }; + useAuthorizedMock.mockReturnValue(session); useOrganizationsMock.mockReturnValue({ data: organizations }); - return renderWithProviders(); + return renderWithProviders(); }; -describe("SpendLogsTable network access by role", () => { +describe("LogsPage network access by role", () => { beforeEach(() => { testQueryClient.clear(); vi.clearAllMocks(); diff --git a/ui/litellm-dashboard/src/components/view_logs/index.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/logs/page.test.tsx similarity index 88% rename from ui/litellm-dashboard/src/components/view_logs/index.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/logs/page.test.tsx index 7c8c14771bd..c7e0a82a363 100644 --- a/ui/litellm-dashboard/src/components/view_logs/index.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/logs/page.test.tsx @@ -1,8 +1,8 @@ import { screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import SpendLogsTable from "./index"; -import { renderWithProviders } from "../../../tests/test-utils"; +import LogsPage from "./page"; +import { renderWithProviders } from "../../../../tests/test-utils"; const { useAuthorizedMock, useOrganizationsMock } = vi.hoisted(() => ({ useAuthorizedMock: vi.fn(), @@ -17,25 +17,25 @@ vi.mock("@/app/(dashboard)/hooks/organizations/useOrganizations", () => ({ useOrganizations: useOrganizationsMock, })); -vi.mock("./RequestLogsPanel", () => ({ +vi.mock("@/components/view_logs/RequestLogsPanel", () => ({ default: function RequestLogsPanelMock({ isActive }: { isActive: boolean }) { return
{isActive ? "active" : "inactive"}
; }, })); -vi.mock("./AuditLogsPanel", () => ({ +vi.mock("@/components/view_logs/AuditLogsPanel", () => ({ default: function AuditLogsPanelMock({ isActive }: { isActive: boolean }) { return
{isActive ? "active" : "inactive"}
; }, })); -vi.mock("../DeletedKeysPage/DeletedKeysPage", () => ({ +vi.mock("@/components/DeletedKeysPage/DeletedKeysPage", () => ({ default: function DeletedKeysPageMock() { return
; }, })); -vi.mock("../DeletedTeamsPage/DeletedTeamsPage", () => ({ +vi.mock("@/components/DeletedTeamsPage/DeletedTeamsPage", () => ({ default: function DeletedTeamsPageMock() { return
; }, @@ -52,16 +52,16 @@ const defaultProps = { const ORG_ADMIN_MEMBERSHIPS = [{ organization_id: "org-1", members: [{ user_id: "user-1", user_role: "org_admin" }] }]; const renderAs = (sessionRole: string, organizations: unknown[] = []) => { - useAuthorizedMock.mockReturnValue({ userId: "user-1", userRole: sessionRole }); + useAuthorizedMock.mockReturnValue({ ...defaultProps, userId: defaultProps.userID, userRole: sessionRole }); useOrganizationsMock.mockReturnValue({ data: organizations }); - return renderWithProviders(); + return renderWithProviders(); }; const tabNames = () => screen.getAllByRole("tab").map((tab) => tab.textContent); -describe("SpendLogsTable", () => { +describe("LogsPage", () => { beforeEach(() => { - useAuthorizedMock.mockReturnValue({ userId: "user-1", userRole: "Admin" }); + useAuthorizedMock.mockReturnValue({ ...defaultProps, userId: defaultProps.userID }); useOrganizationsMock.mockReturnValue({ data: [] }); }); @@ -182,17 +182,17 @@ describe("SpendLogsTable", () => { describe("auth-not-ready guard", () => { it("shows a loading spinner when credentials are not yet resolved", () => { - useAuthorizedMock.mockReturnValue({ userRole: "Admin" }); - renderWithProviders(); + useAuthorizedMock.mockReturnValue({ ...defaultProps, userId: defaultProps.userID, accessToken: null }); + renderWithProviders(); - expect(document.querySelector('[aria-busy="true"]')).toBeInTheDocument(); + expect(screen.getByRole("status", { name: "Loading" })).toHaveAttribute("aria-busy", "true"); expect(screen.queryByRole("tab", { name: "Request Logs" })).not.toBeInTheDocument(); }); it("renders the tabs (no spinner) once all credentials are present", () => { renderAs("Admin"); - expect(document.querySelector('[aria-busy="true"]')).not.toBeInTheDocument(); + expect(screen.queryByRole("status", { name: "Loading" })).not.toBeInTheDocument(); expect(screen.getByRole("tab", { name: "Request Logs" })).toBeInTheDocument(); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/logs/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/logs/page.tsx index 88909e3b87f..51603dec29e 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/logs/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/logs/page.tsx @@ -1,17 +1,74 @@ "use client"; -import SpendLogsTable from "@/components/view_logs"; +import { useState } from "react"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; +import useCan from "@/app/(dashboard)/hooks/useCan"; +import DeletedKeysPage from "@/components/DeletedKeysPage/DeletedKeysPage"; +import DeletedTeamsPage from "@/components/DeletedTeamsPage/DeletedTeamsPage"; +import AuditLogsPanel from "@/components/view_logs/AuditLogsPanel"; +import RequestLogsPanel from "@/components/view_logs/RequestLogsPanel"; +import { Page, PageTabs, PageTabsList, PageTabsTrigger, PageTabsContent } from "@/components/shared/Page"; +import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner"; -export default function Logs() { +type LogsTab = "request logs" | "audit logs" | "deleted keys" | "deleted teams"; + +export default function LogsPage() { const { accessToken, userRole, userId, token, premiumUser } = useAuthorized(); + const [activeTab, setActiveTab] = useState("request logs"); + const canViewAuditLogs = useCan("viewAuditLogs"); + const canViewDeletedTeams = useCan("viewDeletedTeams"); + + const credentialsPending = !accessToken || !token; + const identityPending = !userRole || !userId; + + if (credentialsPending || identityPending) { + return ( +
+ +
+ ); + } + return ( - + + setActiveTab(value)}> + + Request Logs + {canViewAuditLogs && Audit Logs} + Deleted Keys + {canViewDeletedTeams && Deleted Teams} + + + + + + {canViewAuditLogs && ( + + + + )} + + + + {canViewDeletedTeams && ( + + + + )} + + ); } diff --git a/ui/litellm-dashboard/src/app/(dashboard)/model-insights/_components/ModelInsightsView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/model-insights/_components/ModelInsightsView.tsx index c946e8aabe0..ab406597589 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/model-insights/_components/ModelInsightsView.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/model-insights/_components/ModelInsightsView.tsx @@ -1,5 +1,6 @@ "use client"; +import { Page } from "@/components/shared/Page"; import React from "react"; import { Bar, BarChart, CartesianGrid, Treemap, XAxis, YAxis } from "recharts"; import { ArrowDownRight, ArrowUpRight, BarChart3, Layers, Minus } from "lucide-react"; @@ -7,7 +8,7 @@ import { ArrowDownRight, ArrowUpRight, BarChart3, Layers, Minus } from "lucide-r import { apiClient } from "@/components/networking"; import { extractErrorMessage } from "@/utils/errorUtils"; import { ProviderLogo } from "@/components/molecules/models/ProviderLogo"; -import { PageHeader } from "@/components/shared/PageHeader"; +import { PageHeader, PageHeaderDescription, PageHeaderTitle } from "@/components/shared/PageHeader"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart"; @@ -210,12 +211,16 @@ export default function ModelInsightsView({ accessToken }: { accessToken: string ) satisfies ChartConfig; return ( -
- } - title="Model Leaderboard" - subtitle={`See which models your gateway used from ${data.start_date} through ${data.end_date}`} - /> + + + + + Model Leaderboard + + + See which models your gateway used from {data.start_date} through {data.end_date} + + @@ -375,6 +380,6 @@ export default function ModelInsightsView({ accessToken }: { accessToken: string

-
+ ); } diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectsPage.tsx b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectsPage.tsx index 2d3c1acf75e..cb595916041 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectsPage.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectsPage.tsx @@ -1,9 +1,10 @@ +import { Page, PageContent } from "@/components/shared/Page"; import { useProjects } from "@/app/(dashboard)/hooks/projects/useProjects"; import { useTeams } from "@/app/(dashboard)/hooks/teams/useTeams"; import { Folder, Plus, SearchIcon, X } from "lucide-react"; import { parseAsString, useQueryState } from "nuqs"; import { useMemo, useState } from "react"; -import { PageHeader } from "@/components/shared/PageHeader"; +import { PageHeader, PageHeaderControls, PageHeaderDescription, PageHeaderTitle } from "@/components/shared/PageHeader"; import { Button } from "@/components/ui/button"; import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from "@/components/ui/input-group"; import { CreateProjectModal } from "./ProjectModals/CreateProjectModal"; @@ -56,49 +57,53 @@ export function ProjectsPage() { } return ( -
- } - title="Projects" - subtitle="Manage projects within your teams" - primaryAction={ + + + + + Projects + + Manage projects within your teams + - } - /> + + -
- - - - - setSearchText(e.target.value)} - /> - {searchText && ( - - setSearchText("")}> - - + +
+ + + - )} - -
+ setSearchText(e.target.value)} + /> + {searchText && ( + + setSearchText("")}> + + + + )} +
+
- 0} - onProjectClick={(id) => void setSelectedProjectId(id)} - teamAliasMap={teamAliasMap} - isTeamsLoading={isTeamsLoading} - /> + 0} + onProjectClick={(id) => void setSelectedProjectId(id)} + teamAliasMap={teamAliasMap} + isTeamsLoading={isTeamsLoading} + /> + setIsCreateModalVisible(false)} /> -
+ ); } diff --git a/ui/litellm-dashboard/src/app/(dashboard)/roi-calculator/_components/ROICalculatorView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/roi-calculator/_components/ROICalculatorView.tsx index 40c1104354d..2b17d2019ee 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/roi-calculator/_components/ROICalculatorView.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/roi-calculator/_components/ROICalculatorView.tsx @@ -1,11 +1,12 @@ "use client"; +import { Page } from "@/components/shared/Page"; import React from "react"; import { Calculator, RefreshCw } from "lucide-react"; import { apiClient } from "@/components/networking"; -import { PageHeader } from "@/components/shared/PageHeader"; import { DemoNotice } from "@/components/shared/DemoNotice"; +import { PageHeader, PageHeaderDescription, PageHeaderTitle } from "@/components/shared/PageHeader"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; @@ -219,33 +220,33 @@ export default function ROICalculatorView({ const syncedAt = syncIsUpToDate ? summary?.synced_at : null; return ( -
- } - title="ROI Calculator" - titleAction={ - !liveSummary && showLiveStatus ? ( + + +
+ + + ROI Calculator + + {!liveSummary && showLiveStatus && ( - ) : undefined - } - subtitle={ - <> - {summary - ? `${summary.start} through ${summary.end} · UTC` - : "Compare gateway spend with estimated engineering effort for merged pull requests"} - {syncedAt && ( - - Last synced {formatSyncedAt(syncedAt)} - {!status.running && status.phase === "complete" && status.reused > 0 - ? ` · ${status.reused} of ${status.total} estimates reused` - : ""} - - )} - - } - /> + )} +
+ + {summary + ? `${summary.start} through ${summary.end} · UTC` + : "Compare gateway spend with estimated engineering effort for merged pull requests"} + {syncedAt && ( + + Last synced {formatSyncedAt(syncedAt)} + {!status.running && status.phase === "complete" && status.reused > 0 + ? ` · ${status.reused} of ${status.total} estimates reused` + : ""} + + )} + +
{sampleSummary && setSampleSummary(null)} />} {liveSummary && showLiveStatus && (

@@ -365,6 +366,6 @@ export default function ROICalculatorView({ onSave={updateIdentity} /> )} -

+ ); } diff --git a/ui/litellm-dashboard/src/components/DeletedKeysPage/DeletedKeysPage.tsx b/ui/litellm-dashboard/src/components/DeletedKeysPage/DeletedKeysPage.tsx index 0af668b5e9c..2686806c95b 100644 --- a/ui/litellm-dashboard/src/components/DeletedKeysPage/DeletedKeysPage.tsx +++ b/ui/litellm-dashboard/src/components/DeletedKeysPage/DeletedKeysPage.tsx @@ -1,4 +1,5 @@ "use client"; +import { PageContent } from "@/components/shared/Page"; import { useState } from "react"; import { PaginationState } from "@tanstack/react-table"; import { Info } from "lucide-react"; @@ -14,9 +15,9 @@ export default function DeletedKeysPage() { const { data: keysData, isLoading } = useDeletedKeys(pagination.pageIndex + 1, pagination.pageSize); return ( -
+ {!premiumUser && ( - + Coming soon to Enterprise @@ -31,6 +32,6 @@ export default function DeletedKeysPage() { pagination={pagination} onPaginationChange={setPagination} /> -
+ ); } diff --git a/ui/litellm-dashboard/src/components/DeletedKeysPage/DeletedKeysTable/DeletedKeysTable.tsx b/ui/litellm-dashboard/src/components/DeletedKeysPage/DeletedKeysTable/DeletedKeysTable.tsx index bc6941a7860..afe507366eb 100644 --- a/ui/litellm-dashboard/src/components/DeletedKeysPage/DeletedKeysTable/DeletedKeysTable.tsx +++ b/ui/litellm-dashboard/src/components/DeletedKeysPage/DeletedKeysTable/DeletedKeysTable.tsx @@ -44,6 +44,7 @@ export function DeletedKeysTable({ return ( key.token || String(index)} diff --git a/ui/litellm-dashboard/src/components/DeletedTeamsPage/DeletedTeamsPage.tsx b/ui/litellm-dashboard/src/components/DeletedTeamsPage/DeletedTeamsPage.tsx index 8c3aac2cac7..a491fe138f6 100644 --- a/ui/litellm-dashboard/src/components/DeletedTeamsPage/DeletedTeamsPage.tsx +++ b/ui/litellm-dashboard/src/components/DeletedTeamsPage/DeletedTeamsPage.tsx @@ -1,6 +1,7 @@ "use client"; import { PaginationState } from "@tanstack/react-table"; import { Info } from "lucide-react"; +import { PageContent } from "@/components/shared/Page"; import { useState } from "react"; import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert"; import { DEFAULT_PAGE_SIZE_OPTIONS } from "@/components/shared/DataTable"; @@ -17,9 +18,9 @@ export default function DeletedTeamsPage() { const { data: teamsData, isLoading } = useDeletedTeams(pagination.pageIndex + 1, pagination.pageSize); return ( -
+ {!premiumUser && ( - + Coming soon to Enterprise @@ -34,6 +35,6 @@ export default function DeletedTeamsPage() { onPaginationChange={setPagination} rowCount={teamsData?.total ?? 0} /> -
+ ); } diff --git a/ui/litellm-dashboard/src/components/DeletedTeamsPage/DeletedTeamsTable/DeletedTeamsTable.tsx b/ui/litellm-dashboard/src/components/DeletedTeamsPage/DeletedTeamsTable/DeletedTeamsTable.tsx index c7e759754b8..236b8b7cf18 100644 --- a/ui/litellm-dashboard/src/components/DeletedTeamsPage/DeletedTeamsTable/DeletedTeamsTable.tsx +++ b/ui/litellm-dashboard/src/components/DeletedTeamsPage/DeletedTeamsTable/DeletedTeamsTable.tsx @@ -44,6 +44,7 @@ export function DeletedTeamsTable({ return ( team.team_id || String(index)} diff --git a/ui/litellm-dashboard/src/components/Teams.test.tsx b/ui/litellm-dashboard/src/components/Teams.test.tsx index 5ff95d2af0c..3b89ebdd318 100644 --- a/ui/litellm-dashboard/src/components/Teams.test.tsx +++ b/ui/litellm-dashboard/src/components/Teams.test.tsx @@ -529,15 +529,6 @@ describe("Teams - team detail deep link (?team=)", () => { expect(onUrlUpdate.mock.calls.at(-1)![0].searchParams.has("team")).toBe(false); await waitFor(() => expect(screen.queryByTestId("team-info-view")).not.toBeInTheDocument()); }); - - it("should preserve the legacy inset for the team detail view", async () => { - renderWithQueryClient(, { - searchParams: "?team=team-from-url", - }); - - await waitFor(() => expect(mockTeamInfoView).toHaveBeenCalled()); - expect(screen.getByRole("main")).toHaveClass("px-12", "py-6"); - }); }); describe("Teams - Create Team CTA is grouped with the tabs on the left", () => { @@ -553,7 +544,6 @@ describe("Teams - Create Team CTA is grouped with the tabs on the left", () => { const createButton = within(tabNav).getByTestId("create-team-button"); const firstTab = within(tabNav).getByRole("tab", { name: "Your Teams" }); - expect(screen.getByRole("main")).toHaveClass("p-8"); expect(within(tabNav).getByRole("separator")).toBeInTheDocument(); expect(createButton.compareDocumentPosition(firstTab) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); }); diff --git a/ui/litellm-dashboard/src/components/Teams.tsx b/ui/litellm-dashboard/src/components/Teams.tsx index c2a23cef83a..dfc0a59f0a0 100644 --- a/ui/litellm-dashboard/src/components/Teams.tsx +++ b/ui/litellm-dashboard/src/components/Teams.tsx @@ -1,3 +1,4 @@ +import { Page, PageTabs, PageTabsList, PageTabsTrigger } from "@/components/shared/Page"; import { useOrganizations } from "@/app/(dashboard)/hooks/organizations/useOrganizations"; import useCan from "@/app/(dashboard)/hooks/useCan"; import AvailableTeamsPanel from "@/components/team/AvailableTeamsPanel"; @@ -15,12 +16,13 @@ import { SearchSelect } from "@/components/shared/SearchSelect"; import { labelWithDocsHint, labelWithHint } from "@/components/shared/form/LabelWithHint"; import { useZodForm } from "@/lib/forms/useZodForm"; import { TagsInput } from "@/app/(dashboard)/guardrails/_components/content_filter/TagsInput"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { TabsContent } from "@/components/ui/tabs"; import { ChevronDown, Plus, Users } from "lucide-react"; import React, { useEffect, useMemo, useState } from "react"; import { z } from "zod/v4"; import { useQuery, useQueryClient } from "@tanstack/react-query"; -import { PageHeader } from "@/components/shared/PageHeader"; +import { PageHeader, PageHeaderControls, PageHeaderDescription, PageHeaderTitle } from "@/components/shared/PageHeader"; +import { ToolbarSeparator } from "@/components/shared/ToolbarSeparator"; import { Button as UIButton } from "@/components/ui/button"; import { teamsTableKeys } from "@/app/(dashboard)/hooks/teams/useTeams"; import { parseAsString, useQueryState } from "nuqs"; @@ -652,7 +654,7 @@ const Teams: React.FC = ({ accessToken, userID, userRole, premiumUser ]; return ( -
+ {selectedTeamId ? ( = ({ accessToken, userID, userRole, premiumUser premiumUser={premiumUser} /> ) : ( - - } - title="Teams" - subtitle="Manage teams, members, and their access to models and budgets" - primaryAction={ - canCreateOrManageTeams(userRole, userID, organizations) ? ( - - - Create Team - - ) : undefined - } - tabs={({ leadingControls }) => ( - - {leadingControls} + + + + + Teams + + Manage teams, members, and their access to models and budgets + + + {canCreateOrManageTeams(userRole, userID, organizations) && ( + <> + + + Create Team + + + + )} {tabItems.map((item) => ( - + {item.label} - + ))} - - )} - /> + + + {tabItems.map((item) => ( {item.children} ))} - + )} {canCreateOrManageTeams(userRole, userID, organizations) && ( @@ -1312,7 +1309,7 @@ const Teams: React.FC = ({ accessToken, userID, userRole, premiumUser )} -
+ ); }; diff --git a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx index ec405ee9cad..cc25d22e546 100644 --- a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx +++ b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx @@ -16,7 +16,8 @@ import { type UrlTableStateOptions, } from "@/components/shared/DataTable"; import { SearchSelect } from "@/components/shared/SearchSelect"; -import { PageHeader } from "@/components/shared/PageHeader"; +import { PageContent } from "@/components/shared/Page"; +import { PageHeader, PageHeaderControls, PageHeaderDescription, PageHeaderTitle } from "@/components/shared/PageHeader"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { useDebouncedValue } from "@tanstack/react-pacer/debouncer"; @@ -229,13 +230,15 @@ export function VirtualKeysTable({ headerActions }: VirtualKeysTableProps) { } return ( -
- } - title="Virtual Keys" - subtitle="Every key that authenticates requests to the gateway." - primaryAction={headerActions} - /> + + + + + Virtual Keys + + Every key that authenticates requests to the gateway. + {headerActions != null && {headerActions}} + )} /> -
+ ); } diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.test.tsx b/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.test.tsx index df88287b369..05ba7e04c39 100644 --- a/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.test.tsx +++ b/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.test.tsx @@ -899,28 +899,6 @@ describe("DataTable layout", () => { expect(screen.getByTestId("data-table-head")).toHaveClass("sticky", "bg-background"); }); - it("caps fillHeight at the parent's height instead of stretching to it, so a short table stays short", () => { - render(); - const outer = screen.getByTestId("data-table-root"); - const frame = screen.getByTestId("data-table-frame"); - const scroller = screen.getByTestId("data-table-scroller"); - - // A ceiling, not a stretch: flex-1 here would hold the footer at the bottom on a two-row table. - expect(outer).toHaveClass("max-h-full", "flex-col"); - expect(outer).not.toHaveClass("flex-1"); - expect(frame).toHaveClass("flex-col"); - expect(frame).not.toHaveClass("flex-1"); - expect(scroller).not.toHaveClass("flex-1"); - - expect(scroller).toHaveClass("min-h-0", "overflow-auto"); - expect(scroller).toHaveStyle({ maxHeight: "" }); - // Without this the Table primitive's own overflow container captures the sticky header. - expect(scroller).toHaveClass("[&_[data-slot=table-container]]:overflow-visible"); - - // Rows pass under the header, so the semi-transparent row tint alone would let them show through. - expect(screen.getByTestId("data-table-head")).toHaveClass("sticky", "bg-background"); - }); - it("leaves the default layout untouched when neither height mode is set", () => { render(); const scroller = screen.getByTestId("data-table-scroller"); diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.tsx b/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.tsx index feb1615b4e1..a94c4e34611 100644 --- a/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.tsx +++ b/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.tsx @@ -40,7 +40,7 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; -import { cn } from "@/lib/cva.config"; +import { cn, cva } from "@/lib/cva.config"; import "./columnMeta"; import { DataTablePagination, DEFAULT_PAGE_SIZE_OPTIONS } from "./DataTablePagination"; @@ -58,25 +58,40 @@ const INTERACTIVE_SELECTOR = "button, a, input, select, textarea, [role=checkbox const noop = () => {}; -/** - * Height-filling mode. The table still sizes to its rows; the parent's height is only a ceiling, so - * a short table keeps its footer under the last row and a long one scrolls its rows instead of the - * page. - */ -const FILL_CLASSES = { - outer: "flex max-h-full min-h-0 flex-col", - frame: "flex min-h-0 flex-col", - body: "min-h-0", -} as const; +const dataTableRoot = cva("w-full", { + variants: { fill: { true: "flex h-full min-h-0 flex-1 flex-col", false: null } }, +}); -const NO_FILL_CLASSES = { outer: "", frame: "", body: "" } as const; +const dataTableFrame = cva("overflow-hidden rounded-lg border border-border", { + variants: { fill: { true: "flex min-h-0 flex-1 flex-col", false: null } }, +}); -const STICKY_CLASSES = { - body: "[&_[data-slot=table-container]]:overflow-visible", - header: "bg-background", -} as const; +const dataTableScroller = cva("", { + variants: { + sticky: { true: "overflow-auto [&_[data-slot=table-container]]:overflow-visible", false: "overflow-x-auto" }, + fill: { true: "min-h-0 flex-1", false: null }, + stretchEmpty: { true: "[container-type:inline-size] [&_[data-slot=table-container]]:h-full", false: null }, + }, +}); -const NO_STICKY_CLASSES = { body: "", header: "" } as const; +const dataTableTable = cva("", { + variants: { + resizable: { true: "table-fixed", false: null }, + stretchEmpty: { true: "h-full", false: null }, + }, +}); + +const dataTableHeader = cva("", { + variants: { sticky: { true: "sticky top-0 z-sticky bg-background", false: null } }, +}); + +const dataTableBody = cva("", { + variants: { stretchEmpty: { true: "h-full", false: null } }, +}); + +const messageCell = cva("h-24 text-center align-middle text-sm whitespace-normal text-muted-foreground", { + variants: { stretch: { true: "p-0", false: null } }, +}); function columnDefId(column: ColumnDef): string | undefined { if ("id" in column && typeof column.id === "string") { @@ -314,14 +329,23 @@ function DataTableBodyRow({ ); } -function MessageRow({ colSpan, children }: { colSpan: number; children: React.ReactNode }) { +function MessageRow({ + colSpan, + children, + stretch = false, +}: { + colSpan: number; + children: React.ReactNode; + stretch?: boolean; +}) { return ( - - {children} + + {stretch ? ( +
{children}
+ ) : ( + children + )}
); @@ -598,8 +622,7 @@ export function DataTable(props: DataTableProps { @@ -636,7 +659,11 @@ export function DataTable(props: DataTableProps{noDataMessage ?? }; + return ( + + {noDataMessage ?? } + + ); } return rows.map((row) => ( (props: DataTableProps -
+
+
{toolbar !== undefined &&
{toolbar(table)}
}
- - + + {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( @@ -682,7 +706,7 @@ export function DataTable(props: DataTableProps ))} - {renderBody()} + {renderBody()} {footer !== undefined && {footer(table)}}
diff --git a/ui/litellm-dashboard/src/components/shared/Page.tsx b/ui/litellm-dashboard/src/components/shared/Page.tsx new file mode 100644 index 00000000000..2799ab43911 --- /dev/null +++ b/ui/litellm-dashboard/src/components/shared/Page.tsx @@ -0,0 +1,40 @@ +"use client"; + +import type { ComponentProps } from "react"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { cn } from "@/lib/cva.config"; + +export function Page({ className, ...props }: ComponentProps<"div">) { + return
; +} + +export function PageContent({ className, ...props }: ComponentProps<"div">) { + return
; +} + +export function PageTabs({ className, ...props }: ComponentProps) { + return ; +} + +export function PageTabsList({ className, ...props }: ComponentProps) { + return ( + + ); +} + +export function PageTabsTrigger({ className, ...props }: ComponentProps) { + return ( + + ); +} + +export function PageTabsContent({ className, ...props }: ComponentProps) { + return ; +} diff --git a/ui/litellm-dashboard/src/components/shared/PageHeader.test.tsx b/ui/litellm-dashboard/src/components/shared/PageHeader.test.tsx index 3741542abad..b3283611997 100644 --- a/ui/litellm-dashboard/src/components/shared/PageHeader.test.tsx +++ b/ui/litellm-dashboard/src/components/shared/PageHeader.test.tsx @@ -1,77 +1,54 @@ import { renderWithProviders, screen, within } from "@/../tests/test-utils"; +import { Users } from "lucide-react"; import { describe, expect, it } from "vitest"; -import { PageHeader } from "./PageHeader"; - -const identity = { - icon: Teams icon, - title: "Teams", - subtitle: "Manage teams, members, and their access to models and budgets", -}; +import { PageHeader, PageHeaderControls, PageHeaderDescription, PageHeaderTitle } from "./PageHeader"; describe("PageHeader", () => { - it("should render the page identity", () => { - renderWithProviders(); - - expect(screen.getByRole("heading", { name: "Teams" })).toBeInTheDocument(); - expect(screen.getByText("Teams icon").parentElement).toHaveAttribute("aria-hidden", "true"); - expect(screen.getByText(identity.subtitle)).toBeInTheDocument(); - }); - - it("should apply the standard title and subtext typography", () => { - renderWithProviders(); - - const icon = screen.getByText("Teams icon").parentElement; - expect(screen.getByRole("heading", { name: "Teams" })).toHaveClass("text-2xl", "font-semibold", "tracking-tight"); - expect(screen.getByText(identity.subtitle)).toHaveClass("mt-1.5", "text-sm", "text-muted-foreground"); - expect(icon).toHaveClass("size-5", "[&_svg]:size-5", "[&_svg]:stroke-[1.75]"); - expect(icon?.parentElement).toHaveClass("gap-2.5"); - }); - - it("should render the primary action, divider, tabs, and utilities in the standard control row", () => { + it("should name the page heading by its text alone when it carries an icon", () => { renderWithProviders( - Create Team} - tabs={ -
- -
- } - utilities={} - />, + + + + Teams + + Manage teams + , + ); + + expect(screen.getByRole("heading", { level: 1, name: "Teams" })).toBeInTheDocument(); + expect(screen.getByText("Manage teams")).toBeInTheDocument(); + }); + + it("should group the composed controls under one accessible label", () => { + renderWithProviders( + + Teams + + + + + , ); const controls = screen.getByRole("group", { name: "Page controls" }); - expect(controls).toHaveClass("mt-5", "h-9"); - expect(within(controls).getByRole("separator")).toHaveClass("mx-4", "h-6"); - expect(controls).toHaveTextContent("Create TeamYour TeamsRefresh"); + expect( + within(controls) + .getAllByRole("button") + .map((button) => button.textContent), + ).toEqual(["Create Team", "Refresh"]); }); - it("should omit the divider when tabs are absent", () => { - renderWithProviders(Create Team} />); - - expect(screen.queryByRole("separator")).not.toBeInTheDocument(); - }); - - it("should provide standard controls to an embedded tab shell", () => { + it("should forward native attributes to each part", () => { renderWithProviders( - Create Team} - tabs={({ leadingControls, utilities }) => ( -
- {leadingControls} - - {utilities} -
- )} - utilities={} - />, + + Teams + + , ); - const tabs = screen.getByRole("tablist"); - expect(within(tabs).getByRole("separator")).toBeInTheDocument(); - expect(tabs).toHaveTextContent("Create TeamYour TeamsRefresh"); + expect(screen.getByTestId("header")).toContainElement(screen.getByRole("heading", { name: "Teams" })); + expect(screen.getByRole("heading", { name: "Teams" })).toHaveAttribute("id", "page-title"); + expect(screen.getByRole("group", { name: "Team controls" })).toBeInTheDocument(); }); }); diff --git a/ui/litellm-dashboard/src/components/shared/PageHeader.tsx b/ui/litellm-dashboard/src/components/shared/PageHeader.tsx index 6fc5b0f3238..28a22f0a69f 100644 --- a/ui/litellm-dashboard/src/components/shared/PageHeader.tsx +++ b/ui/litellm-dashboard/src/components/shared/PageHeader.tsx @@ -1,62 +1,36 @@ "use client"; -import * as React from "react"; +import type { ComponentProps } from "react"; -import { ToolbarSeparator } from "./ToolbarSeparator"; +import { cn } from "@/lib/cva.config"; -interface EmbeddedTabsSlots { - leadingControls: React.ReactNode; - utilities: React.ReactNode; +export function PageHeader({ className, ...props }: ComponentProps<"div">) { + return
; } -interface PageHeaderProps { - title: React.ReactNode; - subtitle: React.ReactNode; - icon: React.ReactNode; - primaryAction?: React.ReactNode; - titleAction?: React.ReactNode; - tabs?: React.ReactNode | ((slots: EmbeddedTabsSlots) => React.ReactNode); - utilities?: React.ReactNode; -} - -export function PageHeader({ title, subtitle, icon, primaryAction, titleAction, tabs, utilities }: PageHeaderProps) { - const leadingControls = - primaryAction == null ? null : ( -
- {primaryAction} - {tabs != null && } -
- ); - const utilityControls = utilities == null ? null :
{utilities}
; - const hasControlRow = primaryAction != null || tabs != null || utilities != null; - +export function PageHeaderTitle({ className, ...props }: ComponentProps<"h1">) { return ( -
-
-
- -

{title}

-
- {titleAction} -
-

{subtitle}

- - {typeof tabs === "function" ? ( -
{tabs({ leadingControls, utilities: utilityControls })}
- ) : ( - hasControlRow && ( -
- {leadingControls} - {tabs} - {utilityControls != null &&
{utilityControls}
} -
- ) +

+ {...props} + /> + ); +} + +export function PageHeaderDescription({ className, ...props }: ComponentProps<"p">) { + return

; +} + +export function PageHeaderControls({ className, ...props }: ComponentProps<"div">) { + return ( +

); } diff --git a/ui/litellm-dashboard/src/components/view_logs/AuditLogsPanel.tsx b/ui/litellm-dashboard/src/components/view_logs/AuditLogsPanel.tsx index abc00b75884..6480cb59682 100644 --- a/ui/litellm-dashboard/src/components/view_logs/AuditLogsPanel.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/AuditLogsPanel.tsx @@ -100,7 +100,7 @@ export default function AuditLogsPanel({ if (!premiumUser) { return ( -
+

✨ Enterprise Feature.

This is a LiteLLM Enterprise feature, and requires a valid key to use. @@ -128,10 +128,6 @@ export default function AuditLogsPanel({ return ( <> -

-

Audit Logs

-
- row.id} diff --git a/ui/litellm-dashboard/src/components/view_logs/LogsTableToolbar.tsx b/ui/litellm-dashboard/src/components/view_logs/LogsTableToolbar.tsx index 60657872ccc..3496b709d9b 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogsTableToolbar.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogsTableToolbar.tsx @@ -2,72 +2,59 @@ import moment from "moment"; import { CalendarDays } from "lucide-react"; -import { useState } from "react"; +import { type ComponentProps, useState } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Switch } from "@/components/ui/switch"; +import { cn } from "@/lib/cva.config"; import { QUICK_SELECT_OPTIONS } from "./constants"; import { getTimeRangeDisplay } from "./logs_utils"; -interface LogsTableToolbarProps { +const DATETIME_LOCAL_FORMAT = "YYYY-MM-DDTHH:mm"; + +export interface LogsTimeRange { startTime: string; - onStartTimeChange: (value: string) => void; endTime: string; - onEndTimeChange: (value: string) => void; isCustomDate: boolean; - onIsCustomDateChange: (value: boolean) => void; - selectedTimeInterval: { value: number; unit: string }; - onSelectedTimeIntervalChange: (value: { value: number; unit: string }) => void; - isLiveTail: boolean; - onIsLiveTailChange: (value: boolean) => void; - excludeInternalHealthChecks: boolean; - onExcludeInternalHealthChecksChange: (value: boolean) => void; - onResetToFirstPage: () => void; - onResetFilters: () => void; + interval: { value: number; unit: string }; } -export function LogsTableToolbar({ - startTime, - onStartTimeChange, - endTime, - onEndTimeChange, - isCustomDate, - onIsCustomDateChange, - selectedTimeInterval, - onSelectedTimeIntervalChange, - isLiveTail, - onIsLiveTailChange, - excludeInternalHealthChecks, - onExcludeInternalHealthChecksChange, - onResetToFirstPage, - onResetFilters, -}: LogsTableToolbarProps) { - const [quickSelectOpen, setQuickSelectOpen] = useState(false); +const relativeTimeRange = (interval: LogsTimeRange["interval"]): LogsTimeRange => ({ + startTime: moment() + .subtract(interval.value, interval.unit as moment.unitOfTime.DurationConstructor) + .format(DATETIME_LOCAL_FORMAT), + endTime: moment().format(DATETIME_LOCAL_FORMAT), + isCustomDate: false, + interval, +}); - const applyQuickSelect = (option: { label: string; value: number; unit: string }) => { - onResetToFirstPage(); - onEndTimeChange(moment().format("YYYY-MM-DDTHH:mm")); - onStartTimeChange( - moment() - .subtract(option.value, option.unit as moment.unitOfTime.DurationConstructor) - .format("YYYY-MM-DDTHH:mm"), - ); - onSelectedTimeIntervalChange({ value: option.value, unit: option.unit }); - onIsCustomDateChange(false); - setQuickSelectOpen(false); - }; +export const defaultLogsTimeRange = (): LogsTimeRange => relativeTimeRange({ value: 24, unit: "hours" }); + +export function LogsToolbar({ className, ...props }: ComponentProps<"div">) { + return
; +} + +interface LogsTimeRangePickerProps { + value: LogsTimeRange; + onValueChange: (value: LogsTimeRange) => void; +} + +export function LogsTimeRangePicker({ value, onValueChange }: LogsTimeRangePickerProps) { + const [open, setOpen] = useState(false); const selectedOption = QUICK_SELECT_OPTIONS.find( - (option) => option.value === selectedTimeInterval.value && option.unit === selectedTimeInterval.unit, + (option) => option.value === value.interval.value && option.unit === value.interval.unit, ); - const displayLabel = isCustomDate ? getTimeRangeDisplay(isCustomDate, startTime, endTime) : selectedOption?.label; + const displayLabel = value.isCustomDate + ? getTimeRangeDisplay(true, value.startTime, value.endTime) + : selectedOption?.label; return ( -
- + <> + @@ -83,7 +70,10 @@ export function LogsTableToolbar({ key={option.label} variant="ghost" className="w-full justify-start font-normal" - onClick={() => applyQuickSelect(option)} + onClick={() => { + onValueChange(relativeTimeRange({ value: option.value, unit: option.unit })); + setOpen(false); + }} > {option.label} @@ -92,10 +82,7 @@ export function LogsTableToolbar({ @@ -103,58 +90,32 @@ export function LogsTableToolbar({ - {isCustomDate && ( + {value.isCustomDate && (
{ - onStartTimeChange(event.target.value); - onResetToFirstPage(); - }} + value={value.startTime} + onChange={(event) => onValueChange({ ...value, startTime: event.target.value })} /> to { - onEndTimeChange(event.target.value); - onResetToFirstPage(); - }} + value={value.endTime} + onChange={(event) => onValueChange({ ...value, endTime: event.target.value })} />
)} - -
- Live Tail - -
- -
- Hide Health Checks - -
- - -
+ ); } -export function LiveTailBanner({ onStop }: { onStop: () => void }) { +export function LogsToolbarSwitch({ label, ...props }: ComponentProps & { label: string }) { return ( -
- Auto-refreshing every 15 seconds - +
+ {label} +
); } diff --git a/ui/litellm-dashboard/src/components/view_logs/RequestLogsPanel.test.tsx b/ui/litellm-dashboard/src/components/view_logs/RequestLogsPanel.test.tsx index 8a0e529e2e8..aefbf68d662 100644 --- a/ui/litellm-dashboard/src/components/view_logs/RequestLogsPanel.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/RequestLogsPanel.test.tsx @@ -792,15 +792,15 @@ describe("RequestLogsPanel", () => { }); describe("live tail", () => { - it("shows the auto-refresh banner on the first page and hides it once stopped", async () => { + it("shows the refresh status on the first page and hides it when live tail is disabled", async () => { const user = userEvent.setup(); renderPanel(); - expect(await screen.findByText("Auto-refreshing every 15 seconds")).toBeInTheDocument(); + expect(await screen.findByText("Refreshing every 15s")).toBeInTheDocument(); - await user.click(screen.getByRole("button", { name: "Stop" })); + await user.click(screen.getByRole("switch", { name: "Live Tail" })); - expect(screen.queryByText("Auto-refreshing every 15 seconds")).not.toBeInTheDocument(); + expect(screen.queryByText("Refreshing every 15s")).not.toBeInTheDocument(); }); }); }); diff --git a/ui/litellm-dashboard/src/components/view_logs/RequestLogsPanel.tsx b/ui/litellm-dashboard/src/components/view_logs/RequestLogsPanel.tsx index 0b5e5ff9616..ff26e30baca 100644 --- a/ui/litellm-dashboard/src/components/view_logs/RequestLogsPanel.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/RequestLogsPanel.tsx @@ -7,6 +7,7 @@ import moment from "moment"; import { useCallback, useEffect, useMemo, useState } from "react"; import { DEFAULT_PAGE_SIZE_OPTIONS } from "@/components/shared/DataTable"; +import { Button } from "@/components/ui/button"; import { AutoRouterModelGroupsProvider } from "@/components/shared/table_cells"; import { DEBOUNCE_WAIT_MS } from "@/utils/debounceConstants"; import type { KeyResponse } from "../key_team_helpers/key_list"; @@ -23,11 +24,16 @@ import { } from "./log_filter_logic"; import { useLogDetailRouting } from "./logDetailRouting"; import { LogDetailsDrawer } from "./LogDetailsDrawer"; -import { LiveTailBanner, LogsTableToolbar } from "./LogsTableToolbar"; +import { + defaultLogsTimeRange, + type LogsTimeRange, + LogsTimeRangePicker, + LogsToolbar, + LogsToolbarSwitch, +} from "./LogsTableToolbar"; import { RequestLogsTable } from "./RequestLogsTable"; const PAGE_SIZE = DEFAULT_PAGE_SIZE_OPTIONS[0]; -const DEFAULT_INTERVAL = { value: 24, unit: "hours" }; const matchesLogId = (log: LogEntry, logId: string) => log.request_id === logId || log.litellm_call_id === logId; const findLogById = (logs: readonly LogEntry[], logId: string): LogEntry | null => logs.find((log) => log.request_id === logId) ?? logs.find((log) => log.litellm_call_id === logId) ?? null; @@ -46,10 +52,8 @@ export default function RequestLogsPanel({ accessToken, token, userRole, userID, const [columnFilters, setColumnFilters] = useState([]); const [sessionCursors, setSessionCursors] = useState>({}); - const [startTime, setStartTime] = useState(moment().subtract(24, "hours").format("YYYY-MM-DDTHH:mm")); - const [endTime, setEndTime] = useState(moment().format("YYYY-MM-DDTHH:mm")); - const [isCustomDate, setIsCustomDate] = useState(false); - const [selectedTimeInterval, setSelectedTimeInterval] = useState<{ value: number; unit: string }>(DEFAULT_INTERVAL); + const [timeRange, setTimeRange] = useState(defaultLogsTimeRange); + const { startTime, endTime, isCustomDate } = timeRange; const [selectedKeyIdInfoView, setSelectedKeyIdInfoView] = useState(null); const [selectedLog, setSelectedLog] = useState(null); @@ -232,12 +236,17 @@ export default function RequestLogsPanel({ accessToken, token, userRole, userID, [resetToFirstPage], ); + const handleTimeRangeChange = useCallback( + (value: LogsTimeRange) => { + setTimeRange(value); + resetToFirstPage(); + }, + [resetToFirstPage], + ); + const handleResetFilters = useCallback(() => { setColumnFilters([]); - setStartTime(moment().subtract(24, "hours").format("YYYY-MM-DDTHH:mm")); - setEndTime(moment().format("YYYY-MM-DDTHH:mm")); - setIsCustomDate(false); - setSelectedTimeInterval(DEFAULT_INTERVAL); + setTimeRange(defaultLogsTimeRange()); resetToFirstPage(); }, [resetToFirstPage]); @@ -288,12 +297,6 @@ export default function RequestLogsPanel({ accessToken, token, userRole, userID, return ( -
-

Request Logs

-
- - {isLiveTail && pagination.pageIndex === 0 && setIsLiveTail(false)} />} - + + + + {isLiveTail && pagination.pageIndex === 0 && ( + + Refreshing every 15s + + )} + + + } /> diff --git a/ui/litellm-dashboard/src/components/view_logs/index.tsx b/ui/litellm-dashboard/src/components/view_logs/index.tsx deleted file mode 100644 index fe3b99a1882..00000000000 --- a/ui/litellm-dashboard/src/components/view_logs/index.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { useState } from "react"; -import useCan from "@/app/(dashboard)/hooks/useCan"; -import DeletedKeysPage from "../DeletedKeysPage/DeletedKeysPage"; -import DeletedTeamsPage from "../DeletedTeamsPage/DeletedTeamsPage"; -import AuditLogsPanel from "./AuditLogsPanel"; -import RequestLogsPanel from "./RequestLogsPanel"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner"; - -interface SpendLogsTableProps { - accessToken: string | null; - token: string | null; - userRole: string | null; - userID: string | null; - premiumUser: boolean; -} - -type LogsTabId = "request logs" | "audit logs" | "deleted keys" | "deleted teams"; - -interface LogsTab { - id: LogsTabId; - label: string; -} - -const REQUEST_LOGS_TAB: LogsTab = { id: "request logs", label: "Request Logs" }; -const AUDIT_LOGS_TAB: LogsTab = { id: "audit logs", label: "Audit Logs" }; -const DELETED_KEYS_TAB: LogsTab = { id: "deleted keys", label: "Deleted Keys" }; -const DELETED_TEAMS_TAB: LogsTab = { id: "deleted teams", label: "Deleted Teams" }; - -const tabContentClassName = (tabId: LogsTabId): string => - tabId === REQUEST_LOGS_TAB.id ? "flex min-h-0 flex-1 flex-col" : "min-h-0 flex-1 overflow-y-auto"; - -export default function SpendLogsTable({ accessToken, token, userRole, userID, premiumUser }: SpendLogsTableProps) { - const [activeTab, setActiveTab] = useState(REQUEST_LOGS_TAB.id); - const canViewAuditLogs = useCan("viewAuditLogs"); - const canViewDeletedTeams = useCan("viewDeletedTeams"); - - if (!accessToken || !token || !userRole || !userID) { - return ( -
- -
- ); - } - - const tabs: LogsTab[] = [ - REQUEST_LOGS_TAB, - ...(canViewAuditLogs ? [AUDIT_LOGS_TAB] : []), - DELETED_KEYS_TAB, - ...(canViewDeletedTeams ? [DELETED_TEAMS_TAB] : []), - ]; - - const renderPanel = (tabId: LogsTabId) => { - switch (tabId) { - case "request logs": - return ( - - ); - case "audit logs": - return ( - - ); - case "deleted keys": - return ; - case "deleted teams": - return ; - } - }; - - return ( -
- setActiveTab(value as LogsTabId)} - className="min-h-0 flex-1 gap-0" - > - - {tabs.map((tab) => ( - - {tab.label} - - ))} - - {tabs.map((tab) => ( - - {renderPanel(tab.id)} - - ))} - -
- ); -} From 677205b3f5fe918e537a60dbd01c91c9b209c30e Mon Sep 17 00:00:00 2001 From: yujonglee Date: Fri, 2 Oct 2026 18:38:54 -0700 Subject: [PATCH 15/61] fix(proxy): share ownership permissions for spend logs and traces (#44239) * refactor(proxy): extract shared spend log read policy * test(proxy): use named bindings for spend scope regression * test(proxy): reuse existing spend log query harness * test(proxy): cover spend log permission lookup adoption * chore(proxy): relocate existing spend query baseline * refactor(proxy): make scope query returns explicit * refactor(proxy): inject deferred log permission lookup * test(proxy): cover teamless management compatibility lookup * refactor(proxy): compose user and team log grants * refactor(proxy): share generic authorization composition * refactor(proxy): compose trace read permissions * refactor(proxy): centralize spend and trace authorization * refactor(proxy): strengthen spend and trace scope types * refactor(proxy): flatten log read scope into owned logs Replace the AnyOf grant tree with a flat OwnedLogs(user_id, team_ids) scope, and OwnedTraces(logs, api_key_hash) for traces, since every consumer flattened the tree back into that shape. A caller with no user id now gets an empty scope instead of matching ownerless rows through Prisma's IS NULL. The dead request_id guard in ui_view_spend_logs is removed, and the management facets inject the log team lookup and reuse read_scope_sql instead of the list shim. Co-Authored-By: Claude Opus 5.5 * test(proxy): run spend scope tests through one SQLite emulator Replace the string-matching payload emulator and the hand-rolled Prisma where interpreter with one SQLite helper that runs the real scope SQL. Session scope tests now go through the endpoint, including the no-user caller that must not match ownerless rows. Drop duplicated lookup-failure and trace mapping cases. load_permitted_log_team_ids returns no teams without a database instead of relying on the resolver's broad except. Co-Authored-By: Claude Opus 5.5 * fix(proxy): unify log and trace ownership permissions * test(tracing): align fixtures with ownership read scopes * refactor(tracing): align query scopes with row ownership * refactor(spend): make ownership SQL predicates explicit * test(spend): validate ownership SQL against PostgreSQL * docs(traces): drop key-row visibility from query help guide Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test(spend): reach the empty-memberships branch in team lookup test Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * chore(ui): regenerate dashboard API types Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Claude Opus 5.5 Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../traces-clickhouse/query/list_traces.sql | 3 +- .../traces-clickhouse/query/span_detail.sql | 6 +- .../traces-clickhouse/query/span_error.sql | 3 +- .../query/spend_by_response_ids.sql | 3 +- .../query/trace_identity.sql | 3 +- .../traces-clickhouse/query/trace_spans.sql | 3 +- .../crates/traces-clickhouse/src/query.rs | 2 +- .../traces-clickhouse/src/query/named.rs | 6 +- .../traces-clickhouse/src/query_access.rs | 56 +--- .../crates/traces-clickhouse/src/sql.rs | 2 +- .../templates/query_help.jinja | 2 +- .../traces-clickhouse/tests/admin_sql.rs | 2 +- .../traces-clickhouse/tests/migrations.rs | 60 ++-- .../crates/traces-clickhouse/tests/queries.rs | 43 +-- .../tests/queries/read_access.json | 6 +- .../traces-clickhouse/tests/query_access.rs | 45 +-- litellm-rust/crates/traces/src/query/named.rs | 1 - .../crates/traces/src/query_access.rs | 25 +- .../crates/traces/tests/query/named.rs | 16 +- .../crates/traces/tests/query_access.rs | 40 ++- litellm/proxy/auth/authorization.py | 77 +++++ .../proxy/auth/authorization_dependencies.py | 57 ++++ .../management_v1/spend_logs.py | 44 +-- .../proxy/spend_tracking/log_visibility.py | 44 --- .../spend_management_endpoints.py | 247 +++++---------- litellm/proxy/tracing_endpoints.py | 69 ++--- litellm/rust_bridge/trace_queries.py | 6 - litellm/rust_bridge/traces.py | 22 +- litellm/tracing/types.py | 1 - .../unbounded_in_baseline.txt | 5 +- .../spend/test_spend_log_read_scope.py | 226 ++++++++++++++ tests/test_litellm/tracing/test_receiver.py | 2 +- tests/test_litellm/tracing/test_store.py | 22 +- tests/test_litellm_rust/test_traces.py | 41 ++- tests/unit/proxy/auth/test_authorization.py | 29 ++ .../common_utils/test_http_parsing_utils.py | 2 +- .../management_v1/test_spend_logs.py | 62 ++-- .../spend_tracking/test_log_visibility.py | 47 --- .../test_spend_management_endpoints.py | 287 ++++++++---------- .../test_spend_query_optimization.py | 28 +- tests/unit/proxy/test_tracing_endpoints.py | 211 ++++++++++--- tests/unit/rust_bridge/test_trace_queries.py | 2 - ui/litellm-dashboard/src/lib/http/schema.d.ts | 37 ++- 43 files changed, 1051 insertions(+), 844 deletions(-) create mode 100644 litellm/proxy/auth/authorization.py create mode 100644 litellm/proxy/auth/authorization_dependencies.py delete mode 100644 litellm/proxy/spend_tracking/log_visibility.py create mode 100644 tests/integration/spend/test_spend_log_read_scope.py create mode 100644 tests/unit/proxy/auth/test_authorization.py delete mode 100644 tests/unit/proxy/spend_tracking/test_log_visibility.py diff --git a/litellm-rust/crates/traces-clickhouse/query/list_traces.sql b/litellm-rust/crates/traces-clickhouse/query/list_traces.sql index 1598f04aba2..c52adf7ef49 100644 --- a/litellm-rust/crates/traces-clickhouse/query/list_traces.sql +++ b/litellm-rust/crates/traces-clickhouse/query/list_traces.sql @@ -18,8 +18,7 @@ SELECT TraceId AS trace_id, FROM agent_traces_by_key WHERE ({all_teams:UInt8} = 1 OR ({user_id:String} != '' AND UserIds = [{user_id:String}]) - OR has({team_ids:Array(String)}, TeamId) - OR ({api_key_hash:String} != '' AND ApiKeyHash = {api_key_hash:String})) + OR has({team_ids:Array(String)}, TeamId)) GROUP BY TeamId, ApiKeyHash, TraceId HAVING min(StartTs) >= fromUnixTimestamp64Milli({start_ms:Int64}) AND min(StartTs) < fromUnixTimestamp64Milli({end_ms:Int64}) diff --git a/litellm-rust/crates/traces-clickhouse/query/span_detail.sql b/litellm-rust/crates/traces-clickhouse/query/span_detail.sql index 4da48e00d4b..7db742ea3ee 100644 --- a/litellm-rust/crates/traces-clickhouse/query/span_detail.sql +++ b/litellm-rust/crates/traces-clickhouse/query/span_detail.sql @@ -9,8 +9,7 @@ LEFT JOIN ( AND ObservationType = 'llm' AND Output != '' AND ({all_teams:UInt8} = 1 OR ({user_id:String} != '' AND UserId = {user_id:String}) - OR has({team_ids:Array(String)}, TeamId) - OR ({api_key_hash:String} != '' AND ApiKeyHash = {api_key_hash:String})) + OR has({team_ids:Array(String)}, TeamId)) AND ({trace_ref:String} = '' OR hex(SHA256(concat(TeamId, char(0), ApiKeyHash, char(0), TraceId))) = {trace_ref:String}) GROUP BY TeamId, ApiKeyHash, ParentSpanId @@ -19,8 +18,7 @@ LEFT JOIN ( WHERE o.TraceId = {trace_id:String} AND o.SpanId = {span_id:String} AND ({all_teams:UInt8} = 1 OR ({user_id:String} != '' AND o.UserId = {user_id:String}) - OR has({team_ids:Array(String)}, o.TeamId) - OR ({api_key_hash:String} != '' AND o.ApiKeyHash = {api_key_hash:String})) + OR has({team_ids:Array(String)}, o.TeamId)) AND ({trace_ref:String} = '' OR hex(SHA256(concat(o.TeamId, char(0), o.ApiKeyHash, char(0), o.TraceId))) = {trace_ref:String}) LIMIT 1 diff --git a/litellm-rust/crates/traces-clickhouse/query/span_error.sql b/litellm-rust/crates/traces-clickhouse/query/span_error.sql index 087962227a8..e1226c4d23c 100644 --- a/litellm-rust/crates/traces-clickhouse/query/span_error.sql +++ b/litellm-rust/crates/traces-clickhouse/query/span_error.sql @@ -6,8 +6,7 @@ FROM otel_traces WHERE TraceId = {trace_id:String} AND SpanId = {span_id:String} AND ({all_teams:UInt8} = 1 OR ({user_id:String} != '' AND UserId = {user_id:String}) - OR has({team_ids:Array(String)}, TeamId) - OR ({api_key_hash:String} != '' AND ApiKeyHash = {api_key_hash:String})) + OR has({team_ids:Array(String)}, TeamId)) AND ({trace_ref:String} = '' OR hex(SHA256(concat(TeamId, char(0), ApiKeyHash, char(0), TraceId))) = {trace_ref:String}) AND ({error_version:String} = '' OR hex(SHA256(StatusMessage)) = {error_version:String}) diff --git a/litellm-rust/crates/traces-clickhouse/query/spend_by_response_ids.sql b/litellm-rust/crates/traces-clickhouse/query/spend_by_response_ids.sql index eb132099ac5..963c832232b 100644 --- a/litellm-rust/crates/traces-clickhouse/query/spend_by_response_ids.sql +++ b/litellm-rust/crates/traces-clickhouse/query/spend_by_response_ids.sql @@ -6,6 +6,5 @@ WHERE response_id IN {response_ids:Array(String)} AND start_time < fromUnixTimestamp64Milli({end_ms:Int64}) AND ({all_teams:UInt8} = 1 OR ({user_id:String} != '' AND user = {user_id:String}) - OR has({team_ids:Array(String)}, team_id) - OR ({api_key_hash:String} != '' AND api_key = {api_key_hash:String})) + OR has({team_ids:Array(String)}, team_id)) ORDER BY start_time DESC diff --git a/litellm-rust/crates/traces-clickhouse/query/trace_identity.sql b/litellm-rust/crates/traces-clickhouse/query/trace_identity.sql index 6b860065468..e3881b150b7 100644 --- a/litellm-rust/crates/traces-clickhouse/query/trace_identity.sql +++ b/litellm-rust/crates/traces-clickhouse/query/trace_identity.sql @@ -3,7 +3,6 @@ FROM otel_traces WHERE TraceId = {trace_id:String} AND ({all_teams:UInt8} = 1 OR ({user_id:String} != '' AND UserId = {user_id:String}) - OR has({team_ids:Array(String)}, TeamId) - OR ({api_key_hash:String} != '' AND ApiKeyHash = {api_key_hash:String})) + OR has({team_ids:Array(String)}, TeamId)) GROUP BY TeamId, ApiKeyHash, TraceId LIMIT 2 diff --git a/litellm-rust/crates/traces-clickhouse/query/trace_spans.sql b/litellm-rust/crates/traces-clickhouse/query/trace_spans.sql index ade1fdf0d86..f00329d8136 100644 --- a/litellm-rust/crates/traces-clickhouse/query/trace_spans.sql +++ b/litellm-rust/crates/traces-clickhouse/query/trace_spans.sql @@ -12,8 +12,7 @@ FROM otel_traces AS o WHERE o.TraceId = {trace_id:String} AND ({all_teams:UInt8} = 1 OR ({user_id:String} != '' AND o.UserId = {user_id:String}) - OR has({team_ids:Array(String)}, o.TeamId) - OR ({api_key_hash:String} != '' AND o.ApiKeyHash = {api_key_hash:String})) + OR has({team_ids:Array(String)}, o.TeamId)) AND ({trace_ref:String} = '' OR hex(SHA256(concat(o.TeamId, char(0), o.ApiKeyHash, char(0), o.TraceId))) = {trace_ref:String}) ORDER BY o.Timestamp, o.EngineReceivedMs, o.StatusMessage diff --git a/litellm-rust/crates/traces-clickhouse/src/query.rs b/litellm-rust/crates/traces-clickhouse/src/query.rs index 459f0f95fe7..9f9c5463b7e 100644 --- a/litellm-rust/crates/traces-clickhouse/src/query.rs +++ b/litellm-rust/crates/traces-clickhouse/src/query.rs @@ -425,7 +425,7 @@ pub async fn query_help(client: &Client, connection: &Connection) -> Result( - json!({"all_teams": 0, "user_id": "user", "team_ids": ["team-a", "team-b"], "api_key_hash": "key", "start_ms": -1, "end_ms": 10, "cursor_ms": 0, "cursor_trace_id": "", "limit": u32::MAX}), + json!({"all_teams": 0, "user_id": "user", "team_ids": ["team-a", "team-b"], "start_ms": -1, "end_ms": 10, "cursor_ms": 0, "cursor_trace_id": "", "limit": u32::MAX}), quoted, ); round_trip::( - json!({"all_teams": 0, "user_id": "", "team_ids": [], "api_key_hash": "key", "trace_id": "trace", "trace_ref": "ref", "span_id": "span", "error_offset": u64::MAX, "error_version": "version"}), + json!({"all_teams": 0, "user_id": "", "team_ids": [], "trace_id": "trace", "trace_ref": "ref", "span_id": "span", "error_offset": u64::MAX, "error_version": "version"}), quoted, ); round_trip::( - json!({"all_teams": 0, "user_id": "user", "team_ids": ["team-a", "team-b"], "api_key_hash": "", "response_ids": ["response"], "start_ms": -1, "end_ms": 10}), + json!({"all_teams": 0, "user_id": "user", "team_ids": ["team-a", "team-b"], "response_ids": ["response"], "start_ms": -1, "end_ms": 10}), quoted, ); } diff --git a/litellm-rust/crates/traces-clickhouse/src/query_access.rs b/litellm-rust/crates/traces-clickhouse/src/query_access.rs index 5d82a5e2e93..e6ca322098e 100644 --- a/litellm-rust/crates/traces-clickhouse/src/query_access.rs +++ b/litellm-rust/crates/traces-clickhouse/src/query_access.rs @@ -176,17 +176,13 @@ impl QueryReaders { } fn predicate(scope: &QueryScope, table: TraceTable) -> String { - let (team, key) = match table { - TraceTable::OtelTraces | TraceTable::AgentTracesByKey => ("TeamId", "ApiKeyHash"), - TraceTable::SpendLogs => ("team_id", "api_key"), + let team = match table { + TraceTable::OtelTraces | TraceTable::AgentTracesByKey => "TeamId", + TraceTable::SpendLogs => "team_id", }; match scope { - QueryScope::Admin => "1".to_owned(), - QueryScope::Logs { - user_id, - team_ids, - api_key_hash, - } => { + QueryScope::All => "1".to_owned(), + QueryScope::Owned { user_id, team_ids } => { let owner = literal(user_id); let user_clause = match table { TraceTable::OtelTraces => format!("UserId = {owner}"), @@ -203,21 +199,8 @@ fn predicate(scope: &QueryScope, table: TraceTable) -> String { } else { format!("{team} IN ({teams})") }; - format!( - "({owner} != '' AND {user_clause}) OR ({team_clause}) OR ({hash} != '' AND {key} = {hash})", - owner = owner, - hash = literal(api_key_hash), - ) + format!("({owner} != '' AND {user_clause}) OR ({team_clause})") } - QueryScope::Team { team_id } => format!("{team} = {}", literal(team_id)), - QueryScope::Key { - team_id, - api_key_hash, - } => format!( - "{team} = {} AND {key} = {}", - literal(team_id), - literal(api_key_hash) - ), } } @@ -239,33 +222,24 @@ mod tests { use rstest::rstest; #[rstest] - #[case::otel(TraceTable::OtelTraces, "TeamId", "ApiKeyHash")] - #[case::agent(TraceTable::AgentTracesByKey, "TeamId", "ApiKeyHash")] - #[case::spend(TraceTable::SpendLogs, "team_id", "api_key")] + #[case::otel(TraceTable::OtelTraces, "TeamId", "UserId = ''")] + #[case::agent(TraceTable::AgentTracesByKey, "TeamId", "UserIds = ['']")] + #[case::spend(TraceTable::SpendLogs, "team_id", "user = ''")] fn predicates_preserve_scope_and_escape_values( #[case] table: TraceTable, #[case] team: &str, - #[case] key: &str, + #[case] user: &str, ) { - assert_eq!(predicate(&QueryScope::Admin, table), "1"); + assert_eq!(predicate(&QueryScope::All, table), "1"); assert_eq!( predicate( - &QueryScope::Team { - team_id: "team'\\".into() + &QueryScope::Owned { + user_id: String::new(), + team_ids: vec!["team'\\".into()] }, table ), - format!("{team} = 'team\\'\\\\'") - ); - assert_eq!( - predicate( - &QueryScope::Key { - team_id: "".into(), - api_key_hash: "key'\\".into() - }, - table - ), - format!("{team} = '' AND {key} = 'key\\'\\\\'") + format!("('' != '' AND {user}) OR ({team} IN ('team\\'\\\\'))") ); } } diff --git a/litellm-rust/crates/traces-clickhouse/src/sql.rs b/litellm-rust/crates/traces-clickhouse/src/sql.rs index 0f739314418..16a610ca13a 100644 --- a/litellm-rust/crates/traces-clickhouse/src/sql.rs +++ b/litellm-rust/crates/traces-clickhouse/src/sql.rs @@ -64,7 +64,7 @@ mod tests { #[case] specific: serde_json::Value, ) { let common = serde_json::json!({ - "all_teams": 1, "user_id": "", "team_ids": [], "api_key_hash": "", "trace_id": "trace", "trace_ref": "" + "all_teams": 1, "user_id": "", "team_ids": [], "trace_id": "trace", "trace_ref": "" }); let parameters: BTreeMap = common .as_object() diff --git a/litellm-rust/crates/traces-clickhouse/templates/query_help.jinja b/litellm-rust/crates/traces-clickhouse/templates/query_help.jinja index 6e746221022..3bea0ff4efe 100644 --- a/litellm-rust/crates/traces-clickhouse/templates/query_help.jinja +++ b/litellm-rust/crates/traces-clickhouse/templates/query_help.jinja @@ -46,7 +46,7 @@ Gotchas {% block reader_limits %}The reader enforces {{ limits.result_rows }} result rows, {{ limits.result_mib() }} MiB response bytes, {{ limits.memory_mib() }} MiB memory and a {{ limits.execution_seconds }} second query limit; exceeding limits fails instead of returning partial results{% endblock %} -{% block reader_profile %}LiteLLM provisions SELECT-only readers from the configured ClickHouse connection and enforces request-log visibility through row policies. Callers see their own user rows and permitted teams, or their own key rows when no user identity is available. Provisioning requires CREATE USER, ALTER USER, CREATE ROW POLICY, and GRANT SELECT permissions{% endblock %} +{% block reader_profile %}LiteLLM provisions SELECT-only readers from the configured ClickHouse connection and enforces request-log visibility through row policies. Callers see their own user rows and permitted teams. Provisioning requires CREATE USER, ALTER USER, CREATE ROW POLICY, and GRANT SELECT permissions{% endblock %} {% block output_format %}Do not add FORMAT clauses; the endpoint requires ClickHouse JSON output{% endblock %} diff --git a/litellm-rust/crates/traces-clickhouse/tests/admin_sql.rs b/litellm-rust/crates/traces-clickhouse/tests/admin_sql.rs index 614dad7a35a..49499ebfa39 100644 --- a/litellm-rust/crates/traces-clickhouse/tests/admin_sql.rs +++ b/litellm-rust/crates/traces-clickhouse/tests/admin_sql.rs @@ -41,7 +41,7 @@ async fn database() -> Result> { } let readers = QueryReaders::new(Connection::writer(&admin_url)?, "litellm".into()); let connection = readers - .connection(&client, &QueryScope::Admin, "test-secret") + .connection(&client, &QueryScope::All, "test-secret") .await?; let url = connection.url().to_string(); Ok(Database { diff --git a/litellm-rust/crates/traces-clickhouse/tests/migrations.rs b/litellm-rust/crates/traces-clickhouse/tests/migrations.rs index 9fdeb450c70..2f9e88af3e7 100644 --- a/litellm-rust/crates/traces-clickhouse/tests/migrations.rs +++ b/litellm-rust/crates/traces-clickhouse/tests/migrations.rs @@ -104,7 +104,6 @@ async fn schema_supports_span_rollups_and_spend_joins( all_teams: 0, user_id: String::new(), team_ids: vec!["team-1".into()], - api_key_hash: String::new(), }, trace_id: "trace-1".into(), trace_ref: String::new(), @@ -119,7 +118,6 @@ async fn schema_supports_span_rollups_and_spend_joins( ("all_teams".into(), Parameter::Integer(0)), ("user_id".into(), Parameter::Text(String::new())), ("team_ids".into(), Parameter::Strings(vec!["team-1".into()])), - ("api_key_hash".into(), Parameter::Text(String::new())), ( "start_ms".into(), Parameter::Integer(timestamp / 1_000_000 - 1000), @@ -153,7 +151,6 @@ async fn schema_supports_span_rollups_and_spend_joins( ("all_teams".into(), Parameter::Integer(0)), ("user_id".into(), Parameter::Text(String::new())), ("team_ids".into(), Parameter::Strings(vec!["team-1".into()])), - ("api_key_hash".into(), Parameter::Text(String::new())), ( "start_ms".into(), Parameter::Integer(timestamp / 1_000_000 - 1000), @@ -421,6 +418,7 @@ async fn listed_agent_names_preserve_scope_and_cursor( vec![serde_json::from_value(serde_json::json!({ "Timestamp": timestamp, "TraceId": trace, "SpanId": span, "ParentSpanId": parent, "ServiceName": "shared-app", "SpanName": span, "AgentName": agent, + "UserId": if key == "one" { "owner" } else { "other" }, "Framework": framework, "ObservationType": "agent", "ResourceAttributes": {"litellm.team_id": team, "litellm.api_key_hash": key} }))?], @@ -442,9 +440,8 @@ async fn listed_agent_names_preserve_scope_and_cursor( let connection = Connection::configured(&database.url, "trace_test", "default", "")?; let parameters = BTreeMap::from([ ("all_teams".into(), Parameter::Integer(0)), - ("user_id".into(), Parameter::Text(String::new())), + ("user_id".into(), Parameter::Text("owner".into())), ("team_ids".into(), Parameter::Strings(vec![])), - ("api_key_hash".into(), Parameter::Text("one".into())), ( "start_ms".into(), Parameter::Integer(timestamp / 1_000_000 - 1000), @@ -596,7 +593,6 @@ async fn rollup_merges_spans_across_days_without_losing_root_fields( ("all_teams".into(), Parameter::Integer(0)), ("user_id".into(), Parameter::Text(String::new())), ("team_ids".into(), Parameter::Strings(vec!["team-1".into()])), - ("api_key_hash".into(), Parameter::Text(String::new())), ( "start_ms".into(), Parameter::Integer(day_start / 1_000_000 - 2000), @@ -795,7 +791,7 @@ async fn lens_filters_reads_and_evidence_keep_reused_trace_ids_separate( for (key, text) in [("one", "timeout"), ("two", "success")] { insert_rows(&database, "otel_traces", vec![serde_json::from_value(serde_json::json!({ "Timestamp": timestamp, "TraceId": "shared", "SpanId": "root", "ParentSpanId": "", - "ServiceName": "review", "SpanName": "release", "Input": text, + "ServiceName": "review", "SpanName": "release", "Input": text, "UserId": key, "ResourceAttributes": {"litellm.team_id": "team", "litellm.api_key_hash": key, "swarm": "release"} }))?]).await?; } @@ -849,7 +845,6 @@ async fn lens_filters_reads_and_evidence_keep_reused_trace_ids_separate( ("all_teams".into(), Parameter::Integer(0)), ("user_id".into(), Parameter::Text(String::new())), ("team_ids".into(), Parameter::Strings(vec!["team".into()])), - ("api_key_hash".into(), Parameter::Text(String::new())), ]); let identities: serde_json::Value = serde_json::from_str( &execute_named_read( @@ -861,11 +856,11 @@ async fn lens_filters_reads_and_evidence_keep_reused_trace_ids_separate( .await?, )?; assert_eq!(identities["data"].as_array().map(Vec::len), Some(2)); - let key_params = identity_params + let user_params = identity_params .into_iter() .chain([ ("team_ids".into(), Parameter::Strings(vec![])), - ("api_key_hash".into(), Parameter::Text("one".into())), + ("user_id".into(), Parameter::Text("one".into())), ]) .collect(); let identity: serde_json::Value = serde_json::from_str( @@ -873,7 +868,7 @@ async fn lens_filters_reads_and_evidence_keep_reused_trace_ids_separate( &database.client, &connection, ReadQuery::TraceIdentity, - &key_params, + &user_params, ) .await?, )?; @@ -1170,7 +1165,6 @@ async fn trace_error_previews_preserve_paginated_diagnostics( ("all_teams".into(), Parameter::Integer(1)), ("user_id".into(), Parameter::Text(String::new())), ("team_ids".into(), Parameter::Strings(vec![])), - ("api_key_hash".into(), Parameter::Text(String::new())), ("trace_ref".into(), Parameter::Text(String::new())), ]); let body = execute_named_read( @@ -1217,10 +1211,7 @@ async fn trace_error_previews_preserve_paginated_diagnostics( } assert_eq!(recovered, message); parameters.insert("all_teams".into(), Parameter::Integer(0)); - parameters.insert( - "api_key_hash".into(), - Parameter::Text("unrelated-key".into()), - ); + parameters.insert("user_id".into(), Parameter::Text("unrelated-user".into())); let denied = execute_named_read(&database.client, &reader, ReadQuery::SpanError, ¶meters).await?; assert_eq!( @@ -1265,7 +1256,6 @@ async fn duplicate_span_preview_matches_diagnostic( ("all_teams".into(), Parameter::Integer(1)), ("user_id".into(), Parameter::Text(String::new())), ("team_ids".into(), Parameter::Strings(vec![])), - ("api_key_hash".into(), Parameter::Text(String::new())), ("trace_ref".into(), Parameter::Text(String::new())), ("error_version".into(), Parameter::Text(String::new())), ("error_offset".into(), Parameter::Integer(0)), @@ -1716,16 +1706,16 @@ fn field_definitions_match_serialized_normalized_span() { } #[rstest] -#[case::own_user("owner", vec![], "", vec!["own"])] -#[case::own_user_and_permitted_team("owner", vec!["permitted"], "", vec!["own", "team"])] -#[case::key_only("", vec![], "request-key", vec!["own"])] -#[case::no_identity("", vec![], "", vec![])] +#[case::own_user("owner", vec![], None, vec!["own"])] +#[case::own_user_and_permitted_team("owner", vec!["permitted"], None, vec!["own", "team"])] +#[case::no_identity("", vec![], None, vec![])] +#[case::legacy_key_without_identity("", vec![], Some("request-key"), vec![])] #[tokio::test] async fn named_and_sql_readers_share_request_log_visibility( #[future(awt)] database: TestResult, #[case] user: &str, #[case] teams: Vec<&str>, - #[case] key: &str, + #[case] legacy_key: Option<&str>, #[case] expected: Vec<&str>, ) -> TestResult { use litellm_traces_clickhouse::query::named::{ @@ -1748,12 +1738,10 @@ async fn named_and_sql_readers_share_request_log_visibility( let reader = Connection::reader(&database.url, "trace_test")?; let params = SpendByResponseIdsParams::from(litellm_traces::query::named::SpendByResponseIdsParams { - access: ReadAccessParams { - all_teams: 0, - user_id: user.into(), - team_ids: teams.iter().map(|team| (*team).into()).collect(), - api_key_hash: key.into(), - }, + access: serde_json::from_value::(serde_json::json!({ + "all_teams": 0, "user_id": user, "team_ids": teams, + "api_key_hash": legacy_key.unwrap_or_default(), + }))?, response_ids: vec!["shared-response".into()], start_ms: timestamp / 1_000_000 - 1, end_ms: timestamp / 1_000_000 + 1, @@ -1765,10 +1753,9 @@ async fn named_and_sql_readers_share_request_log_visibility( spend.iter().map(|row| row.0.request_id.as_str()).collect(); let expected: std::collections::BTreeSet<_> = expected.into_iter().collect(); assert_eq!(actual, expected); - let scope = QueryScope::Logs { + let scope = QueryScope::Owned { user_id: user.into(), team_ids: teams.into_iter().map(str::to_owned).collect(), - api_key_hash: key.into(), }; if user.is_empty() && scope.validate().is_err() { assert!( @@ -1829,7 +1816,6 @@ async fn rollup_cost_completeness_preserves_missing_ids_and_fails_closed_for_his all_teams: 0, user_id: "".into(), team_ids: vec!["team".into()], - api_key_hash: "".into(), }, start_ms: timestamp / 1_000_000 - 1, end_ms: timestamp / 1_000_000 + 1, @@ -1850,7 +1836,6 @@ async fn rollup_cost_completeness_preserves_missing_ids_and_fails_closed_for_his user_id: "owner".into(), team_ids: vec![], all_teams: 0, - api_key_hash: String::new(), }, ..params.0 }, @@ -1882,18 +1867,16 @@ async fn rollup_cost_completeness_preserves_missing_ids_and_fails_closed_for_his } #[rstest] -#[case::admin(1, "", vec![], "", "own answer")] -#[case::user(0, "owner", vec![], "", "own answer")] -#[case::team(0, "", vec!["alpha"], "", "own answer")] -#[case::key(0, "", vec![], "one", "own answer")] -#[case::no_identity(0, "", vec![], "", "")] +#[case::admin(1, "", vec![], "own answer")] +#[case::user(0, "owner", vec![], "own answer")] +#[case::team(0, "", vec!["alpha"], "own answer")] +#[case::no_identity(0, "", vec![], "")] #[tokio::test] async fn agent_final_answer_preserves_visibility_and_trace_ownership( #[future(awt)] database: TestResult, #[case] all_teams: u8, #[case] user: &str, #[case] teams: Vec<&str>, - #[case] key: &str, #[case] expected: &str, ) -> TestResult { use litellm_traces_clickhouse::query::named::{ReadAccessParams, SpanDetail, SpanDetailParams}; @@ -1952,7 +1935,6 @@ async fn agent_final_answer_preserves_visibility_and_trace_ownership( all_teams, user_id: user.into(), team_ids: teams.into_iter().map(str::to_owned).collect(), - api_key_hash: key.into(), }, trace_id: "shared".into(), trace_ref: String::new(), diff --git a/litellm-rust/crates/traces-clickhouse/tests/queries.rs b/litellm-rust/crates/traces-clickhouse/tests/queries.rs index 3b9f108af94..c72255d5e0d 100644 --- a/litellm-rust/crates/traces-clickhouse/tests/queries.rs +++ b/litellm-rust/crates/traces-clickhouse/tests/queries.rs @@ -23,23 +23,20 @@ use support::TestResult; enum ScopeCase { Admin, Team, - Key, OtherTeam, } impl ScopeCase { fn scope(self) -> QueryScope { match self { - Self::Admin => QueryScope::Admin, - Self::Team => QueryScope::Team { - team_id: "team-a".into(), + Self::Admin => QueryScope::All, + Self::Team => QueryScope::Owned { + user_id: String::new(), + team_ids: vec!["team-a".into()], }, - Self::Key => QueryScope::Key { - team_id: "team-a".into(), - api_key_hash: "key-a".into(), - }, - Self::OtherTeam => QueryScope::Team { - team_id: "team-b".into(), + Self::OtherTeam => QueryScope::Owned { + user_id: String::new(), + team_ids: vec!["team-b".into()], }, } } @@ -60,13 +57,7 @@ async fn curated_queries_return_expected_rows( #[future(awt)] seeded_database: TestResult, #[case] sql: &str, #[case] expected_json: &str, - #[values( - ScopeCase::Admin, - ScopeCase::Team, - ScopeCase::Key, - ScopeCase::OtherTeam - )] - scope: ScopeCase, + #[values(ScopeCase::Admin, ScopeCase::Team, ScopeCase::OtherTeam)] scope: ScopeCase, ) -> TestResult { let fixture = seeded_database?; let reader = fixture @@ -103,11 +94,7 @@ async fn typed_queries_read_normalized_spans_and_keep_trace_identities_separate( let fixture = seeded_database?; let reader = fixture .readers - .connection( - &fixture.database.client, - &QueryScope::Admin, - "fixture-secret", - ) + .connection(&fixture.database.client, &QueryScope::All, "fixture-secret") .await?; let params = ListTracesParams::from(contracts::ListTracesParams { access: admin_access?, @@ -176,11 +163,7 @@ async fn typed_trace_cursor_returns_the_next_fixture_trace( let fixture = seeded_database?; let reader = fixture .readers - .connection( - &fixture.database.client, - &QueryScope::Admin, - "fixture-secret", - ) + .connection(&fixture.database.client, &QueryScope::All, "fixture-secret") .await?; let params = ListTracesParams::from(contracts::ListTracesParams { access: admin_access?, @@ -218,11 +201,7 @@ async fn captured_deeplite_exports_round_trip_through_clickhouse( let decoded = insert_export(&fixture, export, "team-a", "key-a").await?; let reader = fixture .readers - .connection( - &fixture.database.client, - &QueryScope::Admin, - "fixture-secret", - ) + .connection(&fixture.database.client, &QueryScope::All, "fixture-secret") .await?; let params = TraceSpansParams { access: admin_access?, diff --git a/litellm-rust/crates/traces-clickhouse/tests/queries/read_access.json b/litellm-rust/crates/traces-clickhouse/tests/queries/read_access.json index a743b382c20..f0af446092e 100644 --- a/litellm-rust/crates/traces-clickhouse/tests/queries/read_access.json +++ b/litellm-rust/crates/traces-clickhouse/tests/queries/read_access.json @@ -1,6 +1,8 @@ { "all_teams": 1, "user_id": "", - "team_ids": ["team-a", "team-b"], - "api_key_hash": "" + "team_ids": [ + "team-a", + "team-b" + ] } diff --git a/litellm-rust/crates/traces-clickhouse/tests/query_access.rs b/litellm-rust/crates/traces-clickhouse/tests/query_access.rs index be749351a72..ef5b76c2097 100644 --- a/litellm-rust/crates/traces-clickhouse/tests/query_access.rs +++ b/litellm-rust/crates/traces-clickhouse/tests/query_access.rs @@ -25,8 +25,8 @@ async fn database() -> Result> { let writer = Connection::parse(&url)?; ensure_schema(&client, &writer, "trace_test", 7).await?; for sql in [ - "INSERT INTO trace_test.otel_traces (TeamId, ApiKeyHash, TraceId, SpanId, Timestamp, SpanAttributes, UserId) VALUES ('team-a', 'key-a1', 'shared-trace', 'a1', now(), map('visible', 'a'), 'owner'), ('team-a', 'key-a2', 'shared-trace', 'a2', now(), map('visible', 'a'), 'other'), ('team-b', 'key-b', 'shared-trace', 'b', now(), map('secret-b', 'b'), 'owner'), ('', 'key-teamless', 'shared-trace', 'teamless', now(), map('visible', 'teamless'), ''), ('', 'key-other', 'shared-trace', 'other-teamless', now(), map('visible', 'other'), '')", - "INSERT INTO trace_test.spend_logs (team_id, api_key, request_id, start_time, end_time, metadata, user) VALUES ('team-a', 'key-a1', 'a1', now(), now(), '{\"visible\":1}', 'owner'), ('team-a', 'key-a2', 'a2', now(), now(), '{\"visible\":1}', 'other'), ('team-b', 'key-b', 'b', now(), now(), '{\"secret_b\":1}', 'owner'), ('', 'key-teamless', 'teamless', now(), now(), '{}', ''), ('', 'key-other', 'other-teamless', now(), now(), '{}', '')", + "INSERT INTO trace_test.otel_traces (TeamId, ApiKeyHash, TraceId, SpanId, Timestamp, SpanAttributes, UserId) VALUES ('team-a', 'key-a1', 'shared-trace', 'a1', now(), map('visible', 'a'), 'owner'), ('team-a', 'key-a2', 'shared-trace', 'a2', now(), map('visible', 'a'), 'other'), ('team-b', 'key-b', 'shared-trace', 'b', now(), map('secret-b', 'b'), 'owner'), ('team-c', 'key-a1', 'shared-trace', 'same-key-foreign', now(), map('visible', 'foreign'), 'other'), ('', 'key-teamless', 'shared-trace', 'teamless', now(), map('visible', 'teamless'), ''), ('', 'key-other', 'shared-trace', 'other-teamless', now(), map('visible', 'other'), '')", + "INSERT INTO trace_test.spend_logs (team_id, api_key, request_id, start_time, end_time, metadata, user) VALUES ('team-a', 'key-a1', 'a1', now(), now(), '{\"visible\":1}', 'owner'), ('team-a', 'key-a2', 'a2', now(), now(), '{\"visible\":1}', 'other'), ('team-b', 'key-b', 'b', now(), now(), '{\"secret_b\":1}', 'owner'), ('team-c', 'key-a1', 'same-key-foreign', now(), now(), '{}', 'other'), ('', 'key-teamless', 'teamless', now(), now(), '{}', ''), ('', 'key-other', 'other-teamless', now(), now(), '{}', '')", "CREATE TABLE trace_test.private_data (secret String) ENGINE = Memory", "INSERT INTO trace_test.private_data VALUES ('hidden')", ] { @@ -43,15 +43,12 @@ async fn database() -> Result> { } #[rstest] -#[case::own_user(QueryScope::Logs { user_id: "owner".into(), team_ids: vec![], api_key_hash: "".into() }, vec!["a1", "b"])] -#[case::own_user_and_permitted_team(QueryScope::Logs { user_id: "owner".into(), team_ids: vec!["team-a".into()], api_key_hash: "".into() }, vec!["a1", "a2", "b"])] -#[case::key_only_logs(QueryScope::Logs { user_id: "".into(), team_ids: vec![], api_key_hash: "key-teamless".into() }, vec!["teamless"])] -#[case::quoted_user(QueryScope::Logs { user_id: "owner' OR 1=1 --".into(), team_ids: vec![], api_key_hash: "".into() }, vec![])] -#[case::team(QueryScope::Team { team_id: "team-a".to_owned() }, vec!["a1", "a2"])] -#[case::project_key(QueryScope::Key { team_id: "team-a".to_owned(), api_key_hash: "key-a1".to_owned() }, vec!["a1"])] -#[case::teamless_key(QueryScope::Key { team_id: "".to_owned(), api_key_hash: "key-teamless".to_owned() }, vec!["teamless"])] -#[case::admin(QueryScope::Admin, vec!["a1", "a2", "b", "other-teamless", "teamless"])] -#[case::quoted_team(QueryScope::Team { team_id: "team-a' OR 1=1 --\\".to_owned() }, vec![])] +#[case::own_user(QueryScope::Owned { user_id: "owner".into(), team_ids: vec![] }, vec!["a1", "b"])] +#[case::own_user_and_permitted_team(QueryScope::Owned { user_id: "owner".into(), team_ids: vec!["team-a".into()] }, vec!["a1", "a2", "b"])] +#[case::quoted_user(QueryScope::Owned { user_id: "owner' OR 1=1 --".into(), team_ids: vec![] }, vec![])] +#[case::team(QueryScope::Owned { user_id: String::new(), team_ids: vec!["team-a".to_owned() ] }, vec!["a1", "a2"])] +#[case::admin(QueryScope::All, vec!["a1", "a2", "b", "other-teamless", "same-key-foreign", "teamless"])] +#[case::quoted_team(QueryScope::Owned { user_id: String::new(), team_ids: vec!["team-a' OR 1=1 --\\".to_owned() ] }, vec![])] #[tokio::test] async fn queries_and_help_are_scoped_by_the_database( #[future(awt)] database: Result>, @@ -111,8 +108,9 @@ async fn rotating_master_secret_revokes_previous_reader_credentials( #[future(awt)] database: Result>, ) -> Result<(), Box> { let database = database?; - let scope = QueryScope::Team { - team_id: "team-a".to_owned(), + let scope = QueryScope::Owned { + user_id: String::new(), + team_ids: vec!["team-a".to_owned()], }; let old_reader = database .readers @@ -159,8 +157,9 @@ async fn managed_reader_rejects_privilege_and_scope_bypasses( #[future(awt)] database: Result>, ) -> Result<(), Box> { let database = database?; - let scope = QueryScope::Team { - team_id: "team-a".to_owned(), + let scope = QueryScope::Owned { + user_id: String::new(), + team_ids: vec!["team-a".to_owned()], }; let reader = database .readers @@ -213,14 +212,15 @@ async fn provisioning_failure_never_returns_a_writer_connection( let database = database?; let reader = database .readers - .connection(&database.client, &QueryScope::Admin, "test-master-secret") + .connection(&database.client, &QueryScope::All, "test-master-secret") .await?; let no_provision_privileges = QueryReaders::new(reader, "trace_test".to_owned()); let result = no_provision_privileges .connection( &database.client, - &QueryScope::Team { - team_id: "team-a".to_owned(), + &QueryScope::Owned { + user_id: String::new(), + team_ids: vec!["team-a".to_owned()], }, "other-secret", ) @@ -232,7 +232,7 @@ async fn provisioning_failure_never_returns_a_writer_connection( assert!(matches!( database .readers - .connection(&database.client, &QueryScope::Admin, "") + .connection(&database.client, &QueryScope::All, "") .await, Err(Error::MissingSecret) )); @@ -241,8 +241,9 @@ async fn provisioning_failure_never_returns_a_writer_connection( .readers .connection( &database.client, - &QueryScope::Team { - team_id: String::new() + &QueryScope::Owned { + user_id: String::new(), + team_ids: vec![String::new()] }, "test-master-secret" ) @@ -263,6 +264,6 @@ async fn provisioning_failure_never_returns_a_writer_connection( ) .await?; let rows: Value = serde_json::from_str(&rows)?; - assert_eq!(rows["data"][0]["count"], 5); + assert_eq!(rows["data"][0]["count"], 6); Ok(()) } diff --git a/litellm-rust/crates/traces/src/query/named.rs b/litellm-rust/crates/traces/src/query/named.rs index b39c44d49cb..b45c076c3ba 100644 --- a/litellm-rust/crates/traces/src/query/named.rs +++ b/litellm-rust/crates/traces/src/query/named.rs @@ -6,7 +6,6 @@ pub struct ReadAccessParams { pub all_teams: u8, pub user_id: String, pub team_ids: Vec, - pub api_key_hash: String, } #[derive(Debug, Deserialize, Serialize)] diff --git a/litellm-rust/crates/traces/src/query_access.rs b/litellm-rust/crates/traces/src/query_access.rs index 51bb5c6a097..2f57bd4c0ce 100644 --- a/litellm-rust/crates/traces/src/query_access.rs +++ b/litellm-rust/crates/traces/src/query_access.rs @@ -5,33 +5,20 @@ use crate::InvalidScope; #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)] pub enum QueryScope { - Admin, - Team { - team_id: String, - }, - Logs { + All, + Owned { user_id: String, team_ids: Vec, - api_key_hash: String, - }, - Key { - team_id: String, - api_key_hash: String, }, } impl QueryScope { pub fn validate(&self) -> Result<(), InvalidScope> { match self { - Self::Admin => Ok(()), - Self::Team { team_id } if !team_id.is_empty() => Ok(()), - Self::Key { api_key_hash, .. } if !api_key_hash.is_empty() => Ok(()), - Self::Logs { - user_id, - team_ids, - api_key_hash, - } if (!user_id.is_empty() || !team_ids.is_empty() || !api_key_hash.is_empty()) - && team_ids.iter().all(|team| !team.is_empty()) => + Self::All => Ok(()), + Self::Owned { user_id, team_ids } + if (!user_id.is_empty() || !team_ids.is_empty()) + && team_ids.iter().all(|team| !team.is_empty()) => { Ok(()) } diff --git a/litellm-rust/crates/traces/tests/query/named.rs b/litellm-rust/crates/traces/tests/query/named.rs index 7db77df8a29..bfe50a8684d 100644 --- a/litellm-rust/crates/traces/tests/query/named.rs +++ b/litellm-rust/crates/traces/tests/query/named.rs @@ -9,12 +9,16 @@ fn round_trip(wire: Value) { } #[rstest] -#[case::admin(vec![], "")] -#[case::multiple_teams(vec!["team-a", "team-b"], "")] -#[case::key(vec!["team-a"], "key")] -#[case::teamless_key(vec![], "key")] -fn named_requests_preserve_all_access_cases(#[case] teams: Vec<&str>, #[case] key: &str) { - let access = json!({"all_teams": u8::from(teams.is_empty() && key.is_empty()), "user_id": "", "team_ids": teams, "api_key_hash": key}); +#[case::admin(1, "", vec![])] +#[case::own_user(0, "user", vec![])] +#[case::multiple_teams(0, "user", vec!["team-a", "team-b"])] +#[case::no_identity(0, "", vec![])] +fn named_requests_preserve_all_access_cases( + #[case] all_teams: u8, + #[case] user: &str, + #[case] teams: Vec<&str>, +) { + let access = json!({"all_teams": all_teams, "user_id": user, "team_ids": teams}); round_trip::(access.clone()); let request = |specific: Value| { Value::Object( diff --git a/litellm-rust/crates/traces/tests/query_access.rs b/litellm-rust/crates/traces/tests/query_access.rs index e21abc6f427..79155065605 100644 --- a/litellm-rust/crates/traces/tests/query_access.rs +++ b/litellm-rust/crates/traces/tests/query_access.rs @@ -3,18 +3,12 @@ use rstest::rstest; use serde_json::{Value, json}; #[rstest] -#[case::admin(json!({"kind": "admin"}), true)] -#[case::team(json!({"kind": "team", "team_id": "team"}), true)] -#[case::empty_team(json!({"kind": "team", "team_id": ""}), false)] -#[case::key(json!({"kind": "key", "team_id": "team", "api_key_hash": "key"}), true)] -#[case::teamless_key(json!({"kind": "key", "team_id": "", "api_key_hash": "key"}), true)] -#[case::empty_key(json!({"kind": "key", "team_id": "team", "api_key_hash": ""}), false)] -#[case::user_logs(json!({"kind": "logs", "user_id": "user", "team_ids": [], "api_key_hash": ""}), true)] -#[case::permitted_teams(json!({"kind": "logs", "user_id": "", "team_ids": ["team"], "api_key_hash": ""}), true)] -#[case::key_logs(json!({"kind": "logs", "user_id": "", "team_ids": [], "api_key_hash": "key"}), true)] -#[case::anonymous_logs(json!({"kind": "logs", "user_id": "", "team_ids": [], "api_key_hash": ""}), false)] -#[case::empty_permitted_team(json!({"kind": "logs", "user_id": "user", "team_ids": [""], "api_key_hash": ""}), false)] -#[case::empty_teamless_key(json!({"kind": "key", "team_id": "", "api_key_hash": ""}), false)] +#[case::all(json!({"kind": "all"}), true)] +#[case::own_user(json!({"kind": "owned", "user_id": "user", "team_ids": []}), true)] +#[case::permitted_teams(json!({"kind": "owned", "user_id": "", "team_ids": ["team"]}), true)] +#[case::own_user_and_permitted_teams(json!({"kind": "owned", "user_id": "user", "team_ids": ["team"]}), true)] +#[case::no_identity(json!({"kind": "owned", "user_id": "", "team_ids": []}), false)] +#[case::empty_permitted_team(json!({"kind": "owned", "user_id": "user", "team_ids": [""]}), false)] fn scope_validation_preserves_authorization_and_wire_shape( #[case] wire: Value, #[case] valid: bool, @@ -28,22 +22,22 @@ fn scope_validation_preserves_authorization_and_wire_shape( } #[rstest] -#[case::unknown_kind(json!({"kind": "all"}))] -#[case::unknown_field(json!({"kind": "team", "team_id": "team", "extra": true}))] -#[case::missing_team(json!({"kind": "key", "api_key_hash": "key"}))] -#[case::missing_key(json!({"kind": "key", "team_id": "team"}))] +#[case::unknown_kind(json!({"kind": "unknown"}))] +#[case::unknown_field(json!({"kind": "owned", "user_id": "user", "team_ids": [], "extra": true}))] +#[case::legacy_admin(json!({"kind": "admin"}))] +#[case::legacy_logs(json!({"kind": "logs", "user_id": "user", "team_ids": []}))] +#[case::legacy_team(json!({"kind": "team", "team_id": "team"}))] +#[case::key_scope(json!({"kind": "key", "team_id": "team", "api_key_hash": "key"}))] +#[case::key_grant(json!({"kind": "owned", "user_id": "user", "team_ids": [], "api_key_hash": "key"}))] fn scope_rejects_invalid_wire_shape(#[case] wire: Value) { assert!(serde_json::from_value::(wire).is_err()); } #[rstest] -fn admin_preserves_existing_extra_field_handling() { +fn all_preserves_existing_extra_field_handling() { let scope: QueryScope = - serde_json::from_value(json!({"kind": "admin", "team_id": "ignored"})).unwrap(); - assert!(matches!(scope, QueryScope::Admin)); + serde_json::from_value(json!({"kind": "all", "team_id": "ignored"})).unwrap(); + assert!(matches!(scope, QueryScope::All)); assert!(scope.validate().is_ok()); - assert_eq!( - serde_json::to_value(scope).unwrap(), - json!({"kind": "admin"}) - ); + assert_eq!(serde_json::to_value(scope).unwrap(), json!({"kind": "all"})); } diff --git a/litellm/proxy/auth/authorization.py b/litellm/proxy/auth/authorization.py new file mode 100644 index 00000000000..91549f19d2e --- /dev/null +++ b/litellm/proxy/auth/authorization.py @@ -0,0 +1,77 @@ +from collections.abc import Awaitable, Callable, Iterable, Sequence +from dataclasses import dataclass +from typing import Final, TypeAlias + +from litellm.proxy._types import KeyManagementRoutes, LiteLLM_TeamTable, LitellmUserRoles, UserAPIKeyAuth + + +@dataclass(frozen=True, slots=True) +class AllRows: + """Unrestricted reads, granted by the consuming endpoint's role checks.""" + + +@dataclass(frozen=True, slots=True) +class OwnedRows: + """Rows owned by ``user_id`` or by any of ``team_ids``; a ``None`` user grants no own-user rows.""" + + user_id: str | None + team_ids: tuple[str, ...] = () + + +ReadScope: TypeAlias = AllRows | OwnedRows + + +async def resolve_owned_read_scope( + user_id: str | None, + permitted_team_lookup: Callable[[], Awaitable[Sequence[str]]], +) -> OwnedRows: + """Resolve own-user and permitted-team reads, falling back to own-user on lookup failure.""" + if user_id is None: + return OwnedRows(None) + try: + team_ids: Final = tuple(await permitted_team_lookup()) + except Exception: # noqa: BLE001 # preserve spend-log own-user fallback for every permission lookup failure + return OwnedRows(user_id) + return OwnedRows(user_id, team_ids) + + +def can_read_team_logs(auth: UserAPIKeyAuth, team: LiteLLM_TeamTable) -> bool: + from litellm.proxy.management.teams.access import is_team_admin + from litellm.proxy.management_endpoints.common_utils import ( + _team_member_has_permission, # pyright: ignore[reportPrivateUsage] # reuse existing team permission policy + ) + + return is_team_admin(user_api_key_dict=auth, team_obj=team) or _team_member_has_permission( + user_api_key_dict=auth, + team_obj=team, + permission=KeyManagementRoutes.SPEND_LOGS.value, + ) + + +def permitted_log_team_ids(auth: UserAPIKeyAuth, teams: Iterable[LiteLLM_TeamTable]) -> tuple[str, ...]: + return tuple(team.team_id for team in teams if can_read_team_logs(auth, team)) + + +async def can_read_log_owner( + user_id: str | None, + owner_user: str | None, + owner_team_id: str | None, + team_permission_lookup: Callable[[str], Awaitable[bool]], +) -> bool: + """Authorize stored ownership without swallowing direct team-lookup failures.""" + if owner_user is not None and owner_user == user_id: + return True + if owner_team_id: + return await team_permission_lookup(owner_team_id) + return False + + +async def resolve_trace_read_scope( + auth: UserAPIKeyAuth, + permitted_team_lookup: Callable[[], Awaitable[Sequence[str]]], +) -> ReadScope | None: + if auth.user_role in (LitellmUserRoles.PROXY_ADMIN, LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY): + return AllRows() + if not auth.user_id: + return None + return await resolve_owned_read_scope(auth.user_id, permitted_team_lookup) diff --git a/litellm/proxy/auth/authorization_dependencies.py b/litellm/proxy/auth/authorization_dependencies.py new file mode 100644 index 00000000000..3e7ae75dc86 --- /dev/null +++ b/litellm/proxy/auth/authorization_dependencies.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from collections.abc import Awaitable, Callable +from functools import partial +from typing import TYPE_CHECKING, Annotated, Final, TypeAlias + +from fastapi import Depends + +from litellm.proxy._types import LiteLLM_TeamTable, UserAPIKeyAuth +from litellm.proxy.auth.authorization import permitted_log_team_ids + +if TYPE_CHECKING: + from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache + from litellm.proxy.utils import PrismaClient, ProxyLogging + + +LogTeamLookup: TypeAlias = Callable[[UserAPIKeyAuth], Awaitable[tuple[str, ...]]] + + +async def load_permitted_log_team_ids( + auth: UserAPIKeyAuth, + *, + prisma_client: PrismaClient | None, + user_api_key_cache: UserApiKeyCache, + proxy_logging_obj: ProxyLogging, +) -> tuple[str, ...]: + from litellm.proxy.auth.auth_checks import get_user_object + from litellm.repositories.team_repository import TeamRepository + + if prisma_client is None: + return () + user_obj: Final = await get_user_object( + user_id=auth.user_id, + prisma_client=prisma_client, + user_api_key_cache=user_api_key_cache, + user_id_upsert=False, + proxy_logging_obj=proxy_logging_obj, + ) + if user_obj is None or not user_obj.teams: + return () + team_rows: Final = await TeamRepository(prisma_client).table.find_many(where={"team_id": {"in": user_obj.teams}}) + return permitted_log_team_ids(auth, (LiteLLM_TeamTable.model_validate(row.model_dump()) for row in team_rows)) + + +async def get_log_team_lookup() -> LogTeamLookup: + """Bind infrastructure without performing permission I/O before the handler's checks.""" + from litellm.proxy.proxy_server import prisma_client, proxy_logging_obj, user_api_key_cache + + return partial( + load_permitted_log_team_ids, + prisma_client=prisma_client, + user_api_key_cache=user_api_key_cache, + proxy_logging_obj=proxy_logging_obj, + ) + + +LogTeamLookupDependency: TypeAlias = Annotated[LogTeamLookup, Depends(get_log_team_lookup)] diff --git a/litellm/proxy/management_endpoints/management_v1/spend_logs.py b/litellm/proxy/management_endpoints/management_v1/spend_logs.py index 1cbc454ca5e..e9e6e05ce15 100644 --- a/litellm/proxy/management_endpoints/management_v1/spend_logs.py +++ b/litellm/proxy/management_endpoints/management_v1/spend_logs.py @@ -1,12 +1,15 @@ """`/management/v1/spend_logs` facets.""" from datetime import datetime, timezone +from functools import partial from typing import Annotated, Final, Literal from fastapi import APIRouter, Depends, Query, Request from litellm._logging import verbose_proxy_logger from litellm.proxy._types import CommonProxyErrors, UserAPIKeyAuth +from litellm.proxy.auth.authorization import resolve_owned_read_scope +from litellm.proxy.auth.authorization_dependencies import LogTeamLookup, LogTeamLookupDependency from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.list_api.common import ( PROBLEM_TYPE_BASE, @@ -16,7 +19,6 @@ from litellm.proxy.list_api.common import ( reject_unknown_query_params, ) from litellm.proxy.management_endpoints.management_v1.common import MANAGEMENT_V1_PREFIX -from litellm.proxy.utils import PrismaClient from litellm.types.proxy.management_endpoints.management_v1 import ( FacetListResponse, PageMeta, @@ -37,49 +39,29 @@ def _as_utc(value: datetime) -> datetime: async def _spend_log_scope_clause( user_api_key_dict: UserAPIKeyAuth, - prisma_client: PrismaClient, + log_team_lookup: LogTeamLookup, next_param_index: int, -) -> tuple[str | None, tuple[str | list[str], ...]]: +) -> tuple[str | None, tuple[object, ...]]: """SQL predicate restricting the facet to spend logs this caller may read. Returns ``(None, ())`` for a proxy admin. Mirrors the scoping ``/spend/logs/ui`` applies, so a dropdown can never offer a value from a row the caller could not open. """ - from litellm.proxy.spend_tracking.spend_management_endpoints import ( - _get_permitted_team_ids_for_spend_logs, - _is_admin_view_safe, - ) + from litellm.proxy.spend_tracking.spend_management_endpoints import _is_admin_view_safe, read_scope_sql if _is_admin_view_safe(user_api_key_dict=user_api_key_dict): return None, () - - try: - permitted_team_ids = await _get_permitted_team_ids_for_spend_logs( - prisma_client=prisma_client, - user_api_key_dict=user_api_key_dict, - ) - except Exception: - permitted_team_ids = [] - - caller_user_id: Final = user_api_key_dict.user_id - # = ANY(::text[]) rather than an expanded IN list, matching the clause - # ui_view_spend_logs builds: one parameter whatever the team count. - templates: Final = (('"user" = ${}',) if caller_user_id is not None else ()) + ( - ("team_id = ANY(${}::text[])",) if permitted_team_ids else () + scope: Final = await resolve_owned_read_scope( + user_api_key_dict.user_id, partial(log_team_lookup, user_api_key_dict) ) - params: Final = ((caller_user_id,) if caller_user_id is not None else ()) + ( - (permitted_team_ids,) if permitted_team_ids else () - ) - if not templates: - return "FALSE", () - clauses: Final = tuple(template.format(next_param_index + offset) for offset, template in enumerate(templates)) - return f"({' OR '.join(clauses)})", params + return read_scope_sql(scope, next_param_index) async def _list_spend_log_facet( request: Request, user_api_key_dict: UserAPIKeyAuth, + log_team_lookup: LogTeamLookup, start_time: datetime, end_time: datetime, q: str | None, @@ -107,7 +89,7 @@ async def _list_spend_log_facet( scope_clause, scope_params = await _spend_log_scope_clause( user_api_key_dict=user_api_key_dict, - prisma_client=prisma_client, + log_team_lookup=log_team_lookup, next_param_index=len(window_params) + len(search_params) + 1, ) @@ -178,6 +160,7 @@ async def _list_spend_log_facet( async def list_spend_log_end_users( request: Request, user_api_key_dict: Annotated[UserAPIKeyAuth, Depends(user_api_key_auth)], + log_team_lookup: LogTeamLookupDependency, start_time: Annotated[ datetime, Query(alias="filter[startTime][gte]", description="Window start (UTC when no offset is given)"), @@ -211,6 +194,7 @@ async def list_spend_log_end_users( return await _list_spend_log_facet( request=request, user_api_key_dict=user_api_key_dict, + log_team_lookup=log_team_lookup, start_time=start_time, end_time=end_time, q=q, @@ -229,6 +213,7 @@ async def list_spend_log_end_users( async def list_spend_log_users( request: Request, user_api_key_dict: Annotated[UserAPIKeyAuth, Depends(user_api_key_auth)], + log_team_lookup: LogTeamLookupDependency, start_time: Annotated[ datetime, Query(alias="filter[startTime][gte]", description="Window start (UTC when no offset is given)"), @@ -245,6 +230,7 @@ async def list_spend_log_users( return await _list_spend_log_facet( request=request, user_api_key_dict=user_api_key_dict, + log_team_lookup=log_team_lookup, start_time=start_time, end_time=end_time, q=q, diff --git a/litellm/proxy/spend_tracking/log_visibility.py b/litellm/proxy/spend_tracking/log_visibility.py deleted file mode 100644 index 83f236d3028..00000000000 --- a/litellm/proxy/spend_tracking/log_visibility.py +++ /dev/null @@ -1,44 +0,0 @@ -from collections.abc import Awaitable, Callable -from dataclasses import dataclass -from typing import Final - -from fastapi import HTTPException - -from litellm.proxy._types import UserAPIKeyAuth - - -@dataclass(frozen=True, slots=True) -class LogVisibility: - all_teams: bool = False - user_id: str = "" - team_ids: tuple[str, ...] = () - api_key_hash: str = "" - - -async def permitted_log_teams(auth: UserAPIKeyAuth) -> tuple[str, ...]: - from litellm.proxy.proxy_server import prisma_client - from litellm.proxy.spend_tracking.spend_management_endpoints import ( - _get_permitted_team_ids_for_spend_logs_or_empty, # pyright: ignore[reportPrivateUsage] # Reuse request-log policy - ) - - if prisma_client is None: - return () - return await _get_permitted_team_ids_for_spend_logs_or_empty(prisma_client=prisma_client, user_api_key_dict=auth) - - -async def log_visibility( - auth: UserAPIKeyAuth, - team_lookup: Callable[[UserAPIKeyAuth], Awaitable[tuple[str, ...]]] = permitted_log_teams, -) -> LogVisibility: - from litellm.proxy.spend_tracking.spend_management_endpoints import ( - _is_admin_view_safe, # pyright: ignore[reportPrivateUsage] # Reuse request-log policy - ) - - if _is_admin_view_safe(user_api_key_dict=auth): - return LogVisibility(all_teams=True) - if auth.user_id: - team_ids: Final = await team_lookup(auth) - return LogVisibility(user_id=auth.user_id, team_ids=team_ids, api_key_hash=auth.token or "") - if auth.token: - return LogVisibility(api_key_hash=auth.token) - raise HTTPException(status_code=403, detail="Not allowed to view logs") diff --git a/litellm/proxy/spend_tracking/spend_management_endpoints.py b/litellm/proxy/spend_tracking/spend_management_endpoints.py index 3102fc63cf4..48cc684549f 100644 --- a/litellm/proxy/spend_tracking/spend_management_endpoints.py +++ b/litellm/proxy/spend_tracking/spend_management_endpoints.py @@ -3,8 +3,8 @@ import collections import json import os from collections.abc import Mapping, Sequence -from dataclasses import dataclass from datetime import date, datetime, timedelta, timezone +from functools import partial from itertools import groupby from types import MappingProxyType from typing import ( @@ -37,6 +37,18 @@ from litellm.constants import ( from litellm.litellm_core_utils.classifier_logging import classifier_audit_fields, classifier_input_snapshot from litellm.proxy._types import * from litellm.proxy._types import ProviderBudgetResponse, ProviderBudgetResponseObject +from litellm.proxy.auth.authorization import ( + AllRows, + OwnedRows, + ReadScope, + can_read_log_owner, + can_read_team_logs, + resolve_owned_read_scope, +) +from litellm.proxy.auth.authorization_dependencies import ( + LogTeamLookup, + LogTeamLookupDependency, +) from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.litellm_pre_call_utils import LiteLLMProxyRequestSetup from litellm.proxy.spend_tracking.spend_capture_rate import ( @@ -403,11 +415,6 @@ async def _find_team_row(prisma_client: PrismaClient, team_id: str) -> _Supports return await _team_table(prisma_client).find_unique(where={"team_id": team_id}) -async def _find_team_rows(prisma_client: PrismaClient, team_ids: Sequence[str]) -> Sequence[_SupportsModelDump]: - """Read team rows as Prisma model instances.""" - return await _team_table(prisma_client).find_many(where={"team_id": {"in": team_ids}}) - - @router.get( "/spend/keys", tags=["Budget & Spend Tracking"], @@ -2474,6 +2481,7 @@ def _build_spend_log_search_condition( ) async def ui_view_spend_logs( request: Request, + log_team_lookup: LogTeamLookupDependency, api_key: str | None = fastapi.Query( default=None, description="Get spend logs based on api key", @@ -2775,16 +2783,8 @@ async def ui_view_spend_logs( and team_id is None and (is_request_id_lookup or _can_user_view_spend_log(user_api_key_dict=user_api_key_dict)) ) - permitted_team_ids: Final = ( - await _get_permitted_team_ids_for_spend_logs_or_empty( - prisma_client=prisma_client, - user_api_key_dict=user_api_key_dict, - ) - if user_scope_applies - else () - ) - explicit_user_requires_caller_scope: Final = ( - user_scope_applies and not permitted_team_ids and user_id is not None + read_scope: Final = ( + await _spend_log_read_scope(user_api_key_dict, log_team_lookup) if user_scope_applies else AllRows() ) if not is_admin_view: if team_id is not None: @@ -2799,22 +2799,6 @@ async def ui_view_spend_logs( detail={"error": f"Not authorized to view team spend for team_id={team_id}"}, ) where_conditions["team_id"] = team_id - elif user_scope_applies: - if permitted_team_ids: - if user_id is None: - where_conditions.pop("user", None) - where_conditions["OR"] = [ - {"user": user_api_key_dict.user_id}, - {"team_id": {"in": permitted_team_ids}}, - ] - else: - if user_id is None: - where_conditions["user"] = user_api_key_dict.user_id - else: - where_conditions["AND"] = where_conditions.get("AND", []) + [ - {"user": user_api_key_dict.user_id} - ] - where_conditions.pop("team_id", None) # Calculate skip value for pagination skip: Final = (page - 1) * page_size @@ -2874,17 +2858,11 @@ async def ui_view_spend_logs( sql_params.append(request_id_filter) p += 1 - # Multi-team OR filter: (user = $X OR team_id = ANY($Y)) - if permitted_team_ids: - or_clause: Final = f'("user" = ${p} OR team_id = ANY(${p + 1}::text[]))' - sql_params.append(user_api_key_dict.user_id) - sql_params.append(permitted_team_ids) - p += 2 - sql_conditions.append(or_clause) - elif explicit_user_requires_caller_scope: - sql_conditions.append(f'"user" = ${p}') - sql_params.append(user_api_key_dict.user_id) - p += 1 + scope_clause, scope_params = read_scope_sql(read_scope, p) + if scope_clause: + sql_conditions.append(scope_clause) + sql_params.extend(scope_params) + p += len(scope_params) if session_id is not None and isinstance(session_id, str): like_escaped_session_id: Final = session_id.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_") @@ -3390,6 +3368,7 @@ async def _resolve_request_response_payload( ) async def ui_view_request_response_for_request_id( request_id: str, + log_team_lookup: LogTeamLookupDependency, start_date: str | None = fastapi.Query( default=None, description="Time from which to start viewing key spend", @@ -3442,6 +3421,7 @@ async def ui_view_request_response_for_request_id( user_api_key_dict=user_api_key_dict, request_id=request_id, caller_is_admin=caller_is_admin, + log_team_lookup=log_team_lookup, ) ) stored_request_id: Final = _stored_request_id(spend_log_row, request_id) @@ -4512,6 +4492,7 @@ async def ui_get_spend_by_tags( }, ) async def ui_view_session_spend_logs( + log_team_lookup: LogTeamLookupDependency, session_id: str = fastapi.Query( description="Get all spend logs for a particular session", ), @@ -4549,36 +4530,16 @@ async def ui_view_session_spend_logs( detail="Database not connected", ) - if _is_admin_view_safe(user_api_key_dict=user_api_key_dict): - scope_sql = "" - scope_params = () - where_conditions = {"session_id": session_id} - else: - try: - permitted_team_ids = ( - await _get_permitted_team_ids_for_spend_logs( - prisma_client=prisma_client, - user_api_key_dict=user_api_key_dict, - ) - if _can_user_view_spend_log(user_api_key_dict=user_api_key_dict) - else [] - ) - except Exception: # noqa: BLE001 # mirror /spend/logs/ui: failed team lookup falls back to own-logs-only scope - permitted_team_ids = [] - if permitted_team_ids: - scope_sql = ' AND ("user" = $4 OR team_id = ANY($5::text[]))' - scope_params = (user_api_key_dict.user_id, permitted_team_ids) - where_conditions = { - "session_id": session_id, - "OR": [ - {"user": user_api_key_dict.user_id}, - {"team_id": {"in": permitted_team_ids}}, - ], - } - else: - scope_sql = ' AND "user" = $4' - scope_params = (user_api_key_dict.user_id,) - where_conditions = {"session_id": session_id, "user": user_api_key_dict.user_id} + read_scope: Final = ( + AllRows() + if _is_admin_view_safe(user_api_key_dict=user_api_key_dict) + else await _spend_log_read_scope(user_api_key_dict, log_team_lookup) + if _can_user_view_spend_log(user_api_key_dict=user_api_key_dict) + else OwnedRows(user_api_key_dict.user_id) + ) + scope_clause, scope_params = read_scope_sql(read_scope, 4) + scope_sql: Final = f" AND {scope_clause}" if scope_clause else "" + where_conditions: Final = {"session_id": session_id, **_read_scope_where(read_scope)} # Calculate pagination offsets skip: Final = (page - 1) * page_size @@ -4859,22 +4820,12 @@ async def _can_team_member_view_log( Returns True if the team exists and the user is either a team admin or a team member with the ``/spend/logs`` permission. """ - from litellm.proxy.management.teams.access import is_team_admin - from litellm.proxy.management_endpoints.common_utils import _team_member_has_permission - if team_id is None: return False team_row: Final = await _find_team_row(prisma_client, team_id) if team_row is None: return False - team_obj: Final = LiteLLM_TeamTable.model_validate(team_row.model_dump()) - if is_team_admin(user_api_key_dict=user_api_key_dict, team_obj=team_obj): - return True - return _team_member_has_permission( - user_api_key_dict=user_api_key_dict, - team_obj=team_obj, - permission=KeyManagementRoutes.SPEND_LOGS.value, - ) + return can_read_team_logs(user_api_key_dict, LiteLLM_TeamTable.model_validate(team_row.model_dump())) def _can_user_view_spend_log(user_api_key_dict: UserAPIKeyAuth) -> bool: @@ -4899,15 +4850,12 @@ async def _user_can_view_spend_log_owner( owner_user: str | None, owner_team_id: str | None, ) -> bool: - if owner_user is not None and owner_user == user_api_key_dict.user_id: - return True - if owner_team_id: - return await _can_team_member_view_log( - prisma_client=prisma_client, - user_api_key_dict=user_api_key_dict, - team_id=owner_team_id, - ) - return False + return await can_read_log_owner( + user_api_key_dict.user_id, + owner_user, + owner_team_id, + partial(_can_team_member_view_log, prisma_client, user_api_key_dict), + ) def _spend_log_forbidden(request_id: str) -> HTTPException: @@ -4940,44 +4888,50 @@ async def _assert_user_can_view_request_id( raise _spend_log_forbidden(request_id) -@dataclass(frozen=True, slots=True) -class _SpendLogViewer: - user_id: str | None - team_ids: tuple[str, ...] - - -async def _spend_log_viewer(prisma_client: PrismaClient, user_api_key_dict: UserAPIKeyAuth) -> _SpendLogViewer: - return _SpendLogViewer( - user_id=user_api_key_dict.user_id, - team_ids=await _get_permitted_team_ids_for_spend_logs_or_empty( - prisma_client=prisma_client, - user_api_key_dict=user_api_key_dict, - ), +async def _spend_log_read_scope(user_api_key_dict: UserAPIKeyAuth, log_team_lookup: LogTeamLookup) -> OwnedRows: + return await resolve_owned_read_scope( + user_api_key_dict.user_id, + partial(log_team_lookup, user_api_key_dict), ) -def _viewer_scope_clause(viewer: _SpendLogViewer | None) -> tuple[str, tuple[object, ...]]: - match viewer: - case None: - return ("", ()) - case _SpendLogViewer(user_id=user_id, team_ids=()): - return (' AND "user" = $2', (user_id,)) - case _SpendLogViewer(user_id=user_id, team_ids=team_ids): - return (' AND ("user" = $2 OR team_id = ANY($3::text[]))', (user_id, team_ids)) +def read_scope_sql(scope: ReadScope, next_param: int) -> tuple[str, tuple[object, ...]]: + if isinstance(scope, AllRows): + return "", () + if scope.user_id is not None and scope.team_ids: + return ( + f'("user" = ${next_param} OR team_id = ANY(${next_param + 1}::text[]))', + (scope.user_id, scope.team_ids), + ) + if scope.user_id is not None: + return f'"user" = ${next_param}', (scope.user_id,) + if scope.team_ids: + return f"team_id = ANY(${next_param}::text[])", (scope.team_ids,) + return "FALSE", () -def _spend_log_payload_query(request_id: str, viewer: _SpendLogViewer | None) -> tuple[str, tuple[object, ...]]: +def _read_scope_where(scope: ReadScope) -> Mapping[str, object]: + if isinstance(scope, AllRows): + return {} + user_grant: Final = ({"user": scope.user_id},) if scope.user_id is not None else () + team_grant: Final = ({"team_id": {"in": list(scope.team_ids)}},) if scope.team_ids else () + grants: Final = user_grant + team_grant + return grants[0] if len(grants) == 1 else {"OR": list(grants)} + + +def _spend_log_payload_query(request_id: str, scope: ReadScope) -> tuple[str, tuple[object, ...]]: """ Fetch the one row an id lookup resolves to, preferring the exact ``request_id`` match over rows that merely carry the id as their client-set ``litellm_call_id``. A non-admin viewer only ever gets rows they own or rows of a team they may view. """ - scope, scope_params = _viewer_scope_clause(viewer) + scope_clause, scope_params = read_scope_sql(scope, 2) + scope_sql: Final = f" AND {scope_clause}" if scope_clause else "" return ( f""" SELECT request_id, messages, response, proxy_server_request, metadata, "user", team_id FROM "LiteLLM_SpendLogs" - WHERE (request_id = $1 OR litellm_call_id = $1){scope} + WHERE (request_id = $1 OR litellm_call_id = $1){scope_sql} ORDER BY (request_id = $1) DESC LIMIT 1 """, @@ -4990,6 +4944,7 @@ async def _resolve_spend_log_payload_row( user_api_key_dict: UserAPIKeyAuth, request_id: str, caller_is_admin: bool, + log_team_lookup: LogTeamLookup, ) -> Mapping[str, object] | None: """ Resolve an id lookup to the caller's own spend-log row before any payload @@ -4998,8 +4953,8 @@ async def _resolve_spend_log_payload_row( that id is only the caller's ``litellm_call_id``; the row's stored ``request_id`` is the key that names the caller's own request. """ - viewer: Final = None if caller_is_admin else await _spend_log_viewer(prisma_client, user_api_key_dict) - sql_query, sql_params = _spend_log_payload_query(request_id, viewer) + scope: Final = AllRows() if caller_is_admin else await _spend_log_read_scope(user_api_key_dict, log_team_lookup) + sql_query, sql_params = _spend_log_payload_query(request_id, scope) rows: Final[Sequence[Mapping[str, object]] | None] = await _query_raw_or_none(prisma_client, sql_query, *sql_params) if not rows: return None @@ -5075,57 +5030,3 @@ async def _assert_user_owns_cold_storage_payload( owner_user, owner_team_id = _cold_storage_payload_owner(payload) if not await _user_can_view_spend_log_owner(prisma_client, user_api_key_dict, owner_user, owner_team_id): raise _spend_log_forbidden(request_id) - - -async def _get_permitted_team_ids_for_spend_logs( - prisma_client: PrismaClient, - user_api_key_dict: UserAPIKeyAuth, -) -> list[str]: - """ - Return team IDs where the user is either a team admin or has the - ``/spend/logs`` permission, allowing them to view team-wide spend logs. - """ - # Imported here to avoid circular import: proxy_server imports this module. - from litellm.proxy.auth.auth_checks import get_user_object - from litellm.proxy.management.teams.access import is_team_admin - from litellm.proxy.management_endpoints.common_utils import _team_member_has_permission - from litellm.proxy.proxy_server import proxy_logging_obj, user_api_key_cache - - user_obj: Final = await get_user_object( - user_id=user_api_key_dict.user_id, - prisma_client=prisma_client, - user_api_key_cache=user_api_key_cache, - user_id_upsert=False, - proxy_logging_obj=proxy_logging_obj, - ) - if user_obj is None or not user_obj.teams: - return [] - - team_rows: Final = await _find_team_rows(prisma_client, user_obj.teams) - - permitted: Final[list[str]] = [] - for team_row in team_rows: - team_obj = LiteLLM_TeamTable.model_validate(team_row.model_dump()) - if is_team_admin(user_api_key_dict=user_api_key_dict, team_obj=team_obj) or _team_member_has_permission( - user_api_key_dict=user_api_key_dict, - team_obj=team_obj, - permission=KeyManagementRoutes.SPEND_LOGS.value, - ): - permitted.append(team_obj.team_id) - return permitted - - -async def _get_permitted_team_ids_for_spend_logs_or_empty( - prisma_client: PrismaClient, - user_api_key_dict: UserAPIKeyAuth, -) -> tuple[str, ...]: - """Resolve permitted teams once, falling back to the caller's own-user scope.""" - try: - return tuple( - await _get_permitted_team_ids_for_spend_logs( - prisma_client=prisma_client, - user_api_key_dict=user_api_key_dict, - ) - ) - except Exception: - return () diff --git a/litellm/proxy/tracing_endpoints.py b/litellm/proxy/tracing_endpoints.py index d741e0b29b4..046bcc9f704 100644 --- a/litellm/proxy/tracing_endpoints.py +++ b/litellm/proxy/tracing_endpoints.py @@ -10,6 +10,7 @@ GET /v1/traces/{trace_id}/spans/{span_id} SpanDetail import time from collections.abc import Mapping from dataclasses import dataclass +from functools import partial from http.client import responses from types import MappingProxyType from typing import Annotated, Final @@ -20,12 +21,13 @@ from pydantic import BaseModel, ConfigDict from litellm._logging import verbose_proxy_logger from litellm.constants import OTLP_RETRY_AFTER_SECONDS from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth +from litellm.proxy.auth.authorization import AllRows, ReadScope, resolve_trace_read_scope +from litellm.proxy.auth.authorization_dependencies import LogTeamLookupDependency from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.common_utils.http_parsing_utils import is_otlp_trace_request -from litellm.proxy.spend_tracking.log_visibility import log_visibility from litellm.proxy.tracing_runtime import provide_receiver, require_receiver from litellm.rust_bridge.trace_query_responses import TraceQueryHelp, TraceSQLResponse -from litellm.rust_bridge.traces import ClickHouseStorage, QueryScope +from litellm.rust_bridge.traces import AllQueryScope, ClickHouseStorage, OwnedQueryScope, QueryScope from litellm.tracing import ( Tenant, TraceReceiver, @@ -43,14 +45,14 @@ MS_PER_DAY: Final = 24 * 60 * 60 * 1000 @dataclass(frozen=True, slots=True) class TraceAccessContext: receiver: TraceReceiver | None - read_scope: TraceScope | None + read_scope: ReadScope | None write_tenant: Tenant | None def reader(self) -> tuple[TraceReceiver, TraceScope]: tracing: Final = require_receiver(self.receiver) if self.read_scope is None: raise HTTPException(status_code=403, detail="Not allowed to view agent traces") - return tracing, self.read_scope + return tracing, _trace_scope(self.read_scope) def writer(self) -> tuple[TraceReceiver, Tenant]: if self.write_tenant is None: @@ -61,27 +63,23 @@ class TraceAccessContext: async def provide_trace_access( auth: Annotated[UserAPIKeyAuth, Depends(user_api_key_auth)], tracing: Annotated[TraceReceiver | None, Depends(provide_receiver)], + log_team_lookup: LogTeamLookupDependency, ) -> TraceAccessContext: tenant: Final = Tenant( team_id=auth.team_id or "", api_key_hash=auth.token or "", org_id=auth.org_id or "", user_id=auth.user_id or "" ) write_tenant: Final = None if auth.user_role == LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY else tenant - if ( - not auth.user_id - and not auth.token - and auth.user_role not in (LitellmUserRoles.PROXY_ADMIN, LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY) - ): - return TraceAccessContext(tracing, None, write_tenant) - visibility: Final = await log_visibility(auth) - return TraceAccessContext( - tracing, - TraceScope( - all_teams=1 if visibility.all_teams else 0, - user_id=visibility.user_id, - team_ids=visibility.team_ids, - api_key_hash=visibility.api_key_hash, - ), - write_tenant, + read_scope: Final = await resolve_trace_read_scope(auth, partial(log_team_lookup, auth)) + return TraceAccessContext(tracing, read_scope, write_tenant) + + +def _trace_scope(scope: ReadScope) -> TraceScope: + if isinstance(scope, AllRows): + return TraceScope(all_teams=1, user_id="", team_ids=()) + return TraceScope( + all_teams=0, + user_id=scope.user_id or "", + team_ids=scope.team_ids, ) @@ -160,7 +158,7 @@ class TraceQueryRequest(BaseModel): @dataclass(frozen=True, slots=True) class TraceQueryAccess: storage: ClickHouseStorage - scope: QueryScope + scope: ReadScope secret: str @@ -172,24 +170,27 @@ def provide_trace_query_secret() -> str: return master_key -async def trace_query_scope(auth: UserAPIKeyAuth) -> QueryScope: - visibility: Final = await log_visibility(auth) - if visibility.all_teams: - return {"kind": "admin"} - return { - "kind": "logs", - "user_id": visibility.user_id, - "team_ids": visibility.team_ids, - "api_key_hash": visibility.api_key_hash, - } +def trace_query_scope(scope: ReadScope) -> QueryScope: + if isinstance(scope, AllRows): + return AllQueryScope(kind="all") + return OwnedQueryScope( + kind="owned", + user_id=scope.user_id or "", + team_ids=scope.team_ids, + ) async def provide_trace_query_access( auth: Annotated[UserAPIKeyAuth, Depends(user_api_key_auth)], tracing: Annotated[TraceReceiver | None, Depends(provide_receiver)], secret: Annotated[str, Depends(provide_trace_query_secret)], + log_team_lookup: LogTeamLookupDependency, ) -> TraceQueryAccess: - return TraceQueryAccess(require_receiver(tracing).store.storage, await trace_query_scope(auth), secret) + storage: Final = require_receiver(tracing).store.storage + scope: Final = await resolve_trace_read_scope(auth, partial(log_team_lookup, auth)) + if scope is None: + raise HTTPException(status_code=403, detail="Not allowed to view logs") + return TraceQueryAccess(storage, scope, secret) @router.post("/v1/traces/query", response_model=TraceSQLResponse, response_model_exclude_unset=True) @@ -198,7 +199,7 @@ async def query_agent_traces( access: Annotated[TraceQueryAccess, Depends(provide_trace_query_access)], ) -> TraceSQLResponse: try: - return await access.storage.query_sql(body.sql, access.scope, access.secret) + return await access.storage.query_sql(body.sql, trace_query_scope(access.scope), access.secret) except ValueError as error: raise HTTPException(status_code=400, detail=str(error)) from error except RuntimeError as error: @@ -211,7 +212,7 @@ async def help_agent_trace_queries( access: Annotated[TraceQueryAccess, Depends(provide_trace_query_access)], ) -> TraceQueryHelp: try: - return await access.storage.query_help(access.scope, access.secret) + return await access.storage.query_help(trace_query_scope(access.scope), access.secret) except RuntimeError as error: verbose_proxy_logger.warning("Trace query help unavailable: %s", error) raise HTTPException(status_code=503, detail="Trace query help is temporarily unavailable") from error diff --git a/litellm/rust_bridge/trace_queries.py b/litellm/rust_bridge/trace_queries.py index 0a9fe186646..1add1787241 100644 --- a/litellm/rust_bridge/trace_queries.py +++ b/litellm/rust_bridge/trace_queries.py @@ -30,7 +30,6 @@ class ListTracesParams(BaseModel): all_teams: Literal[0, 1] user_id: str team_ids: tuple[str, ...] - api_key_hash: str start_ms: Int64 end_ms: Int64 cursor_ms: Int64 @@ -43,7 +42,6 @@ class TraceSpansParams(BaseModel): all_teams: Literal[0, 1] user_id: str team_ids: tuple[str, ...] - api_key_hash: str trace_id: str trace_ref: str @@ -53,7 +51,6 @@ class SpanDetailParams(BaseModel): all_teams: Literal[0, 1] user_id: str team_ids: tuple[str, ...] - api_key_hash: str trace_id: str trace_ref: str span_id: str @@ -64,7 +61,6 @@ class SpanErrorParams(BaseModel): all_teams: Literal[0, 1] user_id: str team_ids: tuple[str, ...] - api_key_hash: str trace_id: str trace_ref: str span_id: str @@ -77,7 +73,6 @@ class SpendByResponseIdsParams(BaseModel): all_teams: Literal[0, 1] user_id: str team_ids: tuple[str, ...] - api_key_hash: str response_ids: tuple[str, ...] start_ms: Int64 end_ms: Int64 @@ -143,7 +138,6 @@ class TraceIdentityParams(BaseModel): all_teams: Literal[0, 1] user_id: str team_ids: tuple[str, ...] - api_key_hash: str trace_id: str diff --git a/litellm/rust_bridge/traces.py b/litellm/rust_bridge/traces.py index 23778983d75..f70b5ec3f93 100644 --- a/litellm/rust_bridge/traces.py +++ b/litellm/rust_bridge/traces.py @@ -77,29 +77,17 @@ class DecodedSpan(TypedDict): consumed_attributes: ReadOnly[tuple[str, str]] -class AdminQueryScope(TypedDict): - kind: ReadOnly[Literal["admin"]] +class AllQueryScope(TypedDict): + kind: ReadOnly[Literal["all"]] -class TeamQueryScope(TypedDict): - kind: ReadOnly[Literal["team"]] - team_id: ReadOnly[str] - - -class KeyQueryScope(TypedDict): - kind: ReadOnly[Literal["key"]] - team_id: ReadOnly[str] - api_key_hash: ReadOnly[str] - - -class LogQueryScope(TypedDict): - kind: ReadOnly[Literal["logs"]] +class OwnedQueryScope(TypedDict): + kind: ReadOnly[Literal["owned"]] user_id: ReadOnly[str] team_ids: ReadOnly[tuple[str, ...]] - api_key_hash: ReadOnly[str] -QueryScope = AdminQueryScope | TeamQueryScope | KeyQueryScope | LogQueryScope +QueryScope = AllQueryScope | OwnedQueryScope class NativeStore(Protocol): diff --git a/litellm/tracing/types.py b/litellm/tracing/types.py index 15b28293590..e177094d464 100644 --- a/litellm/tracing/types.py +++ b/litellm/tracing/types.py @@ -108,7 +108,6 @@ class TraceScope(TypedDict): all_teams: ReadOnly[Literal[0, 1]] user_id: ReadOnly[str] team_ids: ReadOnly[tuple[str, ...]] - api_key_hash: ReadOnly[str] class SpanRow(TypedDict): diff --git a/tests/code_coverage_tests/unbounded_in_baseline.txt b/tests/code_coverage_tests/unbounded_in_baseline.txt index c42a6b0ddf5..01d8760855f 100644 --- a/tests/code_coverage_tests/unbounded_in_baseline.txt +++ b/tests/code_coverage_tests/unbounded_in_baseline.txt @@ -125,9 +125,8 @@ litellm/proxy/proxy_server.py _fetch_db_models_for_search prisma not.in `list(db litellm/proxy/proxy_server.py _gather_team_accessible_model_ids prisma model_name.in `_resolved_names` 0 litellm/proxy/proxy_server.py get_all_team_models prisma team_id.in `user_teams` 0 litellm/proxy/spend_tracking/ptu_flat_cost_rollup.py _prune_filter prisma model.in `chunk` 0 -litellm/proxy/spend_tracking/spend_management_endpoints.py _find_team_rows prisma team_id.in `team_ids` 0 -litellm/proxy/spend_tracking/spend_management_endpoints.py ui_view_session_spend_logs prisma team_id.in `permitted_team_ids` 0 -litellm/proxy/spend_tracking/spend_management_endpoints.py ui_view_spend_logs prisma team_id.in `permitted_team_ids` 0 +litellm/proxy/auth/authorization_dependencies.py load_permitted_log_team_ids prisma team_id.in `user_obj.teams` 0 +litellm/proxy/spend_tracking/spend_management_endpoints.py _read_scope_where prisma team_id.in `list(scope.team_ids)` 0 litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py _validate_default_teams_exist prisma team_id.in `list(team_ids)` 0 litellm/proxy/utils.py PrismaClient.check_view_exists raw-sql viewname.IN `IN ( {expected_views_str} )` 0 litellm/proxy/utils.py PrismaClient.delete_data prisma team_id.in `team_id_list` 0 diff --git a/tests/integration/spend/test_spend_log_read_scope.py b/tests/integration/spend/test_spend_log_read_scope.py new file mode 100644 index 00000000000..f9034371e35 --- /dev/null +++ b/tests/integration/spend/test_spend_log_read_scope.py @@ -0,0 +1,226 @@ +import os +import uuid +from collections.abc import AsyncIterator +from dataclasses import dataclass +from datetime import datetime, timedelta, timezone +from typing import Final +from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit + +import psycopg +import pytest +import pytest_asyncio +from integration._support.client import Gateway +from prisma import Prisma +from psycopg import sql +from psycopg.types.json import Jsonb +from pydantic import TypeAdapter + +from litellm.proxy.auth.authorization import AllRows, OwnedRows, ReadScope +from litellm.proxy.spend_tracking.spend_management_endpoints import _spend_log_payload_query, read_scope_sql + + +@dataclass(frozen=True, slots=True) +class SpendRow: + request_id: str + user: str | None + team_id: str | None + call_id: str | None = None + + +@dataclass(frozen=True, slots=True) +class RequestId: + request_id: str + + +REQUEST_IDS: Final = TypeAdapter(tuple[RequestId, ...]) +ROWS: Final = ( + SpendRow("own", "caller", None, "foreign"), + SpendRow("team-1", "other", "first"), + SpendRow("team-2", "third", "second"), + SpendRow("foreign", "other", "outside"), + SpendRow("ownerless", None, None), + SpendRow("team-ownerless", None, "first"), +) + + +def _seed_rows( + connection: psycopg.Connection, + schema: str, + rows: tuple[SpendRow, ...], + session_id: str, + started: datetime, +) -> None: + utc_timestamp: Final = started.astimezone(timezone.utc).replace(tzinfo=None) + with connection.cursor() as cursor: + cursor.executemany( + sql.SQL( + 'INSERT INTO {} (request_id, "user", team_id, litellm_call_id, session_id, ' + '"startTime", "endTime", messages, response, call_type) ' + "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, 'acompletion')" + ).format(sql.Identifier(schema, "LiteLLM_SpendLogs")), + tuple( + ( + row.request_id, + row.user, + row.team_id, + row.call_id, + session_id, + utc_timestamp, + utc_timestamp, + Jsonb([{"role": "user", "content": row.request_id + " payload"}]), + Jsonb({"id": row.request_id}), + ) + for row in rows + ), + ) + + +@pytest_asyncio.fixture(loop_scope="function") +async def spend_database() -> AsyncIterator[Prisma]: + schema: Final = f"integration_spend_scope_{uuid.uuid4().hex}" + url: Final = os.environ["DATABASE_URL"] + parsed: Final = urlsplit(url) + scoped_url: Final = urlunsplit( + parsed._replace(query=urlencode({**dict(parse_qsl(parsed.query)), "schema": schema})) + ) + with psycopg.connect(url, autocommit=True) as setup: + setup.execute(sql.SQL("CREATE SCHEMA {}").format(sql.Identifier(schema))) + try: + setup.execute( + sql.SQL('CREATE TABLE {} (LIKE public."LiteLLM_SpendLogs" INCLUDING ALL)').format( + sql.Identifier(schema, "LiteLLM_SpendLogs") + ) + ) + _seed_rows(setup, schema, ROWS, "scope-session", datetime(2026, 1, 1, tzinfo=timezone.utc)) + database: Final = Prisma(datasource={"url": scoped_url}) + await database.connect() + try: + yield database + finally: + await database.disconnect() + finally: + setup.execute(sql.SQL("DROP SCHEMA {} CASCADE").format(sql.Identifier(schema))) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("preceding_filters", [False, True]) +@pytest.mark.parametrize( + ("scope", "user_filter", "expected"), + [ + (AllRows(), None, ("foreign", "own", "ownerless", "team-1", "team-2", "team-ownerless")), + (OwnedRows("caller"), None, ("own",)), + (OwnedRows(None), None, ()), + (OwnedRows(None, ("first", "second")), None, ("team-1", "team-2", "team-ownerless")), + (OwnedRows(None, ("first", "second")), "other", ("team-1",)), + (OwnedRows("caller", ("first", "second")), None, ("own", "team-1", "team-2", "team-ownerless")), + (OwnedRows("caller", ("first", "second")), "other", ("team-1",)), + (OwnedRows("caller", ("first' OR TRUE --",)), None, ("own",)), + (OwnedRows("caller' OR TRUE --", ("first",)), None, ("team-1", "team-ownerless")), + ], +) +async def test_ownership_sql_selects_allowed_rows_and_intersects_filters( + spend_database: Prisma, + scope: ReadScope, + user_filter: str | None, + expected: tuple[str, ...], + preceding_filters: bool, +) -> None: + window_params: Final = ("scope-session", "2026-01-01", "2026-01-02") if preceding_filters else () + window_sql: Final = ( + 'session_id = $1 AND "startTime" >= $2::timestamp AND "startTime" < $3::timestamp AND ' + if preceding_filters + else "" + ) + clause, scope_params = read_scope_sql(scope, len(window_params) + 1) + filter_sql: Final = f' AND "user" = ${len(window_params) + len(scope_params) + 1}' if user_filter else "" + params: Final = window_params + scope_params + ((user_filter,) if user_filter else ()) + result: Final = await spend_database.query_raw( + f'SELECT request_id FROM "LiteLLM_SpendLogs" WHERE {window_sql}{clause or "TRUE"}{filter_sql} ' + "ORDER BY request_id", + *params, + ) + assert tuple(row.request_id for row in REQUEST_IDS.validate_python(result)) == expected + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("scope", "expected"), + [(AllRows(), ("foreign",)), (OwnedRows("caller"), ("own",)), (OwnedRows(None), ())], +) +async def test_payload_sql_filters_foreign_collisions_and_prefers_exact_ids_for_admins( + spend_database: Prisma, scope: ReadScope, expected: tuple[str, ...] +) -> None: + query, params = _spend_log_payload_query("foreign", scope) + result: Final = await spend_database.query_raw(query, *params) + assert tuple(row.request_id for row in REQUEST_IDS.validate_python(result)) == expected + + +def _delete_session(session_id: str) -> None: + with psycopg.connect(os.environ["DATABASE_URL"]) as connection: + connection.execute('DELETE FROM "LiteLLM_SpendLogs" WHERE session_id = %s', (session_id,)) + + +@pytest.mark.parametrize( + ("member_role", "permissions", "team_access"), + [ + ("admin", [], True), + ("user", ["/spend/logs"], True), + ("user", ["/key/info"], False), + ("user", [], False), + ], +) +def test_spend_log_routes_preserve_user_and_permitted_team_access( + gateway: Gateway, member_role: str, permissions: list[str], team_access: bool +) -> None: + session_id: Final = f"scope-{uuid.uuid4().hex}" + started: Final = datetime.now(timezone.utc) - timedelta(hours=1) + with gateway.scenario() as scenario: + caller: Final = scenario.user(user_role="internal_user") + other: Final = scenario.user(user_role="internal_user") + team: Final = scenario.team( + members_with_roles=[{"user_id": caller, "role": member_role}], + team_member_permissions=list(permissions), + ) + outside_team: Final = scenario.team( + members_with_roles=[{"user_id": other, "role": "admin"}], + team_member_permissions=["/spend/logs"], + ) + key: Final = scenario.key(user_id=caller) + other_key: Final = scenario.key(user_id=other) + rows: Final = ( + SpendRow(session_id + "-own", caller, None, session_id + "-foreign"), + SpendRow(session_id + "-team", other, team), + SpendRow(session_id + "-foreign", other, outside_team), + SpendRow(session_id + "-ownerless", None, None), + SpendRow(session_id + "-outside", other, outside_team), + ) + scenario.cleanups.callback(_delete_session, session_id) + with psycopg.connect(os.environ["DATABASE_URL"]) as connection: + _seed_rows(connection, "public", rows, session_id, started) + expected: Final = (rows[0].request_id, rows[1].request_id) if team_access else (rows[0].request_id,) + session: Final = gateway.request("GET", "/spend/logs/session/ui", key=key, params={"session_id": session_id}) + assert session.status_code == 200, session.text + assert session.json()["total"] == len(expected), session.text + assert sorted(row["request_id"] for row in session.json()["data"]) == list(expected), session.text + filters: Final = { + "session_id": session_id, + "start_date": (started - timedelta(hours=1)).strftime("%Y-%m-%d %H:%M:%S"), + "end_date": (started + timedelta(hours=1)).strftime("%Y-%m-%d %H:%M:%S"), + } + listed: Final = gateway.request("GET", "/spend/logs/ui", key=key, params=filters) + assert listed.status_code == 200, listed.text + assert sorted(row["request_id"] for row in listed.json()["data"]) == list(expected), listed.text + narrowed: Final = gateway.request("GET", "/spend/logs/ui", key=key, params={**filters, "user_id": other}) + assert narrowed.status_code == 200, narrowed.text + assert [row["request_id"] for row in narrowed.json()["data"]] == ( + [rows[1].request_id] if team_access else [] + ), narrowed.text + refused: Final = gateway.request("GET", f"/spend/logs/ui/{rows[4].request_id}", key=key) + assert refused.status_code == 403, refused.text + for caller_key, expected_id in ((key, rows[0].request_id), (other_key, rows[2].request_id)): + payload: Final = gateway.request("GET", f"/spend/logs/ui/{rows[2].request_id}", key=caller_key) + assert payload.status_code == 200, payload.text + assert payload.json()["messages"] == [{"role": "user", "content": expected_id + " payload"}], payload.text + admin: Final = gateway.request("GET", f"/spend/logs/ui/{rows[2].request_id}") + assert admin.status_code == 200, admin.text + assert admin.json()["messages"] == [{"role": "user", "content": rows[2].request_id + " payload"}], admin.text diff --git a/tests/test_litellm/tracing/test_receiver.py b/tests/test_litellm/tracing/test_receiver.py index b8a92606417..36a10ea1e42 100644 --- a/tests/test_litellm/tracing/test_receiver.py +++ b/tests/test_litellm/tracing/test_receiver.py @@ -106,7 +106,7 @@ async def test_empty_export_writes_nothing(): async def test_reads_delegate_to_store(): store = _fake_store() tracing = TraceReceiver(store) - scope: TraceScope = {"team_ids": ("team-research",), "api_key_hash": ""} + scope: Final[TraceScope] = {"all_teams": 0, "user_id": "", "team_ids": ("team-research",)} assert await tracing.get_trace("t1", scope) is None store.get_trace.assert_awaited_once_with("t1", scope, "") diff --git a/tests/test_litellm/tracing/test_store.py b/tests/test_litellm/tracing/test_store.py index 0c1820d7c5f..18a5865db7f 100644 --- a/tests/test_litellm/tracing/test_store.py +++ b/tests/test_litellm/tracing/test_store.py @@ -354,7 +354,7 @@ async def test_list_traces_sets_next_cursor_on_full_page(): } client.query = AsyncMock(return_value=[row, {**row, "trace_id": "t1", "trace_ref": "ref1", "start_ms": 900}]) store = TraceStore(client) - scope: TraceScope = {"all_teams": 0, "user_id": "", "team_ids": ("team-a",), "api_key_hash": ""} + scope: Final[TraceScope] = {"all_teams": 0, "user_id": "", "team_ids": ("team-a",)} page = await store.list_traces(scope, 0, 2000, limit=2) assert [t["trace_id"] for t in page["data"]] == ["t2", "t1"] @@ -373,7 +373,7 @@ async def test_get_span_not_found_and_found(): client = MagicMock() client.query = AsyncMock(return_value=[]) store = TraceStore(client) - scope: TraceScope = {"all_teams": 1, "user_id": "", "team_ids": (), "api_key_hash": ""} + scope: Final[TraceScope] = {"all_teams": 1, "user_id": "", "team_ids": ()} assert await store.get_span("t", "s", scope, "ref") is None stored_input = '[{"role": "user", "content": "hi"}]' client.query = AsyncMock( @@ -425,7 +425,7 @@ async def test_trace_cost_is_scoped_and_counts_repeated_request_once(): ] client.query = AsyncMock(side_effect=[spans, tuple(SpendRow.model_validate({**row, "user": ""}) for row in spend)]) store = TraceStore(client) - scope: TraceScope = {"all_teams": 0, "user_id": "", "team_ids": ("team-a",), "api_key_hash": ""} + scope: Final[TraceScope] = {"all_teams": 0, "user_id": "", "team_ids": ("team-a",)} trace = await store.get_trace("trace-1", scope, "ref") @@ -473,7 +473,7 @@ async def test_run_list_uses_matching_spend_and_leaves_missing_cost_unavailable( } ] client.query = AsyncMock(side_effect=[rows, tuple(SpendRow.model_validate({**row, "user": ""}) for row in spend)]) - scope: TraceScope = {"all_teams": 0, "user_id": "", "team_ids": ("team-a",), "api_key_hash": ""} + scope: Final[TraceScope] = {"all_teams": 0, "user_id": "", "team_ids": ("team-a",)} page = await TraceStore(client).list_traces(scope, 0, 2000) @@ -484,7 +484,7 @@ async def test_run_list_uses_matching_spend_and_leaves_missing_cost_unavailable( @pytest.mark.asyncio async def test_ambiguous_cache_response_id_keeps_cost_unavailable(): client = MagicMock() - span = _llm_row("llm-1", "", "agent", "response-1", team_id="", api_key_hash="key-a") + span: Final = _llm_row("llm-1", "", "agent", "response-1", team_id="", user_id="user", api_key_hash="key-a") spend = [ { "request_id": request_id, @@ -496,9 +496,11 @@ async def test_ambiguous_cache_response_id_keeps_cost_unavailable(): } for request_id, cost in (("response-1", 0.25), ("response-1_cache_hit123", 0.0)) ] - client.query = AsyncMock(side_effect=[[span], tuple(SpendRow.model_validate({**row, "user": ""}) for row in spend)]) + client.query = AsyncMock( + side_effect=[[span], tuple(SpendRow.model_validate({**row, "user": "user"}) for row in spend)] + ) store = TraceStore(client) - scope: TraceScope = {"all_teams": 0, "user_id": "", "team_ids": (), "api_key_hash": "key-a"} + scope: Final[TraceScope] = {"all_teams": 0, "user_id": "user", "team_ids": ()} trace = await store.get_trace("trace-1", scope, "ref") @@ -521,7 +523,7 @@ async def test_diagnostic_continuation_preserves_content_version_scope_and_unico ] ) store = TraceStore(client) - scope = {"all_teams": 0, "user_id": "", "team_ids": ("team-a",), "api_key_hash": "key-a"} + scope: Final[TraceScope] = {"all_teams": 0, "user_id": "", "team_ids": ("team-a",)} first = await store.get_span_error("trace-1", "span-1", scope, "scoped-run") assert first is not None and first["next_cursor"] is not None last = await store.get_span_error("trace-1", "span-1", scope, "scoped-run", first["next_cursor"]) @@ -548,7 +550,7 @@ async def test_malformed_diagnostic_cursor_never_reaches_storage(cursor): client.query = AsyncMock() with pytest.raises(ValueError, match="Invalid diagnostic cursor"): await TraceStore(client).get_span_error( - "trace", "span", {"all_teams": 1, "user_id": "", "team_ids": (), "api_key_hash": ""}, cursor=cursor + "trace", "span", {"all_teams": 1, "user_id": "", "team_ids": ()}, cursor=cursor ) client.query.assert_not_awaited() @@ -660,7 +662,7 @@ async def test_trace_id_collision_requires_a_visible_reference_before_reading_co storage: Final = MagicMock() storage.query = AsyncMock(return_value=(TraceIdentityRow(trace_ref="first"), TraceIdentityRow(trace_ref="second"))) store: Final = TraceStore(storage) - scope: Final[TraceScope] = {"all_teams": 1, "user_id": "", "team_ids": (), "api_key_hash": ""} + scope: Final[TraceScope] = {"all_teams": 1, "user_id": "", "team_ids": ()} with pytest.raises(AmbiguousTraceError, match="provide trace_ref"): await store.get_trace("shared-id", scope) with pytest.raises(AmbiguousTraceError, match="provide trace_ref"): diff --git a/tests/test_litellm_rust/test_traces.py b/tests/test_litellm_rust/test_traces.py index 264f489520d..e0208312f18 100644 --- a/tests/test_litellm_rust/test_traces.py +++ b/tests/test_litellm_rust/test_traces.py @@ -43,6 +43,7 @@ def span_row() -> dict[str, JsonValue]: "name": "root", "type": "agent", "agent": "", + "framework": "", "status": "STATUS_CODE_OK", "status_message": "", "error_truncated": 0, @@ -62,7 +63,7 @@ def span_row() -> dict[str, JsonValue]: @pytest.fixture def span_params() -> dict[str, str | int | list[str]]: - return {"trace_id": "trace-1", "trace_ref": "", "all_teams": 1, "user_id": "", "team_ids": [], "api_key_hash": ""} + return {"trace_id": "trace-1", "trace_ref": "", "all_teams": 1, "user_id": "", "team_ids": []} @pytest.mark.asyncio @@ -128,7 +129,7 @@ async def test_from_env_reads_with_clickhouse_url( recording_server.enqueue(ResponseSpec(body={"data": []})) monkeypatch.setenv("CLICKHOUSE_URL", recording_server.base_url) monkeypatch.delenv("CLICKHOUSE_READER_URL", raising=False) - scope: Final[TraceScope] = {"all_teams": 1, "user_id": "", "team_ids": (), "api_key_hash": ""} + scope: Final[TraceScope] = {"all_teams": 1, "user_id": "", "team_ids": ()} page: Final = await TraceReceiver.from_env().list_traces(scope, 0, 1) assert page == {"data": (), "next_cursor": None} assert len(recording_server.requests) == 1 @@ -295,14 +296,23 @@ async def test_insert_validates_values_without_pydantic_copy(recording_server: R assert stored["SpanAttributes"] == attributes -@pytest.mark.parametrize("role", ["proxy_admin", "proxy_admin_viewer", "internal_user"]) -def test_trace_sql_endpoint_executes_for_admin_and_preserves_clickhouse_envelope( - recording_server: RecordingServer, role: str +@pytest.mark.parametrize( + ("role", "user_id", "expected_status"), + ( + ("proxy_admin", None, 200), + ("proxy_admin_viewer", None, 200), + ("internal_user", "user", 200), + ("internal_user", None, 403), + ), +) +def test_trace_sql_endpoint_enforces_ownership_and_preserves_clickhouse_envelope( + recording_server: RecordingServer, role: str, user_id: str | None, expected_status: int ) -> None: from fastapi import FastAPI from fastapi.testclient import TestClient from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.auth.authorization_dependencies import get_log_team_lookup from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.tracing_endpoints import provide_receiver, provide_trace_query_secret, router @@ -312,19 +322,28 @@ def test_trace_sql_endpoint_executes_for_admin_and_preserves_clickhouse_envelope "rows": 1, "statistics": {"elapsed": 0.01, "rows_read": 1, "bytes_read": 1}, } - recording_server.expected_requests = 12 - for _ in range(11): - recording_server.enqueue(ResponseSpec(body="")) - recording_server.enqueue(ResponseSpec(body=envelope)) + recording_server.expected_requests = 12 if expected_status == 200 else 0 + if expected_status == 200: + for _ in range(11): + recording_server.enqueue(ResponseSpec(body="")) + recording_server.enqueue(ResponseSpec(body=envelope)) storage: Final = ClickHouseStorage(TraceStorageConfig(recording_server.base_url, "trace_test")) app: Final = FastAPI() app.include_router(router) app.dependency_overrides[provide_trace_query_secret] = lambda: "test-master-secret" - app.dependency_overrides[user_api_key_auth] = lambda: UserAPIKeyAuth(user_role=role, token="test") + app.dependency_overrides[user_api_key_auth] = lambda: UserAPIKeyAuth(user_role=role, user_id=user_id, token="test") app.dependency_overrides[provide_receiver] = lambda: TraceReceiver(TraceStore(storage)) + + async def permitted_teams(auth: UserAPIKeyAuth) -> tuple[str, ...]: + return () + + app.dependency_overrides[get_log_team_lookup] = lambda: permitted_teams with TestClient(app) as client: result: Final = client.post("/v1/traces/query", json={"sql": "SELECT 42 AS answer"}) - assert result.status_code == 200, result.text + assert result.status_code == expected_status, result.text + if expected_status == 403: + assert result.json() == {"detail": "Not allowed to view logs"} + return assert result.json() == envelope assert recording_server.requests[-1].raw_body == b"SELECT 42 AS answer" assert client.post("/v1/traces/query", json={"sql": " "}).status_code == 400 diff --git a/tests/unit/proxy/auth/test_authorization.py b/tests/unit/proxy/auth/test_authorization.py new file mode 100644 index 00000000000..7d1548dd828 --- /dev/null +++ b/tests/unit/proxy/auth/test_authorization.py @@ -0,0 +1,29 @@ +from typing import Final + +import pytest + +from litellm.proxy._types import UserAPIKeyAuth +from litellm.proxy.auth.authorization import OwnedRows, resolve_owned_read_scope, resolve_trace_read_scope + + +@pytest.mark.asyncio +@pytest.mark.parametrize("token", (None, "key")) +async def test_team_membership_or_key_without_user_does_not_grant_log_access(token: str | None) -> None: + async def unexpected_lookup() -> tuple[str, ...]: + pytest.fail("Identity-less callers cannot consult team permissions") + + assert await resolve_trace_read_scope(UserAPIKeyAuth(team_id="team", token=token), unexpected_lookup) is None + + +@pytest.mark.asyncio +@pytest.mark.parametrize("token", (None, "key")) +@pytest.mark.parametrize("lookup_fails", (False, True)) +async def test_trace_reads_share_user_and_team_scope_regardless_of_key(token: str | None, lookup_fails: bool) -> None: + async def lookup() -> tuple[str, ...]: + if lookup_fails: + raise RuntimeError("team lookup failed") + return ("permitted",) + + expected: Final = OwnedRows("caller", () if lookup_fails else ("permitted",)) + assert await resolve_owned_read_scope("caller", lookup) == expected + assert await resolve_trace_read_scope(UserAPIKeyAuth(user_id="caller", token=token), lookup) == expected diff --git a/tests/unit/proxy/common_utils/test_http_parsing_utils.py b/tests/unit/proxy/common_utils/test_http_parsing_utils.py index fd747d5a6f2..f00be0f8a65 100644 --- a/tests/unit/proxy/common_utils/test_http_parsing_utils.py +++ b/tests/unit/proxy/common_utils/test_http_parsing_utils.py @@ -1354,7 +1354,7 @@ async def test_auth_body_read_and_trace_handler_leave_stream_for_receiver_limit( store: Final = MagicMock() store.insert_spans = AsyncMock() context: Final = await tracing_endpoints.provide_trace_access( - auth=UserAPIKeyAuth(token="key", team_id="team"), tracing=TraceReceiver(store) + auth=UserAPIKeyAuth(token="key", team_id="team"), tracing=TraceReceiver(store), log_team_lookup=AsyncMock() ) parsed, parse_error = await _read_request_body_deferring_parse_failure(request) diff --git a/tests/unit/proxy/management_endpoints/management_v1/test_spend_logs.py b/tests/unit/proxy/management_endpoints/management_v1/test_spend_logs.py index b6867d338c5..7523c864985 100644 --- a/tests/unit/proxy/management_endpoints/management_v1/test_spend_logs.py +++ b/tests/unit/proxy/management_endpoints/management_v1/test_spend_logs.py @@ -1,5 +1,5 @@ from datetime import datetime, timezone -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import AsyncMock, MagicMock import pytest from fastapi import FastAPI, Request @@ -7,6 +7,7 @@ from fastapi.exceptions import RequestValidationError from fastapi.testclient import TestClient from litellm.proxy._types import LiteLLMRoutes, LitellmUserRoles +from litellm.proxy.auth.authorization_dependencies import get_log_team_lookup from litellm.proxy.auth.user_api_key_auth import UserAPIKeyAuth, user_api_key_auth from litellm.proxy.list_api.common import ( PROBLEM_TYPE_BASE, @@ -51,7 +52,7 @@ WINDOW = "filter[startTime][gte]=2026-07-23T00:00:00Z&filter[startTime][lte]=202 @pytest.fixture def mock_prisma_client(monkeypatch): prisma_client = MagicMock() - prisma_client.db.query_raw = AsyncMock(return_value=[]) + prisma_client.db.query_raw = AsyncMock(return_value=()) monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", prisma_client) return prisma_client @@ -71,9 +72,10 @@ def _mock_rows(mock_prisma_client, end_users: list[str]) -> AsyncMock: return query_raw -def _as_role(role: LitellmUserRoles, user_id): +def _as_role(role: LitellmUserRoles, user_id, log_team_lookup): original = app.dependency_overrides.copy() app.dependency_overrides[user_api_key_auth] = lambda: UserAPIKeyAuth(user_id=user_id, user_role=role) + app.dependency_overrides[get_log_team_lookup] = lambda: log_team_lookup return original @@ -283,13 +285,9 @@ def test_applies_no_scope_for_a_proxy_admin(mock_prisma_client, as_proxy_admin): def test_scopes_a_team_admin_to_their_own_rows_and_teams(mock_prisma_client, role): """A team admin must not see end users belonging to teams they cannot read.""" query_raw = _mock_rows(mock_prisma_client, ["cust-a"]) - original = _as_role(role, user_id="team-admin-1") + original = _as_role(role, user_id="team-admin-1", log_team_lookup=AsyncMock(return_value=("team-a", "team-b"))) try: - with patch( - "litellm.proxy.spend_tracking.spend_management_endpoints._get_permitted_team_ids_for_spend_logs", - new=AsyncMock(return_value=["team-a", "team-b"]), - ): - response = _get() + response = _get() finally: app.dependency_overrides = original @@ -297,24 +295,20 @@ def test_scopes_a_team_admin_to_their_own_rows_and_teams(mock_prisma_client, rol # Same clause shape ui_view_spend_logs builds, so the two cannot diverge. assert '("user" = $3 OR team_id = ANY($4::text[]))' in query_raw.call_args.args[0] assert query_raw.call_args.args[3] == "team-admin-1" - assert query_raw.call_args.args[4] == ["team-a", "team-b"] + assert query_raw.call_args.args[4] == ("team-a", "team-b") def test_scopes_a_teamless_user_to_their_own_rows(mock_prisma_client): query_raw = _mock_rows(mock_prisma_client, []) - original = _as_role(LitellmUserRoles.INTERNAL_USER, user_id="solo") + original = _as_role(LitellmUserRoles.INTERNAL_USER, user_id="solo", log_team_lookup=AsyncMock(return_value=())) try: - with patch( - "litellm.proxy.spend_tracking.spend_management_endpoints._get_permitted_team_ids_for_spend_logs", - new=AsyncMock(return_value=[]), - ): - response = _get() + response = _get() finally: app.dependency_overrides = original assert response.status_code == 200 sql = query_raw.call_args.args[0] - assert '("user" = $3)' in sql + assert '"user" = $3' in sql assert "team_id" not in sql assert query_raw.call_args.args[3] == "solo" @@ -322,13 +316,9 @@ def test_scopes_a_teamless_user_to_their_own_rows(mock_prisma_client): def test_returns_nothing_when_the_caller_owns_no_scope(mock_prisma_client): """Unidentifiable caller must match no rows, never fall through to unscoped.""" query_raw = _mock_rows(mock_prisma_client, []) - original = _as_role(LitellmUserRoles.INTERNAL_USER, user_id=None) + original = _as_role(LitellmUserRoles.INTERNAL_USER, user_id=None, log_team_lookup=AsyncMock(return_value=())) try: - with patch( - "litellm.proxy.spend_tracking.spend_management_endpoints._get_permitted_team_ids_for_spend_logs", - new=AsyncMock(return_value=[]), - ): - response = _get() + response = _get() finally: app.dependency_overrides = original @@ -339,19 +329,17 @@ def test_returns_nothing_when_the_caller_owns_no_scope(mock_prisma_client): def test_scopes_when_the_permitted_team_lookup_fails(mock_prisma_client): """A failed team lookup must degrade to own-rows-only, never to unscoped.""" query_raw = _mock_rows(mock_prisma_client, []) - original = _as_role(LitellmUserRoles.INTERNAL_USER, user_id="solo") + original = _as_role( + LitellmUserRoles.INTERNAL_USER, user_id="solo", log_team_lookup=AsyncMock(side_effect=RuntimeError("db down")) + ) try: - with patch( - "litellm.proxy.spend_tracking.spend_management_endpoints._get_permitted_team_ids_for_spend_logs", - new=AsyncMock(side_effect=RuntimeError("db down")), - ): - response = _get() + response = _get() finally: app.dependency_overrides = original assert response.status_code == 200 sql = query_raw.call_args.args[0] - assert '("user" = $3)' in sql + assert '"user" = $3' in sql assert "team_id" not in sql @@ -422,24 +410,22 @@ def test_user_facet_reads_internal_users_from_spend_logs(mock_prisma_client, as_ def test_user_facet_uses_the_same_team_scope_as_request_logs(mock_prisma_client): query_raw = AsyncMock(return_value=[{"user": "member@example.com"}]) mock_prisma_client.db.query_raw = query_raw - original = _as_role(LitellmUserRoles.INTERNAL_USER, user_id="team-admin-1") + original = _as_role( + LitellmUserRoles.INTERNAL_USER, user_id="team-admin-1", log_team_lookup=AsyncMock(return_value=("team-a",)) + ) try: - with patch( - "litellm.proxy.spend_tracking.spend_management_endpoints._get_permitted_team_ids_for_spend_logs", - new=AsyncMock(return_value=["team-a"]), - ): - response = _get_users() + response = _get_users() finally: app.dependency_overrides = original assert response.status_code == 200 assert '("user" = $3 OR team_id = ANY($4::text[]))' in query_raw.call_args.args[0] assert query_raw.call_args.args[3] == "team-admin-1" - assert query_raw.call_args.args[4] == ["team-a"] + assert query_raw.call_args.args[4] == ("team-a",) def test_user_facet_searches_the_internal_user_value(mock_prisma_client, as_proxy_admin): - query_raw = AsyncMock(return_value=[]) + query_raw = AsyncMock(return_value=()) mock_prisma_client.db.query_raw = query_raw _get_users(f"{WINDOW}&q=alice%40example.com") diff --git a/tests/unit/proxy/spend_tracking/test_log_visibility.py b/tests/unit/proxy/spend_tracking/test_log_visibility.py deleted file mode 100644 index 140225d2000..00000000000 --- a/tests/unit/proxy/spend_tracking/test_log_visibility.py +++ /dev/null @@ -1,47 +0,0 @@ -from typing import Final - -import pytest -from fastapi import HTTPException - -from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth -from litellm.proxy.spend_tracking.log_visibility import LogVisibility, log_visibility - - -@pytest.mark.asyncio -@pytest.mark.parametrize( - ("auth", "expected"), - ( - (UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN), LogVisibility(all_teams=True)), - (UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY), LogVisibility(all_teams=True)), - ( - UserAPIKeyAuth(user_id="user", token="key", team_id="unpermitted"), - LogVisibility(user_id="user", team_ids=("permitted",), api_key_hash="key"), - ), - (UserAPIKeyAuth(token="key", team_id="unpermitted"), LogVisibility(api_key_hash="key")), - ), -) -async def test_log_visibility_uses_user_and_permitted_teams_instead_of_key_team_membership( - auth: UserAPIKeyAuth, - expected: LogVisibility, -) -> None: - async def permitted_teams(caller: UserAPIKeyAuth) -> tuple[str, ...]: - assert caller is auth - return ("permitted",) - - assert await log_visibility(auth, permitted_teams) == expected - - -@pytest.mark.asyncio -async def test_missing_team_permissions_preserve_authenticated_user_and_key_visibility() -> None: - async def no_teams(auth: UserAPIKeyAuth) -> tuple[str, ...]: - return () - - auth: Final = UserAPIKeyAuth(user_id="user", token="key", team_id="team") - assert await log_visibility(auth, no_teams) == LogVisibility(user_id=auth.user_id, api_key_hash="key") - - -@pytest.mark.asyncio -async def test_team_membership_without_authenticated_identity_does_not_grant_log_access() -> None: - with pytest.raises(HTTPException) as error: - await log_visibility(UserAPIKeyAuth(team_id="team")) - assert error.value.status_code == 403 diff --git a/tests/unit/proxy/spend_tracking/test_spend_management_endpoints.py b/tests/unit/proxy/spend_tracking/test_spend_management_endpoints.py index 506de58e438..6aaa536a945 100644 --- a/tests/unit/proxy/spend_tracking/test_spend_management_endpoints.py +++ b/tests/unit/proxy/spend_tracking/test_spend_management_endpoints.py @@ -14,6 +14,8 @@ from fastapi.testclient import TestClient import litellm import litellm.proxy.proxy_server as ps +from litellm.proxy.auth.authorization import OwnedRows +from litellm.proxy.auth.authorization_dependencies import get_log_team_lookup, load_permitted_log_team_ids def _default_date_range(): @@ -1656,10 +1658,7 @@ async def test_ui_view_spend_logs_explicit_user_filter_cannot_escape_own_scope(c "litellm.proxy.proxy_server.prisma_client", make_ui_spend_logs_mock_prisma([caller_log], lambda _where: [], query_observer=observe_query), ) - monkeypatch.setattr( - "litellm.proxy.spend_tracking.spend_management_endpoints._get_permitted_team_ids_for_spend_logs", - AsyncMock(return_value=[]), - ) + monkeypatch.setitem(app.dependency_overrides, get_log_team_lookup, lambda: AsyncMock(return_value=())) app.dependency_overrides[ps.user_api_key_auth] = lambda: UserAPIKeyAuth( user_role=LitellmUserRoles.INTERNAL_USER, user_id="caller@example.com" ) @@ -1715,10 +1714,7 @@ async def test_ui_view_spend_logs_without_user_filter_includes_permitted_team_sc "litellm.proxy.proxy_server.prisma_client", make_ui_spend_logs_mock_prisma([caller_log, member_log, outside_log], filter_by_scope), ) - monkeypatch.setattr( - "litellm.proxy.spend_tracking.spend_management_endpoints._get_permitted_team_ids_for_spend_logs", - AsyncMock(return_value=["team-9"]), - ) + monkeypatch.setitem(app.dependency_overrides, get_log_team_lookup, lambda: AsyncMock(return_value=("team-9",))) app.dependency_overrides[ps.user_api_key_auth] = lambda: UserAPIKeyAuth( user_role=LitellmUserRoles.INTERNAL_USER, user_id="team-admin@example.com" ) @@ -1738,21 +1734,13 @@ async def test_ui_view_spend_logs_without_user_filter_includes_permitted_team_sc @pytest.mark.asyncio -async def test_permitted_team_scope_falls_back_to_own_user_when_lookup_fails(monkeypatch): - monkeypatch.setattr( - "litellm.proxy.spend_tracking.spend_management_endpoints._get_permitted_team_ids_for_spend_logs", - AsyncMock(side_effect=RuntimeError("database unavailable")), - ) +async def test_permitted_team_scope_falls_back_to_own_user_when_lookup_fails(): + from litellm.proxy.auth.authorization import resolve_owned_read_scope - permitted_team_ids = await spend_management_endpoints._get_permitted_team_ids_for_spend_logs_or_empty( - prisma_client=MagicMock(), - user_api_key_dict=UserAPIKeyAuth( - user_role=LitellmUserRoles.INTERNAL_USER, - user_id="caller@example.com", - ), - ) + async def unavailable(): + raise RuntimeError("database unavailable") - assert permitted_team_ids == () + assert await resolve_owned_read_scope("caller", unavailable) == OwnedRows("caller") @pytest.mark.asyncio @@ -1876,10 +1864,7 @@ async def test_ui_view_spend_logs_user_filter_intersects_permitted_team_scope(cl "litellm.proxy.proxy_server.prisma_client", make_ui_spend_logs_mock_prisma([member_log, other_team_log], filter_by_user_and_scope), ) - monkeypatch.setattr( - "litellm.proxy.spend_tracking.spend_management_endpoints._get_permitted_team_ids_for_spend_logs", - AsyncMock(return_value=["team-9"]), - ) + monkeypatch.setitem(app.dependency_overrides, get_log_team_lookup, lambda: AsyncMock(return_value=("team-9",))) app.dependency_overrides[ps.user_api_key_auth] = lambda: UserAPIKeyAuth( user_role=LitellmUserRoles.INTERNAL_USER, user_id="team-admin" ) @@ -2129,61 +2114,6 @@ async def test_ui_view_session_spend_logs_rehydrates_metadata_jsonb_text(client, app.dependency_overrides.pop(ps.user_api_key_auth, None) -@pytest.mark.asyncio -async def test_ui_view_session_spend_logs_scopes_non_admin_to_own_logs(client, monkeypatch): - own_log = { - "id": "log1", - "request_id": "req1", - "session_id": "session-123", - "user": "user-1", - "startTime": "2024-01-01T00:00:00Z", - } - - class MockDB: - async def count(self, *args, **kwargs): - assert kwargs.get("where") == {"session_id": "session-123", "user": "user-1"} - return 1 - - async def query_raw(self, sql_query, session_id, page_size, skip, scoped_user): - assert session_id == "session-123" - assert scoped_user == "user-1" - assert '"user" = $4' in sql_query - return [own_log] - - class MockPrismaClient: - def __init__(self): - self.db = MockDB() - self.db.litellm_spendlogs = self.db - - monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", MockPrismaClient()) - - async def no_permitted_teams(*args, **kwargs): - return [] - - monkeypatch.setattr( - "litellm.proxy.spend_tracking.spend_management_endpoints._get_permitted_team_ids_for_spend_logs", - no_permitted_teams, - ) - - app.dependency_overrides[ps.user_api_key_auth] = lambda: UserAPIKeyAuth( - user_role=LitellmUserRoles.INTERNAL_USER, user_id="user-1" - ) - - try: - response = client.get( - "/spend/logs/session/ui", - params={"session_id": "session-123", "page": 1, "page_size": 50}, - headers={"Authorization": "Bearer sk-test"}, - ) - - assert response.status_code == 200 - data = response.json() - assert data["total"] == 1 - assert [row["request_id"] for row in data["data"]] == ["req1"] - finally: - app.dependency_overrides.pop(ps.user_api_key_auth, None) - - @pytest.mark.asyncio async def test_ui_view_session_spend_logs_includes_permitted_team_logs(client, monkeypatch): class MockDB: @@ -2200,7 +2130,7 @@ async def test_ui_view_session_spend_logs_includes_permitted_team_logs(client, m async def query_raw(self, sql_query, session_id, page_size, skip, scoped_user, team_ids): assert session_id == "session-123" assert scoped_user == "user-1" - assert team_ids == ["team-9"] + assert tuple(team_ids) == ("team-9",) assert '("user" = $4 OR team_id = ANY($5::text[]))' in sql_query return [ { @@ -2222,10 +2152,7 @@ async def test_ui_view_session_spend_logs_includes_permitted_team_logs(client, m async def permitted_teams(*args, **kwargs): return ["team-9"] - monkeypatch.setattr( - "litellm.proxy.spend_tracking.spend_management_endpoints._get_permitted_team_ids_for_spend_logs", - permitted_teams, - ) + monkeypatch.setitem(app.dependency_overrides, get_log_team_lookup, lambda: permitted_teams) app.dependency_overrides[ps.user_api_key_auth] = lambda: UserAPIKeyAuth( user_role=LitellmUserRoles.INTERNAL_USER, user_id="user-1" @@ -2658,31 +2585,6 @@ async def test_ui_view_spend_logs_request_id_rejects_foreign_row_inserted_after_ app.dependency_overrides.pop(ps.user_api_key_auth, None) -def _make_payload_lookup_prisma(rows): - """Emulate the detail endpoint's SQL over an in-memory corpus: the owner - pre-check, the caller scope on ``"user"`` and permitted teams, and the - exact-request_id-first ordering with LIMIT 1.""" - - class MockDB: - async def query_raw(self, sql_query, *params): - if 'SELECT DISTINCT "user", team_id' in sql_query: - return _emulate_spend_log_owner_lookup(rows, sql_query, params) - lookup_id = params[0] - matches = [r for r in rows if lookup_id in (r["request_id"], r["litellm_call_id"])] - if '"user" = $2' in sql_query: - team_ids = params[2] if "ANY($3::text[])" in sql_query else () - matches = [r for r in matches if r["user"] == params[1] or r["team_id"] in team_ids] - if "ORDER BY (request_id = $1) DESC" in sql_query: - matches = sorted(matches, key=lambda r: r["request_id"] == lookup_id, reverse=True) - return matches[:1] - - class MockPrisma: - def __init__(self): - self.db = MockDB() - - return MockPrisma() - - def _payload_row(request_id, litellm_call_id, user, prompt): return { "request_id": request_id, @@ -2696,36 +2598,6 @@ def _payload_row(request_id, litellm_call_id, user, prompt): } -@pytest.mark.asyncio -async def test_ui_view_request_response_collision_serves_callers_own_row(client, monkeypatch): - """The attacker's row carries the victim's request_id as its client-set call id - and was written first. Each tenant's detail lookup of that id serves only their - own payload, and an admin's lookup resolves the exact request_id match rather - than whichever colliding row the database happens to return first.""" - prisma = _make_payload_lookup_prisma( - [ - _payload_row("attacker-req", "victim-req", "attacker_user", "attacker prompt"), - _payload_row("victim-req", "victim-call-id", "victim_user", "victim prompt"), - ] - ) - monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", prisma) - try: - for role, user_id, own_prompt, other_prompt in ( - (LitellmUserRoles.INTERNAL_USER, "victim_user", "victim prompt", "attacker prompt"), - (LitellmUserRoles.INTERNAL_USER, "attacker_user", "attacker prompt", "victim prompt"), - (LitellmUserRoles.PROXY_ADMIN, "admin", "victim prompt", "attacker prompt"), - ): - app.dependency_overrides[ps.user_api_key_auth] = lambda role=role, user_id=user_id: UserAPIKeyAuth( - user_role=role, user_id=user_id - ) - response = client.get("/spend/logs/ui/victim-req", headers={"Authorization": "Bearer sk-test"}) - assert response.status_code == 200, response.text - assert own_prompt in response.text - assert other_prompt not in response.text - finally: - app.dependency_overrides.pop(ps.user_api_key_auth, None) - - @pytest.mark.asyncio async def test_ui_view_request_response_rejects_foreign_row_inserted_after_owner_check(client, monkeypatch): """Backstop behind the SQL scope on the detail endpoint (the mock ignores the @@ -2818,11 +2690,15 @@ async def test_ui_view_request_response_custom_logger_is_keyed_by_callers_own_re that id as its request_id. The custom logger is asked for the caller's own stored request_id, so the caller gets their payload rather than a 403 from the foreign payload's owner check, and the foreign payload is never fetched.""" - prisma = _make_payload_lookup_prisma( - [ - _payload_row("shared-id", "other-call-id", "other_user", "other tenant prompt"), - _payload_row("caller-req", "shared-id", "caller_user", "caller prompt"), - ] + prisma = MagicMock( + db=MagicMock( + query_raw=AsyncMock( + side_effect=[ + [{"user": "other_user", "team_id": None}, {"user": "caller_user", "team_id": None}], + [_payload_row("caller-req", "shared-id", "caller_user", "caller prompt")], + ] + ) + ) ) cold_storage = { "shared-id": { @@ -3161,10 +3037,7 @@ async def test_ui_view_spend_logs_search_keeps_non_admin_scope(client, monkeypat "litellm.proxy.proxy_server.prisma_client", make_ui_spend_logs_mock_prisma(logs, _search_filter_fn(logs, captured)), ) - monkeypatch.setattr( - "litellm.proxy.spend_tracking.spend_management_endpoints._get_permitted_team_ids_for_spend_logs", - AsyncMock(return_value=[]), - ) + monkeypatch.setitem(app.dependency_overrides, get_log_team_lookup, lambda: AsyncMock(return_value=())) ownership_check = AsyncMock() monkeypatch.setattr( "litellm.proxy.spend_tracking.spend_management_endpoints._assert_user_can_view_request_id", @@ -3405,9 +3278,7 @@ async def test_ui_view_spend_logs_with_used_client_oauth_token_filter(client, mo start_date, end_date = _default_date_range() - app.dependency_overrides[ps.user_api_key_auth] = lambda: UserAPIKeyAuth( - user_role=LitellmUserRoles.PROXY_ADMIN - ) + app.dependency_overrides[ps.user_api_key_auth] = lambda: UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN) try: for flag, expected_ids in (("true", ["req-seat"]), ("false", ["req-key"])): response = client.get( @@ -7906,3 +7777,115 @@ def test_capture_rate_reports_an_unreadable_bill_as_502(client, monkeypatch): app.dependency_overrides.pop(ps.user_api_key_auth, None) assert response.status_code == 502 assert "HTTP 401" in response.json()["detail"] + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("user_id", "owner_user", "owner_team", "permitted", "expected"), + [ + ("caller", "caller", "broken", False, True), + ("caller", "other", "allowed", True, True), + ("caller", "other", "allowed", False, False), + ("caller", "other", None, True, False), + (None, None, None, True, False), + (None, None, "allowed", True, True), + ], +) +async def test_shared_owner_policy_preserves_own_user_and_team_access( + user_id, owner_user, owner_team, permitted, expected +): + from litellm.proxy.auth.authorization import can_read_log_owner + + async def lookup(team_id): + if team_id == "broken": + raise RuntimeError("team lookup failed") + return permitted + + assert await can_read_log_owner(user_id, owner_user, owner_team, lookup) is expected + + +@pytest.mark.asyncio +async def test_shared_owner_policy_propagates_team_lookup_failure(): + from litellm.proxy.auth.authorization import can_read_log_owner + + async def unavailable(team_id): + raise RuntimeError("team lookup failed") + + with pytest.raises(RuntimeError, match="team lookup failed"): + await can_read_log_owner("caller", "other", "team", unavailable) + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("params", "expected_status"), + [ + ({"start_date": "invalid", "end_date": "invalid"}, 400), + ({"request_id": "foreign"}, 403), + ], +) +async def test_log_team_dependency_preserves_checks_before_permission_lookup( + client, monkeypatch, params, expected_status +): + from litellm.proxy._types import LiteLLM_UserTable + from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache + + team_reads = [] + + class TeamTable: + async def find_many(self, where): + team_reads.append(where) + return [] + + cache = UserApiKeyCache() + await cache.async_set_cache( + key="caller", value=LiteLLM_UserTable(user_id="caller", teams=["team"]), model_type=LiteLLM_UserTable + ) + prisma = MagicMock( + db=MagicMock( + query_raw=AsyncMock(return_value=[{"user": "other", "team_id": None}]), + litellm_teamtable=TeamTable(), + ) + ) + monkeypatch.setattr(ps, "prisma_client", prisma) + monkeypatch.setattr(ps, "user_api_key_cache", cache) + monkeypatch.setitem( + app.dependency_overrides, + ps.user_api_key_auth, + lambda: UserAPIKeyAuth(user_role=LitellmUserRoles.INTERNAL_USER, user_id="caller"), + ) + + response = client.get("/spend/logs/ui", params=params, headers={"Authorization": "Bearer sk-test"}) + + assert response.status_code == expected_status, response.text + assert team_reads == [] + + +@pytest.mark.asyncio +async def test_management_team_lookup_without_memberships_keeps_own_user_scope(): + from litellm.proxy._types import LiteLLM_UserTable + from litellm.proxy.auth.authorization import resolve_owned_read_scope + from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache + + cache = UserApiKeyCache() + await cache.async_set_cache( + key="caller", value=LiteLLM_UserTable(user_id="caller", teams=[]), model_type=LiteLLM_UserTable + ) + auth = UserAPIKeyAuth(user_id="caller", user_role=LitellmUserRoles.INTERNAL_USER) + team_reads = [] + + class TeamTable: + async def find_many(self, where): + team_reads.append(where) + return [] + + prisma = MagicMock(db=MagicMock(litellm_teamtable=TeamTable())) + + async def lookup(): + return await load_permitted_log_team_ids( + auth, prisma_client=prisma, user_api_key_cache=cache, proxy_logging_obj=ps.proxy_logging_obj + ) + + assert await lookup() == () + scope = await resolve_owned_read_scope(auth.user_id, lookup) + assert scope == OwnedRows("caller") + assert team_reads == [] diff --git a/tests/unit/proxy/spend_tracking/test_spend_query_optimization.py b/tests/unit/proxy/spend_tracking/test_spend_query_optimization.py index 6752c91e9f2..93fae093340 100644 --- a/tests/unit/proxy/spend_tracking/test_spend_query_optimization.py +++ b/tests/unit/proxy/spend_tracking/test_spend_query_optimization.py @@ -11,7 +11,7 @@ from unittest.mock import AsyncMock, MagicMock import pytest - +from litellm.proxy.auth.authorization_dependencies import get_log_team_lookup from litellm.proxy.spend_tracking.spend_tracking_utils import ( get_spend_by_team, get_spend_by_team_and_customer, @@ -180,6 +180,7 @@ async def test_spend_logs_ui_wraps_params_in_at_time_zone_utc(monkeypatch): mock_request.url.path = "/spend/logs/ui" await ui_view_spend_logs( + log_team_lookup=await get_log_team_lookup(), request=mock_request, api_key=None, user_id=None, @@ -209,9 +210,7 @@ def _make_ui_spend_logs_mock(count_total, page_rows): """ mock_prisma = MagicMock() mock_prisma.db = MagicMock() - mock_prisma.db.query_raw = AsyncMock( - side_effect=[[{"total_count": count_total}], page_rows] - ) + mock_prisma.db.query_raw = AsyncMock(side_effect=[[{"total_count": count_total}], page_rows]) mock_prisma.db.litellm_spendlogs = MagicMock() mock_prisma.db.litellm_spendlogs.count = AsyncMock(return_value=0) return mock_prisma @@ -244,6 +243,7 @@ async def test_spend_logs_ui_uses_bounded_count_not_full_scan(monkeypatch): mock_request.url.path = "/spend/logs/ui" response = await ui_view_spend_logs( + log_team_lookup=await get_log_team_lookup(), request=mock_request, api_key=None, user_id=None, @@ -264,17 +264,13 @@ async def test_spend_logs_ui_uses_bounded_count_not_full_scan(monkeypatch): count_sql = count_call[0][0] assert "COUNT(*) OVER ()" not in count_sql assert "LIMIT" in count_sql and "FROM (" in count_sql, ( - "the total must come from a bounded subquery count, not a full-window " - f"scan. SQL was:\n{count_sql}" - ) - assert count_call[0][-1] == SPEND_LOGS_PAGINATION_COUNT_CAP + 1, ( - "the bounded count must probe at most cap+1 rows" + f"the total must come from a bounded subquery count, not a full-window scan. SQL was:\n{count_sql}" ) + assert count_call[0][-1] == SPEND_LOGS_PAGINATION_COUNT_CAP + 1, "the bounded count must probe at most cap+1 rows" page_sql = mock_prisma.db.query_raw.call_args_list[1][0][0] assert "COUNT(*) OVER ()" not in page_sql, ( - "the page query must not carry a window count that forces a full-window " - f"scan. SQL was:\n{page_sql}" + f"the page query must not carry a window count that forces a full-window scan. SQL was:\n{page_sql}" ) assert "GROUP BY" not in count_sql and "DISTINCT ON" not in page_sql, ( "without group_by_session the endpoint must keep raw per-call pagination" @@ -302,9 +298,7 @@ async def test_spend_logs_ui_caps_total_for_large_result_sets(monkeypatch): ) page_rows = [{"request_id": "req-1", "metadata": "{}", "session_id": None}] - mock_prisma = _make_ui_spend_logs_mock( - count_total=SPEND_LOGS_PAGINATION_COUNT_CAP + 1, page_rows=page_rows - ) + mock_prisma = _make_ui_spend_logs_mock(count_total=SPEND_LOGS_PAGINATION_COUNT_CAP + 1, page_rows=page_rows) monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma) auth = UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN, user_id="admin") @@ -312,6 +306,7 @@ async def test_spend_logs_ui_caps_total_for_large_result_sets(monkeypatch): mock_request.url.path = "/spend/logs/ui" response = await ui_view_spend_logs( + log_team_lookup=await get_log_team_lookup(), request=mock_request, api_key=None, user_id=None, @@ -358,6 +353,7 @@ async def test_spend_logs_ui_empty_page_reports_zero_total(monkeypatch): mock_request.url.path = "/spend/logs/ui" response = await ui_view_spend_logs( + log_team_lookup=await get_log_team_lookup(), request=mock_request, api_key=None, user_id=None, @@ -406,6 +402,7 @@ async def test_spend_logs_ui_out_of_range_page_keeps_total(monkeypatch): mock_request.url.path = "/spend/logs/ui" response = await ui_view_spend_logs( + log_team_lookup=await get_log_team_lookup(), request=mock_request, api_key=None, user_id=None, @@ -553,6 +550,7 @@ async def test_spend_logs_ui_group_by_session_paginates_sessions(monkeypatch): mock_request.url.path = "/spend/logs/ui" response = await ui_view_spend_logs( + log_team_lookup=await get_log_team_lookup(), request=mock_request, api_key=None, user_id=None, @@ -620,6 +618,7 @@ async def test_spend_logs_ui_group_by_session_offset_pages_for_other_sorts(monke mock_request.url.path = "/spend/logs/ui" response = await ui_view_spend_logs( + log_team_lookup=await get_log_team_lookup(), request=mock_request, api_key=None, user_id=None, @@ -669,6 +668,7 @@ async def test_spend_logs_ui_request_id_lookup_with_grouping_returns_exact_row(m mock_request.url.path = "/spend/logs/ui" response = await ui_view_spend_logs( + log_team_lookup=await get_log_team_lookup(), request=mock_request, api_key=None, user_id=None, diff --git a/tests/unit/proxy/test_tracing_endpoints.py b/tests/unit/proxy/test_tracing_endpoints.py index fd05d54dcb6..d901222aaf4 100644 --- a/tests/unit/proxy/test_tracing_endpoints.py +++ b/tests/unit/proxy/test_tracing_endpoints.py @@ -5,7 +5,7 @@ Tests for the agent tracing endpoints (litellm/proxy/tracing_endpoints.py). from collections.abc import AsyncGenerator, Mapping from contextlib import asynccontextmanager from types import ModuleType -from typing import Final +from typing import Final, Literal from unittest.mock import AsyncMock, MagicMock import pytest @@ -14,12 +14,14 @@ from fastapi.testclient import TestClient from litellm.proxy import tracing_endpoints from litellm.proxy._types import LitellmUserRoles, ProxyLifespanState, UserAPIKeyAuth +from litellm.proxy.auth.authorization import OwnedRows, ReadScope +from litellm.proxy.auth.authorization_dependencies import get_log_team_lookup from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.tracing_runtime import manage_tracing, provide_storage from litellm.rust_bridge import loader from litellm.rust_bridge.trace_queries import SPAN_DETAIL, SpanDetailParams from litellm.rust_bridge.trace_query_responses import TraceQueryHelp, TraceSQLResponse -from litellm.rust_bridge.traces import AdminQueryScope, ClickHouseStorage, TraceStorageConfig +from litellm.rust_bridge.traces import AllQueryScope, ClickHouseStorage, TraceStorageConfig from litellm.tracing import TraceReceiver, TracingPayloadTooLargeError from litellm.tracing.store import TraceStore from litellm.tracing.types import TraceScope @@ -57,7 +59,11 @@ QUERY_HELP: Final[Mapping[str, object]] = { TEAM_KEY = UserAPIKeyAuth( - token="hashed-key", team_id="team-research", org_id="org-1", user_role=LitellmUserRoles.INTERNAL_USER + user_id="user", + token="hashed-key", + team_id="team-research", + org_id="org-1", + user_role=LitellmUserRoles.INTERNAL_USER, ) TRACE_RESPONSE: Final = { "summary": { @@ -97,38 +103,47 @@ SPAN_DETAIL_RESPONSE: Final = { ( pytest.param( UserAPIKeyAuth(token="admin-key", team_id="team-a", user_role=LitellmUserRoles.PROXY_ADMIN), - TraceScope(all_teams=1, user_id="", team_ids=(), api_key_hash=""), + TraceScope(all_teams=1, user_id="", team_ids=()), True, id="admin", ), pytest.param( UserAPIKeyAuth(token="view-key", team_id="team-a", user_role=LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY), - TraceScope(all_teams=1, user_id="", team_ids=(), api_key_hash=""), + TraceScope(all_teams=1, user_id="", team_ids=()), False, id="view-only-admin", ), pytest.param( TEAM_KEY, - TraceScope(all_teams=0, user_id="", team_ids=(), api_key_hash="hashed-key"), + TraceScope(all_teams=0, user_id="user", team_ids=()), True, id="team-key", ), pytest.param( - UserAPIKeyAuth(token="hashed-key", user_role=LitellmUserRoles.INTERNAL_USER), - TraceScope(all_teams=0, user_id="", team_ids=(), api_key_hash="hashed-key"), + UserAPIKeyAuth(user_id="user", token="hashed-key", user_role=LitellmUserRoles.INTERNAL_USER), + TraceScope(all_teams=0, user_id="user", team_ids=()), True, id="teamless-key", ), + pytest.param( + UserAPIKeyAuth(token="hashed-key", user_role=LitellmUserRoles.INTERNAL_USER), + None, + True, + id="key-without-user-can-only-write", + ), ), ) def test_trace_read_and_write_permissions( - client: TestClient, receiver: MagicMock, auth: UserAPIKeyAuth, scope: TraceScope, can_write: bool + client: TestClient, receiver: MagicMock, auth: UserAPIKeyAuth, scope: TraceScope | None, can_write: bool ) -> None: client.app.dependency_overrides[user_api_key_auth] = lambda: auth read: Final = client.get("/v1/traces?start_ms=1&end_ms=2") - assert read.status_code == 200, read.text - receiver.list_traces.assert_awaited_once_with(scope=scope, start_ms=1, end_ms=2, cursor=None) + assert read.status_code == (403 if scope is None else 200), read.text + if scope is None: + receiver.list_traces.assert_not_awaited() + else: + receiver.list_traces.assert_awaited_once_with(scope=scope, start_ms=1, end_ms=2, cursor=None) write: Final = client.post("/v1/traces", json={}) assert write.status_code == (200 if can_write else 403), write.text @@ -160,6 +175,11 @@ def client() -> TestClient: app = FastAPI() app.include_router(tracing_endpoints.router) app.dependency_overrides[user_api_key_auth] = lambda: TEAM_KEY + + async def lookup(auth: UserAPIKeyAuth) -> tuple[str, ...]: + return () + + app.dependency_overrides[get_log_team_lookup] = lambda: lookup return TestClient(app) @@ -227,7 +247,7 @@ def test_list_traces_passes_scope_window_and_cursor(client, receiver): assert response.status_code == 200 assert response.json() == {"data": [], "next_cursor": None} receiver.list_traces.assert_awaited_once_with( - scope={"all_teams": 0, "user_id": "", "team_ids": (), "api_key_hash": "hashed-key"}, + scope={"all_teams": 0, "user_id": "user", "team_ids": ()}, start_ms=1, end_ms=2, cursor="abc", @@ -247,9 +267,7 @@ def test_get_trace_404_and_200(client, receiver): response = client.get("/v1/traces/t1") assert response.status_code == 200 assert response.json() == TRACE_RESPONSE - receiver.get_trace.assert_awaited_with( - "t1", {"all_teams": 0, "user_id": "", "team_ids": (), "api_key_hash": "hashed-key"}, "" - ) + receiver.get_trace.assert_awaited_with("t1", {"all_teams": 0, "user_id": "user", "team_ids": ()}, "") def test_get_span_404_and_200(client, receiver): @@ -258,9 +276,7 @@ def test_get_span_404_and_200(client, receiver): response = client.get("/v1/traces/t1/spans/s1") assert response.status_code == 200 assert response.json()["span_id"] == "s1" - receiver.get_span.assert_awaited_with( - "t1", "s1", {"all_teams": 0, "user_id": "", "team_ids": (), "api_key_hash": "hashed-key"}, "" - ) + receiver.get_span.assert_awaited_with("t1", "s1", {"all_teams": 0, "user_id": "user", "team_ids": ()}, "") def test_get_span_serves_ui_content_from_stored_payloads(client): @@ -284,9 +300,7 @@ def test_get_span_serves_ui_content_from_stored_payloads(client): def test_trace_detail_passes_scoped_reference(client, receiver): receiver.get_trace.return_value = TRACE_RESPONSE assert client.get("/v1/traces/t1?trace_ref=run-one").status_code == 200 - receiver.get_trace.assert_awaited_with( - "t1", {"all_teams": 0, "user_id": "", "team_ids": (), "api_key_hash": "hashed-key"}, "run-one" - ) + receiver.get_trace.assert_awaited_with("t1", {"all_teams": 0, "user_id": "user", "team_ids": ()}, "run-one") def test_invalid_export_and_cursor_are_client_errors(client, receiver): @@ -298,12 +312,34 @@ def test_invalid_export_and_cursor_are_client_errors(client, receiver): assert client.get("/v1/traces?cursor=broken").status_code == 400 -def test_teamless_key_without_token_gets_403_on_reads(client, receiver): - client.app.dependency_overrides[user_api_key_auth] = lambda: UserAPIKeyAuth( - user_role=LitellmUserRoles.INTERNAL_USER - ) - assert client.get("/v1/traces").status_code == 403 - receiver.list_traces.assert_not_called() +@pytest.mark.parametrize( + "auth", + ( + UserAPIKeyAuth(user_role=LitellmUserRoles.INTERNAL_USER), + UserAPIKeyAuth(token="key"), + UserAPIKeyAuth(token="key", team_id="unpermitted"), + UserAPIKeyAuth(user_id="", token="key"), + ), +) +def test_key_without_user_cannot_read_traces(client: TestClient, auth: UserAPIKeyAuth) -> None: + storage: Final = MagicMock(spec=ClickHouseStorage) + client.app.dependency_overrides[user_api_key_auth] = lambda: auth + client.app.dependency_overrides[tracing_endpoints.provide_receiver] = lambda: TraceReceiver(TraceStore(storage)) + client.app.dependency_overrides[tracing_endpoints.provide_trace_query_secret] = lambda: "test-secret" + for path in ( + "/v1/traces", + "/v1/traces/t1", + "/v1/traces/t1/spans/s1", + "/v1/traces/t1/spans/s1/error", + "/v1/traces/query/help", + ): + response: Final = client.get(path) + assert response.status_code == 403, response.text + query: Final = client.post("/v1/traces/query", json={"sql": "SELECT * FROM otel_traces"}) + assert query.status_code == 403, query.text + storage.query.assert_not_called() + storage.query_sql.assert_not_called() + storage.query_help.assert_not_called() def test_view_only_admin_cannot_ingest_traces(client, receiver): @@ -477,9 +513,8 @@ def test_lifespan_receivers_are_app_local() -> None: SPAN_DETAIL, SpanDetailParams( all_teams=0, - user_id="", + user_id=TEAM_KEY.user_id, team_ids=(), - api_key_hash=TEAM_KEY.token, trace_id="t1", span_id="first-span", trace_ref="first-run", @@ -489,9 +524,8 @@ def test_lifespan_receivers_are_app_local() -> None: SPAN_DETAIL, SpanDetailParams( all_teams=0, - user_id="", + user_id=TEAM_KEY.user_id, team_ids=(), - api_key_hash=TEAM_KEY.token, trace_id="t1", span_id="second-span", trace_ref="second-run", @@ -582,14 +616,17 @@ def test_lens_reads_from_injected_storage_without_receiver() -> None: @pytest.mark.parametrize( ("auth", "expected_scope"), ( - (UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN), {"kind": "admin"}), - (UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY), {"kind": "admin"}), - (TEAM_KEY, {"kind": "logs", "user_id": "", "team_ids": (), "api_key_hash": "hashed-key"}), + (UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN), {"kind": "all"}), + (UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY), {"kind": "all"}), + (TEAM_KEY, {"kind": "owned", "user_id": "user", "team_ids": ()}), ( - UserAPIKeyAuth(token="project-key", team_id="team-a", project_id="project-a"), - {"kind": "logs", "user_id": "", "team_ids": (), "api_key_hash": "project-key"}, + UserAPIKeyAuth(user_id="user", token="project-key", team_id="team-a", project_id="project-a"), + {"kind": "owned", "user_id": "user", "team_ids": ()}, + ), + ( + UserAPIKeyAuth(user_id="user", token="solo-key"), + {"kind": "owned", "user_id": "user", "team_ids": ()}, ), - (UserAPIKeyAuth(token="solo-key"), {"kind": "logs", "user_id": "", "team_ids": (), "api_key_hash": "solo-key"}), ), ) def test_sql_and_help_use_authenticated_scope( @@ -609,7 +646,7 @@ def test_sql_and_help_use_authenticated_scope( assert help_result.status_code == 200, help_result.text assert help_result.json() == QUERY_HELP receiver.store.storage.query_help.assert_awaited_once_with(expected_scope, "test-secret") - forged: Final = client.post("/v1/traces/query", json={"sql": "SELECT 1", "scope": {"kind": "admin"}}) + forged: Final = client.post("/v1/traces/query", json={"sql": "SELECT 1", "scope": {"kind": "all"}}) assert forged.status_code == 422, forged.text assert receiver.store.storage.query_sql.await_count == 1 @@ -638,7 +675,7 @@ def test_sql_reports_rejected_queries_and_unavailable_readers( result: Final = client.post("/v1/traces/query", json={"sql": "SELECT 1"}) assert result.status_code == status, result.text receiver.store.storage.query_sql.assert_awaited_once_with( - "SELECT 1", {"kind": "logs", "user_id": "", "team_ids": (), "api_key_hash": "hashed-key"}, "test-secret" + "SELECT 1", {"kind": "owned", "user_id": "user", "team_ids": ()}, "test-secret" ) @@ -648,7 +685,7 @@ def test_query_help_does_not_fall_back_when_reader_provisioning_fails(client: Te result: Final = client.get("/v1/traces/query/help") assert result.status_code == 503, result.text receiver.store.storage.query_help.assert_awaited_once_with( - {"kind": "logs", "user_id": "", "team_ids": (), "api_key_hash": "hashed-key"}, "test-secret" + {"kind": "owned", "user_id": "user", "team_ids": ()}, "test-secret" ) @@ -668,10 +705,96 @@ def test_queries_require_a_proxy_secret( return assert result.status_code == 200, result.text receiver.store.storage.query_sql.assert_awaited_once_with( - "SELECT 1", {"kind": "logs", "user_id": "", "team_ids": (), "api_key_hash": "hashed-key"}, secret + "SELECT 1", {"kind": "owned", "user_id": "user", "team_ids": ()}, secret ) +@pytest.mark.parametrize( + ("auth", "teams", "expected"), + ( + (UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN), ("team-a",), (1, "", ())), + (UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY), ("team-a",), (1, "", ())), + (UserAPIKeyAuth(user_id="user", token="key", team_id="unpermitted"), ("a", "b"), (0, "user", ("a", "b"))), + (UserAPIKeyAuth(user_id="user", token="key"), (), (0, "user", ())), + (UserAPIKeyAuth(user_id="user"), ("a",), (0, "user", ("a",))), + ), +) +def test_shared_trace_permissions_reach_read_and_sql_boundaries( + client: TestClient, + auth: UserAPIKeyAuth, + teams: tuple[str, ...], + expected: tuple[Literal[0, 1], str, tuple[str, ...]], +) -> None: + async def lookup(caller: UserAPIKeyAuth) -> tuple[str, ...]: + assert caller is auth + return teams + + team_lookup: Final = AsyncMock(side_effect=lookup) + storage: Final = MagicMock(spec=ClickHouseStorage) + storage.query = AsyncMock(return_value=[{"span_id": "s1", "input": "", "output": "", "attributes": {}}]) + storage.query_sql = AsyncMock(return_value=TraceSQLResponse.model_validate(SQL_ENVELOPE)) + storage.query_help = AsyncMock(return_value=TraceQueryHelp.model_validate(QUERY_HELP)) + client.app.dependency_overrides[user_api_key_auth] = lambda: auth + client.app.dependency_overrides[get_log_team_lookup] = lambda: team_lookup + client.app.dependency_overrides[tracing_endpoints.provide_receiver] = lambda: TraceReceiver(TraceStore(storage)) + client.app.dependency_overrides[tracing_endpoints.provide_trace_query_secret] = lambda: "test-secret" + + response: Final = client.get("/v1/traces/t1/spans/s1?trace_ref=run-one") + assert response.status_code == 200, response.text + assert response.json()["span_id"] == "s1" + storage.query.assert_awaited_once_with( + SPAN_DETAIL, + SpanDetailParams( + all_teams=expected[0], + user_id=expected[1], + team_ids=expected[2], + trace_id="t1", + span_id="s1", + trace_ref="run-one", + ), + ) + sql_response: Final = client.post("/v1/traces/query", json={"sql": "SELECT * FROM otel_traces"}) + assert sql_response.status_code == 200, sql_response.text + assert sql_response.json() == SQL_ENVELOPE + assert client.get("/v1/traces/query/help").json() == QUERY_HELP + query_scope: Final = ( + {"kind": "all"} + if expected[0] + else { + "kind": "owned", + "user_id": expected[1], + "team_ids": expected[2], + } + ) + storage.query_sql.assert_awaited_once_with("SELECT * FROM otel_traces", query_scope, "test-secret") + storage.query_help.assert_awaited_once_with(query_scope, "test-secret") + assert team_lookup.await_count == ( + 3 + if auth.user_id and auth.user_role not in (LitellmUserRoles.PROXY_ADMIN, LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY) + else 0 + ) + + +@pytest.mark.parametrize( + ("scope", "expected"), + ( + (OwnedRows(None), ("", ())), + (OwnedRows("user"), ("user", ())), + (OwnedRows("user", ("a", "b")), ("user", ("a", "b"))), + ), +) +def test_trace_storage_permissions_map_owned_rows( + scope: ReadScope, + expected: tuple[str, tuple[str, ...]], +) -> None: + assert tracing_endpoints._trace_scope(scope) == TraceScope(all_teams=0, user_id=expected[0], team_ids=expected[1]) + assert tracing_endpoints.trace_query_scope(scope) == { + "kind": "owned", + "user_id": expected[0], + "team_ids": expected[1], + } + + class _NativeConfig: def __init__(self, database: str, url: str, retention_days: int) -> None: pass @@ -685,7 +808,7 @@ class _NativeReturningHelp(ModuleType): def __init__(self, config: _NativeConfig) -> None: pass - async def query_help(self, scope: AdminQueryScope, secret: str) -> Mapping[str, object]: + async def query_help(self, scope: AllQueryScope, secret: str) -> Mapping[str, object]: return help_payload self.NativeTraceConfig: Final = _NativeConfig @@ -698,7 +821,7 @@ class _NativeReturningHelp(ModuleType): async def test_storage_validates_the_native_query_help_value(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(loader, "_cached_bridge", _NativeReturningHelp(QUERY_HELP)) storage: Final = ClickHouseStorage(TraceStorageConfig("http://clickhouse:8123")) - assert await storage.query_help({"kind": "admin"}, "secret") == TraceQueryHelp.model_validate(QUERY_HELP) + assert await storage.query_help({"kind": "all"}, "secret") == TraceQueryHelp.model_validate(QUERY_HELP) @pytest.mark.parametrize( @@ -726,4 +849,4 @@ async def test_storage_rejects_native_query_help_that_drifts_from_the_contract( monkeypatch.setattr(loader, "_cached_bridge", _NativeReturningHelp({**QUERY_HELP, **drift})) storage: Final = ClickHouseStorage(TraceStorageConfig("http://clickhouse:8123")) with pytest.raises(RuntimeError, match="invalid response"): - await storage.query_help({"kind": "admin"}, "secret") + await storage.query_help({"kind": "all"}, "secret") diff --git a/tests/unit/rust_bridge/test_trace_queries.py b/tests/unit/rust_bridge/test_trace_queries.py index 5ffe9ea0404..76ead3582a4 100644 --- a/tests/unit/rust_bridge/test_trace_queries.py +++ b/tests/unit/rust_bridge/test_trace_queries.py @@ -15,7 +15,6 @@ def test_named_query_rejects_offsets_outside_the_native_integer_range(offset: in "all_teams": 0, "user_id": "", "team_ids": ["team"], - "api_key_hash": "key", "trace_id": "trace", "trace_ref": "ref", "span_id": "span", @@ -31,7 +30,6 @@ def test_named_query_rejects_parameters_for_a_different_query() -> None: all_teams=0, user_id="", team_ids=("team",), - api_key_hash="key", trace_id="trace", trace_ref="ref", span_id="span", diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index df968e0bc9f..722f10cd571 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -46901,8 +46901,11 @@ export interface components { expression: string; /** Key */ key: string; - /** Type */ - type: string; + /** + * Type + * @constant + */ + type: "String"; }; /** TraceQueryAttributes */ TraceQueryAttributes: { @@ -46916,8 +46919,11 @@ export interface components { fields: components["schemas"]["TraceQueryAttributeField"][]; /** Scope */ scope: string; - /** Table */ - table: string; + /** + * Table + * @enum {string} + */ + table: "otel_traces" | "agent_traces_by_key" | "spend_logs"; /** Truncated */ truncated: boolean; }; @@ -46977,8 +46983,11 @@ export interface components { sampled_rows: number; /** Scope */ scope: string; - /** Table */ - table: string; + /** + * Table + * @enum {string} + */ + table: "otel_traces" | "agent_traces_by_key" | "spend_logs"; /** Truncated */ truncated: boolean; }; @@ -46989,7 +46998,7 @@ export interface components { /** Path */ path: (string | number)[]; /** Types */ - types: string[]; + types: ("array" | "boolean" | "integer" | "null" | "number" | "object" | "string")[]; }; /** TraceQueryNormalizedField */ TraceQueryNormalizedField: { @@ -46999,8 +47008,11 @@ export interface components { meaning: string; /** Name */ name: string; - /** Table */ - table: string; + /** + * Table + * @enum {string} + */ + table: "otel_traces" | "agent_traces_by_key" | "spend_logs"; /** Type */ type: string; }; @@ -47035,8 +47047,11 @@ export interface components { TraceQueryTable: { /** Columns */ columns: components["schemas"]["TraceQueryColumn"][]; - /** Name */ - name: string; + /** + * Name + * @enum {string} + */ + name: "otel_traces" | "agent_traces_by_key" | "spend_logs"; }; /** TraceSQLResponse */ TraceSQLResponse: { From 8b11b682f46fab627972d7f129bb0dceed6a88a2 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2026 18:39:06 -0700 Subject: [PATCH 16/61] fix(proxy-extras): retry P3009 when a peer already recovered the named migration row (#44283) * fix(proxy-extras): retry P3009 when a peer already recovered the named migration row * fix(proxy-extras): pin P3009 recovery locals as Final and cover a recovered sibling row * test(proxy-extras): stop the P3009 ledger fakes shadowing the partition detector cursor * fix(proxy-extras): annotate the migration ledger reader locals as Final --------- Co-authored-by: yuneng --- .../litellm_proxy_extras/utils.py | 68 ++++--- .../test_litellm_proxy_extras_utils.py | 172 +++++++++++++++++- 2 files changed, 216 insertions(+), 24 deletions(-) diff --git a/litellm-proxy-extras/litellm_proxy_extras/utils.py b/litellm-proxy-extras/litellm_proxy_extras/utils.py index 2062ca93fb3..245244250ee 100644 --- a/litellm-proxy-extras/litellm_proxy_extras/utils.py +++ b/litellm-proxy-extras/litellm_proxy_extras/utils.py @@ -353,9 +353,8 @@ class ProxyExtrasDBManager: pass @staticmethod - def _failed_migration_logs(migration_name: str) -> Optional[str]: - """Return failed migration logs, or None if the ledger is unavailable.""" - database_url = os.getenv("DATABASE_URL") + def _read_migration_ledger(query: str, params: tuple[str, ...]) -> "tuple[object, ...] | None": + database_url: Final = os.getenv("DATABASE_URL") if not database_url: return None @@ -364,28 +363,37 @@ class ProxyExtrasDBManager: except ImportError: return None - cleaned_url = ProxyExtrasDBManager._strip_prisma_query_params(database_url) - ledger_table = psycopg.sql.SQL("{}.{}").format( - psycopg.sql.Identifier( - ProxyExtrasDBManager._prisma_schema_param(database_url) or "public" - ), + cleaned_url: Final = ProxyExtrasDBManager._strip_prisma_query_params(database_url) + ledger_table: Final = psycopg.sql.SQL("{}.{}").format( + psycopg.sql.Identifier(ProxyExtrasDBManager._prisma_schema_param(database_url) or "public"), psycopg.sql.Identifier("_prisma_migrations"), ) try: - with psycopg.connect( - cleaned_url, connect_timeout=10, autocommit=True - ) as conn: - row = conn.execute( - psycopg.sql.SQL( - "SELECT logs FROM {} " - "WHERE migration_name = %s AND finished_at IS NULL " - "AND rolled_back_at IS NULL" - ).format(ledger_table), - (migration_name,), - ).fetchone() + with psycopg.connect(cleaned_url, connect_timeout=10, autocommit=True) as conn: + row: Final = conn.execute(psycopg.sql.SQL(query).format(ledger_table), params).fetchone() except (psycopg.OperationalError, psycopg.DatabaseError): return None - return (row[0] or "") if row else "" + return tuple(row) if row is not None else () + + @staticmethod + def _failed_migration_logs(migration_name: str, started_at: str) -> Optional[str]: + row: Final = ProxyExtrasDBManager._read_migration_ledger( + "SELECT logs FROM {} WHERE migration_name = %s AND started_at = %s::timestamptz " + "AND finished_at IS NULL AND rolled_back_at IS NULL", + (migration_name, started_at), + ) + if row is None: + return None + return row[0] if row and isinstance(row[0], str) else "" + + @staticmethod + def _failed_migration_recovered(migration_name: str, started_at: str) -> bool: + row: Final = ProxyExtrasDBManager._read_migration_ledger( + "SELECT 1 FROM {} WHERE migration_name = %s AND started_at = %s::timestamptz " + "AND (finished_at IS NOT NULL OR rolled_back_at IS NOT NULL)", + (migration_name, started_at), + ) + return bool(row) @staticmethod def _resolve_specific_migration(migration_name: str): @@ -1102,6 +1110,11 @@ class ProxyExtrasDBManager: return match.group(1) if match else None return None + @staticmethod + def _v2_failed_migration_started_at(stderr: str, migration_name: str) -> "str | None": + match: Final = re.search(rf"`{re.escape(migration_name)}` migration started at ([^\r\n]+?) failed", stderr) + return match.group(1) if match else None + @staticmethod def _v2_roll_back_migration_best_effort(migration_name: str) -> None: from litellm_proxy_extras.migration_lock import migration_environment @@ -1130,8 +1143,11 @@ class ProxyExtrasDBManager: if "P3009" in stderr: migration_name = ProxyExtrasDBManager._v2_failed_migration_name(stderr) - if migration_name: - ledger_logs = ProxyExtrasDBManager._failed_migration_logs(migration_name) + started_at: Final = ( + ProxyExtrasDBManager._v2_failed_migration_started_at(stderr, migration_name) if migration_name else None + ) + if migration_name and started_at: + ledger_logs: Final = ProxyExtrasDBManager._failed_migration_logs(migration_name, started_at) if ledger_logs and _MIGRATION_DEADLOCK_MARKER in ledger_logs: logger.info( "Migration %s failed in a concurrent migrate deploy " @@ -1140,6 +1156,14 @@ class ProxyExtrasDBManager: ) ProxyExtrasDBManager._v2_roll_back_migration_best_effort(migration_name) return budget.spend() + if ProxyExtrasDBManager._failed_migration_recovered(migration_name, started_at): + logger.info( + "Migration %s started at %s was already rolled back or completed by a concurrent " + "migrate deploy, retrying", + migration_name, + started_at, + ) + return budget.spend() raise RuntimeError( "Migration completion could not be verified. LiteLLM startup has stopped.\n\n" f"Prisma migration history (migration name and start time):\n{stderr}\n\n" diff --git a/tests/unit/litellm_proxy_extras/test_litellm_proxy_extras_utils.py b/tests/unit/litellm_proxy_extras/test_litellm_proxy_extras_utils.py index 29c9ec56d91..ea4a25283a1 100644 --- a/tests/unit/litellm_proxy_extras/test_litellm_proxy_extras_utils.py +++ b/tests/unit/litellm_proxy_extras/test_litellm_proxy_extras_utils.py @@ -3,6 +3,7 @@ import os import re import sys import threading +from dataclasses import dataclass from pathlib import Path from typing import Final @@ -709,6 +710,9 @@ class TestSpendLogsPartitionDetectionMissingPsycopg: _ATTEMPT_BUDGET = 4 +_P3009_MIGRATION_NAME = "20260415120000_health_check_latest_per_model_index" +_P3009_STARTED_AT = "2026-10-02 23:20:56.439594 UTC" +_P3009_DEADLOCK_LOGS = "ERROR: deadlock detected\nDETAIL: Process 72 waits for ShareLock on transaction 991" _P3005_STDERR = """Error: P3005 @@ -730,6 +734,80 @@ ERROR: relation "SomeTable" already exists """ +def _p3009_stderr(migration_name: str, started_at: str) -> str: + return ( + "Error: P3009\n\n" + "migrate found failed migrations in the target database, new migrations will not be applied. " + "Read more about how to resolve migration issues in a production database: " + "https://pris.ly/d/migrate-resolve\n" + f"The `{migration_name}` migration started at {started_at} failed\n" + ) + + +@dataclass(frozen=True, slots=True) +class _LedgerRow: + migration_name: str + started_at: str + finished: bool = False + rolled_back: bool = False + logs: str | None = None + + +@dataclass(frozen=True, slots=True) +class _LedgerCursor: + row: tuple[object, ...] | None = None + + def fetchone(self) -> tuple[object, ...] | None: + return self.row + + def fetchall(self) -> tuple[tuple[object, ...], ...]: + return () + + +class _LedgerConnection: + def __init__(self, ledger: "_FakeLedger") -> None: + self.ledger = ledger + + def __enter__(self) -> "_LedgerConnection": + return self + + def __exit__(self, *args: object) -> None: + return None + + def execute(self, query: object, params: tuple[object, ...] = ()) -> _LedgerCursor: + return self.ledger.execute(query, params) + + +class _FakeLedger: + def __init__(self, at_error: tuple[_LedgerRow, ...], after_peer: tuple[_LedgerRow, ...]) -> None: + self.rows = at_error + self.after_peer = after_peer + self._peer_observed = False + + def connect(self, *args: object, **kwargs: object) -> _LedgerConnection: + return _LedgerConnection(self) + + def execute(self, query: object, params: tuple[object, ...]) -> _LedgerCursor: + text: Final = str(query) + if "WHERE migration_name = %s" not in text or not params: + return _LedgerCursor() + if not self._peer_observed: + self.rows = self.after_peer + self._peer_observed = True + matching: Final = tuple( + row + for row in self.rows + if row.migration_name == params[0] and (len(params) == 1 or row.started_at == params[1]) + ) + if "rolled_back_at IS NULL" in text: + unresolved: Final = next((row for row in matching if not row.finished and not row.rolled_back), None) + return _LedgerCursor((unresolved.logs,) if unresolved else None) + if "IS NOT NULL" in text: + resolved: Final = next((row for row in matching if row.finished or row.rolled_back), None) + return _LedgerCursor((1,) if resolved else None) + return _LedgerCursor() + + @pytest.mark.parametrize( "pooled,direct,expected", ( @@ -759,7 +837,15 @@ class _MigrateDeployHarness: `prisma migrate deploy` outcomes, with every recovery command faked out so nothing touches a database or the packaged migrations directory.""" - def __init__(self, monkeypatch, tmp_path, outcomes, repeat_last=False, confirmed_migrations=()): + def __init__( + self, + monkeypatch, + tmp_path, + outcomes, + repeat_last=False, + confirmed_migrations=(), + ledger: "_FakeLedger | None" = None, + ): import subprocess as subprocess_module import litellm_proxy_extras.utils as utils_module @@ -772,7 +858,11 @@ class _MigrateDeployHarness: self._subprocess_module = subprocess_module self.confirmed_migrations = set(confirmed_migrations) - monkeypatch.delenv("DATABASE_URL", raising=False) + if ledger is None: + monkeypatch.delenv("DATABASE_URL", raising=False) + else: + monkeypatch.setenv("DATABASE_URL", "postgresql://u:p@localhost:9/x") + monkeypatch.setattr("psycopg.connect", ledger.connect) monkeypatch.setenv("LITELLM_MIGRATION_DIR", str(tmp_path)) monkeypatch.setattr(utils_module.prisma_toolchain, "run_prisma", self._fake_run) monkeypatch.setattr(utils_module, "_get_prisma_env", lambda: {}) @@ -819,6 +909,84 @@ class _MigrateDeployHarness: return True +class TestConcurrentP3009Recovery: + @pytest.mark.parametrize( + "after_peer", + ( + (_LedgerRow(_P3009_MIGRATION_NAME, _P3009_STARTED_AT, rolled_back=True, logs=_P3009_DEADLOCK_LOGS),), + ( + _LedgerRow(_P3009_MIGRATION_NAME, _P3009_STARTED_AT, rolled_back=True, logs=_P3009_DEADLOCK_LOGS), + _LedgerRow(_P3009_MIGRATION_NAME, "2026-10-02 23:21:11.539224 UTC"), + ), + (_LedgerRow(_P3009_MIGRATION_NAME, _P3009_STARTED_AT, finished=True, logs=_P3009_DEADLOCK_LOGS),), + ), + ids=("rolled-back", "rolled-back-beside-a-fresh-in-flight-row", "finished"), + ) + def test_a_p3009_row_a_peer_already_recovered_is_retried( + self, + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + after_peer: tuple[_LedgerRow, ...], + ) -> None: + deadlocked_row: Final = _LedgerRow( + _P3009_MIGRATION_NAME, + _P3009_STARTED_AT, + logs=_P3009_DEADLOCK_LOGS, + ) + harness: Final = _MigrateDeployHarness( + monkeypatch, + tmp_path, + [_p3009_stderr(_P3009_MIGRATION_NAME, _P3009_STARTED_AT), "ok"], + ledger=_FakeLedger(at_error=(deadlocked_row,), after_peer=after_peer), + ) + + assert harness.run() is True + assert len(harness.deploy_calls) == 2 + + @pytest.mark.parametrize( + "ledger_rows", + ( + ( + _LedgerRow( + _P3009_MIGRATION_NAME, + _P3009_STARTED_AT, + logs='ERROR: syntax error at or near "SLECT"', + ), + ), + ( + _LedgerRow( + _P3009_MIGRATION_NAME, + _P3009_STARTED_AT, + logs='ERROR: syntax error at or near "SLECT"', + ), + _LedgerRow( + _P3009_MIGRATION_NAME, + "2026-10-02 23:19:40.120000 UTC", + rolled_back=True, + logs=_P3009_DEADLOCK_LOGS, + ), + ), + ), + ids=("only-row", "beside-a-recovered-earlier-attempt"), + ) + def test_an_unresolved_p3009_row_without_the_deadlock_marker_stops( + self, + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + ledger_rows: tuple[_LedgerRow, ...], + ) -> None: + harness: Final = _MigrateDeployHarness( + monkeypatch, + tmp_path, + [_p3009_stderr(_P3009_MIGRATION_NAME, _P3009_STARTED_AT)], + ledger=_FakeLedger(at_error=ledger_rows, after_peer=ledger_rows), + ) + + with pytest.raises(RuntimeError, match="Migration completion could not be verified"): + harness.run() + assert len(harness.deploy_calls) == 1 + + class TestMigrateDeployAttemptAccounting: def test_a_push_created_database_finishes_bootstrapping(self, monkeypatch, tmp_path): harness = _MigrateDeployHarness( From c5c0a48ef149a6c4077f23861612f2020c1f01ba Mon Sep 17 00:00:00 2001 From: "berriai-litellm-provider-info-sync[bot]" <328147090+berriai-litellm-provider-info-sync[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2026 19:05:52 -0700 Subject: [PATCH 17/61] fix(bedrock): price amazon nova 2 pro preview at the standard tier (#44302) Price-Sync: litellm-providers Co-authored-by: berriai-litellm-provider-info-sync[bot] <328147090+berriai-litellm-provider-info-sync[bot]@users.noreply.github.com> --- ...odel_prices_and_context_window_backup.json | 44 ++++++++++--------- model_prices_and_context_window.json | 44 ++++++++++--------- 2 files changed, 48 insertions(+), 40 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 2b613ff1aea..31e150f1f7d 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -386,16 +386,17 @@ "supports_vision": true }, "amazon.nova-2-pro-preview-20251202-v1:0": { - "cache_read_input_token_cost": 5.46875e-07, - "input_cost_per_token": 2.1875e-06, - "input_cost_per_image_token": 2.1875e-06, - "input_cost_per_audio_token": 2.1875e-06, + "cache_read_input_token_cost": 3.125e-07, + "input_cost_per_token": 1.25e-06, + "input_cost_per_image_token": 1.25e-06, + "input_cost_per_audio_token": 1.25e-06, "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, "max_tokens": 64000, "mode": "chat", - "output_cost_per_token": 1.75e-05, + "output_cost_per_token": 1e-05, + "source": "https://aws.amazon.com/nova/pricing/", "supports_function_calling": true, "supports_pdf_input": true, "supports_prompt_caching": true, @@ -424,16 +425,17 @@ "supports_vision": true }, "apac.amazon.nova-2-pro-preview-20251202-v1:0": { - "cache_read_input_token_cost": 5.46875e-07, - "input_cost_per_token": 2.1875e-06, - "input_cost_per_image_token": 2.1875e-06, - "input_cost_per_audio_token": 2.1875e-06, + "cache_read_input_token_cost": 3.4375e-07, + "input_cost_per_token": 1.375e-06, + "input_cost_per_image_token": 1.375e-06, + "input_cost_per_audio_token": 1.375e-06, "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, "max_tokens": 64000, "mode": "chat", - "output_cost_per_token": 1.75e-05, + "output_cost_per_token": 1.1e-05, + "source": "https://aws.amazon.com/nova/pricing/", "supports_function_calling": true, "supports_pdf_input": true, "supports_prompt_caching": true, @@ -462,16 +464,17 @@ "supports_vision": true }, "eu.amazon.nova-2-pro-preview-20251202-v1:0": { - "cache_read_input_token_cost": 5.46875e-07, - "input_cost_per_token": 2.1875e-06, - "input_cost_per_image_token": 2.1875e-06, - "input_cost_per_audio_token": 2.1875e-06, + "cache_read_input_token_cost": 3.4375e-07, + "input_cost_per_token": 1.375e-06, + "input_cost_per_image_token": 1.375e-06, + "input_cost_per_audio_token": 1.375e-06, "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, "max_tokens": 64000, "mode": "chat", - "output_cost_per_token": 1.75e-05, + "output_cost_per_token": 1.1e-05, + "source": "https://aws.amazon.com/nova/pricing/", "supports_function_calling": true, "supports_pdf_input": true, "supports_prompt_caching": true, @@ -500,16 +503,17 @@ "supports_vision": true }, "us.amazon.nova-2-pro-preview-20251202-v1:0": { - "cache_read_input_token_cost": 5.46875e-07, - "input_cost_per_token": 2.1875e-06, - "input_cost_per_image_token": 2.1875e-06, - "input_cost_per_audio_token": 2.1875e-06, + "cache_read_input_token_cost": 3.4375e-07, + "input_cost_per_token": 1.375e-06, + "input_cost_per_image_token": 1.375e-06, + "input_cost_per_audio_token": 1.375e-06, "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, "max_tokens": 64000, "mode": "chat", - "output_cost_per_token": 1.75e-05, + "output_cost_per_token": 1.1e-05, + "source": "https://aws.amazon.com/nova/pricing/", "supports_function_calling": true, "supports_pdf_input": true, "supports_prompt_caching": true, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 2b613ff1aea..31e150f1f7d 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -386,16 +386,17 @@ "supports_vision": true }, "amazon.nova-2-pro-preview-20251202-v1:0": { - "cache_read_input_token_cost": 5.46875e-07, - "input_cost_per_token": 2.1875e-06, - "input_cost_per_image_token": 2.1875e-06, - "input_cost_per_audio_token": 2.1875e-06, + "cache_read_input_token_cost": 3.125e-07, + "input_cost_per_token": 1.25e-06, + "input_cost_per_image_token": 1.25e-06, + "input_cost_per_audio_token": 1.25e-06, "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, "max_tokens": 64000, "mode": "chat", - "output_cost_per_token": 1.75e-05, + "output_cost_per_token": 1e-05, + "source": "https://aws.amazon.com/nova/pricing/", "supports_function_calling": true, "supports_pdf_input": true, "supports_prompt_caching": true, @@ -424,16 +425,17 @@ "supports_vision": true }, "apac.amazon.nova-2-pro-preview-20251202-v1:0": { - "cache_read_input_token_cost": 5.46875e-07, - "input_cost_per_token": 2.1875e-06, - "input_cost_per_image_token": 2.1875e-06, - "input_cost_per_audio_token": 2.1875e-06, + "cache_read_input_token_cost": 3.4375e-07, + "input_cost_per_token": 1.375e-06, + "input_cost_per_image_token": 1.375e-06, + "input_cost_per_audio_token": 1.375e-06, "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, "max_tokens": 64000, "mode": "chat", - "output_cost_per_token": 1.75e-05, + "output_cost_per_token": 1.1e-05, + "source": "https://aws.amazon.com/nova/pricing/", "supports_function_calling": true, "supports_pdf_input": true, "supports_prompt_caching": true, @@ -462,16 +464,17 @@ "supports_vision": true }, "eu.amazon.nova-2-pro-preview-20251202-v1:0": { - "cache_read_input_token_cost": 5.46875e-07, - "input_cost_per_token": 2.1875e-06, - "input_cost_per_image_token": 2.1875e-06, - "input_cost_per_audio_token": 2.1875e-06, + "cache_read_input_token_cost": 3.4375e-07, + "input_cost_per_token": 1.375e-06, + "input_cost_per_image_token": 1.375e-06, + "input_cost_per_audio_token": 1.375e-06, "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, "max_tokens": 64000, "mode": "chat", - "output_cost_per_token": 1.75e-05, + "output_cost_per_token": 1.1e-05, + "source": "https://aws.amazon.com/nova/pricing/", "supports_function_calling": true, "supports_pdf_input": true, "supports_prompt_caching": true, @@ -500,16 +503,17 @@ "supports_vision": true }, "us.amazon.nova-2-pro-preview-20251202-v1:0": { - "cache_read_input_token_cost": 5.46875e-07, - "input_cost_per_token": 2.1875e-06, - "input_cost_per_image_token": 2.1875e-06, - "input_cost_per_audio_token": 2.1875e-06, + "cache_read_input_token_cost": 3.4375e-07, + "input_cost_per_token": 1.375e-06, + "input_cost_per_image_token": 1.375e-06, + "input_cost_per_audio_token": 1.375e-06, "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 64000, "max_tokens": 64000, "mode": "chat", - "output_cost_per_token": 1.75e-05, + "output_cost_per_token": 1.1e-05, + "source": "https://aws.amazon.com/nova/pricing/", "supports_function_calling": true, "supports_pdf_input": true, "supports_prompt_caching": true, From f63d989ff9437809d509f276001e0fbe135aa198 Mon Sep 17 00:00:00 2001 From: tin-berri Date: Fri, 2 Oct 2026 19:37:08 -0700 Subject: [PATCH 18/61] feat: add Bespoke Nimble gateway and OSS classifier support (#44246) * feat: add Bespoke Nimble gateway and OSS classifier support * feat: accept Ollama's nimble model name for the Bespoke provider * test: exempt the POST-only bespoke decisions route from the all-methods check test_pass_through_routes_support_all_methods requires every built-in pass-through route to accept every HTTP method unless it is listed in PROTOCOL_CONSTRAINED_PASS_THROUGH_ROUTES. /bespoke/v1/systemone is POST-only like /laya/v1/systemone, so the test failed at this branch and passed at the merge base. List it alongside Laya. Co-Authored-By: Claude Fable 5.1 --------- Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 --- litellm/llms/laya/common_utils.py | 48 +------- litellm/llms/oss_decision.py | 56 ++++++++++ ...odel_prices_and_context_window_backup.json | 42 +++++++ litellm/proxy/_lazy_features.py | 1 + litellm/proxy/_lazy_openapi_snapshot.json | 24 ++++ litellm/proxy/_types.py | 1 + litellm/proxy/auth/auth_utils.py | 11 +- .../auto_router_permissions.py | 2 +- .../llm_passthrough_endpoints.py | 25 ++++- .../pass_through_endpoints.py | 26 +++-- .../pass_through_endpoints/success_handler.py | 2 +- .../complexity_router/complexity_router.py | 14 +-- .../complexity_router/config.py | 22 ++-- .../complexity_router/jev_classifier.py | 6 +- model_prices_and_context_window.json | 42 +++++++ provider_endpoints_support.json | 7 ++ .../test_pass_through_unit_tests.py | 1 + tests/unit/llms/laya/test_common_utils.py | 42 +------ tests/unit/llms/test_oss_decision.py | 60 ++++++++++ tests/unit/proxy/auth/test_auth_utils.py | 20 ++-- .../test_model_management_endpoints.py | 6 +- .../test_auto_router_permissions.py | 8 +- ...st_typesafe_passthrough_logging_handler.py | 51 +++++---- .../test_llm_pass_through_endpoints.py | 103 ++++++++++-------- .../complexity_router/test_jev_classifier.py | 44 ++++---- .../test_auto_router_model_naming.py | 1 + .../add_model/AutoRouterClassifierTabs.tsx | 8 +- .../JevClassifierConfig.integration.test.tsx | 25 +++-- .../add_model/JevClassifierConfig.tsx | 21 ++-- ...d_auto_router_routing_test_request.test.ts | 21 +++- .../build_complexity_router_config.test.ts | 33 +++++- .../add_model/jev_classifier_config.ts | 27 +++-- ui/litellm-dashboard/src/lib/http/schema.d.ts | 47 +++++++- 33 files changed, 577 insertions(+), 270 deletions(-) create mode 100644 litellm/llms/oss_decision.py create mode 100644 tests/unit/llms/test_oss_decision.py diff --git a/litellm/llms/laya/common_utils.py b/litellm/llms/laya/common_utils.py index f400eef22d3..3e423a9e742 100644 --- a/litellm/llms/laya/common_utils.py +++ b/litellm/llms/laya/common_utils.py @@ -1,51 +1,7 @@ from collections.abc import Mapping -from dataclasses import dataclass, field -from typing import Final, Literal, TypeAlias +from typing import Final -from pydantic import AnyHttpUrl, BaseModel, TypeAdapter, ValidationError - -from litellm.secret_managers.main import get_secret_str - -LayaCheckpoint: TypeAlias = Literal["english", "multilingual", "typed-decisions"] - - -def validate_laya_model(value: object) -> LayaCheckpoint: - try: - return TypeAdapter(LayaCheckpoint).validate_python(value) - except ValidationError as exc: - raise ValueError("Laya model must be 'english', 'multilingual', or 'typed-decisions'") from exc - - -def validate_laya_request(body: Mapping[str, object]) -> LayaCheckpoint: - if "custom_body" in body: - raise ValueError("custom_body is not supported for Laya requests") - if body.get("stream"): - raise ValueError("Streaming is not supported for Laya requests") - return validate_laya_model(body.get("model")) - - -@dataclass(frozen=True, slots=True) -class LayaConnection: - api_base: str - api_key: str | None = field(repr=False) - - -def validate_laya_api_base(value: str) -> str: - try: - url: Final = TypeAdapter(AnyHttpUrl).validate_python(value) - except ValidationError as exc: - raise ValueError("Laya api_base must be an HTTP or HTTPS server URL") from exc - if url.username or url.password or url.query or url.fragment: - raise ValueError("Laya api_base must not contain credentials, a query, or a fragment") - return str(url).rstrip("/") - - -def laya_connection(api_base: str | None = None, api_key: str | None = None) -> LayaConnection: - base: Final = api_base if api_base is not None else get_secret_str("LAYA_API_BASE") - if not base: - raise ValueError("Laya requires api_base or LAYA_API_BASE pointing to a self-hosted server") - key: Final = api_key if api_base is not None else api_key or get_secret_str("LAYA_API_KEY") - return LayaConnection(api_base=validate_laya_api_base(base), api_key=key) +from pydantic import BaseModel, TypeAdapter, ValidationError class _LayaRouting(BaseModel): diff --git a/litellm/llms/oss_decision.py b/litellm/llms/oss_decision.py new file mode 100644 index 00000000000..1483adad93f --- /dev/null +++ b/litellm/llms/oss_decision.py @@ -0,0 +1,56 @@ +from collections.abc import Mapping +from dataclasses import dataclass, field +from types import MappingProxyType +from typing import Final, Literal, TypeAlias + +from pydantic import AnyHttpUrl, TypeAdapter, ValidationError + +from litellm.secret_managers.main import get_secret_str + +OssDecisionProvider: TypeAlias = Literal["laya", "bespoke"] +OSS_DECISION_MODELS: Final = MappingProxyType( + { + "laya": ("english", "multilingual", "typed-decisions"), + "bespoke": ("nimble-latest", "nimble", "bespokelabs/Bespoke-Nimble-9B"), + } +) + + +def validate_oss_model(provider: OssDecisionProvider, value: object) -> str: + if not isinstance(value, str) or value not in OSS_DECISION_MODELS[provider]: + raise ValueError(f"{provider} model must be one of {', '.join(OSS_DECISION_MODELS[provider])}") + return value + + +def validate_oss_request(provider: OssDecisionProvider, body: Mapping[str, object]) -> str: + if "custom_body" in body: + raise ValueError(f"custom_body is not supported for {provider} requests") + if body.get("stream"): + raise ValueError(f"Streaming is not supported for {provider} requests") + return validate_oss_model(provider, body.get("model")) + + +@dataclass(frozen=True, slots=True) +class OssDecisionConnection: + api_base: str + api_key: str | None = field(repr=False) + + +def validate_oss_api_base(provider: OssDecisionProvider, value: str) -> str: + try: + url: Final = TypeAdapter(AnyHttpUrl).validate_python(value) + except ValidationError as exc: + raise ValueError(f"{provider} api_base must be an HTTP or HTTPS server URL") from exc + if url.username or url.password or url.query or url.fragment: + raise ValueError(f"{provider} api_base must not contain credentials, a query, or a fragment") + return str(url).rstrip("/") + + +def oss_connection( + provider: OssDecisionProvider, api_base: str | None = None, api_key: str | None = None +) -> OssDecisionConnection: + base: Final = api_base if api_base is not None else get_secret_str(f"{provider.upper()}_API_BASE") + if not base: + raise ValueError(f"{provider} requires api_base or {provider.upper()}_API_BASE pointing to its server") + key: Final = api_key if api_base is not None else api_key or get_secret_str(f"{provider.upper()}_API_KEY") + return OssDecisionConnection(api_base=validate_oss_api_base(provider, base), api_key=key) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 31e150f1f7d..21bdee73464 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -72632,6 +72632,48 @@ "supports_audio_input": true, "supports_video_input": true }, + "bespoke/nimble-latest": { + "input_cost_per_token": 0.0, + "litellm_provider": "bespoke", + "max_input_tokens": 8192, + "mode": "evaluation", + "output_cost_per_token": 0.0, + "source": "https://github.com/bespokelabsai/nimble", + "supported_endpoints": [ + "/v1/systemone" + ], + "metadata": { + "notes": "Self-hosted decision model; infrastructure costs are paid separately" + } + }, + "bespoke/nimble": { + "input_cost_per_token": 0.0, + "litellm_provider": "bespoke", + "max_input_tokens": 8192, + "mode": "evaluation", + "output_cost_per_token": 0.0, + "source": "https://ollama.com/library/nimble", + "supported_endpoints": [ + "/v1/systemone" + ], + "metadata": { + "notes": "Self-hosted decision model under the name Ollama serves it as; infrastructure costs are paid separately" + } + }, + "bespoke/bespokelabs/Bespoke-Nimble-9B": { + "input_cost_per_token": 0.0, + "litellm_provider": "bespoke", + "max_input_tokens": 8192, + "mode": "evaluation", + "output_cost_per_token": 0.0, + "source": "https://github.com/bespokelabsai/nimble", + "supported_endpoints": [ + "/v1/systemone" + ], + "metadata": { + "notes": "Self-hosted decision model; infrastructure costs are paid separately" + } + }, "laya/english": { "input_cost_per_token": 0.0, "litellm_provider": "laya", diff --git a/litellm/proxy/_lazy_features.py b/litellm/proxy/_lazy_features.py index 837552e522f..54d757d75aa 100644 --- a/litellm/proxy/_lazy_features.py +++ b/litellm/proxy/_lazy_features.py @@ -230,6 +230,7 @@ LAZY_FEATURES: Final[tuple[LazyFeature, ...]] = ( "/transcribe", "/typesafe/", "/laya/", + "/bespoke/", "/openrouter/", "/vertex-ai/", "/vertex_ai/", diff --git a/litellm/proxy/_lazy_openapi_snapshot.json b/litellm/proxy/_lazy_openapi_snapshot.json index 3346b0c9ff8..727ecb235bb 100644 --- a/litellm/proxy/_lazy_openapi_snapshot.json +++ b/litellm/proxy/_lazy_openapi_snapshot.json @@ -26318,6 +26318,30 @@ ] } }, + "/bespoke/v1/systemone": { + "post": { + "operationId": "bespoke_proxy_route_bespoke_v1_systemone_post", + "responses": { + "200": { + "content": { + "application/json": { + "schema": {} + } + }, + "description": "Successful Response" + } + }, + "security": [ + { + "APIKeyHeader": [] + } + ], + "summary": "Bespoke Proxy Route", + "tags": [ + "llm_passthrough" + ] + } + }, "/cohere/{endpoint}": { "delete": { "description": "[Docs](https://docs.litellm.ai/docs/pass_through/cohere)", diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 2084f6b6ee3..59302e9f08e 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -511,6 +511,7 @@ class LiteLLMRoutes(enum.Enum): "/mistral", "/typesafe", "/laya", + "/bespoke", "/openrouter", "/milvus", "/gigachat", diff --git a/litellm/proxy/auth/auth_utils.py b/litellm/proxy/auth/auth_utils.py index e5a1430b3d8..7ef82009184 100644 --- a/litellm/proxy/auth/auth_utils.py +++ b/litellm/proxy/auth/auth_utils.py @@ -1883,15 +1883,16 @@ def _extract_model_candidates_from_request( llm_router: Router | None = None, team_id: str | None = None, ) -> list[str]: - if route.rstrip("/") == "/laya/v1/systemone": - from litellm.llms.laya.common_utils import validate_laya_model + if route.rstrip("/") in ("/laya/v1/systemone", "/bespoke/v1/systemone"): + from litellm.llms.oss_decision import validate_oss_model + provider: Final = "bespoke" if route.startswith("/bespoke/") else "laya" try: - laya_request: Final = TypeAdapter(Mapping[str, object]).validate_python(request_data) - laya_model: Final = validate_laya_model(laya_request.get("model")) + decision_request: Final = TypeAdapter(Mapping[str, object]).validate_python(request_data) + decision_model: Final = validate_oss_model(provider, decision_request.get("model")) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc - return _dedupe_model_candidates((f"laya/{laya_model}",)) + return _dedupe_model_candidates((f"{provider}/{decision_model}",)) if route == "/cost/predict-cache": prediction_models: Final = _cache_prediction_model_candidates(request_data, llm_router, team_id) # pyright: ignore[reportUnknownArgumentType] # the typed reader validates each deployment ID from this legacy payload return _dedupe_model_candidates(prediction_models) diff --git a/litellm/proxy/management_helpers/auto_router_permissions.py b/litellm/proxy/management_helpers/auto_router_permissions.py index e0d8fda5b1c..5845194fa9b 100644 --- a/litellm/proxy/management_helpers/auto_router_permissions.py +++ b/litellm/proxy/management_helpers/auto_router_permissions.py @@ -74,7 +74,7 @@ class _MemberOpenSourceClassifierConfig(BaseModel): model_config = ConfigDict(extra="forbid") - provider: Literal["jev", "laya"] = "jev" + provider: Literal["jev", "laya", "bespoke"] = "jev" model: str api_key: None = None api_base: None = None diff --git a/litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py b/litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py index ed2ea475c7a..0cff558985b 100644 --- a/litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py +++ b/litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py @@ -58,8 +58,8 @@ from litellm.llms.deepgram.common_utils import ( deepgram_listen_websocket_target, ) from litellm.llms.fal_ai.cost_calculator import fal_ai_passthrough_cost, fal_ai_queue_base -from litellm.llms.laya.common_utils import laya_connection, validate_laya_request from litellm.llms.nvidia_nim.passthrough.transformation import nvidia_nim_model_group_in_path +from litellm.llms.oss_decision import OssDecisionProvider, oss_connection, validate_oss_request from litellm.llms.vertex_ai.vertex_llm_base import VertexBase from litellm.passthrough.main import AsyncPassthroughStreamingResponse from litellm.proxy._types import * @@ -646,17 +646,32 @@ async def laya_proxy_route( request: Request, fastapi_response: Response, user_api_key_dict: Annotated[UserAPIKeyAuth, Depends(user_api_key_auth)], +) -> Response: + return await _oss_decision_proxy_route("laya", request, fastapi_response, user_api_key_dict) + + +@router.post("/bespoke/v1/systemone", tags=["Bespoke Nimble Pass-through", "pass-through"]) +async def bespoke_proxy_route( + request: Request, + fastapi_response: Response, + user_api_key_dict: Annotated[UserAPIKeyAuth, Depends(user_api_key_auth)], +) -> Response: + return await _oss_decision_proxy_route("bespoke", request, fastapi_response, user_api_key_dict) + + +async def _oss_decision_proxy_route( + provider: OssDecisionProvider, request: Request, fastapi_response: Response, user_api_key_dict: UserAPIKeyAuth ) -> Response: body: Final = TypeAdapter(dict[str, object]).validate_python(await _read_request_body(request)) try: - _ = validate_laya_request(body) + _ = validate_oss_request(provider, body) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc try: - connection: Final = laya_connection() + connection: Final = oss_connection(provider) except ValueError as exc: raise HTTPException( - status_code=503, detail="Laya server is not configured correctly; check LAYA_API_BASE" + status_code=503, detail=f"{provider} server is not configured correctly; check {provider.upper()}_API_BASE" ) from exc base_url: Final = httpx.URL(connection.api_base) updated_url: Final = base_url.copy_with( @@ -671,7 +686,7 @@ async def laya_proxy_route( endpoint="v1/systemone", target=str(updated_url), custom_headers=MappingProxyType({**authorization, "Content-Type": "application/json"}), - custom_llm_provider="laya", + custom_llm_provider=provider, is_streaming_request=False, ) return TypeAdapter(Response, config=ConfigDict(arbitrary_types_allowed=True)).validate_python( diff --git a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py index 865374a0430..a5b414e5a18 100644 --- a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py +++ b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py @@ -65,7 +65,7 @@ from litellm.llms.base_llm.managed_resources.utils import ( resolve_passthrough_managed_id_provider, ) from litellm.llms.custom_httpx.http_handler import get_async_httpx_client -from litellm.llms.laya.common_utils import validate_laya_request +from litellm.llms.oss_decision import validate_oss_request from litellm.passthrough import BasePassthroughUtils from litellm.proxy._types import ( ConfigFieldInfo, @@ -387,7 +387,9 @@ class HttpPassThroughEndpointHelpers(BasePassthroughUtils): @staticmethod def get_endpoint_type(url: str, custom_llm_provider: str | None = None) -> EndpointType: parsed_url: Final = urlparse(url) - if custom_llm_provider == "typesafe" and parsed_url.path.removesuffix("/").endswith("/v1/systemone"): + if custom_llm_provider in ("typesafe", "laya", "bespoke") and parsed_url.path.removesuffix("/").endswith( + "/v1/systemone" + ): return EndpointType.DECISIONS if ( ("generateContent") in url @@ -1163,10 +1165,10 @@ async def pass_through_request( pricing_body: Final = TypeAdapter(dict[str, object]).validate_python(_parsed_body) _strip_client_pricing_overrides(pricing_body) _parsed_body = pricing_body - if custom_llm_provider == "laya": - laya_request: Final = TypeAdapter(Mapping[str, object]).validate_python(_parsed_body) - checkpoint: Final = validate_laya_request(laya_request) - _parsed_body["model"] = f"laya/{checkpoint}" + if custom_llm_provider in ("laya", "bespoke"): + decision_request: Final = TypeAdapter(Mapping[str, object]).validate_python(_parsed_body) + checkpoint: Final = validate_oss_request(custom_llm_provider, decision_request) + _parsed_body["model"] = f"{custom_llm_provider}/{checkpoint}" ### COLLECT GUARDRAILS FOR PASSTHROUGH ENDPOINT ### # Passthrough endpoints are opt-in only for guardrails @@ -1223,17 +1225,19 @@ async def pass_through_request( call_type="pass_through_endpoint", endpoint_type=endpoint_type, ) - if custom_llm_provider == "laya": + if custom_llm_provider in ("laya", "bespoke"): hook_body: Final = TypeAdapter(dict[str, object]).validate_python(_parsed_body) hook_model: Final = hook_body.get("model") - laya_body: Final = MappingProxyType( + decision_body: Final = MappingProxyType( { **hook_body, - "model": hook_model.removeprefix("laya/") if isinstance(hook_model, str) else hook_model, + "model": hook_model.removeprefix(f"{custom_llm_provider}/") + if isinstance(hook_model, str) + else hook_model, } ) - _ = validate_laya_request(laya_body) - _parsed_body = TypeAdapter(dict[str, object]).validate_python(laya_body) + _ = validate_oss_request(custom_llm_provider, decision_body) + _parsed_body = TypeAdapter(dict[str, object]).validate_python(decision_body) resolved_timeout: Final = resolve_pass_through_request_timeout(timeout) async_client_obj: Final = get_async_httpx_client( llm_provider=httpxSpecialProvider.PassThroughEndpoint, diff --git a/litellm/proxy/pass_through_endpoints/success_handler.py b/litellm/proxy/pass_through_endpoints/success_handler.py index 3c4733d0bf0..da3e28e25e4 100644 --- a/litellm/proxy/pass_through_endpoints/success_handler.py +++ b/litellm/proxy/pass_through_endpoints/success_handler.py @@ -336,7 +336,7 @@ class PassThroughEndpointLogging: kwargs = transcribe_handler_result["kwargs"] # rebind-ok: elif-chain contract elif ( self.is_typesafe_route(custom_llm_provider) - or custom_llm_provider == "laya" + or custom_llm_provider in ("laya", "bespoke") or self.is_openrouter_decisions_route(url_route, custom_llm_provider) ): from .llm_provider_handlers.typesafe_passthrough_logging_handler import ( diff --git a/litellm/router_strategy/complexity_router/complexity_router.py b/litellm/router_strategy/complexity_router/complexity_router.py index 39fb237917c..3610991a20d 100644 --- a/litellm/router_strategy/complexity_router/complexity_router.py +++ b/litellm/router_strategy/complexity_router/complexity_router.py @@ -1309,15 +1309,15 @@ class ComplexityRouter(CustomLogger): @staticmethod def _build_jev_client(config: OpenSourceClassifierConfig) -> JevClassifierClient: - if config.provider == "laya": - from litellm.llms.laya.common_utils import laya_connection + if config.provider in ("laya", "bespoke"): + from litellm.llms.oss_decision import oss_connection - connection: Final = laya_connection(config.api_base, config.api_key) + connection: Final = oss_connection(config.provider, config.api_base, config.api_key) return HttpJevClassifierClient( api_key=connection.api_key, api_base=connection.api_base, http_client=get_async_httpx_client(httpxSpecialProvider.PassThroughEndpoint), - provider="laya", + provider=config.provider, ) api_key: Final = config.api_key or get_secret_str("TYPESAFE_API_KEY") if not api_key: @@ -2228,7 +2228,7 @@ class ComplexityRouter(CustomLogger): if not self._tier_pools().get(tier_name): raise ValueError(f"Jev classifier returned tier {tier_name!r}, which has no models configured") model: Final = response.model or config.model - accounting_provider: Final = "laya" if config.provider == "laya" else "typesafe" + accounting_provider: Final = "typesafe" if config.provider == "jev" else config.provider verdict: Final = JevVerdict( label=answer.choice, probabilities=answer.probabilities, @@ -2243,8 +2243,8 @@ class ComplexityRouter(CustomLogger): tier=tier, score=None, signals=( - f"{'laya' if config.provider == 'laya' else 'jev'}-classifier:{tier_name}", - f"{'laya' if config.provider == 'laya' else 'jev'}-confidence={answer.confidence:.6f}", + f"{config.provider}-classifier:{tier_name}", + f"{config.provider}-confidence={answer.confidence:.6f}", *( f"tier-probability:{label}={probability:.6f}" for label, probability in answer.probabilities.items() diff --git a/litellm/router_strategy/complexity_router/config.py b/litellm/router_strategy/complexity_router/config.py index 41f389db7d8..88907731468 100644 --- a/litellm/router_strategy/complexity_router/config.py +++ b/litellm/router_strategy/complexity_router/config.py @@ -698,17 +698,17 @@ def normalize_classifier_config_aliases(config: Mapping[str, object]) -> Mapping class OpenSourceClassifierConfig(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) - provider: Literal["jev", "laya"] = "jev" + provider: Literal["jev", "laya", "bespoke"] = "jev" model: str = "jev-latest" - api_key: str | None = Field(default=None, description="Provider API key; optional for self-hosted Laya") + api_key: str | None = Field(default=None, description="Provider API key; optional for self-hosted providers") api_base: str | None = Field( default=None, - description="Provider API base; defaults to TYPESAFE_API_BASE or LAYA_API_BASE for the selected provider", + description="Provider API base; defaults to the selected provider API_BASE environment variable", ) timeout_ms: int = Field(default=3000, ge=1) instructions: str | None = Field( default=None, - description="Replaces the built-in Jev question instructions", + description="Replaces the built-in classification instructions", ) circuit_breaker_enabled: bool = True circuit_breaker_cooldown_seconds: float = Field(default=30.0, gt=0.0) @@ -729,17 +729,19 @@ class OpenSourceClassifierConfig(BaseModel): @classmethod def _reject_blank_api_key(cls, value: str | None) -> str | None: if value is not None and not value.strip(): - raise ValueError("opensource_classifier_config.api_key must be non-empty; omit it to use TYPESAFE_API_KEY") + raise ValueError( + "opensource_classifier_config.api_key must be non-empty; omit it to use the provider environment key" + ) return value @model_validator(mode="after") def _keep_the_environment_key_on_the_environment_base(self) -> "OpenSourceClassifierConfig": - if self.provider == "laya": - from litellm.llms.laya.common_utils import validate_laya_api_base, validate_laya_model + if self.provider in ("laya", "bespoke"): + from litellm.llms.oss_decision import validate_oss_api_base, validate_oss_model - _ = validate_laya_model(self.model) + _ = validate_oss_model(self.provider, self.model) if self.api_base is not None: - _ = validate_laya_api_base(self.api_base) + _ = validate_oss_api_base(self.provider, self.api_base) return self if self.api_base is not None and self.api_key is None: raise ValueError( @@ -1150,7 +1152,7 @@ class ComplexityRouterConfig(BaseModel): "an LLM tier-selection call, a Switchyard-compatible capability forecast, a joint Fuse V2 forecast, " "a custom classifier plugin, 'heuristic_first', which scores locally and only pays for the LLM classifier when the " "local scorer does not confidently land a cheap tier, or 'hybrid', which trusts the local scorer " - "everywhere except when its score lands near a tier boundary, or 'oss_classifier', a structured choice call using Jev or Laya" + "everywhere except when its score lands near a tier boundary, or 'oss_classifier', a structured choice call using Jev, Laya or Bespoke Nimble" ), ) llm_v2_config: LLMV2Config | None = Field( diff --git a/litellm/router_strategy/complexity_router/jev_classifier.py b/litellm/router_strategy/complexity_router/jev_classifier.py index 073d87c25a6..2b97ae824dd 100644 --- a/litellm/router_strategy/complexity_router/jev_classifier.py +++ b/litellm/router_strategy/complexity_router/jev_classifier.py @@ -84,7 +84,7 @@ class HttpJevClassifierClient: api_key: str | None, api_base: str, http_client: AsyncHTTPHandler, - provider: Literal["typesafe", "laya"] = "typesafe", + provider: Literal["typesafe", "laya", "bespoke"] = "typesafe", ) -> None: self._api_key = api_key self._api_base = api_base.rstrip("/") @@ -201,7 +201,7 @@ class JevVerdict(NamedTuple): confidence: float model: str cost: float | None - provider: Literal["typesafe", "laya"] = "typesafe" + provider: Literal["typesafe", "laya", "bespoke"] = "typesafe" class _RegistryPricing(BaseModel): @@ -225,7 +225,7 @@ def build_jev_request( def jev_classifier_cost( - response: JevSystemOneResponse, configured_model: str, provider: Literal["typesafe", "laya"] = "typesafe" + response: JevSystemOneResponse, configured_model: str, provider: Literal["typesafe", "laya", "bespoke"] = "typesafe" ) -> float | None: usage: Final = response.usage if usage is None: diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 31e150f1f7d..21bdee73464 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -72632,6 +72632,48 @@ "supports_audio_input": true, "supports_video_input": true }, + "bespoke/nimble-latest": { + "input_cost_per_token": 0.0, + "litellm_provider": "bespoke", + "max_input_tokens": 8192, + "mode": "evaluation", + "output_cost_per_token": 0.0, + "source": "https://github.com/bespokelabsai/nimble", + "supported_endpoints": [ + "/v1/systemone" + ], + "metadata": { + "notes": "Self-hosted decision model; infrastructure costs are paid separately" + } + }, + "bespoke/nimble": { + "input_cost_per_token": 0.0, + "litellm_provider": "bespoke", + "max_input_tokens": 8192, + "mode": "evaluation", + "output_cost_per_token": 0.0, + "source": "https://ollama.com/library/nimble", + "supported_endpoints": [ + "/v1/systemone" + ], + "metadata": { + "notes": "Self-hosted decision model under the name Ollama serves it as; infrastructure costs are paid separately" + } + }, + "bespoke/bespokelabs/Bespoke-Nimble-9B": { + "input_cost_per_token": 0.0, + "litellm_provider": "bespoke", + "max_input_tokens": 8192, + "mode": "evaluation", + "output_cost_per_token": 0.0, + "source": "https://github.com/bespokelabsai/nimble", + "supported_endpoints": [ + "/v1/systemone" + ], + "metadata": { + "notes": "Self-hosted decision model; infrastructure costs are paid separately" + } + }, "laya/english": { "input_cost_per_token": 0.0, "litellm_provider": "laya", diff --git a/provider_endpoints_support.json b/provider_endpoints_support.json index d18f8d2e6d1..223711a92b6 100644 --- a/provider_endpoints_support.json +++ b/provider_endpoints_support.json @@ -1477,6 +1477,13 @@ "rerank": false } }, + "bespoke": { + "display_name": "Bespoke Nimble (`bespoke`)", + "url": "https://docs.litellm.ai/docs/auto_router/decision_classifiers", + "endpoints": { + "systemone": true + } + }, "laya": { "display_name": "Laya (`laya`)", "url": "https://docs.litellm.ai/docs/auto_router/decision_classifiers", diff --git a/tests/pass_through_unit_tests/test_pass_through_unit_tests.py b/tests/pass_through_unit_tests/test_pass_through_unit_tests.py index 7fb23223845..82cb652950c 100644 --- a/tests/pass_through_unit_tests/test_pass_through_unit_tests.py +++ b/tests/pass_through_unit_tests/test_pass_through_unit_tests.py @@ -416,6 +416,7 @@ PROTOCOL_CONSTRAINED_PASS_THROUGH_ROUTES = { "/transcribe/{operation}": {"POST"}, "/tinyfish/{endpoint:path}": {"GET", "POST"}, "/laya/v1/systemone": {"POST"}, + "/bespoke/v1/systemone": {"POST"}, } diff --git a/tests/unit/llms/laya/test_common_utils.py b/tests/unit/llms/laya/test_common_utils.py index c9ee0062cd2..408bd300beb 100644 --- a/tests/unit/llms/laya/test_common_utils.py +++ b/tests/unit/llms/laya/test_common_utils.py @@ -1,48 +1,8 @@ from collections.abc import Mapping -from typing import Final import pytest -from litellm.llms.laya.common_utils import laya_connection, laya_response_model - - -@pytest.mark.parametrize( - ("base", "key", "expected_base", "expected_key"), - [ - (None, None, "http://laya.test/root", "laya-env-key"), - ("http://custom.test/", None, "http://custom.test", None), - ("http://custom.test/", "explicit-key", "http://custom.test", "explicit-key"), - ], -) -def test_laya_credentials_stay_with_their_configured_destination( - monkeypatch: pytest.MonkeyPatch, - base: str | None, - key: str | None, - expected_base: str, - expected_key: str | None, -) -> None: - monkeypatch.setenv("LAYA_API_BASE", "http://laya.test/root/") - monkeypatch.setenv("LAYA_API_KEY", "laya-env-key") - monkeypatch.setenv("TYPESAFE_API_KEY", "never-send-this") - connection: Final = laya_connection(base, key) - assert (connection.api_base, connection.api_key) == (expected_base, expected_key) - assert "key" not in repr(connection) - - -@pytest.mark.parametrize( - "base", - ["", "ftp://laya.test", "http://user:password@laya.test", "https://laya.test?key=x", "http://laya.test/#x"], -) -def test_laya_rejects_ambiguous_server_urls(base: str) -> None: - with pytest.raises(ValueError, match="Laya"): - laya_connection(base) - - -def test_laya_missing_server_does_not_fall_back_to_typesafe(monkeypatch: pytest.MonkeyPatch) -> None: - monkeypatch.delenv("LAYA_API_BASE", raising=False) - monkeypatch.setenv("TYPESAFE_API_BASE", "https://typesafe.test") - with pytest.raises(ValueError, match="LAYA_API_BASE"): - laya_connection() +from litellm.llms.laya.common_utils import laya_response_model @pytest.mark.parametrize( diff --git a/tests/unit/llms/test_oss_decision.py b/tests/unit/llms/test_oss_decision.py new file mode 100644 index 00000000000..05c5d2bbff5 --- /dev/null +++ b/tests/unit/llms/test_oss_decision.py @@ -0,0 +1,60 @@ +from typing import Final + +import pytest + +from litellm.llms.oss_decision import OssDecisionProvider, oss_connection, validate_oss_request + +pytestmark: Final = pytest.mark.parametrize("provider", ["laya", "bespoke"]) + + +@pytest.mark.parametrize( + ("base", "key", "expected_base", "expected_key"), + [ + (None, None, "http://decision.test/root", "oss-env-key"), + ("http://custom.test/", None, "http://custom.test", None), + ("http://custom.test/", "explicit-key", "http://custom.test", "explicit-key"), + ], +) +def test_oss_credentials_stay_with_their_configured_destination( + monkeypatch: pytest.MonkeyPatch, + provider: OssDecisionProvider, + base: str | None, + key: str | None, + expected_base: str, + expected_key: str | None, +) -> None: + monkeypatch.setenv(f"{provider.upper()}_API_BASE", "http://decision.test/root/") + monkeypatch.setenv(f"{provider.upper()}_API_KEY", "oss-env-key") + monkeypatch.setenv("TYPESAFE_API_KEY", "never-send-this") + monkeypatch.setenv("NIMBLE_API_KEY", "never-send-nimble-search-key") + connection: Final = oss_connection(provider, base, key) + assert (connection.api_base, connection.api_key) == (expected_base, expected_key) + assert "key" not in repr(connection) + + +@pytest.mark.parametrize( + "base", + ["", "ftp://laya.test", "http://user:password@laya.test", "https://laya.test?key=x", "http://laya.test/#x"], +) +def test_oss_rejects_ambiguous_server_urls(provider: OssDecisionProvider, base: str) -> None: + with pytest.raises(ValueError, match=provider): + oss_connection(provider, base) + + +def test_oss_missing_server_does_not_fall_back_to_typesafe( + monkeypatch: pytest.MonkeyPatch, provider: OssDecisionProvider +) -> None: + monkeypatch.delenv(f"{provider.upper()}_API_BASE", raising=False) + monkeypatch.setenv("TYPESAFE_API_BASE", "https://typesafe.test") + monkeypatch.setenv("NIMBLE_API_BASE", "https://nimble-search.test") + with pytest.raises(ValueError, match=f"{provider.upper()}_API_BASE"): + oss_connection(provider) + + +def test_oss_request_accepts_the_name_ollama_serves_nimble_under_only_for_bespoke(provider: OssDecisionProvider) -> None: + body: Final = {"model": "nimble"} + if provider == "bespoke": + assert validate_oss_request(provider, body) == "nimble" + return + with pytest.raises(ValueError, match=f"{provider} model must be one of"): + validate_oss_request(provider, body) diff --git a/tests/unit/proxy/auth/test_auth_utils.py b/tests/unit/proxy/auth/test_auth_utils.py index 6cf4456a0ff..bc4a6e0155d 100644 --- a/tests/unit/proxy/auth/test_auth_utils.py +++ b/tests/unit/proxy/auth/test_auth_utils.py @@ -463,16 +463,22 @@ def test_get_model_from_request_no_request_extracts_model(): ) -@pytest.mark.parametrize("model", ["english", "multilingual", "typed-decisions"]) -@pytest.mark.parametrize("route", ["/laya/v1/systemone", "/laya/v1/systemone/"]) -def test_laya_native_model_uses_the_classifier_permission_identity(model: str, route: str) -> None: - assert get_model_from_request(request_data={"model": model}, route=route) == f"laya/{model}" +@pytest.mark.parametrize("provider,model", [ + ("laya", "english"), ("laya", "multilingual"), ("laya", "typed-decisions"), + ("bespoke", "nimble-latest"), ("bespoke", "bespokelabs/Bespoke-Nimble-9B"), +]) +@pytest.mark.parametrize("suffix", ["", "/"]) +def test_oss_native_model_uses_the_classifier_permission_identity(provider: str, model: str, suffix: str) -> None: + assert get_model_from_request( + request_data={"model": model}, route=f"/{provider}/v1/systemone{suffix}" + ) == f"{provider}/{model}" -@pytest.mark.parametrize("model", [None, "", "auto", "laya/english", "unknown", ["english"], 7]) -def test_laya_native_model_cannot_implicitly_select_an_unauthorized_checkpoint(model: object) -> None: +@pytest.mark.parametrize("provider", ["laya", "bespoke"]) +@pytest.mark.parametrize("model", [None, "", "auto", "laya/english", "bespoke/nimble-latest", "unknown", ["english"], 7]) +def test_oss_native_model_cannot_implicitly_select_an_unauthorized_checkpoint(provider: str, model: object) -> None: with pytest.raises(HTTPException) as denied: - get_model_from_request(request_data={"model": model}, route="/laya/v1/systemone") + get_model_from_request(request_data={"model": model}, route=f"/{provider}/v1/systemone") assert denied.value.status_code == 400 diff --git a/tests/unit/proxy/management_endpoints/test_model_management_endpoints.py b/tests/unit/proxy/management_endpoints/test_model_management_endpoints.py index 7eda03c560b..19cc8bf15ab 100644 --- a/tests/unit/proxy/management_endpoints/test_model_management_endpoints.py +++ b/tests/unit/proxy/management_endpoints/test_model_management_endpoints.py @@ -7706,6 +7706,10 @@ class TestTeamMemberAutoRouterWrites: @pytest.mark.parametrize( "stored_provider,stored_base,supplied,expected_transport", [ + ("bespoke", "https://decision.test", {"provider": "bespoke", "model": "nimble-latest"}, {"api_base": "https://decision.test", "api_key": "stored-secret"}), + ("bespoke", "https://decision.test", {"provider": "bespoke", "model": "nimble-latest", "api_base": "https://new.test"}, {}), + ("bespoke", "https://decision.test", {"provider": "laya", "model": "english"}, {}), + ("laya", "https://decision.test", {"provider": "bespoke", "model": "nimble-latest"}, {}), ("laya", "https://decision.test", {"provider": "laya", "model": "english"}, {"api_base": "https://decision.test", "api_key": "stored-secret"}), ("laya", "https://decision.test", {"provider": "laya", "model": "english", "api_base": "https://decision.test"}, {"api_base": "https://decision.test", "api_key": "stored-secret"}), ( @@ -7743,7 +7747,7 @@ class TestTeamMemberAutoRouterWrites: "model": "auto_router/complexity_router", "complexity_router_config": self._classifier_config( { - "provider": stored_provider, "model": "english" if stored_provider == "laya" else "jev-latest", + "provider": stored_provider, "model": {"laya": "english", "bespoke": "nimble-latest"}.get(stored_provider, "jev-latest"), "api_base": stored_base, "api_key": "stored-secret", }, stored_legacy, diff --git a/tests/unit/proxy/management_helpers/test_auto_router_permissions.py b/tests/unit/proxy/management_helpers/test_auto_router_permissions.py index b60fd4ac7ad..1ccfbab7b1f 100644 --- a/tests/unit/proxy/management_helpers/test_auto_router_permissions.py +++ b/tests/unit/proxy/management_helpers/test_auto_router_permissions.py @@ -144,6 +144,8 @@ def test_tier_config_is_normalized_and_unknown_router_extras_are_rejected() -> N ({"api_base": "https://collector.invalid", "api_key": ""}, "opensource_classifier_config.api_key"), ({"provider": "laya", "model": "english", "api_base": "https://collector.invalid"}, "api_base"), ({"provider": "laya", "model": "english", "api_key": "sk-member"}, "api_key"), + ({"provider": "bespoke", "model": "nimble-latest", "api_base": "https://collector.invalid"}, "api_base"), + ({"provider": "bespoke", "model": "nimble-latest", "api_key": "sk-member"}, "api_key"), ], ) @pytest.mark.parametrize("legacy", [False, True]) @@ -162,7 +164,7 @@ def test_members_cannot_move_the_jev_classifier_off_the_proxys_typesafe_account( assert denied.value.detail == f"Invalid member auto-router configuration at {rejected_at}." -@pytest.mark.parametrize(("provider", "model"), [("typesafe", "jev-preview"), ("laya", "english")]) +@pytest.mark.parametrize(("provider", "model"), [("typesafe", "jev-preview"), ("laya", "english"), ("bespoke", "nimble-latest")]) @pytest.mark.parametrize("legacy", [False, True]) def test_members_can_still_tune_the_jev_classifier(provider: str, model: str, legacy: bool) -> None: validated: Final = validate_member_auto_router_config( @@ -348,7 +350,7 @@ async def test_member_dependencies_require_plain_configured_models(target: str) @pytest.mark.asyncio @pytest.mark.parametrize("restricted", ["key", "team", None]) -@pytest.mark.parametrize(("provider", "model"), [("typesafe", "jev-latest"), ("laya", "english")]) +@pytest.mark.parametrize(("provider", "model"), [("typesafe", "jev-latest"), ("laya", "english"), ("bespoke", "nimble-latest")]) async def test_jev_evaluation_requires_model_access_but_no_completion_deployment( catalog: Router, restricted: str | None, provider: str, model: str ) -> None: @@ -376,7 +378,7 @@ async def test_jev_evaluation_requires_model_access_but_no_completion_deployment @pytest.mark.asyncio @pytest.mark.parametrize("restricted", ["member", "project", "organization", None]) -@pytest.mark.parametrize(("provider", "model"), [("typesafe", "jev-latest"), ("laya", "english")]) +@pytest.mark.parametrize(("provider", "model"), [("typesafe", "jev-latest"), ("laya", "english"), ("bespoke", "nimble-latest")]) async def test_jev_evaluation_obeys_each_containing_scope( catalog: Router, restricted: str | None, provider: str, model: str ) -> None: diff --git a/tests/unit/proxy/pass_through_endpoints/llm_provider_handlers/test_typesafe_passthrough_logging_handler.py b/tests/unit/proxy/pass_through_endpoints/llm_provider_handlers/test_typesafe_passthrough_logging_handler.py index acf05dcdfde..7961d2a911b 100644 --- a/tests/unit/proxy/pass_through_endpoints/llm_provider_handlers/test_typesafe_passthrough_logging_handler.py +++ b/tests/unit/proxy/pass_through_endpoints/llm_provider_handlers/test_typesafe_passthrough_logging_handler.py @@ -142,60 +142,65 @@ def test_success_handler_dispatches_to_typesafe_handler(): @pytest.mark.asyncio @pytest.mark.parametrize("guardrail_cost", [0.0, 0.25]) @pytest.mark.parametrize("metadata_slot", ["metadata", "litellm_metadata"]) -@pytest.mark.parametrize("routing_model", ["multilingual", None]) -async def test_laya_gateway_accounts_for_checkpoint_usage_and_registered_cost( - monkeypatch: pytest.MonkeyPatch, routing_model: str | None, metadata_slot: str, guardrail_cost: float +@pytest.mark.parametrize("provider,requested,routing_model", [ + ("laya", "english", "multilingual"), ("laya", "english", None), + ("bespoke", "nimble-latest", None), + ("bespoke", "bespokelabs/Bespoke-Nimble-9B", None), +]) +async def test_oss_gateway_accounts_for_checkpoint_usage_and_registered_cost( + monkeypatch: pytest.MonkeyPatch, routing_model: str | None, metadata_slot: str, guardrail_cost: float, + provider: str, requested: str ) -> None: - checkpoint: Final = routing_model or "english" - model: Final = f"laya/{checkpoint}" + checkpoint: Final = routing_model or requested + model: Final = f"{provider}/{checkpoint}" input_rate: Final = 0.002 output_rate: Final = 0.005 monkeypatch.setitem(litellm.model_cost, model, { "input_cost_per_token": input_rate, "output_cost_per_token": output_rate, - "litellm_provider": "laya", "mode": "evaluation", + "litellm_provider": provider, "mode": "evaluation", }) start: Final = datetime.now() logging_obj: Final = Logging( - model="english", messages=[], stream=False, call_type="pass_through_endpoint", - start_time=start, litellm_call_id="laya-accounting", function_id="laya-accounting", kwargs={}, + model=requested, messages=[], stream=False, call_type="pass_through_endpoint", + start_time=start, litellm_call_id="oss-accounting", function_id="oss-accounting", kwargs={}, ) from fastapi import Request from litellm.proxy._types import UserAPIKeyAuth from litellm.proxy.pass_through_endpoints.pass_through_endpoints import HttpPassThroughEndpointHelpers request: Final = Request({ - "type": "http", "method": "POST", "path": "/laya/v1/systemone", + "type": "http", "method": "POST", "path": f"/{provider}/v1/systemone", "headers": [], "query_string": b"", }) auth: Final = UserAPIKeyAuth( - api_key="laya-budget-key", token="laya-budget-key", - model_max_budget={"laya/english": {"budget_limit": 0.01, "time_period": "1d"}}, + api_key="oss-budget-key", token="oss-budget-key", + model_max_budget={f"{provider}/{requested}": {"budget_limit": 0.01, "time_period": "1d"}}, ) - request_body: Final = {"model": "english", metadata_slot: {"model_group": "unbounded-client-choice"}} + request_body: Final = {"model": requested, metadata_slot: {"model_group": "unbounded-client-choice"}} logging_kwargs: Final = HttpPassThroughEndpointHelpers._init_kwargs_for_pass_through_endpoint( request=request, user_api_key_dict=auth, logging_obj=logging_obj, - passthrough_logging_payload={"url": "https://laya.test/v1/systemone"}, _parsed_body=request_body, + passthrough_logging_payload={"url": f"https://{provider}.test/v1/systemone"}, _parsed_body=request_body, ) logging_kwargs["litellm_params"]["metadata"]["standard_logging_guardrail_information"] = [ {"guardrail_name": "trusted-hook", "guardrail_cost": guardrail_cost}, ] logging_obj.update_environment_variables( - model="english", user="unknown", optional_params={}, + model=requested, user="unknown", optional_params={}, litellm_params=logging_kwargs["litellm_params"], call_type="pass_through_endpoint", ) body: Final = { - "model": "laya-rl-agent", "usage": {"input_tokens": 10, "output_tokens": 3}, + "model": "laya-rl-agent" if provider == "laya" else requested, "usage": {"input_tokens": 10, "output_tokens": 3}, **({"routing": {"model": routing_model}} if routing_model else {}), } normalized: Final = PassThroughEndpointLogging().normalize_llm_passthrough_logging_payload( - httpx_response=httpx.Response(200, request=httpx.Request("POST", "https://laya.test/v1/systemone"), json=body), - response_body=body, request_body={"model": "english"}, logging_obj=logging_obj, - url_route="https://laya.test/v1/systemone", result="{}", start_time=start, - end_time=datetime.now(), cache_hit=False, custom_llm_provider="laya", **logging_kwargs, + httpx_response=httpx.Response(200, request=httpx.Request("POST", f"https://{provider}.test/v1/systemone"), json=body), + response_body=body, request_body={"model": requested}, logging_obj=logging_obj, + url_route=f"https://{provider}.test/v1/systemone", result="{}", start_time=start, + end_time=datetime.now(), cache_hit=False, custom_llm_provider=provider, **logging_kwargs, ) logged: Final = normalized["kwargs"] expected_cost: Final = 10 * input_rate + 3 * output_rate - assert (logged["model"], logged["custom_llm_provider"]) == (model, "laya") + assert (logged["model"], logged["custom_llm_provider"]) == (model, provider) assert logged["response_cost"] == pytest.approx(expected_cost) assert logged["combined_usage_object"].model_dump(exclude_none=True) == { "prompt_tokens": 10, "completion_tokens": 3, "total_tokens": 13, @@ -203,7 +208,7 @@ async def test_laya_gateway_accounts_for_checkpoint_usage_and_registered_cost( assert logging_obj.model_call_details["model"] == model assert logging_obj.model_call_details["response_cost"] == pytest.approx(expected_cost) assert logged["standard_logging_object"]["model"] == model - assert logged["standard_logging_object"]["model_group"] == "laya/english" + assert logged["standard_logging_object"]["model_group"] == f"{provider}/{requested}" assert logged["standard_logging_object"]["response_cost"] == pytest.approx(expected_cost + guardrail_cost) from litellm.caching.caching import DualCache @@ -211,10 +216,10 @@ async def test_laya_gateway_accounts_for_checkpoint_usage_and_registered_cost( from litellm.proxy.hooks.model_max_budget_limiter import _PROXY_VirtualKeyModelMaxBudgetLimiter budget_limiter: Final = _PROXY_VirtualKeyModelMaxBudgetLimiter(DualCache()) - assert await budget_limiter.is_key_within_model_budget(auth, "laya/english") + assert await budget_limiter.is_key_within_model_budget(auth, f"{provider}/{requested}") await budget_limiter.async_log_success_event(logged, None, start, datetime.now()) with pytest.raises(BudgetExceededError): - await budget_limiter.is_key_within_model_budget(auth, "laya/english") + await budget_limiter.is_key_within_model_budget(auth, f"{provider}/{requested}") def test_openrouter_decisions_response_is_priced_from_request_model_registry_row(): diff --git a/tests/unit/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py b/tests/unit/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py index 22171f4ffb0..cad065c64d1 100644 --- a/tests/unit/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py +++ b/tests/unit/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py @@ -7298,6 +7298,8 @@ class TestTypeSafePassthroughRoute: "provider, endpoint, is_decision_request", ( ("typesafe", "systemone", True), + ("laya", "systemone", True), + ("bespoke", "systemone", True), ("typesafe", "systemone/", True), ("typesafe", "systemone?trace=1", True), ("typesafe", "systemone/?trace=1", True), @@ -7316,7 +7318,7 @@ class TestTypeSafePassthroughRoute: self, client: TestClient, monkeypatch: pytest.MonkeyPatch, - provider: Literal["typesafe", "openrouter"], + provider: Literal["typesafe", "openrouter", "laya", "bespoke"], endpoint: str, is_decision_request: bool, quota_scope: Literal["key", "project_output"], @@ -7337,12 +7339,15 @@ class TestTypeSafePassthroughRoute: monkeypatch.setattr(proxy_server, "proxy_logging_obj", ProxyLogging(user_api_key_cache=cache)) monkeypatch.setenv("OPENROUTER_API_KEY", "openrouter-test-key") monkeypatch.setenv("OPENROUTER_API_BASE", "https://typesafe.example/base") - model: Final = "jev-latest" if provider == "typesafe" else "test-generative-model" + monkeypatch.setenv("LAYA_API_BASE", "https://typesafe.example/base") + monkeypatch.setenv("BESPOKE_API_BASE", "https://typesafe.example/base") + model: Final = {"typesafe": "jev-latest", "laya": "english", "bespoke": "nimble-latest"}.get(provider, "test-generative-model") + permission_model: Final = f"{provider}/{model}" if provider in ("laya", "bespoke") else model auth: Final = UserAPIKeyAuth( api_key="sk-limited", tpm_limit=token_limit if quota_scope == "key" else None, project_id="test-project" if quota_scope == "project_output" else None, - project_metadata={"model_otpm_limit": {model: token_limit}} if quota_scope == "project_output" else {}, + project_metadata={"model_otpm_limit": {permission_model: token_limit}} if quota_scope == "project_output" else {}, ) monkeypatch.setitem(proxy_server.app.dependency_overrides, user_api_key_auth, lambda: auth) body: Final = ( @@ -7409,36 +7414,44 @@ class TestTypeSafePassthroughRoute: ) -class TestLayaPassthroughRoute: +@pytest.mark.parametrize("provider", ["laya", "bespoke"]) +class TestOssDecisionPassthroughRoute: @pytest.fixture - def client(self, monkeypatch: pytest.MonkeyPatch) -> Iterator[TestClient]: + def checkpoint(self, provider: str) -> str: + return "english" if provider == "laya" else "nimble-latest" + + @pytest.fixture + def client(self, monkeypatch: pytest.MonkeyPatch, provider: str) -> Iterator[TestClient]: from litellm.proxy.proxy_server import app - monkeypatch.setenv("LAYA_API_BASE", "http://laya.test/base") + monkeypatch.setenv(f"{provider.upper()}_API_BASE", f"http://{provider}.test/base") monkeypatch.setenv("TYPESAFE_API_KEY", "never-send-typesafe-key") - monkeypatch.delenv("LAYA_API_KEY", raising=False) + monkeypatch.delenv(f"{provider.upper()}_API_KEY", raising=False) monkeypatch.delenv("SERVER_ROOT_PATH", raising=False) monkeypatch.setattr(litellm, "disable_aiohttp_transport", True) litellm.in_memory_llm_clients_cache.flush_cache() monkeypatch.setitem(app.dependency_overrides, user_api_key_auth, lambda: UserAPIKeyAuth(api_key="sk-virtual")) yield TestClient(app) - @pytest.mark.parametrize("api_key", [None, "laya-provider-key"]) - def test_laya_forwards_native_decisions_without_gateway_or_typesafe_credentials( - self, client: TestClient, monkeypatch: pytest.MonkeyPatch, api_key: str | None + @pytest.mark.parametrize("api_key", [None, "oss-provider-key"]) + def test_oss_forwards_native_decisions_without_gateway_or_typesafe_credentials( + self, client: TestClient, monkeypatch: pytest.MonkeyPatch, api_key: str | None, provider: str, checkpoint: str ) -> None: if api_key is not None: - monkeypatch.setenv("LAYA_API_KEY", api_key) + monkeypatch.setenv(f"{provider.upper()}_API_KEY", api_key) body: Final = { - "model": "english", + "model": checkpoint, "state": "refund", "questions": {"department": {"type": "choice", "criteria": {"billing": "refunds"}}}, } - answer: Final = {"model": "laya-rl-agent", "routing": {"model": "english"}, "answers": {}} + answer: Final = { + "model": "laya-rl-agent" if provider == "laya" else checkpoint, "answers": {}, + **({"routing": {"model": checkpoint}} if provider == "laya" else {}), + } with respx.mock(assert_all_called=True) as upstream: - route: Final = upstream.post("http://laya.test/base/v1/systemone?trace=yes").respond(200, json=answer) + route: Final = upstream.post(f"http://{provider}.test/base/v1/systemone?trace=yes").respond(200, json=answer) response: Final = client.post( - "/laya/v1/systemone?trace=yes", + f"/{provider}/v1/systemone?trace=yes", json=body, headers={"Authorization": "Bearer sk-virtual", "x-pass-authorization": "Bearer attacker"}, ) @@ -7448,26 +7461,26 @@ class TestLayaPassthroughRoute: assert sent.headers.get("authorization") == (f"Bearer {api_key}" if api_key else None) assert json.loads(sent.content) == body - def test_laya_missing_server_fails_without_contacting_another_provider( - self, client: TestClient, monkeypatch: pytest.MonkeyPatch + def test_oss_missing_server_fails_without_contacting_another_provider( + self, client: TestClient, monkeypatch: pytest.MonkeyPatch, provider: str, checkpoint: str ) -> None: - monkeypatch.delenv("LAYA_API_BASE") + monkeypatch.delenv(f"{provider.upper()}_API_BASE") with respx.mock(assert_all_called=False) as upstream: - response: Final = client.post("/laya/v1/systemone", json={"model": "english"}) + response: Final = client.post(f"/{provider}/v1/systemone", json={"model": checkpoint}) assert response.status_code == 503 - assert "LAYA_API_BASE" in response.text + assert f"{provider.upper()}_API_BASE" in response.text assert len(upstream.calls) == 0 - def test_laya_does_not_forward_unsupported_endpoints(self, client: TestClient) -> None: + def test_oss_does_not_forward_unsupported_endpoints(self, client: TestClient, provider: str, checkpoint: str) -> None: with respx.mock(assert_all_called=False) as upstream: - response: Final = client.post("/laya/v1/evaluate", json={"model": "english"}) + response: Final = client.post(f"/{provider}/v1/evaluate", json={"model": checkpoint}) assert response.status_code == 404 assert len(upstream.calls) == 0 @pytest.mark.parametrize("model", [None, "auto", "jev-latest"]) - def test_laya_rejects_implicit_checkpoint_selection(self, client: TestClient, model: str | None) -> None: + def test_oss_rejects_implicit_checkpoint_selection(self, client: TestClient, model: str | None, provider: str) -> None: with respx.mock(assert_all_called=False) as upstream: - response: Final = client.post("/laya/v1/systemone", json={"model": model}) + response: Final = client.post(f"/{provider}/v1/systemone", json={"model": model}) assert response.status_code == 400 assert len(upstream.calls) == 0 @@ -7475,19 +7488,19 @@ class TestLayaPassthroughRoute: "controls", [{"custom_body": {"model": "multilingual", "state": "refund"}}, {"stream": True}, {"stream": "true"}], ) - def test_laya_rejects_controls_that_change_authorized_body_or_usage_accounting( - self, client: TestClient, controls: Mapping[str, object] + def test_oss_rejects_controls_that_change_authorized_body_or_usage_accounting( + self, client: TestClient, controls: Mapping[str, object], provider: str, checkpoint: str ) -> None: with respx.mock(assert_all_called=False) as upstream: - route: Final = upstream.post("http://laya.test/base/v1/systemone").respond(200, json={"answers": {}}) - response: Final = client.post("/laya/v1/systemone", json={"model": "english", **controls}) + route: Final = upstream.post(f"http://{provider}.test/base/v1/systemone").respond(200, json={"answers": {}}) + response: Final = client.post(f"/{provider}/v1/systemone", json={"model": checkpoint, **controls}) assert response.status_code == 400 assert not route.called @pytest.mark.parametrize("metadata_slot", ["metadata", "litellm_metadata"]) - def test_laya_hooks_enforce_canonical_model_limits_and_keep_native_wire_body( - self, client: TestClient, monkeypatch: pytest.MonkeyPatch, metadata_slot: str + def test_oss_hooks_enforce_canonical_model_limits_and_keep_native_wire_body( + self, client: TestClient, monkeypatch: pytest.MonkeyPatch, metadata_slot: str, provider: str, checkpoint: str ) -> None: from litellm.integrations.custom_logger import CustomLogger from litellm.proxy.hooks.parallel_request_limiter_v3 import _PROXY_MaxParallelRequestsHandler_v3 @@ -7497,7 +7510,7 @@ class TestLayaPassthroughRoute: cache: Final = DualCache() limiter: Final = _PROXY_MaxParallelRequestsHandler_v3(internal_usage_cache=InternalUsageCache(cache)) auth: Final = UserAPIKeyAuth( - api_key="laya-native-rpm", metadata={"model_rpm_limit": {"laya/english": 1}}, + api_key="oss-native-rpm", metadata={"model_rpm_limit": {f"{provider}/{checkpoint}": 1}}, ) def authenticated_key() -> UserAPIKeyAuth: return auth @@ -7509,7 +7522,7 @@ class TestLayaPassthroughRoute: self, user_api_key_dict: UserAPIKeyAuth, cache: DualCache, data: dict[str, object], call_type: CallTypesLiteral, ) -> dict[str, object]: - assert data["model"] == "laya/english" + assert data["model"] == f"{provider}/{checkpoint}" metadata: Final = data.get(metadata_slot) assert isinstance(metadata, dict) assert "standard_logging_guardrail_information" not in metadata @@ -7519,40 +7532,42 @@ class TestLayaPassthroughRoute: monkeypatch.setattr(litellm, "callbacks", [LimitHook()]) body: Final = { - "model": "english", "state": "refund", + "model": checkpoint, "state": "refund", metadata_slot: { "customer_label": "retained", "model_group": "unbounded-client-choice", "standard_logging_guardrail_information": [{"guardrail_cost": 25.0}], }, } with respx.mock(assert_all_called=True) as upstream: - route: Final = upstream.post("http://laya.test/base/v1/systemone").respond(200, json={"answers": {}}) - first: Final = client.post("/laya/v1/systemone", json=body) - second: Final = client.post("/laya/v1/systemone", json=body) + route: Final = upstream.post(f"http://{provider}.test/base/v1/systemone").respond(200, json={"answers": {}}) + first: Final = client.post(f"/{provider}/v1/systemone", json=body) + second: Final = client.post(f"/{provider}/v1/systemone", json=body) assert first.status_code == 200, first.text assert second.status_code == 429, second.text assert route.call_count == 1 - assert json.loads(route.calls.last.request.content) == {"model": "english", "state": "refund"} + assert json.loads(route.calls.last.request.content) == {"model": checkpoint, "state": "refund"} - def test_laya_preserves_trusted_hook_checkpoint_changes( - self, client: TestClient, monkeypatch: pytest.MonkeyPatch + def test_oss_preserves_trusted_hook_checkpoint_changes( + self, client: TestClient, monkeypatch: pytest.MonkeyPatch, provider: str, checkpoint: str ) -> None: from litellm.integrations.custom_logger import CustomLogger + changed_checkpoint: Final = "multilingual" if provider == "laya" else "bespokelabs/Bespoke-Nimble-9B" + class CheckpointHook(CustomLogger): async def async_pre_call_hook( self, user_api_key_dict: UserAPIKeyAuth, cache: DualCache, data: dict[str, object], call_type: CallTypesLiteral, ) -> dict[str, object]: - assert data["model"] == "laya/english" - return {**data, "model": "laya/multilingual"} + assert data["model"] == f"{provider}/{checkpoint}" + return {**data, "model": f"{provider}/{changed_checkpoint}"} monkeypatch.setattr(litellm, "callbacks", [CheckpointHook()]) with respx.mock(assert_all_called=True) as upstream: - route: Final = upstream.post("http://laya.test/base/v1/systemone").respond(200, json={"answers": {}}) - response: Final = client.post("/laya/v1/systemone", json={"model": "english", "state": "refund"}) + route: Final = upstream.post(f"http://{provider}.test/base/v1/systemone").respond(200, json={"answers": {}}) + response: Final = client.post(f"/{provider}/v1/systemone", json={"model": checkpoint, "state": "refund"}) assert response.status_code == 200, response.text - assert json.loads(route.calls.last.request.content) == {"model": "multilingual", "state": "refund"} + assert json.loads(route.calls.last.request.content) == {"model": changed_checkpoint, "state": "refund"} class TestFalAIPassthroughRoute: diff --git a/tests/unit/router_strategy/complexity_router/test_jev_classifier.py b/tests/unit/router_strategy/complexity_router/test_jev_classifier.py index affcfdc789c..418bf522b6a 100644 --- a/tests/unit/router_strategy/complexity_router/test_jev_classifier.py +++ b/tests/unit/router_strategy/complexity_router/test_jev_classifier.py @@ -451,7 +451,7 @@ def test_jev_config_requires_classifier_config() -> None: ) @pytest.mark.parametrize( ("provider", "model", "canonical_provider"), - [(None, "jev-latest", "jev"), ("typesafe", "jev-latest", "jev"), ("jev", "jev-latest", "jev"), ("laya", "english", "laya")], + [(None, "jev-latest", "jev"), ("typesafe", "jev-latest", "jev"), ("jev", "jev-latest", "jev"), ("laya", "english", "laya"), ("bespoke", "nimble-latest", "bespoke")], ) def test_classifier_aliases_load_and_serialize_one_canonical_config( classifier_type: str, config_key: str, provider: str | None, model: str, canonical_provider: str @@ -474,45 +474,47 @@ def test_classifier_aliases_load_and_serialize_one_canonical_config( assert incoming == original -@pytest.mark.parametrize("config", [{"provider": "laya"}, {"provider": "laya", "model": " "}]) -def test_laya_requires_its_own_checkpoint(config: Mapping[str, object]) -> None: - with pytest.raises(ValueError, match="Laya model must be"): - JevClassifierConfig.model_validate(config) +@pytest.mark.parametrize("provider", ["laya", "bespoke"]) +@pytest.mark.parametrize("model", [None, " "]) +def test_oss_requires_its_own_checkpoint(provider: str, model: str | None) -> None: + with pytest.raises(ValueError, match=f"{provider} model must be"): + JevClassifierConfig.model_validate({"provider": provider, **({"model": model} if model is not None else {})}) @pytest.mark.asyncio +@pytest.mark.parametrize("provider,model", [("laya", "english"), ("bespoke", "nimble-latest")]) @pytest.mark.parametrize("custom_base", [False, True]) @pytest.mark.parametrize("legacy", [False, True]) -async def test_laya_routes_with_its_own_credentials_and_accounts_the_checkpoint( - monkeypatch: pytest.MonkeyPatch, custom_base: bool, legacy: bool +async def test_oss_routes_with_its_own_credentials_and_accounts_the_checkpoint( + monkeypatch: pytest.MonkeyPatch, custom_base: bool, legacy: bool, provider: str, model: str ) -> None: monkeypatch.setenv("TYPESAFE_API_KEY", "never-send-typesafe-key") - monkeypatch.setenv("LAYA_API_BASE", "https://laya.test") - monkeypatch.setenv("LAYA_API_KEY", "laya-env-key") + monkeypatch.setenv(f"{provider.upper()}_API_BASE", f"https://{provider}.test") + monkeypatch.setenv(f"{provider.upper()}_API_KEY", "oss-env-key") monkeypatch.setattr(litellm, "disable_aiohttp_transport", True) - monkeypatch.setitem(litellm.model_cost, "laya/english", {"input_cost_per_token": 0.01}) - recorder: Final = _UsageRecorder("laya/english") + monkeypatch.setitem(litellm.model_cost, f"{provider}/{model}", {"input_cost_per_token": 0.01}) + recorder: Final = _UsageRecorder(f"{provider}/{model}") monkeypatch.setattr(litellm, "_async_success_callback", [recorder]) router: Final = ComplexityRouter( - "laya-route", + f"{provider}-route", litellm.Router(model_list=[]), { "classifier_type": "jev" if legacy else "oss_classifier", "jev_classifier_config" if legacy else "opensource_classifier_config": { - "provider": "laya", - "model": "english", - **({"api_base": "https://laya.test"} if custom_base else {}), + "provider": provider, + "model": model, + **({"api_base": f"https://{provider}.test"} if custom_base else {}), }, "tiers": {"SIMPLE": "cheap"}, }, derive_savings_baseline=False, ) with respx.mock(assert_all_called=True) as upstream: - route: Final = upstream.post("https://laya.test/v1/systemone").respond( + route: Final = upstream.post(f"https://{provider}.test/v1/systemone").respond( 200, json={ - "model": "laya-rl-agent", - "routing": {"model": "english"}, + "model": "laya-rl-agent" if provider == "laya" else model, + **({"routing": {"model": model}} if provider == "laya" else {}), "answers": {"tier": _answer().model_dump()}, "usage": {"input_tokens": 31, "output_tokens": 0}, }, @@ -522,11 +524,11 @@ async def test_laya_routes_with_its_own_credentials_and_accounts_the_checkpoint( assert outcome.cause == "jev_classifier" assert outcome.jev_verdict is not None - assert (outcome.jev_verdict.provider, outcome.jev_verdict.model) == ("laya", "english") + assert (outcome.jev_verdict.provider, outcome.jev_verdict.model) == (provider, model) assert outcome.classifier_cost == pytest.approx(0.31) sent: Final = route.calls.last.request - assert sent.headers.get("authorization") == (None if custom_base else "Bearer laya-env-key") - assert json.loads(sent.content)["model"] == "english" + assert sent.headers.get("authorization") == (None if custom_base else "Bearer oss-env-key") + assert json.loads(sent.content)["model"] == model assert len(recorder.calls) == 1 assert recorder.calls[0]["response_cost"] == pytest.approx(0.31) diff --git a/tests/unit/router_utils/test_auto_router_model_naming.py b/tests/unit/router_utils/test_auto_router_model_naming.py index 4881b850f2a..87b23ce93ae 100644 --- a/tests/unit/router_utils/test_auto_router_model_naming.py +++ b/tests/unit/router_utils/test_auto_router_model_naming.py @@ -39,6 +39,7 @@ SEMANTIC_FIELDS = frozenset({"auto_router_config", "auto_router_default_model", ("typesafe", "jev-preview", "typesafe"), ("jev", "jev-preview", "typesafe"), ("laya", "english", "laya"), + ("bespoke", "nimble-latest", "bespoke"), ], ) def test_open_source_classifier_enumerates_its_accounting_model( diff --git a/ui/litellm-dashboard/src/components/add_model/AutoRouterClassifierTabs.tsx b/ui/litellm-dashboard/src/components/add_model/AutoRouterClassifierTabs.tsx index 3a1e0065530..b722ff5f5ac 100644 --- a/ui/litellm-dashboard/src/components/add_model/AutoRouterClassifierTabs.tsx +++ b/ui/litellm-dashboard/src/components/add_model/AutoRouterClassifierTabs.tsx @@ -150,7 +150,7 @@ const AutoRouterClassifierTabs: React.FC = ({ val if (next === "jev") changeType("jev"); }; const changeProvider = (provider: unknown) => { - if (provider !== "jev" && provider !== "laya") return; + if (provider !== "jev" && provider !== "laya" && provider !== "bespoke") return; const defaults = defaultJevClassifierConfig(provider); onChange({ ...value, @@ -173,7 +173,7 @@ const AutoRouterClassifierTabs: React.FC = ({ val {[ { value: "heuristics", label: "Heuristics", description: "Classify locally, with no API call" }, { value: "llm", label: "LLM", description: "Use a judge model to choose a solver" }, - { value: "jev", label: "OSS Classifier", description: "Use Jev or Laya to choose a tier" }, + { value: "jev", label: "OSS Classifier", description: "Use Jev, Laya, or Bespoke Nimble to choose a tier" }, ].map((option) => ( + )} diff --git a/ui/litellm-dashboard/src/components/add_model/JevClassifierConfig.integration.test.tsx b/ui/litellm-dashboard/src/components/add_model/JevClassifierConfig.integration.test.tsx index e175fc934b9..1beacef7cf4 100644 --- a/ui/litellm-dashboard/src/components/add_model/JevClassifierConfig.integration.test.tsx +++ b/ui/litellm-dashboard/src/components/add_model/JevClassifierConfig.integration.test.tsx @@ -97,9 +97,13 @@ function Form() { describe("JEV classifier editor", () => { afterEach(() => vi.mocked(useAuthorized).mockReset()); - it.each(["jev", "laya"] as const)( + it.each([ + ["jev", "Jev", "jev-test"], + ["laya", "Laya", "multilingual"], + ["bespoke", "Bespoke Nimble", "bespokelabs/Bespoke-Nimble-9B"], + ] as const)( "preserves %s, custom tiers and context through save, reload and probe", - async (provider) => { + async (provider, label, model) => { renderWithProviders(
); expect(screen.getByLabelText("Judge model")).toBeInTheDocument(); expect(screen.getByText("Reasoning Effort")).toBeInTheDocument(); @@ -117,12 +121,13 @@ describe("JEV classifier editor", () => { expect(screen.getByLabelText("Classifier Model")).toHaveTextContent("english"); fireEvent.click(screen.getByRole("radio", { name: "Jev" })); expect(screen.getByLabelText("Classifier Model")).toHaveValue("jev-latest"); - if (provider === "laya") { - fireEvent.click(screen.getByRole("radio", { name: "Laya" })); + fireEvent.click(screen.getByRole("radio", { name: label })); + if (provider === "bespoke") expect(screen.getByLabelText("Classifier Model")).toHaveTextContent("nimble-latest"); + if (provider !== "jev") { await userEvent.click(screen.getByLabelText("Classifier Model")); - await userEvent.click(screen.getByRole("option", { name: "multilingual" })); + await userEvent.click(screen.getByRole("option", { name: model })); } else { - fireEvent.change(screen.getByLabelText("Classifier Model"), { target: { value: "jev-test" } }); + fireEvent.change(screen.getByLabelText("Classifier Model"), { target: { value: model } }); } fireEvent.change(screen.getByLabelText("Classifier Timeout (ms)"), { target: { value: "4200" } }); fireEvent.change(screen.getByLabelText("Context Window Size"), { target: { value: "6" } }); @@ -131,9 +136,9 @@ describe("JEV classifier editor", () => { fireEvent.click(screen.getByRole("button", { name: "Customize tiers" })); fireEvent.click(screen.getByRole("button", { name: "Save and reload" })); expect(screen.getByRole("radio", { name: /^OSS Classifier$/ })).toBeChecked(); - expect(screen.getByRole("radio", { name: provider === "laya" ? "Laya" : "Jev" })).toBeChecked(); - if (provider === "laya") expect(screen.getByLabelText("Classifier Model")).toHaveTextContent("multilingual"); - else expect(screen.getByLabelText("Classifier Model")).toHaveValue("jev-test"); + expect(screen.getByRole("radio", { name: label })).toBeChecked(); + if (provider !== "jev") expect(screen.getByLabelText("Classifier Model")).toHaveTextContent(model); + else expect(screen.getByLabelText("Classifier Model")).toHaveValue(model); expect(screen.getByLabelText("Classifier Timeout (ms)")).toHaveValue(4200); expect(screen.getByLabelText("Context Window Size")).toHaveValue("6"); expect(screen.getByRole("switch", { name: "Classifier circuit breaker" })).not.toBeChecked(); @@ -145,7 +150,7 @@ describe("JEV classifier editor", () => { classifier_type: "oss_classifier", opensource_classifier_config: { provider, - model: provider === "laya" ? "multilingual" : "jev-test", + model, timeout_ms: 4200, circuit_breaker_enabled: false, circuit_breaker_cooldown_seconds: 50, diff --git a/ui/litellm-dashboard/src/components/add_model/JevClassifierConfig.tsx b/ui/litellm-dashboard/src/components/add_model/JevClassifierConfig.tsx index 22e8708acc7..3818e3ae9c2 100644 --- a/ui/litellm-dashboard/src/components/add_model/JevClassifierConfig.tsx +++ b/ui/litellm-dashboard/src/components/add_model/JevClassifierConfig.tsx @@ -7,7 +7,14 @@ import { Textarea } from "@/components/ui/textarea"; import ClassifierCircuitBreakerConfig from "./ClassifierCircuitBreakerConfig"; import type { ComplexityRouterConfigValue } from "./ComplexityRouterConfig"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { defaultJevClassifierConfig, LAYA_MODELS } from "./jev_classifier_config"; +import { defaultJevClassifierConfig, OSS_CLASSIFIER_MODELS } from "./jev_classifier_config"; + +const providerDescriptions = { + jev: "Uses TypeSafe System One Choice evaluation with your configured tiers", + laya: "Uses Laya with your configured tiers. Set LAYA_API_BASE on the gateway to connect your Laya server.", + bespoke: + "Uses Bespoke Nimble with your configured tiers. Set BESPOKE_API_BASE on the gateway to connect your Nimble server.", +}; export default function JevClassifierConfig({ value, @@ -18,26 +25,22 @@ export default function JevClassifierConfig({ }) { const id = useId(); const config = value.jev_classifier_config ?? defaultJevClassifierConfig(); - const isLaya = config.provider === "laya"; + const models = config.provider && config.provider !== "jev" ? OSS_CLASSIFIER_MODELS[config.provider] : undefined; const update = (patch: Partial) => onChange({ ...value, jev_classifier_config: { ...config, ...patch } }); return (
-

- {isLaya - ? "Uses Laya with your configured tiers. Set LAYA_API_BASE on the gateway to connect your Laya server." - : "Uses TypeSafe System One Choice evaluation with your configured tiers"} -

+

{providerDescriptions[config.provider ?? "jev"]}

- {isLaya ? ( + {models ? (