inspect-ai 0.3.90__py3-none-any.whl → 0.3.91__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- inspect_ai/_cli/common.py +13 -0
- inspect_ai/_cli/eval.py +40 -0
- inspect_ai/_display/textual/widgets/samples.py +49 -4
- inspect_ai/_display/textual/widgets/vscode.py +4 -2
- inspect_ai/_eval/eval.py +41 -28
- inspect_ai/_eval/evalset.py +4 -0
- inspect_ai/_eval/loader.py +4 -5
- inspect_ai/_eval/registry.py +1 -1
- inspect_ai/_eval/run.py +6 -3
- inspect_ai/_eval/task/log.py +6 -0
- inspect_ai/_eval/task/run.py +108 -41
- inspect_ai/_eval/task/sandbox.py +19 -5
- inspect_ai/_util/_async.py +1 -1
- inspect_ai/_util/constants.py +1 -0
- inspect_ai/_util/environ.py +32 -0
- inspect_ai/_util/file.py +8 -1
- inspect_ai/_util/httpx.py +105 -22
- inspect_ai/_util/registry.py +83 -9
- inspect_ai/_util/text.py +81 -17
- inspect_ai/_util/transcript.py +9 -6
- inspect_ai/_util/vscode.py +7 -2
- inspect_ai/_view/schema.py +1 -1
- inspect_ai/_view/www/babel.config.js +11 -0
- inspect_ai/_view/www/dist/assets/index.css +3640 -3563
- inspect_ai/_view/www/dist/assets/index.js +59204 -52519
- inspect_ai/_view/www/eslint.config.mjs +10 -1
- inspect_ai/_view/www/jest.config.mjs +21 -0
- inspect_ai/_view/www/log-schema.json +111 -2
- inspect_ai/_view/www/package.json +19 -5
- inspect_ai/_view/www/src/{types → @types}/log.d.ts +95 -32
- inspect_ai/_view/www/{App.css → src/app/App.css} +22 -14
- inspect_ai/_view/www/src/app/App.tsx +168 -0
- inspect_ai/_view/www/src/{AppErrorBoundary.tsx → app/AppErrorBoundary.tsx} +1 -1
- inspect_ai/_view/www/src/{appearance → app/appearance}/icons.ts +1 -0
- inspect_ai/_view/www/src/{metadata → app/content}/RenderedContent.tsx +5 -5
- inspect_ai/_view/www/src/{workspace/WorkSpaceView.tsx → app/log-view/LogView.tsx} +59 -40
- inspect_ai/_view/www/src/app/log-view/LogViewContainer.tsx +159 -0
- inspect_ai/_view/www/src/app/log-view/LogViewLayout.tsx +109 -0
- inspect_ai/_view/www/src/{workspace → app/log-view}/error/TaskErrorPanel.tsx +3 -3
- inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/ModelRolesView.tsx +1 -1
- inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/Navbar.tsx +4 -4
- inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/PrimaryBar.tsx +8 -8
- inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/ResultsPanel.tsx +6 -6
- inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/RunningStatusPanel.tsx +1 -1
- inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/ScoreGrid.tsx +1 -1
- inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/SecondaryBar.tsx +8 -8
- inspect_ai/_view/www/src/{workspace → app/log-view}/tabs/InfoTab.tsx +35 -6
- inspect_ai/_view/www/src/app/log-view/tabs/JsonTab.tsx +136 -0
- inspect_ai/_view/www/src/{workspace → app/log-view}/tabs/SamplesTab.tsx +82 -73
- inspect_ai/_view/www/src/{workspace → app/log-view}/tabs/grouping.ts +3 -3
- inspect_ai/_view/www/src/{workspace → app/log-view}/tabs/types.ts +1 -1
- inspect_ai/_view/www/src/{plan → app/plan}/DatasetDetailView.tsx +2 -2
- inspect_ai/_view/www/src/{plan → app/plan}/DetailStep.tsx +1 -1
- inspect_ai/_view/www/src/{plan → app/plan}/ModelCard.tsx +4 -4
- inspect_ai/_view/www/src/{plan → app/plan}/PlanCard.tsx +2 -2
- inspect_ai/_view/www/src/{plan → app/plan}/PlanDetailView.tsx +5 -5
- inspect_ai/_view/www/src/{plan → app/plan}/SolverDetailView.tsx +1 -1
- inspect_ai/_view/www/src/app/routing/AppRouter.tsx +58 -0
- inspect_ai/_view/www/src/app/routing/navigationHooks.ts +182 -0
- inspect_ai/_view/www/src/app/routing/url.ts +43 -0
- inspect_ai/_view/www/src/{samples → app/samples}/InlineSampleDisplay.tsx +11 -27
- inspect_ai/_view/www/src/{samples → app/samples}/SampleDialog.tsx +36 -40
- inspect_ai/_view/www/src/{samples → app/samples}/SampleDisplay.module.css +4 -0
- inspect_ai/_view/www/src/{samples → app/samples}/SampleDisplay.tsx +116 -49
- inspect_ai/_view/www/src/{samples → app/samples}/SampleSummaryView.module.css +1 -1
- inspect_ai/_view/www/src/{samples → app/samples}/SampleSummaryView.tsx +29 -26
- inspect_ai/_view/www/src/{samples → app/samples}/SamplesTools.tsx +3 -3
- inspect_ai/_view/www/src/{samples → app/samples}/chat/ChatMessage.module.css +5 -2
- inspect_ai/_view/www/src/{samples → app/samples}/chat/ChatMessage.tsx +12 -4
- inspect_ai/_view/www/src/{samples → app/samples}/chat/ChatMessageRenderer.tsx +3 -3
- inspect_ai/_view/www/src/{samples → app/samples}/chat/ChatMessageRow.tsx +6 -1
- inspect_ai/_view/www/src/{samples → app/samples}/chat/ChatView.tsx +4 -2
- inspect_ai/_view/www/src/{samples → app/samples}/chat/ChatViewVirtualList.tsx +5 -3
- inspect_ai/_view/www/src/app/samples/chat/MessageContent.module.css +12 -0
- inspect_ai/_view/www/src/{samples → app/samples}/chat/MessageContent.tsx +11 -10
- inspect_ai/_view/www/src/app/samples/chat/MessageContents.module.css +7 -0
- inspect_ai/_view/www/src/{samples → app/samples}/chat/MessageContents.tsx +14 -8
- inspect_ai/_view/www/src/{samples → app/samples}/chat/messages.ts +2 -2
- inspect_ai/_view/www/src/app/samples/chat/tools/ToolCallView.module.css +7 -0
- inspect_ai/_view/www/src/{samples → app/samples}/chat/tools/ToolCallView.tsx +26 -27
- inspect_ai/_view/www/src/app/samples/chat/tools/ToolInput.module.css +19 -0
- inspect_ai/_view/www/src/{samples → app/samples}/chat/tools/ToolInput.tsx +3 -3
- inspect_ai/_view/www/src/{samples → app/samples}/chat/tools/ToolOutput.module.css +1 -0
- inspect_ai/_view/www/src/{samples → app/samples}/chat/tools/ToolOutput.tsx +1 -1
- inspect_ai/_view/www/src/{samples → app/samples}/chat/tools/ToolTitle.module.css +4 -0
- inspect_ai/_view/www/src/{samples → app/samples}/chat/tools/ToolTitle.tsx +2 -2
- inspect_ai/_view/www/src/{samples → app/samples}/chat/tools/tool.ts +1 -1
- inspect_ai/_view/www/src/app/samples/chat/types.ts +1 -0
- inspect_ai/_view/www/src/{samples → app/samples}/descriptor/samplesDescriptor.tsx +38 -15
- inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/BooleanScoreDescriptor.tsx +1 -1
- inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/CategoricalScoreDescriptor.tsx +2 -2
- inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/NumericScoreDescriptor.tsx +3 -3
- inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/ObjectScoreDescriptor.tsx +4 -4
- inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/OtherScoreDescriptor.tsx +2 -2
- inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/PassFailScoreDescriptor.tsx +2 -2
- inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/ScoreDescriptor.tsx +1 -1
- inspect_ai/_view/www/src/{samples → app/samples}/descriptor/types.ts +4 -3
- inspect_ai/_view/www/src/{samples → app/samples}/error/SampleErrorView.module.css +2 -1
- inspect_ai/_view/www/src/{samples → app/samples}/list/SampleHeader.tsx +3 -0
- inspect_ai/_view/www/src/{samples → app/samples}/list/SampleList.tsx +47 -33
- inspect_ai/_view/www/src/{samples → app/samples}/list/SampleRow.module.css +16 -0
- inspect_ai/_view/www/src/{samples → app/samples}/list/SampleRow.tsx +47 -20
- inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/SelectScorer.tsx +1 -1
- inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/SortFilter.tsx +4 -4
- inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/filters.ts +8 -6
- inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/sample-filter/SampleFilter.tsx +4 -3
- inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/sample-filter/completions.ts +1 -1
- inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/sample-filter/language.ts +1 -0
- inspect_ai/_view/www/src/{samples → app/samples}/sampleDataAdapter.ts +3 -3
- inspect_ai/_view/www/src/{samples → app/samples}/sampleLimit.ts +1 -1
- inspect_ai/_view/www/src/{samples → app/samples}/scores/SampleScores.tsx +1 -1
- inspect_ai/_view/www/src/{samples → app/samples}/scores/SampleScoresGrid.tsx +12 -11
- inspect_ai/_view/www/src/{samples → app/samples}/scores/SampleScoresView.tsx +6 -6
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/ApprovalEventView.tsx +1 -1
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/ErrorEventView.tsx +3 -3
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/InfoEventView.tsx +4 -4
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/InputEventView.tsx +3 -3
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/LoggerEventView.tsx +3 -3
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/ModelEventView.module.css +13 -7
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/ModelEventView.tsx +49 -21
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/SampleInitEventView.tsx +11 -9
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/SampleLimitEventView.tsx +1 -1
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/SandboxEventView.tsx +8 -6
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/ScoreEventView.tsx +4 -4
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/StepEventView.tsx +11 -3
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/SubtaskEventView.tsx +2 -2
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/ToolEventView.tsx +2 -2
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/TranscriptView.module.css +8 -7
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/TranscriptView.tsx +32 -114
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/TranscriptVirtualListComponent.module.css +6 -5
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/TranscriptVirtualListComponent.tsx +14 -2
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventPanel.tsx +2 -2
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventTimingPanel.tsx +1 -1
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/utils.ts +1 -1
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/state/StateEventRenderers.tsx +23 -21
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/state/StateEventRenders.module.css +7 -0
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/state/StateEventView.tsx +2 -2
- inspect_ai/_view/www/src/app/samples/transcript/transform/fixups.ts +142 -0
- inspect_ai/_view/www/src/app/samples/transcript/transform/treeify.ts +39 -0
- inspect_ai/_view/www/src/{samples → app/samples}/transcript/types.ts +1 -1
- inspect_ai/_view/www/src/{workspace → app}/sidebar/EvalStatus.tsx +1 -1
- inspect_ai/_view/www/src/app/sidebar/LogDirectoryTitleView.module.css +16 -0
- inspect_ai/_view/www/src/app/sidebar/LogDirectoryTitleView.tsx +70 -0
- inspect_ai/_view/www/src/{workspace → app}/sidebar/Sidebar.module.css +8 -0
- inspect_ai/_view/www/src/{workspace → app}/sidebar/Sidebar.tsx +35 -17
- inspect_ai/_view/www/src/{workspace → app}/sidebar/SidebarLogEntry.tsx +1 -1
- inspect_ai/_view/www/src/{workspace → app}/sidebar/SidebarScoreView.tsx +2 -2
- inspect_ai/_view/www/src/{workspace → app}/sidebar/SidebarScoresView.tsx +2 -2
- inspect_ai/_view/www/src/{types.ts → app/types.ts} +18 -11
- inspect_ai/_view/www/src/{usage → app/usage}/ModelTokenTable.tsx +1 -1
- inspect_ai/_view/www/src/{usage → app/usage}/ModelUsagePanel.tsx +2 -2
- inspect_ai/_view/www/src/{usage → app/usage}/TokenTable.tsx +1 -1
- inspect_ai/_view/www/src/{usage → app/usage}/UsageCard.tsx +6 -6
- inspect_ai/_view/www/src/{api → client/api}/api-browser.ts +2 -2
- inspect_ai/_view/www/src/{api → client/api}/api-http.ts +3 -3
- inspect_ai/_view/www/src/{api → client/api}/api-vscode.ts +2 -2
- inspect_ai/_view/www/src/{api → client/api}/client-api.ts +6 -5
- inspect_ai/_view/www/src/{api → client/api}/index.ts +2 -2
- inspect_ai/_view/www/src/{api → client/api}/types.ts +4 -1
- inspect_ai/_view/www/src/{logfile → client/remote}/remoteLogFile.ts +3 -3
- inspect_ai/_view/www/src/{storage → client/storage}/index.ts +11 -5
- inspect_ai/_view/www/src/components/Card.tsx +1 -1
- inspect_ai/_view/www/src/components/CopyButton.tsx +1 -1
- inspect_ai/_view/www/src/components/DownloadButton.tsx +1 -1
- inspect_ai/_view/www/src/components/ErrorPanel.tsx +1 -1
- inspect_ai/_view/www/src/components/{ExpandablePanel.css → ExpandablePanel.module.css} +14 -11
- inspect_ai/_view/www/src/components/ExpandablePanel.tsx +16 -10
- inspect_ai/_view/www/src/components/FindBand.tsx +1 -1
- inspect_ai/_view/www/src/components/JsonPanel.css +2 -2
- inspect_ai/_view/www/src/components/LargeModal.tsx +12 -1
- inspect_ai/_view/www/src/components/LightboxCarousel.tsx +1 -1
- inspect_ai/_view/www/src/components/MarkdownDiv.tsx +3 -1
- inspect_ai/_view/www/src/components/MessageBand.tsx +1 -1
- inspect_ai/_view/www/src/components/NoContentsPanel.tsx +1 -1
- inspect_ai/_view/www/src/constants.ts +10 -9
- inspect_ai/_view/www/src/index.tsx +27 -11
- inspect_ai/_view/www/src/state/appSlice.ts +44 -5
- inspect_ai/_view/www/src/state/hooks.ts +30 -7
- inspect_ai/_view/www/src/state/logSlice.ts +7 -5
- inspect_ai/_view/www/src/state/logsPolling.ts +1 -1
- inspect_ai/_view/www/src/state/logsSlice.ts +18 -13
- inspect_ai/_view/www/src/state/samplePolling.ts +12 -12
- inspect_ai/_view/www/src/state/sampleSlice.ts +3 -5
- inspect_ai/_view/www/src/state/sampleUtils.ts +1 -1
- inspect_ai/_view/www/src/{scoring/utils.ts → state/scoring.ts} +2 -2
- inspect_ai/_view/www/src/state/store.ts +9 -7
- inspect_ai/_view/www/src/state/utils.ts +1 -1
- inspect_ai/_view/www/src/tests/README.md +49 -0
- inspect_ai/_view/www/src/tests/__mocks__/fileMock.js +1 -0
- inspect_ai/_view/www/src/tests/__mocks__/styleMock.js +1 -0
- inspect_ai/_view/www/src/tests/setupTests.mjs +1 -0
- inspect_ai/_view/www/src/tests/utils/base64.test.ts +23 -0
- inspect_ai/_view/www/src/tests/utils/format.test.ts +127 -0
- inspect_ai/_view/www/src/tests/utils/path.test.ts +54 -0
- inspect_ai/_view/www/src/utils/format.ts +8 -2
- inspect_ai/_view/www/src/utils/path.ts +14 -2
- inspect_ai/_view/www/src/utils/polling.ts +1 -2
- inspect_ai/_view/www/src/utils/uri.ts +32 -0
- inspect_ai/_view/www/yarn.lock +3310 -382
- inspect_ai/agent/_handoff.py +6 -3
- inspect_ai/agent/_human/agent.py +5 -3
- inspect_ai/agent/_human/install.py +16 -7
- inspect_ai/agent/_human/panel.py +14 -1
- inspect_ai/agent/_human/service.py +5 -1
- inspect_ai/agent/_react.py +161 -128
- inspect_ai/agent/_types.py +15 -4
- inspect_ai/approval/_policy.py +2 -2
- inspect_ai/log/_file.py +30 -11
- inspect_ai/log/_log.py +7 -1
- inspect_ai/log/_recorders/eval.py +3 -0
- inspect_ai/log/_recorders/types.py +1 -0
- inspect_ai/log/_samples.py +4 -0
- inspect_ai/model/_call_tools.py +33 -17
- inspect_ai/model/_generate_config.py +10 -2
- inspect_ai/model/_model.py +41 -21
- inspect_ai/model/_model_output.py +2 -1
- inspect_ai/model/_openai.py +10 -8
- inspect_ai/model/_openai_responses.py +83 -42
- inspect_ai/model/_providers/anthropic.py +14 -12
- inspect_ai/model/_providers/google.py +191 -95
- inspect_ai/model/_providers/hf.py +1 -1
- inspect_ai/model/_providers/mistral.py +2 -3
- inspect_ai/model/_providers/openai.py +54 -17
- inspect_ai/model/_providers/openai_o1.py +1 -1
- inspect_ai/model/_providers/openai_responses.py +28 -16
- inspect_ai/model/_providers/openrouter.py +14 -0
- inspect_ai/model/_providers/providers.py +2 -2
- inspect_ai/model/_providers/util/chatapi.py +17 -7
- inspect_ai/model/_providers/vllm.py +1 -1
- inspect_ai/scorer/_metric.py +17 -1
- inspect_ai/scorer/_model.py +51 -6
- inspect_ai/scorer/_scorer.py +1 -1
- inspect_ai/solver/_human_agent.py +3 -0
- inspect_ai/solver/_plan.py +1 -1
- inspect_ai/solver/_solver.py +1 -1
- inspect_ai/solver/_use_tools.py +14 -8
- inspect_ai/tool/__init__.py +16 -1
- inspect_ai/tool/_json_rpc_helpers.py +285 -0
- inspect_ai/tool/_mcp/__init__.py +13 -0
- inspect_ai/tool/_mcp/_context.py +14 -0
- inspect_ai/tool/_mcp/_mcp.py +293 -0
- inspect_ai/tool/_mcp/_sandbox.py +104 -0
- inspect_ai/tool/_mcp/_types.py +31 -0
- inspect_ai/tool/_mcp/connection.py +60 -0
- inspect_ai/tool/_mcp/sampling.py +118 -0
- inspect_ai/tool/_mcp/server.py +112 -0
- inspect_ai/tool/_mcp/tools.py +34 -0
- inspect_ai/tool/_tool.py +13 -0
- inspect_ai/tool/_tool_def.py +24 -7
- inspect_ai/tool/_tool_support_helpers.py +129 -153
- inspect_ai/tool/_tools/_bash_session.py +11 -11
- inspect_ai/tool/_tools/_text_editor.py +6 -6
- inspect_ai/tool/_tools/_web_browser/_web_browser.py +8 -8
- inspect_ai/util/_anyio.py +31 -20
- inspect_ai/util/_json.py +20 -2
- inspect_ai/util/_sandbox/context.py +18 -7
- inspect_ai/util/_sandbox/docker/compose.py +1 -1
- inspect_ai/util/_sandbox/docker/docker.py +92 -21
- inspect_ai/util/_sandbox/environment.py +33 -2
- inspect_ai/util/_sandbox/events.py +2 -2
- inspect_ai/util/_sandbox/service.py +13 -3
- {inspect_ai-0.3.90.dist-info → inspect_ai-0.3.91.dist-info}/METADATA +6 -2
- inspect_ai-0.3.91.dist-info/RECORD +732 -0
- {inspect_ai-0.3.90.dist-info → inspect_ai-0.3.91.dist-info}/WHEEL +1 -1
- inspect_ai/_view/www/src/App.tsx +0 -316
- inspect_ai/_view/www/src/samples/chat/MessageContent.module.css +0 -4
- inspect_ai/_view/www/src/samples/chat/MessageContents.module.css +0 -3
- inspect_ai/_view/www/src/samples/chat/tools/ToolCallView.module.css +0 -3
- inspect_ai/_view/www/src/samples/chat/tools/ToolInput.module.css +0 -14
- inspect_ai/_view/www/src/workspace/WorkSpace.tsx +0 -292
- inspect_ai/_view/www/src/workspace/sidebar/LogDirectoryTitleView.module.css +0 -5
- inspect_ai/_view/www/src/workspace/sidebar/LogDirectoryTitleView.tsx +0 -57
- inspect_ai/_view/www/src/workspace/tabs/JsonTab.tsx +0 -43
- inspect_ai-0.3.90.dist-info/RECORD +0 -705
- /inspect_ai/_view/www/src/{types → @types}/asciicinema-player.d.ts +0 -0
- /inspect_ai/_view/www/src/{types → @types}/jsondiffpatch.d.ts +0 -0
- /inspect_ai/_view/www/src/{types → @types}/markdown-it-katex.d.ts +0 -0
- /inspect_ai/_view/www/src/{types → @types}/prism.d.ts +0 -0
- /inspect_ai/_view/www/src/{appearance → app/appearance}/colors.ts +0 -0
- /inspect_ai/_view/www/src/{appearance → app/appearance}/fonts.ts +0 -0
- /inspect_ai/_view/www/src/{appearance → app/appearance}/styles.ts +0 -0
- /inspect_ai/_view/www/src/{metadata → app/content}/MetaDataGrid.tsx +0 -0
- /inspect_ai/_view/www/src/{metadata → app/content}/MetaDataView.module.css +0 -0
- /inspect_ai/_view/www/src/{metadata → app/content}/MetaDataView.tsx +0 -0
- /inspect_ai/_view/www/src/{metadata → app/content}/MetadataGrid.module.css +0 -0
- /inspect_ai/_view/www/src/{metadata → app/content}/RenderedContent.module.css +0 -0
- /inspect_ai/_view/www/src/{metadata → app/content}/types.ts +0 -0
- /inspect_ai/_view/www/src/{workspace/WorkSpaceView.module.css → app/log-view/LogView.module.css} +0 -0
- /inspect_ai/_view/www/src/{workspace → app/log-view}/error/TaskErrorPanel.module.css +0 -0
- /inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/ModelRolesView.module.css +0 -0
- /inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/Navbar.module.css +0 -0
- /inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/PrimaryBar.module.css +0 -0
- /inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/ResultsPanel.module.css +0 -0
- /inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/RunningStatusPanel.module.css +0 -0
- /inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/ScoreGrid.module.css +0 -0
- /inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/SecondaryBar.module.css +0 -0
- /inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/StatusPanel.module.css +0 -0
- /inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/StatusPanel.tsx +0 -0
- /inspect_ai/_view/www/src/{workspace → app/log-view}/tabs/InfoTab.module.css +0 -0
- /inspect_ai/_view/www/src/{workspace → app/log-view}/tabs/JsonTab.module.css +0 -0
- /inspect_ai/_view/www/src/{workspace → app/log-view}/tabs/RunningNoSamples.module.css +0 -0
- /inspect_ai/_view/www/src/{workspace → app/log-view}/tabs/RunningNoSamples.tsx +0 -0
- /inspect_ai/_view/www/src/{workspace → app/log-view}/types.ts +0 -0
- /inspect_ai/_view/www/src/{workspace → app/log-view}/utils.ts +0 -0
- /inspect_ai/_view/www/src/{plan → app/plan}/DatasetDetailView.module.css +0 -0
- /inspect_ai/_view/www/src/{plan → app/plan}/DetailStep.module.css +0 -0
- /inspect_ai/_view/www/src/{plan → app/plan}/ModelCard.module.css +0 -0
- /inspect_ai/_view/www/src/{plan → app/plan}/PlanDetailView.module.css +0 -0
- /inspect_ai/_view/www/src/{plan → app/plan}/ScorerDetailView.module.css +0 -0
- /inspect_ai/_view/www/src/{plan → app/plan}/ScorerDetailView.tsx +0 -0
- /inspect_ai/_view/www/src/{plan → app/plan}/SolverDetailView.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/InlineSampleDisplay.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/chat/ChatMessageRow.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/chat/ChatViewVirtualList.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/BooleanScoreDescriptor.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/ObjectScoreDescriptor.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/PassFailScoreDescriptor.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/error/FlatSampleErrorView.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/error/FlatSampleErrorView.tsx +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/error/SampleErrorView.tsx +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/error/error.ts +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/list/SampleFooter.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/list/SampleFooter.tsx +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/list/SampleHeader.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/list/SampleList.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/list/SampleSeparator.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/list/SampleSeparator.tsx +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/EpochFilter.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/EpochFilter.tsx +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/SelectScorer.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/SortFilter.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/sample-filter/SampleFilter.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/sample-filter/tokenize.ts +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/scores/SampleScores.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/scores/SampleScoresGrid.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/scores/SampleScoresView.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/InfoEventView.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/LoggerEventView.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/SampleInitEventView.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/SandboxEventView.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/ScoreEventView.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/SubtaskEventView.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/ToolEventView.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventNav.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventNav.tsx +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventNavs.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventNavs.tsx +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventPanel.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventProgressPanel.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventProgressPanel.tsx +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventRow.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventRow.tsx +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventSection.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventSection.tsx +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventTimingPanel.module.css +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/state/StateDiffView.tsx +0 -0
- /inspect_ai/_view/www/src/{samples → app/samples}/transcript/state/StateEventView.module.css +0 -0
- /inspect_ai/_view/www/src/{workspace → app}/sidebar/EvalStatus.module.css +0 -0
- /inspect_ai/_view/www/src/{workspace → app}/sidebar/SidebarLogEntry.module.css +0 -0
- /inspect_ai/_view/www/src/{workspace → app}/sidebar/SidebarScoreView.module.css +0 -0
- /inspect_ai/_view/www/src/{workspace → app}/sidebar/SidebarScoresView.module.css +0 -0
- /inspect_ai/_view/www/src/{usage → app/usage}/ModelUsagePanel.module.css +0 -0
- /inspect_ai/_view/www/src/{usage → app/usage}/TokenTable.module.css +0 -0
- /inspect_ai/_view/www/src/{usage → app/usage}/UsageCard.module.css +0 -0
- /inspect_ai/_view/www/src/{api → client/api}/api-shared.ts +0 -0
- /inspect_ai/_view/www/src/{api → client/api}/jsonrpc.ts +0 -0
- /inspect_ai/_view/www/src/{logfile → client/remote}/remoteZipFile.ts +0 -0
- {inspect_ai-0.3.90.dist-info → inspect_ai-0.3.91.dist-info}/entry_points.txt +0 -0
- {inspect_ai-0.3.90.dist-info → inspect_ai-0.3.91.dist-info}/licenses/LICENSE +0 -0
- {inspect_ai-0.3.90.dist-info → inspect_ai-0.3.91.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,16 @@
|
|
1
|
-
import {
|
2
|
-
import {
|
1
|
+
import { LogsState } from "../app/types";
|
2
|
+
import { EvalLogHeader, LogFiles } from "../client/api/types";
|
3
3
|
import { createLogger } from "../utils/logger";
|
4
4
|
import { createLogsPolling } from "./logsPolling";
|
5
5
|
import { StoreState } from "./store";
|
6
6
|
|
7
7
|
const log = createLogger("Log Slice");
|
8
8
|
|
9
|
+
const kEmptyLogs: LogFiles = {
|
10
|
+
log_dir: "",
|
11
|
+
files: [],
|
12
|
+
};
|
13
|
+
|
9
14
|
export interface LogsSlice {
|
10
15
|
logs: LogsState;
|
11
16
|
logsActions: {
|
@@ -28,7 +33,7 @@ export interface LogsSlice {
|
|
28
33
|
}
|
29
34
|
|
30
35
|
const initialState: LogsState = {
|
31
|
-
logs:
|
36
|
+
logs: kEmptyLogs,
|
32
37
|
logHeaders: {},
|
33
38
|
headersLoading: false,
|
34
39
|
selectedLogIndex: -1,
|
@@ -83,7 +88,7 @@ export const createLogsSlice = (
|
|
83
88
|
|
84
89
|
setSelectedLogFile: (logUrl: string) => {
|
85
90
|
const state = get();
|
86
|
-
const index = state.logs.logs.files.findIndex((val) =>
|
91
|
+
const index = state.logs.logs.files.findIndex((val: { name: string }) =>
|
87
92
|
logUrl.endsWith(val.name),
|
88
93
|
);
|
89
94
|
|
@@ -97,7 +102,7 @@ export const createLogsSlice = (
|
|
97
102
|
const api = get().api;
|
98
103
|
if (!api) {
|
99
104
|
console.error("API not initialized in LogsStore");
|
100
|
-
return
|
105
|
+
return kEmptyLogs;
|
101
106
|
}
|
102
107
|
|
103
108
|
try {
|
@@ -106,7 +111,7 @@ export const createLogsSlice = (
|
|
106
111
|
} catch (e) {
|
107
112
|
console.log(e);
|
108
113
|
get().appActions.setStatus({ loading: false, error: e as Error });
|
109
|
-
return
|
114
|
+
return kEmptyLogs;
|
110
115
|
}
|
111
116
|
},
|
112
117
|
refreshLogs: async () => {
|
@@ -114,15 +119,15 @@ export const createLogsSlice = (
|
|
114
119
|
const state = get();
|
115
120
|
const refreshedLogs = await state.logsActions.loadLogs();
|
116
121
|
|
117
|
-
// Set the logs first
|
118
|
-
state.logsActions.setLogs(refreshedLogs || { log_dir: "", files: [] });
|
119
|
-
|
120
122
|
// Preserve the selected log even if new logs appear
|
121
123
|
const currentLog =
|
122
|
-
|
124
|
+
state.logs.logs.files[
|
123
125
|
state.logs.selectedLogIndex > -1 ? state.logs.selectedLogIndex : 0
|
124
126
|
];
|
125
127
|
|
128
|
+
// Set the logs first
|
129
|
+
state.logsActions.setLogs(refreshedLogs || kEmptyLogs);
|
130
|
+
|
126
131
|
if (currentLog) {
|
127
132
|
const newIndex = refreshedLogs?.files.findIndex((file) =>
|
128
133
|
currentLog.name.endsWith(file.name),
|
@@ -136,7 +141,7 @@ export const createLogsSlice = (
|
|
136
141
|
// Select a specific log file
|
137
142
|
selectLogFile: async (logUrl: string) => {
|
138
143
|
const state = get();
|
139
|
-
const index = state.logs.logs.files.findIndex((val) =>
|
144
|
+
const index = state.logs.logs.files.findIndex((val: { name: string }) =>
|
140
145
|
val.name.endsWith(logUrl),
|
141
146
|
);
|
142
147
|
|
@@ -147,10 +152,10 @@ export const createLogsSlice = (
|
|
147
152
|
// It isn't yet loaded, so refresh the logs and try to load it from there
|
148
153
|
const result = await state.logsActions.loadLogs();
|
149
154
|
const idx = result?.files.findIndex((file) =>
|
150
|
-
|
155
|
+
file.name.endsWith(logUrl),
|
151
156
|
);
|
152
157
|
|
153
|
-
state.logsActions.setLogs(result ||
|
158
|
+
state.logsActions.setLogs(result || kEmptyLogs);
|
154
159
|
state.logsActions.setSelectedLogIndex(
|
155
160
|
idx !== undefined && idx > -1 ? idx : 0,
|
156
161
|
);
|
@@ -1,10 +1,10 @@
|
|
1
|
+
import { Event } from "../app/types";
|
1
2
|
import {
|
2
3
|
AttachmentData,
|
3
4
|
EventData,
|
4
5
|
SampleData,
|
5
6
|
SampleSummary,
|
6
|
-
} from "../api/types";
|
7
|
-
import { Event } from "../types";
|
7
|
+
} from "../client/api/types";
|
8
8
|
import { resolveAttachments } from "../utils/attachments";
|
9
9
|
import { createLogger } from "../utils/logger";
|
10
10
|
import { createPolling } from "../utils/polling";
|
@@ -98,6 +98,7 @@ export function createSamplePolling(
|
|
98
98
|
}
|
99
99
|
|
100
100
|
// Fetch sample data
|
101
|
+
state.sampleActions.setSampleStatus("streaming");
|
101
102
|
const eventId = pollingState.eventId;
|
102
103
|
const attachmentId = pollingState.attachmentId;
|
103
104
|
const sampleDataResponse = await api.get_log_sample_data(
|
@@ -123,10 +124,7 @@ export function createSamplePolling(
|
|
123
124
|
|
124
125
|
// Also fetch a fresh sample and clear the runnning Events
|
125
126
|
// (if there were ever running events)
|
126
|
-
if (
|
127
|
-
state.sample.runningEvents.length > 0 ||
|
128
|
-
state.sample.sampleStatus === "streaming"
|
129
|
-
) {
|
127
|
+
if (state.sample.runningEvents.length > 0) {
|
130
128
|
try {
|
131
129
|
log.debug(
|
132
130
|
`LOADING COMPLETED SAMPLE AFTER FLUSH: ${summary.id}-${summary.epoch}`,
|
@@ -143,12 +141,12 @@ export function createSamplePolling(
|
|
143
141
|
// Update the store with the completed sample
|
144
142
|
set((state) => {
|
145
143
|
state.sample.selectedSample = migratedSample;
|
146
|
-
state.
|
144
|
+
state.sampleActions.setSampleStatus("ok");
|
147
145
|
state.sample.runningEvents = [];
|
148
146
|
});
|
149
147
|
} else {
|
150
148
|
set((state) => {
|
151
|
-
state.
|
149
|
+
state.sampleActions.setSampleStatus("error");
|
152
150
|
state.sample.sampleError = new Error(
|
153
151
|
"Unable to load sample - an unknown error occurred",
|
154
152
|
);
|
@@ -158,12 +156,14 @@ export function createSamplePolling(
|
|
158
156
|
} catch (e) {
|
159
157
|
set((state) => {
|
160
158
|
state.sample.sampleError = e as Error;
|
161
|
-
state.
|
159
|
+
state.sampleActions.setSampleStatus("error");
|
162
160
|
state.sample.runningEvents = [];
|
163
161
|
});
|
164
162
|
}
|
163
|
+
} else {
|
164
|
+
state.sampleActions.setSampleStatus("ok");
|
165
|
+
state.sample.runningEvents = [];
|
165
166
|
}
|
166
|
-
|
167
167
|
return false;
|
168
168
|
}
|
169
169
|
|
@@ -251,8 +251,8 @@ export function createSamplePolling(
|
|
251
251
|
}
|
252
252
|
|
253
253
|
const resetPollingState = (state: PollingState) => {
|
254
|
-
state.eventId =
|
255
|
-
state.attachmentId =
|
254
|
+
state.eventId = kNoId;
|
255
|
+
state.attachmentId = kNoId;
|
256
256
|
state.eventMapping = {};
|
257
257
|
state.attachments = {};
|
258
258
|
state.events = [];
|
@@ -1,7 +1,7 @@
|
|
1
|
-
import {
|
1
|
+
import { EvalSample } from "../@types/log";
|
2
|
+
import { SampleState, SampleStatus } from "../app/types";
|
3
|
+
import { SampleSummary } from "../client/api/types";
|
2
4
|
import { kSampleMessagesTabId } from "../constants";
|
3
|
-
import { SampleState, SampleStatus } from "../types";
|
4
|
-
import { EvalSample } from "../types/log";
|
5
5
|
import { createLogger } from "../utils/logger";
|
6
6
|
import { createSamplePolling } from "./samplePolling";
|
7
7
|
import { resolveSample } from "./sampleUtils"; // Import the shared utility
|
@@ -78,7 +78,6 @@ export const createSampleSlice = (
|
|
78
78
|
const state = get();
|
79
79
|
if (state.log.loadedLog && state.sample.selectedSample) {
|
80
80
|
samplePolling.startPolling(logFile, sampleSummary);
|
81
|
-
state.sampleActions.setSampleStatus("streaming");
|
82
81
|
}
|
83
82
|
},
|
84
83
|
loadSample: async (logFile: string, sampleSummary: SampleSummary) => {
|
@@ -113,7 +112,6 @@ export const createSampleSlice = (
|
|
113
112
|
|
114
113
|
// Poll running sample
|
115
114
|
samplePolling.startPolling(logFile, sampleSummary);
|
116
|
-
sampleActions.setSampleStatus("streaming");
|
117
115
|
}
|
118
116
|
} catch (e) {
|
119
117
|
sampleActions.setSampleError(e as Error);
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import {
|
2
|
-
import {
|
1
|
+
import { EvalResults } from "../@types/log";
|
2
|
+
import { EvalSummary, SampleSummary } from "../client/api/types";
|
3
3
|
|
4
4
|
export interface ScorerInfo {
|
5
5
|
name: string;
|
@@ -1,8 +1,9 @@
|
|
1
1
|
import { create, StoreApi, UseBoundStore } from "zustand";
|
2
2
|
import { devtools, persist } from "zustand/middleware";
|
3
3
|
import { immer } from "zustand/middleware/immer";
|
4
|
-
import { Capabilities, ClientAPI, ClientStorage } from "../api/types";
|
4
|
+
import { Capabilities, ClientAPI, ClientStorage } from "../client/api/types";
|
5
5
|
import { createLogger } from "../utils/logger";
|
6
|
+
import { debounce } from "../utils/sync";
|
6
7
|
import { AppSlice, createAppSlice, initializeAppSlice } from "./appSlice";
|
7
8
|
import { createLogSlice, initalializeLogSlice, LogSlice } from "./logSlice";
|
8
9
|
import { createLogsSlice, initializeLogsSlice, LogsSlice } from "./logsSlice";
|
@@ -24,6 +25,9 @@ export interface StoreState extends AppSlice, LogsSlice, LogSlice, SampleSlice {
|
|
24
25
|
cleanup: () => void;
|
25
26
|
}
|
26
27
|
|
28
|
+
export let storeImplementation: UseBoundStore<StoreApi<StoreState>> | null =
|
29
|
+
null;
|
30
|
+
|
27
31
|
// The data that will actually be persisted
|
28
32
|
export type PersistedState = {
|
29
33
|
app: AppSlice["app"];
|
@@ -32,9 +36,6 @@ export type PersistedState = {
|
|
32
36
|
sample: SampleSlice["sample"];
|
33
37
|
};
|
34
38
|
|
35
|
-
// The store implementation (this will be set when the store is initialized)
|
36
|
-
let storeImplementation: UseBoundStore<StoreApi<StoreState>> | null = null;
|
37
|
-
|
38
39
|
// Create a proxy store that forwards calls to the real store once initialized
|
39
40
|
export const useStore = ((selector?: any) => {
|
40
41
|
if (!storeImplementation) {
|
@@ -56,11 +57,11 @@ export const initializeStore = (
|
|
56
57
|
getItem: <T>(name: string): T | null => {
|
57
58
|
return storage ? (storage.getItem(name) as T) : null;
|
58
59
|
},
|
59
|
-
setItem: <T>(name: string, value: T): void => {
|
60
|
+
setItem: debounce(<T>(name: string, value: T): void => {
|
60
61
|
if (storage) {
|
61
62
|
storage.setItem(name, value);
|
62
63
|
}
|
63
|
-
},
|
64
|
+
}, 1000),
|
64
65
|
removeItem: (name: string): void => {
|
65
66
|
if (storage) {
|
66
67
|
storage.removeItem(name);
|
@@ -139,11 +140,12 @@ export const initializeStore = (
|
|
139
140
|
storage: storageImplementation,
|
140
141
|
partialize: (state) => {
|
141
142
|
const persisted: PersistedState = filterState({
|
142
|
-
app: state.app,
|
143
|
+
app: { ...state.app, rehydrated: true },
|
143
144
|
log: state.log,
|
144
145
|
logs: state.logs,
|
145
146
|
sample: state.sample,
|
146
147
|
});
|
148
|
+
log.debug("PARTIALIZED STATE", persisted);
|
147
149
|
return persisted as unknown as StoreState;
|
148
150
|
},
|
149
151
|
version: 1,
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# Testing Structure
|
2
|
+
|
3
|
+
This directory contains the test files for the application. The test framework is built using Jest and TypeScript.
|
4
|
+
|
5
|
+
## Directory Structure
|
6
|
+
|
7
|
+
- `tests/`: Root directory for all tests
|
8
|
+
- `__mocks__/`: Mock files for CSS modules and other assets
|
9
|
+
- `setupTests.mjs`: Setup file for Jest tests
|
10
|
+
|
11
|
+
## Running Tests
|
12
|
+
|
13
|
+
To run the tests, use the following commands:
|
14
|
+
|
15
|
+
```bash
|
16
|
+
# Run all tests
|
17
|
+
yarn test
|
18
|
+
|
19
|
+
# Run tests in watch mode
|
20
|
+
yarn test:watch
|
21
|
+
|
22
|
+
# Run tests with coverage report
|
23
|
+
yarn test:coverage
|
24
|
+
```
|
25
|
+
|
26
|
+
## Test Philosophy
|
27
|
+
|
28
|
+
Tests are designed to verify functionality rather than implementation details. This means:
|
29
|
+
|
30
|
+
- Tests should not break due to minor changes in HTML structure
|
31
|
+
- Tests focus on the behavior of functions and components
|
32
|
+
- Tests should be fast and reliable
|
33
|
+
|
34
|
+
## Adding Tests
|
35
|
+
|
36
|
+
When adding new tests:
|
37
|
+
|
38
|
+
1. Create a new test file with the `.test.ts` or `.test.tsx` extension
|
39
|
+
2. Import the functions or components you want to test
|
40
|
+
3. Write tests that verify behavior without making assumptions about implementation
|
41
|
+
4. Use descriptive test names to make it clear what's being tested
|
42
|
+
|
43
|
+
## Mocking
|
44
|
+
|
45
|
+
For mocking external services or components:
|
46
|
+
|
47
|
+
1. Create mock files in the `__mocks__` directory
|
48
|
+
2. Mock only what's necessary for the test
|
49
|
+
3. Use Jest's mocking capabilities to replace dependencies
|
@@ -0,0 +1 @@
|
|
1
|
+
module.exports = "test-file-stub";
|
@@ -0,0 +1 @@
|
|
1
|
+
module.exports = {};
|
@@ -0,0 +1 @@
|
|
1
|
+
import "@testing-library/jest-dom";
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import { isBase64 } from "../../utils/base64";
|
2
|
+
|
3
|
+
describe("isBase64", () => {
|
4
|
+
test("identifies valid base64 strings", () => {
|
5
|
+
expect(isBase64("SGVsbG8gV29ybGQ=")).toBe(true); // "Hello World" in base64
|
6
|
+
expect(isBase64("dGVzdA==")).toBe(true); // "test" in base64
|
7
|
+
expect(isBase64("YWJjMTIzIT8kKiYoKSctPUB+")).toBe(true); // "abc123!?$*&()'-=@+" in base64
|
8
|
+
});
|
9
|
+
|
10
|
+
test("identifies invalid base64 strings", () => {
|
11
|
+
expect(isBase64("not-base64")).toBe(false);
|
12
|
+
expect(isBase64("SGVs bG8=")).toBe(false); // Contains space
|
13
|
+
expect(isBase64("Hello World")).toBe(false);
|
14
|
+
expect(isBase64("123")).toBe(false); // Too short for valid base64
|
15
|
+
});
|
16
|
+
|
17
|
+
test("handles edge cases", () => {
|
18
|
+
expect(isBase64("")).toBe(true); // Empty string is technically valid base64
|
19
|
+
expect(isBase64("YQ==")).toBe(true); // Single character 'a'
|
20
|
+
expect(isBase64("YWI=")).toBe(true); // Two characters 'ab'
|
21
|
+
expect(isBase64("YWJj")).toBe(true); // Three characters 'abc'
|
22
|
+
});
|
23
|
+
});
|
@@ -0,0 +1,127 @@
|
|
1
|
+
import {
|
2
|
+
arrayToString,
|
3
|
+
formatTime,
|
4
|
+
formatPrettyDecimal,
|
5
|
+
formatDecimalNoTrailingZeroes,
|
6
|
+
toTitleCase,
|
7
|
+
formatNoDecimal,
|
8
|
+
formatDuration,
|
9
|
+
} from "../../utils/format";
|
10
|
+
|
11
|
+
describe("arrayToString", () => {
|
12
|
+
test("converts array to comma-separated string", () => {
|
13
|
+
expect(arrayToString(["one", "two", "three"])).toBe("one, two, three");
|
14
|
+
});
|
15
|
+
|
16
|
+
test("handles single string input", () => {
|
17
|
+
expect(arrayToString("single")).toBe("single");
|
18
|
+
});
|
19
|
+
|
20
|
+
test("handles empty array", () => {
|
21
|
+
expect(arrayToString([])).toBe("");
|
22
|
+
});
|
23
|
+
});
|
24
|
+
|
25
|
+
describe("formatTime", () => {
|
26
|
+
test("formats seconds when less than a minute", () => {
|
27
|
+
expect(formatTime(45)).toBe("45.0 sec");
|
28
|
+
});
|
29
|
+
|
30
|
+
test("formats minutes and seconds", () => {
|
31
|
+
expect(formatTime(125)).toBe("2 min 5 sec");
|
32
|
+
});
|
33
|
+
|
34
|
+
test("formats hours, minutes, and seconds", () => {
|
35
|
+
expect(formatTime(3665)).toBe("1 hr 1 min 5 sec");
|
36
|
+
});
|
37
|
+
|
38
|
+
test("formats days, hours, minutes, and seconds", () => {
|
39
|
+
expect(formatTime(90061)).toBe("1 days 1 hr 1 min 1 sec");
|
40
|
+
});
|
41
|
+
});
|
42
|
+
|
43
|
+
describe("formatPrettyDecimal", () => {
|
44
|
+
test("adds one decimal place to whole numbers", () => {
|
45
|
+
expect(formatPrettyDecimal(5)).toBe("5.0");
|
46
|
+
});
|
47
|
+
|
48
|
+
test("keeps decimal places if less than maxDecimals", () => {
|
49
|
+
expect(formatPrettyDecimal(5.12, 3)).toBe("5.12");
|
50
|
+
});
|
51
|
+
|
52
|
+
test("truncates decimal places if more than maxDecimals", () => {
|
53
|
+
expect(formatPrettyDecimal(5.12345, 2)).toBe("5.12");
|
54
|
+
});
|
55
|
+
|
56
|
+
test("uses default maxDecimals if not provided", () => {
|
57
|
+
expect(formatPrettyDecimal(5.12345)).toBe("5.123");
|
58
|
+
});
|
59
|
+
});
|
60
|
+
|
61
|
+
describe("formatDecimalNoTrailingZeroes", () => {
|
62
|
+
test("removes trailing zeroes from decimal", () => {
|
63
|
+
expect(formatDecimalNoTrailingZeroes(5.1)).toBe("5.1");
|
64
|
+
});
|
65
|
+
|
66
|
+
test("keeps whole numbers as is", () => {
|
67
|
+
expect(formatDecimalNoTrailingZeroes(5)).toBe("5");
|
68
|
+
});
|
69
|
+
|
70
|
+
test("handles non-numbers (should be fixed later)", () => {
|
71
|
+
const nonNumber = "test" as unknown as number;
|
72
|
+
expect(formatDecimalNoTrailingZeroes(nonNumber)).toBe(nonNumber);
|
73
|
+
});
|
74
|
+
});
|
75
|
+
|
76
|
+
describe("toTitleCase", () => {
|
77
|
+
test("converts string to title case", () => {
|
78
|
+
expect(toTitleCase("hello world")).toBe("Hello World");
|
79
|
+
});
|
80
|
+
|
81
|
+
test("handles uppercase strings", () => {
|
82
|
+
expect(toTitleCase("HELLO WORLD")).toBe("Hello World");
|
83
|
+
});
|
84
|
+
|
85
|
+
test("handles mixed case strings", () => {
|
86
|
+
expect(toTitleCase("hElLo WoRlD")).toBe("Hello World");
|
87
|
+
});
|
88
|
+
|
89
|
+
test("handles empty string", () => {
|
90
|
+
expect(toTitleCase("")).toBe("");
|
91
|
+
});
|
92
|
+
});
|
93
|
+
|
94
|
+
describe("formatNoDecimal", () => {
|
95
|
+
test("rounds number to whole number", () => {
|
96
|
+
expect(formatNoDecimal(5.6)).toBe("6");
|
97
|
+
});
|
98
|
+
|
99
|
+
test("handles integer values", () => {
|
100
|
+
expect(formatNoDecimal(5)).toBe("5");
|
101
|
+
});
|
102
|
+
|
103
|
+
test("handles non-numbers (should be fixed later)", () => {
|
104
|
+
const nonNumber = "test" as unknown as number;
|
105
|
+
expect(formatNoDecimal(nonNumber)).toBe(nonNumber);
|
106
|
+
});
|
107
|
+
});
|
108
|
+
|
109
|
+
describe("formatDuration", () => {
|
110
|
+
test("formats duration between two dates", () => {
|
111
|
+
const start = new Date("2023-01-01T00:00:00Z");
|
112
|
+
const end = new Date("2023-01-01T00:01:30Z");
|
113
|
+
expect(formatDuration(start, end)).toBe("1 min 30 sec");
|
114
|
+
});
|
115
|
+
|
116
|
+
test("handles short durations", () => {
|
117
|
+
const start = new Date("2023-01-01T00:00:00Z");
|
118
|
+
const end = new Date("2023-01-01T00:00:10Z");
|
119
|
+
expect(formatDuration(start, end)).toBe("10.0 sec");
|
120
|
+
});
|
121
|
+
|
122
|
+
test("handles long durations", () => {
|
123
|
+
const start = new Date("2023-01-01T00:00:00Z");
|
124
|
+
const end = new Date("2023-01-02T01:01:01Z");
|
125
|
+
expect(formatDuration(start, end)).toBe("1 days 1 hr 1 min 1 sec");
|
126
|
+
});
|
127
|
+
});
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import { filename, dirname } from "../../utils/path";
|
2
|
+
|
3
|
+
describe("filename", () => {
|
4
|
+
test("extracts filename without extension from a path", () => {
|
5
|
+
expect(filename("/path/to/file.txt")).toBe("file");
|
6
|
+
expect(filename("file.txt")).toBe("file");
|
7
|
+
expect(filename("/path/to/document.pdf")).toBe("document");
|
8
|
+
});
|
9
|
+
|
10
|
+
test("handles paths without extensions", () => {
|
11
|
+
expect(filename("/path/to/file")).toBe("/path/to/file");
|
12
|
+
expect(filename("file")).toBe("file");
|
13
|
+
});
|
14
|
+
|
15
|
+
test("handles paths with multiple dots", () => {
|
16
|
+
expect(filename("/path/to/file.name.txt")).toBe("file.name");
|
17
|
+
expect(filename("archive.tar.gz")).toBe("archive.tar");
|
18
|
+
});
|
19
|
+
|
20
|
+
test("handles edge cases", () => {
|
21
|
+
expect(filename("")).toBe("");
|
22
|
+
expect(filename(".")).toBe(".");
|
23
|
+
// Special case for .hidden files - there's no extension to remove
|
24
|
+
expect(filename(".hidden")).toBe(".hidden");
|
25
|
+
// Dot files with extensions should have the extension removed
|
26
|
+
expect(filename(".hidden.txt")).toBe(".hidden");
|
27
|
+
});
|
28
|
+
});
|
29
|
+
|
30
|
+
describe("dirname", () => {
|
31
|
+
test("extracts directory name from a path", () => {
|
32
|
+
expect(dirname("/path/to/file.txt")).toBe("/path/to");
|
33
|
+
expect(dirname("/path/to/directory/")).toBe("/path/to/directory");
|
34
|
+
expect(dirname("/path/to/file")).toBe("/path/to");
|
35
|
+
});
|
36
|
+
|
37
|
+
test("handles paths without directories", () => {
|
38
|
+
expect(dirname("file.txt")).toBe("");
|
39
|
+
expect(dirname("file")).toBe("");
|
40
|
+
});
|
41
|
+
|
42
|
+
test("handles root directory", () => {
|
43
|
+
expect(dirname("/file.txt")).toBe("");
|
44
|
+
expect(dirname("/file")).toBe("");
|
45
|
+
});
|
46
|
+
|
47
|
+
test("handles empty input", () => {
|
48
|
+
expect(dirname("")).toBe("");
|
49
|
+
});
|
50
|
+
|
51
|
+
test("handles paths with trailing slash", () => {
|
52
|
+
expect(dirname("/path/to/directory/")).toBe("/path/to/directory");
|
53
|
+
});
|
54
|
+
});
|
@@ -3,7 +3,7 @@ import {
|
|
3
3
|
ChatMessageSystem,
|
4
4
|
ChatMessageTool,
|
5
5
|
ChatMessageUser,
|
6
|
-
} from "
|
6
|
+
} from "../@types/log";
|
7
7
|
|
8
8
|
/**
|
9
9
|
* Converts an array or a single value to a comma-separated string.
|
@@ -137,9 +137,15 @@ export function formatDecimalNoTrailingZeroes(num: number): string {
|
|
137
137
|
* Converts a string to title case.
|
138
138
|
*/
|
139
139
|
export function toTitleCase(str: string): string {
|
140
|
+
if (!str) {
|
141
|
+
return str;
|
142
|
+
}
|
143
|
+
|
140
144
|
return str
|
141
145
|
.split(" ")
|
142
|
-
.map((w) =>
|
146
|
+
.map((w) =>
|
147
|
+
w.length > 0 ? w[0].toUpperCase() + w.substr(1).toLowerCase() : w,
|
148
|
+
)
|
143
149
|
.join(" ");
|
144
150
|
}
|
145
151
|
|
@@ -2,8 +2,18 @@
|
|
2
2
|
* Extracts the filename without extension from a given path.
|
3
3
|
*/
|
4
4
|
export const filename = (path: string): string => {
|
5
|
+
if (!path) {
|
6
|
+
return "";
|
7
|
+
}
|
8
|
+
|
5
9
|
const pathparts = path.split("/");
|
6
10
|
const basename = pathparts.slice(-1)[0];
|
11
|
+
|
12
|
+
// Special case for .hidden files
|
13
|
+
if (basename.startsWith(".") && !basename.substring(1).includes(".")) {
|
14
|
+
return basename;
|
15
|
+
}
|
16
|
+
|
7
17
|
const match = basename.match(/(.*)\.\S+$/);
|
8
18
|
if (match) {
|
9
19
|
return match[1];
|
@@ -21,8 +31,10 @@ export const dirname = (path: string): string => {
|
|
21
31
|
// If the path ends with a filename (or no slashes), remove the last part (filename)
|
22
32
|
if (pathparts.length > 1) {
|
23
33
|
pathparts.pop();
|
34
|
+
// Join the remaining parts to form the directory path
|
35
|
+
return pathparts.join("/");
|
24
36
|
}
|
25
37
|
|
26
|
-
//
|
27
|
-
return
|
38
|
+
// If no slashes, return empty string (no directory)
|
39
|
+
return "";
|
28
40
|
};
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import { Timeout } from "../types/log";
|
2
1
|
import { createLogger } from "./logger";
|
3
2
|
|
4
3
|
export interface PollingOptions {
|
@@ -20,7 +19,7 @@ export const createPolling = (
|
|
20
19
|
const log = createLogger(`Polling ${name}`);
|
21
20
|
|
22
21
|
const { maxRetries, interval } = options;
|
23
|
-
let timeoutId:
|
22
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
24
23
|
let retryCount = 0;
|
25
24
|
// Are we currently polling
|
26
25
|
let isPolling = false;
|
@@ -0,0 +1,32 @@
|
|
1
|
+
export const directoryRelativeUrl = (file: string, dir?: string): string => {
|
2
|
+
if (!dir) {
|
3
|
+
return encodeURIComponent(file);
|
4
|
+
}
|
5
|
+
|
6
|
+
// Normalize paths to ensure consistent directory separators
|
7
|
+
const normalizedFile = file.replace(/\\/g, "/");
|
8
|
+
const normalizedLogDir = dir.replace(/\\/g, "/");
|
9
|
+
|
10
|
+
// Ensure log_dir ends with a trailing slash
|
11
|
+
const dirWithSlash = normalizedLogDir.endsWith("/")
|
12
|
+
? normalizedLogDir
|
13
|
+
: normalizedLogDir + "/";
|
14
|
+
|
15
|
+
// Check if file is within the log directory
|
16
|
+
if (normalizedFile.startsWith(dirWithSlash)) {
|
17
|
+
// Get the relative path
|
18
|
+
const relativePath = normalizedFile.substring(dirWithSlash.length);
|
19
|
+
|
20
|
+
// Split the path into segments and encode each segment
|
21
|
+
const segments = relativePath.split("/");
|
22
|
+
const encodedSegments = segments.map((segment) =>
|
23
|
+
encodeURIComponent(segment),
|
24
|
+
);
|
25
|
+
|
26
|
+
// Join the encoded segments back together
|
27
|
+
return encodedSegments.join("/");
|
28
|
+
}
|
29
|
+
|
30
|
+
// If path can't be made relative, return undefined
|
31
|
+
return encodeURIComponent(file);
|
32
|
+
};
|