inspect-ai 0.3.103__py3-none-any.whl → 0.3.105__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.
Files changed (134) hide show
  1. inspect_ai/_cli/common.py +2 -1
  2. inspect_ai/_cli/eval.py +2 -2
  3. inspect_ai/_display/core/active.py +3 -0
  4. inspect_ai/_display/core/config.py +1 -0
  5. inspect_ai/_display/core/panel.py +21 -13
  6. inspect_ai/_display/core/results.py +3 -7
  7. inspect_ai/_display/core/rich.py +3 -5
  8. inspect_ai/_display/log/__init__.py +0 -0
  9. inspect_ai/_display/log/display.py +173 -0
  10. inspect_ai/_display/plain/display.py +2 -2
  11. inspect_ai/_display/rich/display.py +2 -4
  12. inspect_ai/_display/textual/app.py +1 -6
  13. inspect_ai/_display/textual/widgets/task_detail.py +3 -14
  14. inspect_ai/_display/textual/widgets/tasks.py +1 -1
  15. inspect_ai/_eval/eval.py +1 -1
  16. inspect_ai/_eval/evalset.py +3 -3
  17. inspect_ai/_eval/registry.py +6 -1
  18. inspect_ai/_eval/run.py +5 -1
  19. inspect_ai/_eval/task/constants.py +1 -0
  20. inspect_ai/_eval/task/log.py +2 -0
  21. inspect_ai/_eval/task/run.py +65 -39
  22. inspect_ai/_util/citation.py +88 -0
  23. inspect_ai/_util/content.py +24 -2
  24. inspect_ai/_util/json.py +17 -2
  25. inspect_ai/_util/registry.py +19 -4
  26. inspect_ai/_view/schema.py +0 -6
  27. inspect_ai/_view/server.py +17 -0
  28. inspect_ai/_view/www/dist/assets/index.css +93 -31
  29. inspect_ai/_view/www/dist/assets/index.js +10639 -10011
  30. inspect_ai/_view/www/log-schema.json +418 -1
  31. inspect_ai/_view/www/node_modules/flatted/python/flatted.py +149 -0
  32. inspect_ai/_view/www/node_modules/katex/src/fonts/generate_fonts.py +58 -0
  33. inspect_ai/_view/www/node_modules/katex/src/metrics/extract_tfms.py +114 -0
  34. inspect_ai/_view/www/node_modules/katex/src/metrics/extract_ttfs.py +122 -0
  35. inspect_ai/_view/www/node_modules/katex/src/metrics/format_json.py +28 -0
  36. inspect_ai/_view/www/node_modules/katex/src/metrics/parse_tfm.py +211 -0
  37. inspect_ai/_view/www/package.json +2 -2
  38. inspect_ai/_view/www/src/@types/log.d.ts +140 -39
  39. inspect_ai/_view/www/src/app/content/RecordTree.tsx +13 -0
  40. inspect_ai/_view/www/src/app/log-view/LogView.tsx +1 -1
  41. inspect_ai/_view/www/src/app/routing/logNavigation.ts +31 -0
  42. inspect_ai/_view/www/src/app/routing/{navigationHooks.ts → sampleNavigation.ts} +39 -86
  43. inspect_ai/_view/www/src/app/samples/SampleDialog.tsx +1 -1
  44. inspect_ai/_view/www/src/app/samples/SampleDisplay.tsx +1 -1
  45. inspect_ai/_view/www/src/app/samples/chat/ChatMessage.module.css +4 -0
  46. inspect_ai/_view/www/src/app/samples/chat/ChatMessage.tsx +17 -0
  47. inspect_ai/_view/www/src/app/samples/chat/MessageCitations.module.css +16 -0
  48. inspect_ai/_view/www/src/app/samples/chat/MessageCitations.tsx +63 -0
  49. inspect_ai/_view/www/src/app/samples/chat/MessageContent.module.css +6 -0
  50. inspect_ai/_view/www/src/app/samples/chat/MessageContent.tsx +174 -25
  51. inspect_ai/_view/www/src/app/samples/chat/MessageContents.tsx +21 -3
  52. inspect_ai/_view/www/src/app/samples/chat/content-data/ContentDataView.module.css +7 -0
  53. inspect_ai/_view/www/src/app/samples/chat/content-data/ContentDataView.tsx +111 -0
  54. inspect_ai/_view/www/src/app/samples/chat/content-data/WebSearch.module.css +10 -0
  55. inspect_ai/_view/www/src/app/samples/chat/content-data/WebSearch.tsx +14 -0
  56. inspect_ai/_view/www/src/app/samples/chat/content-data/WebSearchResults.module.css +19 -0
  57. inspect_ai/_view/www/src/app/samples/chat/content-data/WebSearchResults.tsx +49 -0
  58. inspect_ai/_view/www/src/app/samples/chat/messages.ts +7 -1
  59. inspect_ai/_view/www/src/app/samples/chat/tools/ToolCallView.tsx +12 -2
  60. inspect_ai/_view/www/src/app/samples/chat/types.ts +4 -0
  61. inspect_ai/_view/www/src/app/samples/list/SampleList.tsx +1 -1
  62. inspect_ai/_view/www/src/app/samples/sample-tools/filters.ts +26 -0
  63. inspect_ai/_view/www/src/app/samples/sample-tools/sample-filter/SampleFilter.tsx +14 -3
  64. inspect_ai/_view/www/src/app/samples/sample-tools/sample-filter/completions.ts +359 -7
  65. inspect_ai/_view/www/src/app/samples/sample-tools/sample-filter/language.ts +6 -0
  66. inspect_ai/_view/www/src/app/samples/sampleLimit.ts +2 -2
  67. inspect_ai/_view/www/src/app/samples/transcript/ModelEventView.tsx +1 -1
  68. inspect_ai/_view/www/src/app/samples/transcript/SampleLimitEventView.tsx +4 -4
  69. inspect_ai/_view/www/src/app/samples/transcript/outline/OutlineRow.tsx +1 -1
  70. inspect_ai/_view/www/src/app/samples/transcript/outline/TranscriptOutline.tsx +1 -1
  71. inspect_ai/_view/www/src/client/api/api-browser.ts +25 -0
  72. inspect_ai/_view/www/src/client/api/api-http.ts +3 -0
  73. inspect_ai/_view/www/src/client/api/api-vscode.ts +6 -0
  74. inspect_ai/_view/www/src/client/api/client-api.ts +3 -0
  75. inspect_ai/_view/www/src/client/api/jsonrpc.ts +1 -0
  76. inspect_ai/_view/www/src/client/api/types.ts +3 -0
  77. inspect_ai/_view/www/src/components/MarkdownDiv.tsx +15 -2
  78. inspect_ai/_view/www/src/state/samplePolling.ts +17 -1
  79. inspect_ai/_view/www/src/tests/README.md +2 -2
  80. inspect_ai/_view/www/src/utils/git.ts +3 -1
  81. inspect_ai/_view/www/src/utils/html.ts +6 -0
  82. inspect_ai/agent/_handoff.py +8 -5
  83. inspect_ai/agent/_react.py +5 -5
  84. inspect_ai/dataset/_dataset.py +1 -1
  85. inspect_ai/log/_condense.py +5 -0
  86. inspect_ai/log/_file.py +4 -1
  87. inspect_ai/log/_log.py +9 -4
  88. inspect_ai/log/_recorders/json.py +4 -2
  89. inspect_ai/log/_samples.py +5 -0
  90. inspect_ai/log/_util.py +2 -0
  91. inspect_ai/model/__init__.py +14 -0
  92. inspect_ai/model/_call_tools.py +17 -8
  93. inspect_ai/model/_chat_message.py +3 -0
  94. inspect_ai/model/_openai_responses.py +80 -34
  95. inspect_ai/model/_providers/_anthropic_citations.py +158 -0
  96. inspect_ai/model/_providers/_google_citations.py +100 -0
  97. inspect_ai/model/_providers/anthropic.py +219 -36
  98. inspect_ai/model/_providers/google.py +98 -22
  99. inspect_ai/model/_providers/mistral.py +20 -7
  100. inspect_ai/model/_providers/openai.py +11 -10
  101. inspect_ai/model/_providers/openai_compatible.py +3 -2
  102. inspect_ai/model/_providers/openai_responses.py +2 -5
  103. inspect_ai/model/_providers/perplexity.py +123 -0
  104. inspect_ai/model/_providers/providers.py +13 -2
  105. inspect_ai/model/_providers/vertex.py +3 -0
  106. inspect_ai/model/_trim.py +5 -0
  107. inspect_ai/tool/__init__.py +14 -0
  108. inspect_ai/tool/_mcp/_mcp.py +5 -2
  109. inspect_ai/tool/_mcp/sampling.py +19 -3
  110. inspect_ai/tool/_mcp/server.py +1 -1
  111. inspect_ai/tool/_tool.py +10 -1
  112. inspect_ai/tool/_tools/_web_search/_base_http_provider.py +104 -0
  113. inspect_ai/tool/_tools/_web_search/_exa.py +78 -0
  114. inspect_ai/tool/_tools/_web_search/_google.py +22 -25
  115. inspect_ai/tool/_tools/_web_search/_tavily.py +47 -65
  116. inspect_ai/tool/_tools/_web_search/_web_search.py +83 -36
  117. inspect_ai/tool/_tools/_web_search/_web_search_provider.py +7 -0
  118. inspect_ai/util/__init__.py +8 -0
  119. inspect_ai/util/_background.py +64 -0
  120. inspect_ai/util/_display.py +11 -2
  121. inspect_ai/util/_limit.py +72 -5
  122. inspect_ai/util/_sandbox/__init__.py +2 -0
  123. inspect_ai/util/_sandbox/docker/compose.py +2 -2
  124. inspect_ai/util/_sandbox/service.py +28 -7
  125. inspect_ai/util/_span.py +12 -1
  126. inspect_ai/util/_subprocess.py +51 -38
  127. {inspect_ai-0.3.103.dist-info → inspect_ai-0.3.105.dist-info}/METADATA +2 -2
  128. {inspect_ai-0.3.103.dist-info → inspect_ai-0.3.105.dist-info}/RECORD +134 -109
  129. /inspect_ai/model/{_openai_computer_use.py → _providers/_openai_computer_use.py} +0 -0
  130. /inspect_ai/model/{_openai_web_search.py → _providers/_openai_web_search.py} +0 -0
  131. {inspect_ai-0.3.103.dist-info → inspect_ai-0.3.105.dist-info}/WHEEL +0 -0
  132. {inspect_ai-0.3.103.dist-info → inspect_ai-0.3.105.dist-info}/entry_points.txt +0 -0
  133. {inspect_ai-0.3.103.dist-info → inspect_ai-0.3.105.dist-info}/licenses/LICENSE +0 -0
  134. {inspect_ai-0.3.103.dist-info → inspect_ai-0.3.105.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,158 @@
1
+ from anthropic.types import (
2
+ CitationCharLocation,
3
+ CitationCharLocationParam,
4
+ CitationContentBlockLocation,
5
+ CitationContentBlockLocationParam,
6
+ CitationPageLocation,
7
+ CitationPageLocationParam,
8
+ CitationsWebSearchResultLocation,
9
+ CitationWebSearchResultLocationParam,
10
+ TextCitation,
11
+ TextCitationParam,
12
+ )
13
+
14
+ from inspect_ai._util.citation import (
15
+ Citation,
16
+ DocumentCitation,
17
+ DocumentRange,
18
+ UrlCitation,
19
+ )
20
+
21
+
22
+ def to_inspect_citation(input: TextCitation) -> Citation:
23
+ match input:
24
+ case CitationsWebSearchResultLocation(
25
+ cited_text=cited_text,
26
+ title=title,
27
+ url=url,
28
+ encrypted_index=encrypted_index,
29
+ ):
30
+ # Sanitize a citation to work around https://github.com/anthropics/anthropic-sdk-python/issues/965.
31
+ return UrlCitation(
32
+ cited_text=cited_text,
33
+ title=title
34
+ if title is None or len(title) <= 255
35
+ else title[:254] + "…",
36
+ url=url,
37
+ internal={"encrypted_index": encrypted_index},
38
+ )
39
+
40
+ case CitationCharLocation(
41
+ cited_text=cited_text,
42
+ document_index=document_index,
43
+ document_title=title,
44
+ end_char_index=end_char_index,
45
+ start_char_index=start_char_index,
46
+ ):
47
+ return DocumentCitation(
48
+ cited_text=cited_text,
49
+ title=title,
50
+ range=DocumentRange(
51
+ type="char", start_index=start_char_index, end_index=end_char_index
52
+ ),
53
+ internal={"document_index": document_index},
54
+ )
55
+
56
+ case CitationContentBlockLocation(
57
+ cited_text=cited_text,
58
+ document_index=document_index,
59
+ document_title=title,
60
+ end_block_index=end_block_index,
61
+ start_block_index=start_block_index,
62
+ ):
63
+ return DocumentCitation(
64
+ cited_text=cited_text,
65
+ title=title,
66
+ range=DocumentRange(
67
+ type="block",
68
+ start_index=start_block_index,
69
+ end_index=end_block_index,
70
+ ),
71
+ internal={"document_index": document_index},
72
+ )
73
+
74
+ case CitationPageLocation(
75
+ cited_text=cited_text,
76
+ document_index=document_index,
77
+ document_title=title,
78
+ end_page_number=end_page_number,
79
+ start_page_number=start_page_number,
80
+ ):
81
+ return DocumentCitation(
82
+ cited_text=cited_text,
83
+ title=title,
84
+ range=DocumentRange(
85
+ type="page",
86
+ start_index=start_page_number - 1,
87
+ end_index=end_page_number - 1,
88
+ ),
89
+ internal={"document_index": document_index},
90
+ )
91
+
92
+ assert False, f"Unexpected citation type: {input.type}"
93
+
94
+
95
+ def to_anthropic_citation(input: Citation) -> TextCitationParam:
96
+ cited_text = input.cited_text
97
+ assert isinstance(cited_text, str), (
98
+ "anthropic citations must have a string cited_text"
99
+ )
100
+
101
+ match input:
102
+ case UrlCitation(title=title, url=url, internal=internal):
103
+ assert internal, "UrlCitation must have internal field"
104
+ encrypted_index = internal.get("encrypted_index", None)
105
+ assert isinstance(encrypted_index, str), (
106
+ "URL citations require encrypted_index in internal field"
107
+ )
108
+
109
+ return CitationWebSearchResultLocationParam(
110
+ type="web_search_result_location",
111
+ cited_text=cited_text,
112
+ title=title,
113
+ url=url,
114
+ encrypted_index=encrypted_index,
115
+ )
116
+
117
+ case DocumentCitation(title=title, range=range, internal=internal):
118
+ assert internal, "DocumentCharCitation must have internal field"
119
+ document_index = internal.get("document_index", None)
120
+ assert isinstance(document_index, int), (
121
+ "DocumentCharCitation require encrypted_index in internal field"
122
+ )
123
+ assert range, "DocumentCitation must have a range"
124
+
125
+ start_index = range.start_index
126
+ end_index = range.end_index
127
+
128
+ match range.type:
129
+ case "char":
130
+ return CitationCharLocationParam(
131
+ type="char_location",
132
+ cited_text=cited_text,
133
+ document_title=title,
134
+ document_index=document_index,
135
+ start_char_index=start_index,
136
+ end_char_index=end_index,
137
+ )
138
+ case "block":
139
+ return CitationContentBlockLocationParam(
140
+ type="content_block_location",
141
+ cited_text=cited_text,
142
+ document_title=title,
143
+ document_index=document_index,
144
+ start_block_index=start_index,
145
+ end_block_index=end_index,
146
+ )
147
+ case "page":
148
+ return CitationPageLocationParam(
149
+ type="page_location",
150
+ cited_text=cited_text,
151
+ document_title=title,
152
+ document_index=document_index,
153
+ start_page_number=start_index + 1,
154
+ end_page_number=end_index + 1,
155
+ )
156
+
157
+ # If we can't handle this citation type, raise an error
158
+ raise ValueError(f"Unsupported citation type: {input.type}")
@@ -0,0 +1,100 @@
1
+ from typing import Sequence
2
+
3
+ from google.genai.types import (
4
+ Candidate,
5
+ GroundingChunk,
6
+ GroundingSupport,
7
+ Segment,
8
+ )
9
+
10
+ from inspect_ai._util.citation import Citation, UrlCitation
11
+
12
+
13
+ def get_candidate_citations(candidate: Candidate) -> list[Citation]:
14
+ """Extract citations from Google AI candidate grounding metadata.
15
+
16
+ Understanding Google API Grounding Citations: `GroundingChunk`'s, `GroundingSupport`, and `Segment`'s
17
+
18
+ 1. Grounding Chunks (`GroundingChunk`)
19
+ What they are: The raw source material that the AI retrieved to support its response.
20
+ Structure:
21
+ - Web (`GroundingChunkWeb`): Content from web searches
22
+ - domain: The website domain
23
+ - title: Page title
24
+ - uri: Web page URL
25
+ Think of chunks as: The library books or web pages that contain the information.
26
+
27
+ 2. Segments (`Segment`)
28
+ What they are: Specific portions of the AI's generated response text.
29
+ Structure:
30
+ - start_index & end_index: Byte positions in the response text
31
+ - text: The actual text from the response that this segment represents
32
+ Think of segments as: Specific sentences or paragraphs in the AI's response that need citations.
33
+
34
+ 3. Grounding Support (`GroundingSupport`)
35
+ What they are: The bridge that connects segments of the AI's response to the chunks that support them.
36
+ Structure:
37
+ - grounding_chunk_indices: Array of integers pointing to specific chunks (e.g., [1,3,4] means chunks 1, 3, and 4 support this claim)
38
+ - segment: Which part of the response this support applies to
39
+ Think of support as: The footnotes that say "this claim in my response is backed up by these specific sources."
40
+
41
+ Args:
42
+ candidate: The Google AI candidate response containing grounding metadata
43
+
44
+ Returns:
45
+ A list of `Citation` objects linking response segments to their web sources.
46
+ Currently only handles `GroundingChunkWeb` sources.
47
+ """
48
+ return (
49
+ []
50
+ if (
51
+ not candidate.content
52
+ or not candidate.content.parts
53
+ or not (metadata := candidate.grounding_metadata)
54
+ or not (chunks := metadata.grounding_chunks)
55
+ or not (supports := metadata.grounding_supports)
56
+ )
57
+ else [
58
+ citation
59
+ for support in supports
60
+ for citation in _citations_from_support(support, chunks)
61
+ ]
62
+ )
63
+
64
+
65
+ def _create_citation_from_chunk_and_segment(
66
+ chunk: GroundingChunk, segment: Segment
67
+ ) -> UrlCitation | None:
68
+ """Create a citation from a chunk and segment, returning None if chunk is not web-based."""
69
+ return (
70
+ UrlCitation(
71
+ url=chunk.web.uri,
72
+ title=chunk.web.title,
73
+ cited_text=(
74
+ (segment.start_index or 0, segment.end_index)
75
+ if segment.end_index is not None
76
+ else None
77
+ ),
78
+ )
79
+ if (chunk.web and chunk.web.uri)
80
+ else None
81
+ )
82
+
83
+
84
+ def _citations_from_support(
85
+ support: GroundingSupport, chunks: Sequence[GroundingChunk]
86
+ ) -> list[Citation]:
87
+ return (
88
+ []
89
+ if support.segment is None or support.grounding_chunk_indices is None
90
+ else [
91
+ citation
92
+ for chunk_index in support.grounding_chunk_indices
93
+ if chunk_index < len(chunks)
94
+ if (
95
+ citation := _create_citation_from_chunk_and_segment(
96
+ chunks[chunk_index], support.segment
97
+ )
98
+ )
99
+ ]
100
+ )